Featured image of post Microsoft Agent Framework: Agent Harness

Microsoft Agent Framework: Agent Harness

Learn how Microsoft Agent Framework Agent Harness turns a chat client into a stateful, multi-step agent runtime with a practical .NET example.

Introduction

Calling a language model is easy. Building an agent that can work through a multi-step task, keep its context, request approval before using tools, and continue after another turn is harder.

Microsoft Agent Framework is Microsoft’s set of APIs and abstractions for building agents, sessions, messages, tools, and workflows. The Agent Harness is one runtime capability in that framework: it wraps an IChatClient and coordinates agent execution around model calls.

This is the first article in the Microsoft Agent Framework series. You do not need a separate framework introduction before reading it; this post introduces only the concepts needed for the Harness. We will build a small .NET API that keeps an agent session across requests.

What You Will Build

You will build a small customer support resolution assistant. It is backed by DeepSeek and the Agent Harness. A support agent can start with a customer issue, then continue with focused questions without resending the original context.

Copilot Prompt
Session 1: "A customer cannot complete checkout after a card payment. Summarize the issue and suggest safe troubleshooting steps."

Session 2: “Turn those troubleshooting steps into a customer-facing reply and include when to escalate.”

Expected result: the second response builds on the first case because both turns use the same AgentSession.

What Is Agent Harness?

An Agent Harness is the execution layer around an agent. The model still decides what to say or which tool to call, but the Harness coordinates the work around those model calls.

In this sample, the most visible Harness behavior is session continuity. The sample does not yet add custom functions, approval workflows, durable storage, or a multi-step plan. Those capabilities are useful extensions for later articles in the series.

Without a Harness, an application commonly has to assemble these concerns itself:

  • Store chat history between turns.
  • Decide how multi-step work is planned and tracked.
  • Invoke functions and apply approval policies.
  • Control context growth and compact old history.
  • Expose progress and operational state to the host application.

The Microsoft Agent Framework Harness gives these concerns a standard runtime surface. It does not replace the model, provide a model key, or decide where the application is hosted.

Agent Framework, Harness, and Hosting

These terms describe different layers:

Layer Responsibility
Agent Framework APIs and abstractions for agents, sessions, messages, tools, and workflows
Agent Harness Runtime scaffolding that coordinates planning, context, tools, approvals, and session state
Hosting The place and infrastructure where the application runs, such as an ASP.NET Core process or a managed agent service

The Harness can run inside a console application, an ASP.NET Core service, or another host. The host owns authentication, persistence, networking, and deployment.

Real-World Example: Customer Support Resolution Assistant

Imagine a support agent handling a customer who cannot complete checkout after a card payment. The case may require clarifying questions, safe troubleshooting, a customer-facing explanation, and escalation when the issue involves a payment or account risk. A single model response can suggest ideas, but the agent needs to continue the case while preserving the original details.

The Harness is useful here for three reasons:

  • The first turn can summarize the issue and propose safe troubleshooting steps.
  • The session keeps the customer and case details available during follow-up questions.
  • The application can add tools later for knowledge-base search, ticket lookup, or escalation, with approval policies around consequential actions.

Start the sample and send the first prompt:

Copilot Prompt
Session 1: "A customer cannot complete checkout after a card payment. Summarize the issue and suggest safe troubleshooting steps."

Session 2: “Turn those troubleshooting steps into a customer-facing reply and include when to escalate.”

Expected result: the second response refines the support case using the details from Session 1.

The API returns a sessionId. Send that value with Session 2 so the Harness can continue the same conversation.

The second request is not a new conversation. The Harness receives the same AgentSession, so the response can refine the original support case. This is a practical starting point: begin with conversation state, then add domain-specific tools as the workflow becomes more automated.

Harness Request Lifecycle

The sample architecture is:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#e0f2fe', 'primaryTextColor': '#172b4d', 'primaryBorderColor': '#0284c7', 'lineColor': '#64748b', 'secondaryColor': '#dcfce7', 'tertiaryColor': '#fef3c7', 'fontSize': '20px', 'fontFamily': 'Arial' }}}%% flowchart LR U[Support Client] -->|POST prompt and optional sessionId| API subgraph HOST[ASP.NET Core Host] API[Agent API] R[AgentRuntime] S[(In-memory session store)] API -->|prompt, sessionId| R R <-->|create or reuse AgentSession| S end subgraph HARNESS[Microsoft Agent Framework] H[Harness Agent] C[IChatClient] R -->|RunAsync with AgentSession| H H --> C H -.-> T[Future: functions, approvals, context providers] end style HOST fill:#f1f5f9,stroke:#94a3b8,stroke-width:2px,color:#334155 style HARNESS fill:#f1f5f9,stroke:#64748b,stroke-width:2px,color:#334155 M[DeepSeek model endpoint] -->|model response| C C -->|chat completion request| M H -->|AgentResponse| R R -->|response and sessionId| API API --> U classDef client fill:#e0e7ff,stroke:#4f46e5,stroke-width:2px,color:#312e81 classDef api fill:#ccfbf1,stroke:#0f766e,stroke-width:2px,color:#134e4a classDef runtime fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#172b4d classDef state fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d classDef model fill:#f3f4f6,stroke:#6b7280,stroke-width:2px,color:#1f2937 classDef future fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843 class U client class API api class R,H,C runtime class S state class M model class T future

