{"id":5798,"date":"2026-08-27T07:57:24","date_gmt":"2026-08-27T14:57:24","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/agent-framework\/?p=5798"},"modified":"2026-08-27T07:57:24","modified_gmt":"2026-08-27T14:57:24","slug":"agent-harness-making-your-claw-production-ready","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/agent-framework\/agent-harness-making-your-claw-production-ready\/","title":{"rendered":"Agent Harness: Making your claw production-ready"},"content":{"rendered":"<p><em>Part 4 of <a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/build-your-own-claw-and-agent-harness-with-microsoft-agent-framework\/\">Build your own claw and agent harness with Microsoft Agent Framework<\/a>.<\/em><\/p>\n<p>Over the last three parts our personal finance assistant grew from a single tool into a genuinely capable agent: it plans, reads your portfolio, asks before it trades, remembers what matters, loads skills on demand, reorganizes files with a shell, computes with CodeAct, and fans research out to background agents. It works on your machine. But &#8220;works on my machine&#8221; isn&#8217;t the same as <em>ready to run for other people<\/em>.<\/p>\n<p>This final part closes that gap along four axes:<\/p>\n<ol>\n<li><strong>Observability<\/strong> &#8211; see what the claw is doing with OpenTelemetry traces, token usage, and tool calls.<\/li>\n<li><strong>Governance<\/strong> &#8211; screen prompts and responses through <strong>Microsoft Purview<\/strong>, so data access is discoverable, classified, and auditable for a regulated finance context.<\/li>\n<li><strong>Deployment<\/strong> &#8211; host the claw as a <strong>Foundry Hosted Agent<\/strong>.<\/li>\n<li><strong>Evals<\/strong> &#8211; measure quality with local finance checks and hosted Foundry evals, then use the results to tune prompts, tools, and skills.<\/li>\n<\/ol>\n<p>To get there we make one structural change. Up to now each step was a single program. A production agent is usually <em>run more than one way<\/em> &#8211; interactively while you develop, hosted in the cloud for real use, and inside an eval harness in CI. So we split the claw into a <strong>shared agent factory<\/strong> plus three thin hosts that consume it: a <strong>console<\/strong>, a <strong>hosted<\/strong> service, and an <strong>evals<\/strong> runner. The agent is defined once; only the host around it changes.<\/p>\n<h2 id=\"define-once-the-shared-agent\">Define once: the shared agent<\/h2>\n<p>Everything that makes the claw <em>ours<\/em> &#8211; instructions, file access, the valuation and risk skills, memory, approvals, the shell, CodeAct, and the background research agent &#8211; now lives in one factory. Each host just calls it.<\/p>\n<p>In <strong>.NET<\/strong> the factory returns the built agent plus the resources the host should dispose:<\/p>\n<pre><code class=\"language-csharp\">await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions\r\n{\r\n    Log = Console.WriteLine,\r\n});\r\n\r\n\/\/ build.Agent is the same claw from Part 3 - skills, shell, CodeAct, background agents, approvals.<\/code><\/pre>\n<p>In <strong>Python<\/strong> it&#8217;s an <code>async<\/code> factory that returns an async context manager, so the shell and any MCP skills sessions are torn down cleanly:<\/p>\n<pre><code class=\"language-python\">agent = await build_claw_agent(credential=AzureCliCredential())\r\nasync with agent:\r\n    # \u2026 run the agent \u2026<\/code><\/pre>\n<p>With one definition feeding every host, observability, governance, deployment, and evals all apply to <em>the same agent<\/em> &#8211; not three subtly different copies.<\/p>\n<h2 id=\"see-what-its-doing-observability\">See what it&#8217;s doing: observability<\/h2>\n<p>An agent that reads files, runs code, and calls tools is a small distributed system. When something goes wrong &#8211; a skill misfires, a tool loops, a response costs 10x what you expected &#8211; you need to <em>see<\/em> it. The harness already emits OpenTelemetry spans, metrics, and logs for model calls, tool invocations, and token usage; you just wire up an exporter.<\/p>\n<p>The agent carries a single OpenTelemetry <strong>source name<\/strong> so hosts can subscribe to exactly its signals.<\/p>\n<p>In <strong>.NET<\/strong> that&#8217;s one option on the harness:<\/p>\n<pre><code class=\"language-csharp\">AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions\r\n{\r\n    OpenTelemetrySourceName = ClawAgentFactory.OpenTelemetrySourceName,\r\n    \/\/ \u2026 file access, skills, shell, CodeAct, background agents \u2026\r\n});<\/code><\/pre>\n<p>When <code>OTEL_EXPORTER_OTLP_ENDPOINT<\/code> is set, the <strong>console<\/strong> host stands up trace and metric providers pointed at that source and sends their telemetry to the configured OTLP collector:<\/p>\n<pre><code class=\"language-csharp\">var otlpEndpoint = Environment.GetEnvironmentVariable(\"OTEL_EXPORTER_OTLP_ENDPOINT\");\r\nvar telemetryEnabled = !string.IsNullOrWhiteSpace(otlpEndpoint);\r\n\r\nusing var tracerProvider = telemetryEnabled\r\n    ? Sdk.CreateTracerProviderBuilder()\r\n        .AddSource(ClawAgentFactory.OpenTelemetrySourceName)\r\n        .AddOtlpExporter(options =&gt; options.Endpoint = new Uri(otlpEndpoint!))\r\n        .Build()\r\n    : null;<\/code><\/pre>\n<p>In <strong>Python<\/strong>, instrumentation is on by default &#8211; a single call wires the providers from environment variables (OTLP endpoint, console exporters, sensitive-data capture):<\/p>\n<pre><code class=\"language-python\">from agent_framework.observability import configure_otel_providers, get_tracer\r\n\r\nconfigure_otel_providers()\r\nwith get_tracer().start_as_current_span(\"Claw Console Session\"):\r\n    agent = await build_claw_agent(credential=AzureCliCredential())\r\n    async with agent:\r\n        # \u2026 run the agent; spans, metrics, and logs flow to your collector \u2026<\/code><\/pre>\n<blockquote><p><strong>What the harness does for you vs. what you wire by hand:<\/strong> the harness <em>produces<\/em> the telemetry &#8211; spans for each tool call and model turn, token-usage metrics, structured logs. You choose where it <em>goes<\/em>: an OTLP collector, the console, or <strong>Azure Monitor \/ Application Insights<\/strong>. Locally you wire the exporters yourself; <strong>when hosted on Foundry you wire nothing at all<\/strong> &#8211; the hosting runtime registers the exporter pipeline and Foundry injects <code>APPLICATIONINSIGHTS_CONNECTION_STRING<\/code> for you (see the deployment section below).<\/p><\/blockquote>\n<h2 id=\"keep-it-governed-purview\">Keep it governed: Purview<\/h2>\n<p>A finance assistant touches sensitive material. In a regulated setting you need prompts and responses screened against organizational policy &#8211; credit-card numbers, confidential holdings, disallowed content &#8211; with an audit trail. <strong>Microsoft Purview<\/strong> does exactly that, and the integration is a thin wrapper around the chat client, so it composes with everything else the claw does.<\/p>\n<p>We make it <strong>opt-in<\/strong>: when <code>PURVIEW_CLIENT_APP_ID<\/code> is set the factory adds Purview; otherwise it runs unchanged. In <strong>.NET<\/strong> it&#8217;s a builder step on the chat client:<\/p>\n<pre><code class=\"language-csharp\">if (!string.IsNullOrWhiteSpace(purviewClientAppId))\r\n{\r\n    chatClient = chatClient\r\n        .AsBuilder()\r\n        .WithPurview(browserCredential, new PurviewSettings(\"Claw\"))\r\n        .Build();\r\n}<\/code><\/pre>\n<p>In <strong>Python<\/strong> it&#8217;s chat middleware handed to the <code>FoundryChatClient<\/code>:<\/p>\n<pre><code class=\"language-python\">from agent_framework.microsoft import PurviewChatPolicyMiddleware, PurviewSettings\r\n\r\nmiddleware = []\r\nif client_app_id := os.environ.get(\"PURVIEW_CLIENT_APP_ID\"):\r\n    credential = InteractiveBrowserCredential(client_id=client_app_id)\r\n    middleware = [PurviewChatPolicyMiddleware(credential, PurviewSettings(app_name=\"Claw\"))]\r\n\r\nclient = FoundryChatClient(credential=..., middleware=middleware)<\/code><\/pre>\n<p>Now every prompt is checked before it reaches the model and every response before it reaches the user; blocked content is replaced with a policy message, and the interaction is logged for audit. Purview needs a Microsoft 365 E5 tenant with the right Graph permissions &#8211; see the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/dotnet\/samples\/05-end-to-end\/AgentWithPurview\"><code>AgentWithPurview<\/code><\/a> and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/python\/samples\/05-end-to-end\/purview_agent\"><code>purview_agent<\/code><\/a> samples for the full setup.<\/p>\n<h2 id=\"ship-it-deploy-as-a-foundry-hosted-agent\">Ship it: deploy as a Foundry Hosted Agent<\/h2>\n<h3>Creating the agent host application<\/h3>\n<p>Because the agent is defined once, hosting it is mostly <em>wiring<\/em>, not rewriting. The <strong>hosted<\/strong> host takes the same <code>build.Agent<\/code> and exposes it over the Responses protocol so Foundry can run it. In <strong>.NET<\/strong> the entire host is a thin ASP.NET app:<\/p>\n<pre><code class=\"language-csharp\">using Azure.Core;\r\nusing Azure.Identity;\r\nusing ClawAgent;\r\nusing Microsoft.Agents.AI.Foundry.Hosting;\r\n\r\n\/\/ A specific credential is preferable in production (e.g. ManagedIdentityCredential); the chained\r\n\/\/ credential below tries a dev token first (for local Docker debugging), then DefaultAzureCredential.\r\nTokenCredential credential = new ChainedTokenCredential(\r\n    new DevTemporaryTokenCredential(),\r\n    new DefaultAzureCredential());\r\n\r\nawait using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions\r\n{\r\n    ProjectEndpoint = Environment.GetEnvironmentVariable(\"FOUNDRY_PROJECT_ENDPOINT\"),\r\n    DeploymentName = Environment.GetEnvironmentVariable(\"FOUNDRY_MODEL\"),\r\n    Credential = credential,\r\n\r\n    \/\/ Disable filesystem and shell access on the hosted container (see the risk note below).\r\n    EnableFileAccess = false,\r\n    EnableShell = false,\r\n});\r\n\r\nvar builder = WebApplication.CreateBuilder(args);\r\n\r\n\/\/ Registers the Responses API host for the agent AND auto-applies OpenTelemetry.\r\nbuilder.Services.AddFoundryResponses(build.Agent);\r\n\r\nvar app = builder.Build();\r\n\r\n\/\/ The endpoint that live Foundry calls.\r\napp.MapFoundryResponses();\r\n\r\napp.Run();<\/code><\/pre>\n<p>In <strong>Python<\/strong> it&#8217;s the responses host server:<\/p>\n<pre><code class=\"language-python\">from agent_framework_foundry_hosting import ResponsesHostServer\r\n\r\nagent = await build_claw_agent(\r\n    credential=DefaultAzureCredential(),\r\n    enable_file_access=False,   # off on the hosted container\r\n    enable_shell=False,         # off on the hosted container\r\n)\r\nawait ResponsesHostServer(agent).run_async()<\/code><\/pre>\n<p><strong><span data-teams=\"true\">Core telemetry collection and export are automatically configured when hosted.<\/span><\/strong>\u00a0There are no exporters to configure in either language. In <strong>.NET<\/strong>, <code>AddFoundryResponses<\/code> automatically wraps the agent with <code>OpenTelemetryAgent<\/code>, and the Foundry hosting runtime registers the OTLP exporter pipeline. In <strong>Python<\/strong>, Agent Framework is natively instrumented (on by default) and the hosting runtime collects and exports its spans \u2014 so the hosted host makes <em>no<\/em> <code>configure_otel_providers()<\/code> call at all (unlike the local console). When your agent runs on Foundry, it injects <code>APPLICATIONINSIGHTS_CONNECTION_STRING<\/code> automatically, so traces, metrics, and logs land in Application Insights with zero configuration. To capture prompt and response content in those traces (off by default), set <code>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true<\/code> in <strong>.NET<\/strong> or <code>ENABLE_SENSITIVE_DATA=true<\/code> in <strong>Python<\/strong>.<\/p>\n<h3>Turn off risky features<\/h3>\n<p>For the hosted agent, we turn off file access and shell. This is the one place we <em>deliberately<\/em> diverge from the console. In a shared, hosted environment, giving the model arbitrary read\/write access to the container filesystem, or letting it run shell commands, is a serious security risk &#8211; data exfiltration, tampering, and persistence &#8211; even behind a deny-list. So the hosted build sets <code>EnableFileAccess = false<\/code> and <code>EnableShell = false<\/code>. Background agents stay on. If you <em>do<\/em> enable file access or shell on a hosted container, treat it as a production security decision and scope it tightly.<\/p>\n<p>If you genuinely need file access when hosted, don&#8217;t reach for the container disk &#8211; supply an <strong>external <code>AgentFileStore<\/code><\/strong> instead, for example one backed by Azure Blob Storage. In <strong>.NET<\/strong>:<\/p>\n<pre><code class=\"language-csharp\">await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions\r\n{\r\n    \/\/ ...\r\n    EnableFileAccess = true,\r\n    FileStore = new MyBlobAgentFileStore(blobContainerClient),\r\n});<\/code><\/pre>\n<p>In <strong>Python<\/strong> it&#8217;s the <code>file_access_store<\/code> argument:<\/p>\n<pre><code class=\"language-python\">agent = await build_claw_agent(\r\n    credential=DefaultAzureCredential(),\r\n    enable_file_access=True,\r\n    file_access_store=MyBlobAgentFileStore(blob_container_client),\r\n)\r\nasync with agent:\r\n    # \u2026 run the agent \u2026<\/code><\/pre>\n<p>The claw reads and writes through the store&#8217;s abstraction, so files live in blob storage (governed, durable, shared) rather than on ephemeral container disk.<\/p>\n<h3>Enabling CodeAct in the container<\/h3>\n<p>This is the second deliberate divergence. Locally, CodeAct runs on <strong>Hyperlight<\/strong>, which isolates guest code in a VM-backed micro-sandbox.\u00a0 For the hosted build, we pass a <code>CodeActProvider<\/code> backed by <strong><code>LocalCodeAct<\/code><\/strong>, which runs the generated Python in a child process and leans on the hosted container itself as the sandbox \u2014 the same approach as the canonical <code>Hosted-LocalCodeAct<\/code> sample (the container image installs <code>python3<\/code> for it). In <strong>.NET<\/strong>:<\/p>\n<pre><code class=\"language-csharp\">await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions\r\n{\r\n    \/\/ ...\r\n    CodeActProvider = new LocalCodeActProvider(\r\n        Environment.GetEnvironmentVariable(\"LOCAL_CODEACT_PYTHON\") ?? \"python3\"),\r\n});<\/code><\/pre>\n<p><code>LocalCodeAct<\/code> is not itself a sandbox \u2014 it executes model-generated Python, so only run it inside an externally sandboxed environment such as a hosted-agent container. To drop CodeAct entirely instead, set <code>EnableCodeAct = false<\/code>.<\/p>\n<h3>Build and deploy<\/h3>\n<p>Both hosts ship an <code>agent.manifest.yaml<\/code> and an <code>agent.yaml<\/code>, but the two files have different jobs. The <strong>manifest<\/strong> is the template passed to <code>azd ai agent init<\/code>: it carries the agent&#8217;s name, metadata, protocol, and configurable parameters. The <strong>agent definition<\/strong> describes what Foundry runs &#8211; the Responses protocol, CPU and memory, and any environment variables the container needs. See the actual <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Hosted\/agent.manifest.yaml\">.NET manifest<\/a>, <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Hosted\/agent.yaml\">.NET agent definition<\/a>, <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\/agent.manifest.yaml\">Python manifest<\/a>, and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\/agent.yaml\">Python agent definition<\/a>.<\/p>\n<p>The deployment path then differs by language. <strong>Python<\/strong> uses Foundry&#8217;s default code (ZIP) deployment: <code>azd<\/code> uploads the self-contained sample folder, installs <code>requirements.txt<\/code>, and starts <code>hosted.py<\/code>. Pass that entry point when you initialize the project:<\/p>\n<pre><code class=\"language-bash\">cd python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\r\nazd ai agent init -m agent.manifest.yaml --entry-point hosted.py\r\nazd up      # or `azd deploy` on subsequent pushes<\/code><\/pre>\n<p>The <strong>.NET<\/strong> sample deploys as a container image because it builds against the Agent Framework repo source (<code>ProjectReference<\/code>) and uses repo-level Central Package Management. A ZIP deployment uploads only the project folder, so a server-side restore cannot resolve those out-of-folder references or <code>Directory.Packages.props<\/code>. Publish locally first, then let <code>azd<\/code> build and push the image:<\/p>\n<pre><code class=\"language-bash\"># From ClawAgent.Hosted:\r\ndotnet publish -c Release -f net10.0 -r linux-x64 --self-contained false -o out\r\n\r\n# First time only:\r\nazd ai agent init -m agent.manifest.yaml --deploy-mode container\r\nazd up      # or `azd deploy` after republishing on subsequent pushes<\/code><\/pre>\n<p>The .NET <code>Dockerfile<\/code> copies the pre-published <code>out\/<\/code> into a <code>python3<\/code>-enabled <code>aspnet:10.0<\/code> image. For the complete prerequisites and identity assignments, follow the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/dotnet\/samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Hosted#deploy-to-foundry-container-path\">.NET deployment guide<\/a> or the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready#deploy-to-foundry\">Python deployment guide<\/a>.<\/p>\n<h2 id=\"prove-its-good-evals\">Prove it&#8217;s good: evals<\/h2>\n<p>Before and after you deploy, you want to evaluate whether the claw is <em>actually good<\/em>. You may also want to ensure that no regressions follow any changes, or test different prompts to find the most optimal version. The <strong>evals<\/strong> host builds the agent and runs it against a small set of finance queries with two layers of checks.<\/p>\n<p><strong>Local checks<\/strong> are plain functions &#8211; fast, free, and runnable in CI. In <strong>.NET<\/strong>:<\/p>\n<pre><code class=\"language-csharp\">LocalEvaluator localEvaluator = new(\r\n    FunctionEvaluator.Create(\"numeric_valuation\", item =&gt;\r\n        !item.Query.Contains(\"Value MSFT\", StringComparison.OrdinalIgnoreCase)\r\n        || Regex.IsMatch(item.Response, @\"\\d\")));\r\n\r\nAgentEvaluationResults results = await build.Agent.EvaluateAsync(queries, localEvaluator);\r\nConsole.WriteLine($\"Passed: {results.Passed}\/{results.Total}\");<\/code><\/pre>\n<p>In <strong>Python<\/strong> the same idea with the <code>@evaluator<\/code> decorator:<\/p>\n<pre><code class=\"language-python\">@evaluator(name=\"numeric_valuation_answer\")\r\ndef numeric_valuation_answer(query: str, response: str) -&gt; bool:\r\n    return \"msft\" not in query.lower() or any(c.isdigit() for c in response)\r\n\r\nlocal = LocalEvaluator(numeric_valuation_answer)\r\nresults = await evaluate_agent(agent=agent, queries=queries, evaluators=local)\r\nprint(f\"{results[0].passed}\/{results[0].total}\")<\/code><\/pre>\n<p><strong>Hosted Foundry evals<\/strong> add model-graded quality scores (relevance, coherence) and a shareable report &#8211; gated on <code>FOUNDRY_PROJECT_ENDPOINT<\/code> so the local checks always run:<\/p>\n<pre><code class=\"language-csharp\">\/\/ .NET\r\nFoundryEvals foundryEvals = new(projectClient, deploymentName, FoundryEvals.Relevance, FoundryEvals.Coherence);\r\nAgentEvaluationResults quality = await build.Agent.EvaluateAsync(queries, foundryEvals);<\/code><\/pre>\n<pre><code class=\"language-python\"># Python\r\nfrom agent_framework.foundry import FoundryChatClient, FoundryEvals\r\n\r\nfoundry = FoundryEvals(\r\n    client=FoundryChatClient(credential=credential),\r\n    evaluators=[FoundryEvals.RELEVANCE, FoundryEvals.COHERENCE],\r\n)\r\nquality = await evaluate_agent(agent=agent, queries=queries, evaluators=foundry)<\/code><\/pre>\n<p>Run these on every change and the score tells you whether a new instruction, tool, or skill helped or hurt &#8211; the tuning loop that keeps a deployed agent honest.<\/p>\n<h2 id=\"run-it\">Run it<\/h2>\n<p><strong>.NET<\/strong> &#8211; run the console, the evals, or the hosted service:<\/p>\n<pre><code class=\"language-bash\">cd dotnet\r\ndotnet run --project samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Console\r\ndotnet run --project samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Evals\r\ndotnet run --project samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\/ClawAgent.Hosted<\/code><\/pre>\n<p><strong>Python<\/strong><\/p>\n<pre><code class=\"language-bash\">uv run python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\/console.py\r\nuv run python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\/evals.py\r\nuv run python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\/hosted.py<\/code><\/pre>\n<p>The console behaves exactly like Part 3&#8217;s claw &#8211; now with telemetry flowing to your collector.<\/p>\n<ul>\n<li>To watch traces locally, point <code>OTEL_EXPORTER_OTLP_ENDPOINT<\/code> at a collector (or set <code>ENABLE_CONSOLE_EXPORTERS=true<\/code> in Python).<\/li>\n<li>To turn on governance, set <code>PURVIEW_CLIENT_APP_ID<\/code>.<\/li>\n<li>To send telemetry to Application Insights when hosted, set <code>APPLICATIONINSIGHTS_CONNECTION_STRING<\/code>.<\/li>\n<\/ul>\n<p><strong>Hosted<\/strong><\/p>\n<p>You can also call the deployed agent from the Foundry Agent playground.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-content\/uploads\/sites\/78\/2026\/08\/FoundryAgentPlayground.webp\"><img decoding=\"async\" class=\"size-full wp-image-5804 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-content\/uploads\/sites\/78\/2026\/08\/FoundryAgentPlayground.webp\" alt=\"Foundry Agent Playground screenshot\" width=\"959\" height=\"421\" srcset=\"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-content\/uploads\/sites\/78\/2026\/08\/FoundryAgentPlayground.webp 959w, https:\/\/devblogs.microsoft.com\/agent-framework\/wp-content\/uploads\/sites\/78\/2026\/08\/FoundryAgentPlayground-300x132.webp 300w, https:\/\/devblogs.microsoft.com\/agent-framework\/wp-content\/uploads\/sites\/78\/2026\/08\/FoundryAgentPlayground-768x337.webp 768w\" sizes=\"(max-width: 959px) 100vw, 959px\" \/><\/a><\/p>\n<h2 id=\"the-runnable-samples\">The runnable samples<\/h2>\n<ul>\n<li><strong>.NET:<\/strong> <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/dotnet\/samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady\"><code>dotnet\/samples\/02-agents\/Harness\/BuildYourOwnClaw\/Claw_Step04_ProductionReady<\/code><\/a><\/li>\n<li><strong>Python:<\/strong> <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready\"><code>python\/samples\/02-agents\/harness\/build_your_own_claw\/claw_step04_production_ready<\/code><\/a><\/li>\n<\/ul>\n<h2 id=\"use-these-building-blocks-in-your-own-agent\">Use these building blocks in your own agent<\/h2>\n<p>As always, each capability is available on its own &#8211; none of it is locked inside the harness:<\/p>\n<table>\n<thead>\n<tr>\n<th>Feature<\/th>\n<th>.NET<\/th>\n<th>Python<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Observability<\/strong><\/td>\n<td>Decorate any <code>AIAgent<\/code> with <code>agent.AsBuilder().UseOpenTelemetry(sourceName).Build()<\/code>; a chat-backed agent can also instrument its <code>IChatClient<\/code> in <code>clientFactory<\/code>. See the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/samples\/02-agents\/AgentOpenTelemetry\/Program.cs\">complete sample<\/a> and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI\/OpenTelemetryAgentBuilderExtensions.cs\"><code>UseOpenTelemetry<\/code> source<\/a>.<\/td>\n<td>Configure exporters with <code>from agent_framework.observability import configure_otel_providers<\/code>. See the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/packages\/core\/agent_framework\/observability.py\">observability source<\/a>.<\/td>\n<\/tr>\n<tr>\n<td><strong>Governance<\/strong><\/td>\n<td><code>WithPurview<\/code> is a <code>ChatClientBuilder<\/code> extension: <code>chatClient.AsBuilder().WithPurview(credential, settings).Build()<\/code>. See the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI.Purview\/PurviewExtensions.cs\"><code>WithPurview<\/code> source<\/a> and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/samples\/05-end-to-end\/AgentWithPurview\/Program.cs\">sample<\/a>.<\/td>\n<td>Add <code>PurviewChatPolicyMiddleware<\/code> to a chat client. See the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/packages\/purview\/agent_framework_purview\/_middleware.py\">middleware source<\/a> and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/tree\/main\/python\/samples\/05-end-to-end\/purview_agent\">sample<\/a>.<\/td>\n<\/tr>\n<tr>\n<td><strong>Hosting<\/strong><\/td>\n<td>Register an <code>AIAgent<\/code> with <code>builder.Services.AddFoundryResponses(agent)<\/code>, then map the endpoint with <code>app.MapFoundryResponses()<\/code>. Both are ASP.NET extensions in the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI.Foundry.Hosting\/ServiceCollectionExtensions.cs\">hosting source<\/a>.<\/td>\n<td>Wrap an agent with <code>ResponsesHostServer(agent)<\/code> and run it with <code>run_async()<\/code>. See the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/packages\/foundry_hosting\/agent_framework_foundry_hosting\/_responses.py\">responses host source<\/a>.<\/td>\n<\/tr>\n<tr>\n<td><strong>Evals<\/strong><\/td>\n<td>Use <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI\/Evaluation\/LocalEvaluator.cs\"><code>LocalEvaluator<\/code><\/a> \/ <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI\/Evaluation\/FunctionEvaluator.cs\"><code>FunctionEvaluator<\/code><\/a> for local checks and <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/dotnet\/src\/Microsoft.Agents.AI.Foundry\/Evaluation\/FoundryEvals.cs\"><code>FoundryEvals<\/code><\/a> for hosted quality evaluation.<\/td>\n<td>Use <code>LocalEvaluator<\/code>, <code>evaluate_agent<\/code>, and <code>@evaluator<\/code> from the <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/python\/packages\/core\/agent_framework\/_evaluation.py\">core evaluation module<\/a>, or <a href=\"https:\/\/github.com\/microsoft\/agent-framework\/blob\/main\/pyxthon\/packages\/foundry\/agent_framework_foundry\/_foundry_evals.py\"><code>FoundryEvals<\/code><\/a>.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The pattern that ties them together is the <strong>shared agent factory<\/strong>: define the agent once, then wrap it in whatever host you need. Observability can decorate the agent and its underlying chat client; governance is chat-client middleware; hosting and evals operate on the finished agent.<\/p>\n<h2 id=\"whats-next\">What&#8217;s next<\/h2>\n<p>That&#8217;s the series. Across four parts we started from a single tool and finished with a governed, observable, deployable, continuously-evaluated finance assistant &#8211; a <em>claw<\/em> &#8211; built entirely from Microsoft Agent Framework building blocks, each of which you can lift into your own agent. The harness gave us planning, file access, approvals, memory, skills, a shell, CodeAct, and background agents; this part made the whole thing production-ready without changing what the agent <em>is<\/em>.<\/p>\n<p>Take the claw, swap in your own domain &#8211; support, ops, research, whatever you build &#8211; and you have a running start on an agent you can actually ship.<\/p>\n<h2 id=\"the-series\">\ud83d\udcda The series<\/h2>\n<p>Part of <strong>Build your own claw and agent harness with Microsoft Agent Framework<\/strong>:<\/p>\n<ul>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/build-your-own-claw-and-agent-harness-with-microsoft-agent-framework\/\">Overview &#8211; Build your own claw and agent harness<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/meet-your-agent-harness-and-claw\/\">Part 1 &#8211; Meet your agent harness and claw<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/agent-harness-working-with-your-data-safely\/\">Part 2 &#8211; Working with your data, safely<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/agent-framework\/agent-harness-scaling-the-claw-or-harness-capabilities\/\">Part 3 &#8211; Scaling its capabilities<\/a><\/li>\n<li><strong>Part 4 &#8211; Making your claw production-ready<\/strong>\u00a0<em>(you are here)<\/em><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Part 4 of Build your own claw and agent harness with Microsoft Agent Framework. Over the last three parts our personal finance assistant grew from a single tool into a genuinely capable agent: it plans, reads your portfolio, asks before it trades, remembers what matters, loads skills on demand, reorganizes files with a shell, computes [&hellip;]<\/p>\n","protected":false},"author":162052,"featured_media":5808,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[78,143,159,34],"tags":[],"class_list":["post-5798","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-agent-framework","category-agent-harness","category-python-2"],"acf":[],"blog_post_summary":"<p>Part 4 of Build your own claw and agent harness with Microsoft Agent Framework. Over the last three parts our personal finance assistant grew from a single tool into a genuinely capable agent: it plans, reads your portfolio, asks before it trades, remembers what matters, loads skills on demand, reorganizes files with a shell, computes [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/5798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/users\/162052"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/comments?post=5798"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/5798\/revisions"}],"predecessor-version":[{"id":5839,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/5798\/revisions\/5839"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media\/5808"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media?parent=5798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/categories?post=5798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/tags?post=5798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}