Microsoft Agent Framework for building agentic AI applications in .NET. Covers agents, workflows, multi-agent
orchestration, tool integration (MCP servers, function calling), provider configuration (Azure OpenAI, OpenAI, Microsoft
Foundry, Anthropic, Ollama), chat history management, middleware, and enterprise features (observability,
authentication, responsible AI).
Scope
Agent creation and configuration for all supported providers
Workflow orchestration (sequential, concurrent, group chat, handoff, magentic)
Tool integration (MCP servers, function calling, hosted tools)
Chat history and session management
Middleware for intercepting agent actions
Enterprise observability with OpenTelemetry
Authentication with Microsoft Entra
Responsible AI features (prompt injection protection, content safety)
Out of scope
General async/await patterns and cancellation token propagation -- see [skill:dotnet-csharp-async-patterns]
DI container mechanics and service lifetime management -- see [skill:dotnet-csharp-dependency-injection]
HTTP client resilience and retry policies -- see [skill:dotnet-resilience]
Configuration binding (options pattern, secrets) -- see [skill:dotnet-csharp-configuration]
Cross-references: [skill:dotnet-csharp-async-patterns] for async streaming patterns used with chat completions,
[skill:dotnet-csharp-dependency-injection] for agent service registration in ASP.NET Core, [skill:dotnet-resilience] for
retry policies on AI service calls, [skill:dotnet-csharp-configuration] for managing API keys and model configuration.
Package Landscape
Package
Purpose
Microsoft.Agents.AI
Core abstractions, AIAgent base class
Microsoft.Agents.AI.OpenAI
OpenAI provider support (Chat Completion, Responses, Assistants)
The OpenAI provider supports three client types with different capabilities:
Client Type
API
Best For
Chat Completion
Chat Completions API
Simple agents, broad model support
Responses
Responses API
Full-featured agents with hosted tools
Assistants
Assistants API
Server-managed agents with persistent threads
Chat Completion Agent
using Microsoft.Agents.AI;
using OpenAI;
OpenAIClient client = new OpenAIClient("<your_api_key>");
var chatClient = client.GetChatClient("gpt-4o-mini");
AIAgent agent = chatClient.AsAIAgent(
instructions: "You are a helpful assistant specialized in data analysis.",
name: "DataAnalyst");
var response = await agent.RunAsync("Analyze Q3 sales trends.");
Console.WriteLine(response);
Responses API Agent
using Microsoft.Agents.AI.OpenAI;
using OpenAI;
OpenAIClient client = new OpenAIClient("<your_api_key>");
var responsesClient = client.GetResponsesClient("gpt-4o");
AIAgent agent = responsesClient.AsAIAgent(
instructions: "You are a research assistant with access to web search.",
name: "ResearchAssistant");
// Responses API supports hosted tools (web search, file search, code interpreter)var response = await agent.RunAsync("Find recent articles about .NET 10 features.");
OpenAI Assistants
using Microsoft.Agents.AI.OpenAI;
using OpenAI;
OpenAIClient client = new OpenAIClient("<your_api_key>");
var assistantClient = client.GetAssistantClient();
// Create a persistent assistant with built-in toolsvar assistant = await assistantClient.CreateAssistantAsync(
model: "gpt-4o",
name: "CodeReviewer",
instructions: "You review code for best practices and potential issues.",
tools: ["code_interpreter"]);
AIAgent agent = assistant.AsAIAgent();
Azure OpenAI
Azure OpenAI provides enterprise-grade AI with managed endpoints, private networking, and Microsoft Entra
authentication.
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
AzureOpenAIClient client = new AzureOpenAIClient(
new Uri("https://<resource>.openai.azure.com"),
new DefaultAzureCredential());
var chatClient = client.GetChatClient("gpt-4o");
AIAgent agent = chatClient.AsAIAgent(
instructions: "You are a customer support agent for Contoso.");
var response = await agent.RunAsync("I need help with my order.");
Azure OpenAI also supports Responses and Assistants APIs:
Foundry Agent Service provides managed, scalable agents with built-in content safety, persistent storage, and Microsoft
365 integration.
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Agents.AI;
var persistentAgentsClient = new PersistentAgentsClient(
"https://<resource>.services.ai.azure.com/api/projects/<project>",
new DefaultAzureCredential());
// Create a new agent in the service
AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
model: "gpt-4o-mini",
name: "SupportBot",
instructions: "You handle customer support inquiries.");
// Use the agentvar response = await agent.RunAsync("My account is locked.");
// Or retrieve an existing agent
AIAgent existingAgent = await persistentAgentsClient.GetAIAgentAsync("<agent-id>");
Anthropic
using Microsoft.Agents.AI.Anthropic;
using Anthropic;
var client = new AnthropicClient("<api_key>");
var agent = client.AsAIAgent(
model: "claude-3-opus-20240229",
instructions: "You are a helpful assistant.");
var response = await agent.RunAsync("Explain quantum computing.");
Ollama (Local Models)
using Microsoft.Agents.AI.Ollama;
var client = new OllamaClient(new Uri("http://localhost:11434"));
var agent = client.AsAIAgent(
model: "llama3.2",
instructions: "You are a local AI assistant.");
var response = await agent.RunAsync("Summarize this document.");
Generic IChatClient Agent
Any service implementing Microsoft.Extensions.AI.IChatClient can be used with ChatClientAgent:
using Microsoft.Agents.AI;
// IChatClient from any provider
IChatClient chatClient = GetChatClientFromAnyProvider();
AIAgent agent = new ChatClientAgent(
chatClient,
instructions: "You are a helpful assistant.");
var response = await agent.RunAsync("Hello!");
Workflows
Workflows provide graph-based orchestration for multi-step tasks with type-safe routing, checkpointing, and
human-in-the-loop support.
Sequential Workflow
using Microsoft.Agents.AI.Workflows;
var workflow = WorkflowBuilder.CreateSequential()
.AddStep<ResearchStep>("research")
.AddStep<WriteStep>("write")
.AddStep<ReviewStep>("review")
.Build();
var result = await workflow.ExecuteAsync(new ResearchInput { Topic = "AI in Healthcare" });
Conditional/Branching Workflow
var workflow = WorkflowBuilder.Create()
.AddStep<AnalyzeIntentStep>("analyze")
.AddBranch(
condition: ctx => ctx.Get<Intent>("intent") == Intent.Support,
thenBranch: b => b.AddStep<HandleSupportStep>("support"),
elseBranch: b => b.AddStep<HandleSalesStep>("sales"))
.Build();
Agent Group Chat
Multiple agents collaborating with termination conditions:
using Microsoft.Agents.AI.Workflows;
var analyst = new ChatClientAgent(chatClient,
instructions: "You analyze data and provide insights. Be concise.");
var writer = new ChatClientAgent(chatClient,
instructions: "You take analysis and write clear reports.");
var workflow = WorkflowBuilder.CreateGroupChat()
.AddAgent(analyst, "analyst")
.AddAgent(writer, "writer")
.WithTerminationCondition(ctx =>
ctx.Messages.Last().Content.Contains("[COMPLETE]"))
.WithMaxIterations(10)
.Build();
var result = await workflow.ExecuteAsync("Analyze Q4 sales and write a summary report.");
Handoff Pattern
Transfer control between specialized agents:
var triage = new ChatClientAgent(chatClient,
instructions: "You triage requests and hand off to specialists.");
var billing = new ChatClientAgent(chatClient,
instructions: "You handle billing questions.");
var technical = new ChatClientAgent(chatClient,
instructions: "You handle technical support.");
var workflow = WorkflowBuilder.Create()
.AddStep<AgentStep>("triage", triage)
.AddHandoff(
from: "triage",
condition: ctx => ctx.Get<string>("department") == "billing",
to: billing)
.AddHandoff(
from: "triage",
condition: ctx => ctx.Get<string>("department") == "technical",
to: technical)
.Build();
Magentic Pattern (Lead Agent)
A lead agent directs other agents:
var orchestrator = new ChatClientAgent(chatClient,
instructions: "You are an orchestrator. Delegate tasks to specialists and synthesize results.");
var workflow = WorkflowBuilder.CreateMagentic()
.WithLeadAgent(orchestrator)
.AddSpecialist("researcher", researchAgent, "Research specialist")
.AddSpecialist("coder", codeAgent, "Code specialist")
.Build();
var result = await workflow.ExecuteAsync("Build a web scraper that extracts product prices.");
Tools and Function Calling
Function Tools
publicsealedclassOrderTools
{
[Function("get_order_status")]
[Description("Retrieves the status of an order by ID")]
publicasync Task<OrderStatus> GetOrderStatusAsync(
[Description("The order ID to look up")] string orderId,
CancellationToken ct = default)
{
// Implementationreturnawait _orderService.GetStatusAsync(orderId, ct);
}
[Function("cancel_order")]
[Description("Cancels an order if eligible")]
publicasync Task<CancelResult> CancelOrderAsync(
[Description("The order ID to cancel")] string orderId,
CancellationToken ct = default)
{
// Implementationreturnawait _orderService.CancelAsync(orderId, ct);
}
}
// Register tools with agentvar agent = chatClient.AsAIAgent(
instructions: "You help customers with their orders.",
tools: new OrderTools());
MCP (Model Context Protocol) Tools
Connect to MCP servers for external tools:
using Microsoft.Agents.AI.Tools.MCP;
// Connect to an MCP servervar mcpClient = new MCPClient("https://api.example.com/mcp");
// Get available tools from the servervar tools = await mcpClient.ListToolsAsync();
// Create agent with MCP toolsvar agent = chatClient.AsAIAgent(
instructions: "You have access to external data sources via MCP.",
tools: tools);
// Or connect to hosted MCP servers (Azure OpenAI, OpenAI Responses API)var responsesClient = client.GetResponsesClient("gpt-4o");
var agent = responsesClient.AsAIAgent(
instructions: "You have access to hosted tools.",
hostedTools: ["web_search", "file_search", "code_interpreter"]);
Hosted Tools (Responses API)
using Microsoft.Agents.AI.OpenAI;
var responsesClient = client.GetResponsesClient("gpt-4o");
var agent = responsesClient.AsAIAgent(
instructions: "You are a research assistant.",
hostedTools: new HostedTools
{
WebSearch = new WebSearchTool(),
FileSearch = new FileSearchTool { VectorStoreIds = ["vs_123"] },
CodeInterpreter = new CodeInterpreterTool()
});
var response = await agent.RunAsync("Search for recent papers on climate change and analyze the data.");
Chat History and Sessions
Session Management
using Microsoft.Agents.AI.Sessions;
// Create a new sessionvar session = new AgentSession();
// Multi-turn conversationawait agent.RunAsync("What's the weather?", session);
await agent.RunAsync("Will it rain tomorrow?", session); // Has context from previous turn// Persist session for latervar sessionData = session.Serialize();
// ... save to database ...// Restore session latervar restoredSession = AgentSession.Deserialize(sessionData);
Chat History Providers
Use Redis or other providers for distributed session storage:
using Microsoft.Agents.AI.Telemetry;
// Configure OpenTelemetry
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddAgentFrameworkInstrumentation();
tracing.AddAzureMonitorTraceExporter();
})
.WithMetrics(metrics =>
{
metrics.AddAgentFrameworkMetrics();
metrics.AddAzureMonitorMetricExporter();
});
// All agent interactions automatically emit traces and metrics
Authentication with Microsoft Entra
using Azure.Identity;
using Microsoft.Agents.AI;
// Use managed identity in production
AzureOpenAIClient client = new AzureOpenAIClient(
new Uri("https://<resource>.openai.azure.com"),
new ManagedIdentityCredential());
// Or use Entra ID for user-delegated accessvar credential = new InteractiveBrowserCredential();
var agent = chatClient.AsAIAgent(instructions: "...");
var session = new AgentSession();
awaitforeach (var chunk in agent.RunStreamingAsync("Tell me a story.", session))
{
Console.Write(chunk.Content);
}
Key Principles
Prefer Agents for open-ended tasks -- Use agents when the task is conversational or requires autonomous tool use.
If you can write a deterministic function, do that instead.
Use Workflows for structured processes -- When the process has well-defined steps or requires explicit control
over execution order, use workflows over single agents.
Choose the right API -- Chat Completion for simple cases, Responses API for hosted tools, Assistants for
persistent stateful conversations.
Design focused tools -- Each tool should represent a single domain. Use [Description] attributes so the model
knows when and how to call them.
Do not store API keys in code -- Use environment variables, Azure Key Vault, or Managed Identity (see
[skill:dotnet-csharp-configuration])
Implement middleware for cross-cutting concerns -- Use middleware for logging, authorization, and rate limiting
rather than duplicating logic in tools.
Use chat history providers for distributed apps -- In multi-instance deployments, use Redis or other providers for
session persistence.
Enable observability in production -- Always configure OpenTelemetry tracing and metrics for production
deployments.
Validate inputs in tools -- AI models may call tools with unexpected arguments. Validate all inputs before
executing operations.
Handle cancellation tokens -- Always propagate CancellationToken through tool methods to support timeouts and
user cancellations.
Agent Gotchas
Do not hardcode API keys -- Use DefaultAzureCredential for development and ManagedIdentityCredential for
production. Hardcoded secrets leak into source control.
Do not ignore function return types -- The model receives the serialized result. Return summary DTOs, not full
entity graphs, to avoid exceeding token limits.
Do not create agents per request -- Agents are designed to be long-lived. Register in DI and reuse across
requests.
Do not forget middleware ordering -- Middleware executes in registration order. Place authorization before
logging if you want to log only authorized requests.
Do not assume MCP availability -- Always handle cases where MCP servers are unavailable or return errors.
Do not store sensitive data in chat history -- Filter out PII before persisting chat history. Use context
providers for sensitive data instead.
Do not skip cancellation token propagation -- AI calls can hang or take too long. Always propagate
CancellationToken to allow cancellation.
Do not mix streaming and non-streaming inconsistently -- Choose one pattern per endpoint to avoid confusing
client behavior.