0
A modern AI application may need to search the web, call APIs, work with files, remember previous information, execute tools, follow a multi-step plan, ask for human approval, and continue working until a task is completed.
At that point, simply sending a prompt to a language model is no longer enough.
This is where an AI agent harness becomes useful.
Microsoft's Agent Framework now provides a harness that can wrap an IChatClient and add capabilities such as tool invocation, conversation history, planning, context management, memory, approvals, and other agent features. Microsoft recently demonstrated this approach in a C# series focused on building an agent incrementally from a simple model connection into a more capable and observable application.
In this article, we'll look at what an AI agent harness is, how it differs from a simple chatbot, how to create one in C#, and why it can be useful when building real-world .NET AI applications.
Note: Microsoft Agent Framework and its harness capabilities are evolving. Always check the current Microsoft documentation and package versions before using specific APIs in production.
Before understanding an agent harness, it helps to understand what makes an application an AI agent.
A basic chatbot generally follows a simple flow:
User
↓
Prompt
↓
AI Model
↓
Response
For example:
User: What is ASP.NET Core?
AI: ASP.NET Core is a cross-platform framework...
An agent can follow a more complex process:
User
↓
AI Agent
↓
Plan the task
↓
Choose a tool
↓
Execute the tool
↓
Read the result
↓
Decide what to do next
↓
Repeat if necessary
↓
Final response
For example, a user might ask:
"Analyze these sales files and prepare a summary."
An agent may need to:
Find the files.
Read the data.
Analyze the numbers.
Identify important changes.
Create a report.
Ask for approval before performing certain actions.
Return the final result.
The language model provides the reasoning capability, but something needs to coordinate the entire process.
That coordinating layer is where an agent harness comes in.
An agent harness is the runtime scaffolding around a language model that helps it perform multi-step work.
Microsoft describes the harness as a runtime layer that can drive model and tool calls, maintain conversation state and context, apply approval policies, and help an agent continue through a multi-step task.
Instead of building every piece yourself, you can start with an existing IChatClient and create a harness agent.
The basic C# concept is:
AIAgent agent = chatClient.AsHarnessAgent();
The important idea is that the harness does not replace the underlying model.
It sits around the model and provides the machinery needed to turn a model interaction into an agent workflow.
The difference becomes easier to understand with an example.
var response = await chatClient.GetResponseAsync(
"Explain dependency injection in ASP.NET Core.");
The model receives a prompt and generates a response.
An agent may receive:
"Review my project and tell me which dependencies need updating."
The agent may then:
1. Inspect project files
2. Identify dependencies
3. Check available information
4. Analyze versions
5. Prepare a summary
The application therefore needs more than model inference.
It needs orchestration.
Microsoft's current Agent Framework documentation shows that a harness agent can be created from an existing IChatClient.
A basic example looks like this:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = chatClient.AsHarnessAgent();
AgentResponse response =
await agent.RunAsync("Explain how dependency injection works in ASP.NET Core.");
Console.WriteLine(response.Text);
The exact IChatClient implementation depends on the model provider you're using.
The important part is:
chatClient.AsHarnessAgent();
This converts the chat client into a harness-based agent.
A real application normally needs instructions that define the agent's role.
For example:
AIAgent agent = chatClient.AsHarnessAgent(
new HarnessAgentOptions
{
Name = "dotnet-assistant",
HarnessInstructions =
"Work carefully through multi-step development tasks.",
ChatOptions = new ChatOptions
{
Instructions =
"""
You are a .NET development assistant.
Prefer practical C# examples.
Explain assumptions before making changes.
"""
}
});
This separates two different ideas.
Harness instructions can control how the agent operates.
Chat instructions describe what the agent should do from a domain perspective.
This separation becomes increasingly useful as an agent grows.
One of the biggest differences between a chatbot and an agent is the ability to use tools.
Suppose we have a simple C# function:
[Description("Gets the current stock quantity for a product.")]
public static int GetStockQuantity(
[Description("The product ID.")] int productId)
{
return 125;
}
We can expose the function as an AI tool:
AIFunction stockTool =
AIFunctionFactory.Create(
GetStockQuantity,
"get_stock_quantity");
Then provide it to the agent:
AIAgent agent = chatClient.AsHarnessAgent(
new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions =
"Use the stock tool when the user asks about inventory.",
Tools = [stockTool]
}
});
Now the model doesn't need to guess the inventory.
It can request the appropriate tool.
The general flow becomes:
User
↓
Agent
↓
Model decides a tool is required
↓
Tool executes
↓
Tool result returns to model
↓
Agent generates response
Microsoft's current Agent Framework documentation describes function invocation as one of the capabilities provided by the harness.
Consider an inventory application.
A user asks:
"How many units of product 1001 are currently available?"
A language model doesn't have access to your PostgreSQL database simply because it knows C#.
Your application needs to provide a controlled tool such as:
GetStockQuantity(1001)
The model decides when the tool is appropriate, while your application controls what the tool actually does.
This creates a useful separation:
AI Model
Reasoning
↓
Tool Selection
↓
Application Code
↓
Database / API / Service
The model should not directly receive unrestricted database credentials.
The application should expose narrowly defined operations.
Real-world tasks often require multiple steps.
For example:
"Analyze this month's sales and identify the three products with the largest decline."
An agent may need to:
1. Read sales data
2. Calculate totals
3. Compare periods
4. Sort products
5. Identify the largest declines
6. Prepare an explanation
A harness can provide planning and todo capabilities so the agent can keep track of the work.
Microsoft's current Agent Harness documentation describes planning/execution modes and todo state as part of the harness capabilities.
This is different from simply asking a model to "think step by step."
The application has an actual runtime responsible for managing the work.
An agent often needs more than one interaction.
For example:
User:
Analyze my sales data.
Agent:
I found the sales file.
User:
Now compare it with last month.
Agent:
...
The agent needs to preserve relevant state between turns.
Agent Framework uses sessions and context providers for this purpose.
A typical application can maintain an AgentSession while the conversation continues.
Conceptually:
var session = await agent.CreateSessionAsync();
var response1 =
await agent.RunAsync(
"Analyze this month's sales.",
session);
var response2 =
await agent.RunAsync(
"Now compare them with last month.",
session);
The exact APIs can evolve with the framework, but the important architectural concept is persistent agent state.
Microsoft's Agent Framework documentation recommends keeping an AgentSession for interactive multi-step harness tasks so state such as history, plan, and todos can persist across turns.
Long-running agents can accumulate a large amount of conversation history.
For example:
User message
↓
Tool call
↓
Tool result
↓
Model response
↓
Another tool call
↓
Another result
↓
...
Eventually, sending the entire history to the model can become inefficient or exceed context limits.
A harness can provide context compaction capabilities to reduce unnecessary context while preserving useful information.
This is important for agents that perform long-running tasks.
Without context management, an agent can become increasingly expensive and less reliable as its history grows.
Agents often need to work with files.
Examples include:
CSV files
JSON files
Markdown documents
Reports
Source code
Configuration files
However, unrestricted filesystem access is dangerous.
An application should establish a clear boundary.
For example:
var workingDirectory =
Path.Combine(
AppContext.BaseDirectory,
"working");
A file access provider can then be configured around that approved directory.
The idea is simple:
Agent
↓
Approved File Store
↓
Specific Directory
rather than:
Agent
↓
Entire Operating System
Microsoft's recent C# agent-harness example specifically demonstrates restricting file access to an approved working directory rather than giving the agent arbitrary filesystem access.
Not every tool should execute automatically.
Imagine an agent that can:
Read a database
Create a report
Delete a file
Send an email
Modify a record
Execute a command
These actions don't have the same risk level.
A useful agent architecture separates them.
For example:
| Action | Example | Approval |
|---|---|---|
| Read | Read report | Usually automatic |
| Analyze | Calculate totals | Usually automatic |
| Create | Generate report | Depends |
| Modify | Update database | Often appropriate |
| Delete | Delete files | Strong approval |
| External side effect | Send email | Often appropriate |
The goal isn't to ask the user for permission for every operation.
The goal is to put human control around meaningful side effects.
Microsoft's current Agent Framework material demonstrates approval handling as part of the harness approach.
Memory is another important capability.
There are two different ideas that developers should distinguish.
This is information needed to continue the current interaction.
This is information intentionally stored for future interactions.
For example:
User prefers:
- PostgreSQL
- Blazor
- ASP.NET Core
A production application needs an actual persistence mechanism for this information.
The model saying:
"I've remembered that."
doesn't prove that anything has been stored.
The application needs:
Memory request
↓
Storage
↓
Persistence result
↓
Future retrieval
This distinction becomes particularly important in production AI systems.
As an agent becomes more capable, its system instructions can become very large.
Instead of putting everything into one enormous prompt, skills can package domain-specific instructions.
For example:
skills/
├── inventory/
├── reporting/
├── customer-support/
└── accounting/
The agent can discover or load the relevant skill when required.
This can make large agent systems easier to maintain.
Microsoft's recent Agent Framework harness series demonstrates skills as one of the ways to expand an agent without continuously increasing the size of its primary instructions.
Some tasks can be performed independently.
Suppose a user asks:
"Research Microsoft, Apple, and Nvidia and summarize their latest product announcements."
Instead of doing everything sequentially, an agent architecture can delegate separate research tasks.
Conceptually:
Main Agent
│
├── Microsoft Research Agent
│
├── Apple Research Agent
│
└── Nvidia Research Agent
The results can then be combined by the main agent.
Microsoft's Agent Framework harness includes support for background-agent delegation as a separate capability from provider-managed background responses.
This can be useful for workloads where independent tasks can run concurrently.
Some tasks are better solved by executing code than by asking a language model to perform calculations manually.
For example:
Calculate monthly revenue for 500,000 transactions.
A model can describe how to calculate it.
But a program can actually calculate it.
This leads to a useful architecture:
Model
↓
Determine calculation
↓
Generate/choose code
↓
Controlled execution environment
↓
Result
↓
Model explanation
The important word is controlled.
Code execution should not automatically mean unrestricted operating-system access.
Isolation, permissions, timeouts, resource limits, and approval policies remain important.
Traditional applications already use logging, metrics, and tracing.
Agents need observability too.
When an agent produces an unexpected result, developers may need to know:
What did the user ask?
↓
Which model was called?
↓
Which tools were selected?
↓
What arguments were passed?
↓
What did the tools return?
↓
How many model calls occurred?
↓
What was the final response?
Without this information, debugging an AI agent can be difficult.
The Microsoft Agent Framework harness includes OpenTelemetry instrumentation as one of its optional capabilities.
For production applications, observability should be treated as part of the architecture rather than an afterthought.
A chatbot that only generates text has a relatively limited action surface.
An agent can potentially:
Read
Write
Execute
Call APIs
Modify data
Send messages
Run background tasks
That changes the security model.
Developers should consider:
Least-privilege tool access
File-system boundaries
Authentication
Authorization
Input validation
Tool approval
Execution timeouts
Sandboxing
Audit logging
Sensitive-data handling
Rate limiting
The language model should not become an unrestricted administrator of your application.
A good architecture keeps the model inside clearly defined application boundaries.
A practical architecture could look like this:
┌──────────────────┐
│ User │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ ASP.NET Core │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Agent Harness │
└────────┬─────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
Tools Memory Planning
│ │ │
▼ ▼ ▼
Database Storage Todo/Tasks
│
▼
External APIs
This architecture keeps responsibilities separated.
The model handles reasoning.
The harness coordinates the agent.
Your application owns the tools and business rules.
Your infrastructure controls data, permissions, storage, and external services.
An agent harness makes more sense when the application needs multiple agent capabilities.
For example:
If your application only needs:
Prompt → Response
you may not need a full harness.
If the application needs:
Prompt → Tool → Response
a harness becomes more useful.
If the application needs:
Planning
Tools
Memory
Approvals
Multiple steps
Background tasks
Observability
a harness can significantly reduce the amount of orchestration code you need to build yourself.
A framework isn't automatically the right answer for every project.
You may want a custom orchestration layer when:
The workflow is very specialized.
You need strict control over every model call.
The application has unusual execution requirements.
You have existing orchestration infrastructure.
You need a minimal runtime rather than a broad framework.
The advantage of a framework is speed and reusable infrastructure.
The advantage of building your own is complete control.
The right choice depends on the application.
For C# developers, one of the interesting aspects of the current Agent Framework approach is that the agent remains close to normal .NET development.
You can work with:
IChatClient
AIAgent
AIFunction
AgentSession
HarnessAgentOptions
and integrate these components with familiar .NET application architecture.
That means an existing ASP.NET Core application doesn't necessarily need to become a completely separate AI platform.
An agent can be another application capability.
For example:
ASP.NET Core
│
├── Authentication
├── Authorization
├── Business Services
├── Repository Layer
├── APIs
└── AI Agent
├── Tools
├── Memory
├── Planning
└── Approvals
This is particularly relevant for enterprise applications where AI needs to work with existing business systems.
An AI agent is more than a language model that generates text.
Once an application needs to perform real work, it needs orchestration around the model.
That is the role of an agent harness.
With Microsoft's Agent Framework, C# developers can create a harness agent from an IChatClient and progressively add capabilities such as tools, planning, conversation state, memory, file access, approvals, skills, background agents, and observability.
The most important architectural lesson is that the model should not be given unrestricted control.
Your application should define:
What the agent can access
Which tools it can use
Which actions require approval
What information can be remembered
Where files can be accessed
How code can be executed
What should be logged
How the agent is evaluated
That separation makes an AI system easier to understand, test, secure, and maintain.
For .NET developers interested in building AI-powered applications, an agent harness is therefore worth understanding—not because every application needs one, but because it provides a structured way to move from a simple AI chat experience toward an application that can actually perform multi-step work.
As the Microsoft Agent Framework continues to evolve, the important skill for developers will not simply be learning another API. It will be understanding how to combine models, tools, application code, data, permissions, and human oversight into a reliable system.
That is where AI development starts to look much more like software engineering.
An AI agent harness is runtime infrastructure around a language model that helps an agent perform multi-step tasks using tools, planning, state, memory, approvals, and other capabilities.
Microsoft Agent Framework is Microsoft's framework for building AI agents and agentic applications. Its current documentation provides capabilities for agents, tools, sessions, memory, workflows, hosting, and agent harnesses.
With an IChatClient, the current Agent Framework API provides the AsHarnessAgent() extension method:
AIAgent agent = chatClient.AsHarnessAgent();
You can then configure the agent using HarnessAgentOptions.
Depending on its configuration, an AI agent can call tools, work with files, maintain state, plan tasks, use memory, request approval, delegate work, and interact with external services.
No. A chatbot can primarily generate conversational responses. An agent can use tools and follow a multi-step workflow to accomplish a task.
Yes. A harness can be integrated into a .NET application, including applications built with ASP.NET Core. The agent can be exposed through an API, web application, background service, or other application interface.
No. A simple question-and-answer application may not need one. A harness becomes more useful when the application needs tools, planning, memory, approvals, or long-running multi-step tasks.
Security depends on how the application configures its tools, permissions, data access, execution environment, and approval policies. Developers should use least privilege, controlled tool access, appropriate isolation, and auditing for sensitive operations.
It can if the application exposes a database-related tool or service to the agent. The recommended approach is to expose controlled business operations rather than giving the language model unrestricted database credentials.
Agent behavior can involve multiple model calls and tool executions. Tracing these operations helps developers understand what happened, diagnose failures, measure performance, and evaluate the quality of the agent's behavior.
Contact us today to schedule a free, 20-minute call to learn how DotNet Expert Solutions can help you revolutionize the way your company conducts business.
Comments 0