Skip to main content

Overview

In the Microsoft Agent Framework .NET SDK, AIAgent is the foundational abstraction for creating AI-powered agents. Agents encapsulate an AI model, instructions, tools, and conversation management capabilities.

Core Concepts

AIAgent Base Class

All agents derive from the AIAgent abstract base class, which provides:
  • Unique Identity: Every agent has an Id and optional Name and Description
  • Conversation Management: Agents can create and manage AgentSession instances for multi-turn conversations
  • Execution Methods: RunAsync() and RunStreamingAsync() for synchronous and streaming responses
public abstract class AIAgent
{
    public string Id { get; }
    public virtual string? Name { get; }
    public virtual string? Description { get; }
    
    public abstract Task<AgentResponse> RunAsync(
        IEnumerable<ChatMessage> messages,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default);
    
    public abstract IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(
        IEnumerable<ChatMessage> messages,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default);
    
    public ValueTask<AgentSession> CreateSessionAsync(
        CancellationToken cancellationToken = default);
}

ChatClientAgent

The primary concrete implementation is ChatClientAgent, which wraps an IChatClient from Microsoft.Extensions.AI to interact with various AI providers.

Creating Agents

Basic Agent Creation

Use the AsAIAgent() extension method on any IChatClient to create an agent:
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Chat;

var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";

// Create an agent using Azure OpenAI
AIAgent agent = new AzureOpenAIClient(
    new Uri(endpoint),
    new DefaultAzureCredential())
    .GetChatClient(deploymentName)
    .AsAIAgent(
        instructions: "You are a helpful assistant.",
        name: "Assistant");

// Run the agent
AgentResponse response = await agent.RunAsync("What is the capital of France?");
Console.WriteLine(response.Text);
DefaultAzureCredential is convenient for development but requires careful consideration in production. Consider using a specific credential (e.g., ManagedIdentityCredential) to avoid latency issues and potential security risks.

Agent with Configuration Options

For more control, use ChatClientAgentOptions:
using Microsoft.Extensions.AI;

AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions()
{
    Name = "ResearchAssistant",
    Description = "An agent that helps with research tasks",
    ChatOptions = new ChatOptions()
    {
        Instructions = "You are an expert research assistant.",
        Temperature = 0.7f,
        MaxOutputTokens = 2000
    }
});

Running Agents

Simple Invocation

// Single-turn interaction
AgentResponse response = await agent.RunAsync("Tell me a joke about programming.");
Console.WriteLine(response.Text);

Streaming Responses

// Stream responses for real-time output
await foreach (var update in agent.RunStreamingAsync("Write a short story about AI."))
{
    Console.Write(update.Text);
}

Multi-Turn Conversations

Use AgentSession to maintain conversation context across multiple turns:
// Create a session for the conversation
AgentSession session = await agent.CreateSessionAsync();

// First turn
var response1 = await agent.RunAsync("Tell me a joke about pirates.", session);
Console.WriteLine(response1.Text);

// Second turn - agent remembers previous context
var response2 = await agent.RunAsync(
    "Now tell it in the voice of a pirate's parrot.", 
    session);
Console.WriteLine(response2.Text);

Streaming Multi-Turn Conversations

AgentSession session = await agent.CreateSessionAsync();

await foreach (var update in agent.RunStreamingAsync("Tell me a joke.", session))
{
    Console.Write(update.Text);
}

await foreach (var update in agent.RunStreamingAsync("Make it funnier.", session))
{
    Console.Write(update.Text);
}

Structured Output

Using ResponseFormat

Request structured JSON output matching a specific schema:
using System.ComponentModel;
using System.Text.Json.Serialization;

[Description("Information about a city")]
public class CityInfo
{
    [JsonPropertyName("name")]
    public string? Name { get; set; }
    
    [JsonPropertyName("population")]
    public int? Population { get; set; }
    
    [JsonPropertyName("country")]
    public string? Country { get; set; }
}

AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions()
{
    Name = "CityExpert",
    ChatOptions = new ChatOptions()
    {
        Instructions = "You provide information about cities.",
        ResponseFormat = ChatResponseFormat.ForJsonSchema<CityInfo>()
    }
});

AgentResponse response = await agent.RunAsync(
    "Tell me about Paris, France.");

// Deserialize the structured output
CityInfo city = JsonSerializer.Deserialize<CityInfo>(response.Text);
Console.WriteLine($"City: {city.Name}, Population: {city.Population}");

Using Generic RunAsync<T>

// Directly get typed result
AgentResponse<CityInfo> response = await agent.RunAsync<CityInfo>(
    "Tell me about Tokyo, Japan.");

CityInfo city = response.Result;
Console.WriteLine($"City: {city.Name}");

Agent as a Tool

Agents can be converted to AIFunction instances and used as tools by other agents:
// Create a specialized agent
AIAgent weatherAgent = weatherChatClient.AsAIAgent(
    name: "WeatherAgent",
    instructions: "You provide weather information for cities.");

// Convert agent to a function tool
AIFunction weatherTool = weatherAgent.AsAIFunction();

// Use it as a tool in another agent
AIAgent mainAgent = chatClient.AsAIAgent(
    instructions: "You are a travel assistant.",
    tools: [weatherTool]);

var response = await mainAgent.RunAsync(
    "What's the weather like in Paris?");

Agent Builder Pattern

Use AIAgentBuilder to compose agents with middleware and additional functionality:
using Microsoft.Extensions.AI;

AIAgent baseAgent = chatClient.AsAIAgent(
    instructions: "You are a helpful assistant.");

// Build agent with middleware
AIAgent enhancedAgent = baseAgent
    .AsBuilder()
    .Use(LoggingMiddleware)
    .Use(FilteringMiddleware)
    .Build();

async Task<AgentResponse> LoggingMiddleware(
    IEnumerable<ChatMessage> messages,
    AgentSession? session,
    AgentRunOptions? options,
    AIAgent innerAgent,
    CancellationToken cancellationToken)
{
    Console.WriteLine($"[{DateTime.Now}] Agent invoked");
    var response = await innerAgent.RunAsync(messages, session, options, cancellationToken);
    Console.WriteLine($"[{DateTime.Now}] Agent completed");
    return response;
}

Learn More

Explore middleware patterns for agents

Session Management

Creating Sessions

// Create a new session
AgentSession session = await agent.CreateSessionAsync();

Serializing Sessions

Sessions can be serialized for persistence:
// Serialize session state
JsonElement sessionData = await agent.SerializeSessionAsync(session);

// Store in database, file, etc.
string json = JsonSerializer.Serialize(sessionData);

// Later, deserialize
JsonElement restored = JsonSerializer.Deserialize<JsonElement>(json);
AgentSession restoredSession = await agent.DeserializeSessionAsync(restored);

// Continue conversation
var response = await agent.RunAsync("Continue our discussion.", restoredSession);

Agent Run Options

Customize agent behavior per request with AgentRunOptions:
var options = new ChatClientAgentRunOptions(new ChatOptions()
{
    Temperature = 0.9f,
    MaxOutputTokens = 500,
    Tools = [additionalTool]
});

var response = await agent.RunAsync(
    "Be creative!", 
    session, 
    options);

Best Practices

Always use AgentSession for multi-turn conversations to maintain context and state.
Pass CancellationToken to agent methods to support graceful cancellation:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var response = await agent.RunAsync(query, cancellationToken: cts.Token);
Agents may hold resources. Consider disposal patterns:
// If agent implements IDisposable/IAsyncDisposable
await using var agent = CreateAgent();
When extracting data, use structured output with strongly-typed classes rather than parsing text.

Next Steps

Tools & Functions

Add function calling capabilities to agents

Middleware

Enhance agents with middleware pipelines

Memory & Context

Add memory and context providers

Providers

Connect to different AI providers

Build docs developers (and LLMs) love