AgentSession is important. It is the state container passed to each run. Reusing the same session lets the Harness preserve conversation history and Harness-managed state across turns. Creating a new session for every request creates a new conversation.

Create a Harness Agent

The entry point is the AsHarnessAgent extension method. It accepts an IChatClient and returns an agent with Harness behavior configured around it. This example uses DeepSeek’s OpenAI-compatible API, so the Harness code stays independent from the model provider.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System.ClientModel;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;

IChatClient chatClient = new OpenAIClient(
  new ApiKeyCredential("<deepseek-api-key>"),
  new OpenAIClientOptions
  {
    Endpoint = new Uri("https://api.deepseek.com")
  })
  .GetChatClient("deepseek-v4-flash")
  .AsIChatClient();

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    ChatOptions = new ChatOptions
    {
        Instructions = "You are a customer support resolution assistant."
    },
    DisableWebSearch = true
});

AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync(
    "A customer cannot complete checkout after a card payment. Summarize the issue and suggest safe troubleshooting steps.",
    session);

Console.WriteLine(response.Text);

The sample disables the built-in web search capability so the example has an explicit, predictable tool boundary. Enable and govern capabilities deliberately when an application needs them.

The Sample Application

  • AgentHarness contains the agent runtime and Harness configuration.
  • AgentHarness.Api contains the HTTP host and endpoint.
  • AgentSession instances are held in an in-memory dictionary for clarity.

The runtime creates one Harness agent and reuses it for requests. Each request supplies a session ID. The first request creates a session; later requests with that ID reuse it:

1
2
3
4
5
var session = await _agent.CreateSessionAsync(cancellationToken);
var response = await _agent.RunAsync(
    prompt,
    session,
    cancellationToken: cancellationToken);

In a real service, replace the dictionary with a durable session store. Also authenticate the caller and verify that the caller owns the session before resuming it. A session ID is a lookup key, not an authorization boundary.

Configure and Run

From the sample directory:

1
2
$env:DS_KEY = "<your-deepseek-api-key>"
dotnet run --project AgentHarness.Api

The sample reads the API key from the DS_KEY environment variable. It uses https://api.deepseek.com and deepseek-v4-flash by default. Both endpoint and model are configurable through DeepSeek:Endpoint and DeepSeek:Model.

Send a first request:

1
2
3
curl -X POST http://localhost:5000/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A customer cannot complete checkout after a card payment. Summarize the issue and suggest safe troubleshooting steps."}'

The response contains a sessionId. Send it with the next request to continue the conversation:

1
2
3
curl -X POST http://localhost:5000/api/agent/chat \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"<returned-session-id>","prompt":"Turn those troubleshooting steps into a customer-facing reply and include when to escalate."}'

When to Use Agent Harness

Use a Harness when work naturally spans multiple steps or turns and the application benefits from consistent session and tool behavior. Examples include research assistants, coding agents, data analysis workflows, and operations assistants.

A plain chat client may be a better fit for a single request and response, especially when the application owns all state and does not need tool orchestration. The Harness adds useful behavior, but it also adds runtime policy that should be understood before production use.

Production Considerations

The sample is intentionally small. Production systems need additional decisions:

  • Persist and version sessions instead of keeping them in process memory.
  • Authenticate users and authorize every session resume.
  • Restrict tools and require approval for consequential actions.
  • Set execution, timeout, and token budgets.
  • Record traces, tool calls, errors, and model usage.
  • Pin package versions and review release notes because provider APIs are evolving.

Conclusion

Microsoft Agent Framework Agent Harness is a runtime layer that turns an IChatClient into a more capable, stateful agent. Its central idea is simple: create the Harness agent once, create a session for a conversation, and reuse that session across runs.

The next posts in this series can build on this foundation with function tools, approvals, durable sessions, context providers, and observability.

You can find the sample code in this repository:

Reference

Built with Hugo
Theme Stack designed by Jimmy