If you have been building AI-powered features in .NET over the past two years, you have probably wrestled with a fragmented landscape. Semantic Kernel gave you enterprise-grade plumbing — dependency injection, telemetry, filters — but its agent abstractions felt heavy. AutoGen offered elegant multi-agent patterns, but lacked the production hardening you needed before deploying to real users. Choosing between them meant committing to a set of trade-offs before you even wrote your first prompt.
That tension is now resolved. On 3 April 2026, Microsoft shipped Agent Framework 1.0 — a single, open-source SDK that merges the best of both projects into one coherent programming model. It ships with stable APIs, long-term support commitments, and first-party connectors for every major model provider. If you are starting a new agent project in .NET today, this is where you should begin.
What Agent Framework actually is
Agent Framework is the direct successor to both Semantic Kernel and AutoGen, built by the same teams. It takes AutoGen's simple agent abstractions and combines them with Semantic Kernel's enterprise features: session-based state management, type safety, middleware pipelines, and OpenTelemetry integration.
The framework is organised around two primary concepts:
- Agents — individual units that use an LLM to process input, call tools, and generate responses
- Workflows — graph-based orchestrations that connect multiple agents and deterministic functions into multi-step processes
Everything else — model clients, sessions, memory providers, middleware — exists to support those two building blocks.
Getting started with a single agent
The core NuGet package is Microsoft.Agents.AI. A minimal agent needs nothing more than an IChatClient implementation and a set of instructions:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
IChatClient chatClient = /* your preferred provider */;
var agent = new ChatClientAgent(chatClient, instructions: "You are a helpful assistant.");
Console.WriteLine(await agent.RunAsync("Summarise the latest .NET release notes."));
The ChatClientAgent class wraps any IChatClient from Microsoft.Extensions.AI, which means you are not locked into a single model provider. The same agent code works against Azure OpenAI, OpenAI, Anthropic Claude, Amazon Bedrock, Google Gemini, and Ollama — you only swap the client construction.
For convenience, the framework ships helper extension methods on each provider's SDK client. Here is an agent backed by Azure Foundry:
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
AIAgent agent = new AIProjectClient(
new Uri("https://your-foundry-service.services.ai.azure.com/api/projects/your-project"),
new DefaultAzureCredential())
.AsAIAgent(
model: "gpt-4o-mini",
instructions: "You are good at summarising documents.",
name: "Summariser");
Console.WriteLine(await agent.RunAsync("What changed in .NET 10?"));
And here is the same pattern using the Anthropic SDK directly:
using Anthropic;
using Microsoft.Agents.AI;
var client = new AnthropicClient { ApiKey = apiKey };
AIAgent agent = client.AsAIAgent(
model: "claude-sonnet-4-20250514",
instructions: "You answer concisely.",
name: "Concise");
The AIAgent base class provides a consistent interface regardless of the provider behind it. This matters when you start composing agents into workflows or multi-agent orchestrations — the orchestration layer does not care which model is running underneath.
Adding tools
An agent without tools is just a chat wrapper. Agent Framework supports several tool types, but function tools are the most common in .NET applications. You define a regular C# method and register it with the agent:
using Microsoft.Extensions.AI;
public static class InventoryTools
{
[Description("Check current stock level for a product by SKU")]
public static async Task<int> CheckStock(
[Description("The product SKU")] string sku,
IInventoryService inventory)
{
return await inventory.GetStockLevelAsync(sku);
}
}
var agent = new AIProjectClient(endpoint, credential)
.AsAIAgent(
model: "gpt-4o-mini",
instructions: "You help warehouse staff check inventory.",
name: "InventoryAgent",
tools: [AIFunctionFactory.Create(InventoryTools.CheckStock)]);
The framework handles the full tool-calling loop: it sends the tool definitions to the model, receives tool-call requests, invokes your functions, and feeds the results back into the conversation. You never touch the raw message plumbing.
Beyond function tools, the framework supports code interpreters, file search, web search, and — critically — both hosted and local MCP (Model Context Protocol) servers. MCP support means your agents can dynamically discover and invoke tools from any MCP-compatible server without hardcoding function definitions.
Agents as tools
One of the more interesting patterns is using an agent as a tool for another agent. If you have a specialist agent that handles, say, currency conversion, you can expose it as a function tool to a broader assistant:
AIAgent currencyAgent = openAiClient.AsAIAgent(
model: "gpt-4o-mini",
instructions: "You convert currencies using current exchange rates.",
name: "CurrencyConverter",
description: "Converts amounts between currencies.",
tools: [AIFunctionFactory.Create(GetExchangeRate)]);
AIAgent assistantAgent = openAiClient.AsAIAgent(
model: "gpt-4o-mini",
instructions: "You are a travel planning assistant.",
tools: [currencyAgent.AsAIFunction()]);
Console.WriteLine(await assistantAgent.RunAsync("How much is 500 GBP in Japanese yen?"));
The outer agent sees the inner agent as a callable function. The inner agent runs its own tool-calling loop independently. This is a clean way to compose capabilities without coupling agent implementations.
Middleware: cross-cutting concerns without prompt pollution
If you have worked with ASP.NET Core, the middleware model will feel familiar. Agent Framework provides three interception points:
- Agent run middleware — intercepts the entire agent execution, letting you inspect or modify input messages and output responses
- Function calling middleware — intercepts individual tool invocations, useful for logging, validation, or overriding results
- IChatClient middleware — intercepts the raw model calls, giving you access to the messages and options sent to the inference service
Here is an agent run middleware that logs conversation sizes:
async Task<AgentResponse> LoggingMiddleware(
IEnumerable<ChatMessage> messages,
AgentSession? session,
AgentRunOptions? options,
AIAgent innerAgent,
CancellationToken cancellationToken)
{
Log.Information("Agent invoked with {Count} messages", messages.Count());
var response = await innerAgent.RunAsync(messages, session, options, cancellationToken);
Log.Information("Agent responded with {Count} messages", response.Messages.Count);
return response;
}
And here is a function calling middleware that audits every tool invocation:
async ValueTask<object?> AuditToolCalls(
AIAgent agent,
FunctionInvocationContext context,
Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
CancellationToken cancellationToken)
{
Log.Information("Tool call: {Function} with args: {@Args}",
context.Function.Name, context.Arguments);
var result = await next(context, cancellationToken);
Log.Information("Tool result: {@Result}", result);
return result;
}
You register middleware using a builder pattern:
var secureAgent = agent
.AsBuilder()
.Use(runFunc: LoggingMiddleware, runStreamingFunc: LoggingStreamingMiddleware)
.Use(AuditToolCalls)
.Build();
This keeps your agent instructions clean. Content safety filters, compliance policies, rate limiting, and telemetry all live in middleware rather than being baked into prompts.
Workflows for deterministic orchestration
Not everything should be left to an LLM to decide. When your process has well-defined steps — validate input, enrich data, call an agent, format the output — you want explicit control over execution order.
Agent Framework workflows are graph-based: you define executors (processing units) and edges (connections between them), then the engine runs them in a deterministic order with optional branching and parallelism.
Key workflow features include:
- Type-safe routing between executors, preventing runtime mismatches
- Checkpointing for saving and resuming long-running processes
- Human-in-the-loop patterns via built-in request/response integration
- Parallel execution of independent branches
The framework also ships several pre-built multi-agent orchestration patterns: sequential, concurrent, handoff, group chat, and the Magentic-One pattern for complex collaborative problem-solving.
The rule of thumb from the documentation is straightforward: if you can write a function to handle the task, do that. If you need autonomous tool use and planning, use an agent. If you need explicit control over a multi-step process involving agents and functions, use a workflow.
What is stable and what is preview
Microsoft drew a clear line in the 1.0 release between stable and preview features. This is worth paying attention to before you ship to production:
Stable (1.0):
- Agent abstractions and the
AIAgent/ChatClientAgenttypes - All first-party model provider connectors
- Function tools, MCP clients, code interpreters, and file search
- Middleware pipeline (agent run, function calling,
IChatClient) - Graph-based workflows with checkpointing
- Multi-agent orchestration patterns
- Declarative YAML-based agent configuration
- Session-based state management and memory providers
Preview:
- DevUI (browser-based agent debugger)
- Foundry-hosted agent integration on Azure Durable Functions
- GitHub Copilot SDK and Claude Code SDK adapters
- Skills (reusable domain capability packages)
- Frontend streaming adapters (AG-UI, CopilotKit, ChatKit)
The stable surface area is substantial. You can build and deploy production agents today without touching any preview features.
Common pitfalls
Reaching for workflows when a single agent suffices. If your task is conversational and open-ended, a single agent with tools is usually the right choice. Workflows add value when you need deterministic step ordering, not when you want to feel organised.
Ignoring the IChatClient abstraction. Constructing agents directly against a specific provider SDK works, but it couples your code to that provider. Building against IChatClient means switching from Azure OpenAI to Anthropic is a configuration change, not a rewrite.
Skipping middleware for cross-cutting concerns. It is tempting to stuff safety checks, logging, and compliance rules into the agent instructions. This makes prompts fragile and hard to test. Middleware keeps these concerns separate and composable.
Using DefaultAzureCredential in production without thought. The docs warn about this explicitly: DefaultAzureCredential probes multiple credential sources sequentially, which adds latency and can cause surprising authentication behaviour. In production, use a specific credential like ManagedIdentityCredential.
Not providing both streaming and non-streaming middleware. If you only register non-streaming middleware, the framework will use it for streaming calls too — by running them in non-streaming mode. This silently degrades the user experience. Always provide both overloads.
Summary
- Agent Framework 1.0 unifies Semantic Kernel and AutoGen into a single SDK with stable APIs and long-term support
- The
AIAgentandChatClientAgenttypes work with anyIChatClientimplementation, keeping your code provider-agnostic - Function tools, MCP servers, code interpreters, and agent-as-tool composition cover most practical tool-calling scenarios
- The middleware pipeline handles logging, security, and compliance without polluting your prompts
- Graph-based workflows give you deterministic control over multi-step processes involving agents and functions
- The stable surface is broad enough for production use today; preview features add developer tooling and hosting integrations