Microsoft Agent Framework and Multi-Agent Patterns in .NET

Intermediate Original .NET 9 Microsoft.SemanticKernel 1.34.0 Microsoft.SemanticKernel.Agents.Core 1.34.0
By Rajesh Mishra · Feb 28, 2026 · Verified: Feb 28, 2026 · 14 min read

Agents Are Not Chatbots

The terms get conflated constantly, but the distinction matters for architecture decisions. A chatbot operates in a simple request-response loop: the user sends a message, the model generates a reply, the conversation continues. There is no persistent goal, no tool use, no autonomous decision-making.

An AI agent is fundamentally different. It maintains an objective, can observe its environment through tools, reasons about what to do next, acts by invoking functions or APIs, and evaluates the results before deciding whether to continue. Agents are goal-directed systems. They don’t just answer questions — they accomplish tasks.

This distinction shapes everything downstream. If you need a customer-facing Q&A interface, a chatbot is appropriate. If you need a system that researches a topic, drafts a report, reviews it for accuracy, and publishes it — that’s agent territory.

Microsoft’s Agent Framework, built on Semantic Kernel, provides the abstractions you need to build both single-agent and multi-agent systems in .NET.

The Microsoft Agent Framework

The Agent Framework ships as part of the Semantic Kernel ecosystem. At its core, it extends SK with agent-specific primitives while reusing everything you already know — the Kernel, plugins, AI service connectors, and filters.

Install the required packages:

dotnet add package Microsoft.SemanticKernel --version 1.34.0
dotnet add package Microsoft.SemanticKernel.Agents.Core --version 1.34.0

The framework introduces two primary abstractions:

  • ChatCompletionAgent — A single agent backed by a chat completion model. It holds a name, instructions (system prompt), and a Kernel with registered plugins.
  • AgentGroupChat — An orchestration container that manages conversations between one or more agents, with configurable selection and termination strategies.

These building blocks are deliberately minimal. Microsoft designed the framework so that complex behavior emerges from composition, not from a sprawling API surface.

Defining Your First Agent

A ChatCompletionAgent wraps a configured Kernel with identity and purpose. Here is a straightforward example — a code review agent that analyzes C# code for common issues:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;

// Build the kernel with your AI service
Kernel kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName: "gpt-4o",
        endpoint: "https://your-resource.openai.azure.com/",
        apiKey: Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!)
    .Build();

// Define the agent
ChatCompletionAgent reviewerAgent = new()
{
    Name = "CodeReviewer",
    Instructions = """
        You are an expert .NET code reviewer. Analyze submitted C# code for:
        - Potential null reference exceptions
        - Missing disposal of IDisposable resources
        - Async/await anti-patterns
        - Security vulnerabilities
        Provide specific, actionable feedback with corrected code examples.
        """,
    Kernel = kernel
};

To have a conversation with this single agent:

ChatHistory history = [];
history.AddUserMessage("Review this code:\n```csharp\nvar client = new HttpClient();\nvar result = client.GetStringAsync(url).Result;\n```");

await foreach (ChatMessageContent response in reviewerAgent.InvokeAsync(history))
{
    Console.WriteLine(response.Content);
}

The agent uses the kernel’s configured AI service to generate responses, guided by its instructions. Nothing more, nothing less.

Registering Tools for Agents

Agents become genuinely useful when they can interact with the outside world. Tool registration works through the same plugin system that Semantic Kernel uses — you register plugins on the agent’s kernel, and the AI model decides when to call them.

public class GitPlugin
{
    [KernelFunction("get_recent_commits")]
    [Description("Retrieves the last N commits from the current git branch")]
    public async Task<string> GetRecentCommitsAsync(
        [Description("Number of commits to retrieve")] int count = 5)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo("git", $"log --oneline -n {count}")
            {
                RedirectStandardOutput = true,
                UseShellExecute = false
            }
        };
        process.Start();
        return await process.StandardOutput.ReadToEndAsync();
    }

    [KernelFunction("get_file_diff")]
    [Description("Gets the git diff for a specific file")]
    public async Task<string> GetFileDiffAsync(
        [Description("Path to the file")] string filePath)
    {
        var process = new Process
        {
            StartInfo = new ProcessStartInfo("git", $"diff {filePath}")
            {
                RedirectStandardOutput = true,
                UseShellExecute = false
            }
        };
        process.Start();
        return await process.StandardOutput.ReadToEndAsync();
    }
}

Register the plugin and enable automatic function calling:

Kernel toolKernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        deploymentName: "gpt-4o",
        endpoint: "https://your-resource.openai.azure.com/",
        apiKey: Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY")!)
    .Build();

toolKernel.Plugins.AddFromType<GitPlugin>();

ChatCompletionAgent commitReviewAgent = new()
{
    Name = "CommitReviewer",
    Instructions = "Review recent git commits. Use the available tools to inspect commit history and file diffs. Summarize changes and flag potential issues.",
    Kernel = toolKernel,
    Arguments = new KernelArguments(
        new OpenAIPromptExecutionSettings
        {
            FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
        })
};

When this agent receives a message like “Review the last 3 commits,” it autonomously calls get_recent_commits, examines the results, optionally calls get_file_diff for specific files, and synthesizes a review. That’s the agent loop in action — observe, reason, act, evaluate.

Multi-Agent Patterns

Single agents handle focused tasks well. But many real-world workflows require specialization — different perspectives, different capabilities, or different stages of processing. That’s where multi-agent patterns come in.

Pattern 1: Writer/Editor (Two-Agent Collaboration)

The most intuitive multi-agent pattern. One agent produces content, another critiques and refines it.

ChatCompletionAgent writerAgent = new()
{
    Name = "TechnicalWriter",
    Instructions = """
        You are a technical writer for .NET documentation.
        Write clear, accurate content with code examples.
        When the editor provides feedback, revise accordingly.
        Say APPROVED when the editor approves your work.
        """,
    Kernel = kernel
};

ChatCompletionAgent editorAgent = new()
{
    Name = "TechnicalEditor",
    Instructions = """
        You are a senior technical editor. Review the writer's output for:
        - Technical accuracy
        - Code correctness
        - Clarity and conciseness
        Provide specific feedback. Say APPROVED when the content meets standards.
        """,
    Kernel = kernel
};

Orchestrate them with AgentGroupChat:

AgentGroupChat chat = new(writerAgent, editorAgent)
{
    ExecutionSettings = new()
    {
        TerminationStrategy = new ApprovalTerminationStrategy()
        {
            Agents = [editorAgent],
            MaximumIterations = 8
        }
    }
};

chat.AddChatMessage(new ChatMessageContent(
    AuthorRole.User,
    "Write a 200-word explanation of dependency injection in .NET."));

await foreach (ChatMessageContent message in chat.InvokeAsync())
{
    Console.WriteLine($"[{message.AuthorName}]: {message.Content}");
}

You need a termination strategy to prevent infinite loops. Here is a simple one that watches for the keyword “APPROVED”:

public class ApprovalTerminationStrategy : TerminationStrategy
{
    protected override Task<bool> ShouldAgentTerminateAsync(
        Agent agent, IReadOnlyList<ChatMessageContent> history,
        CancellationToken cancellationToken = default)
    {
        bool isApproved = history.Last().Content?.Contains("APPROVED",
            StringComparison.OrdinalIgnoreCase) ?? false;
        return Task.FromResult(isApproved);
    }
}

Pattern 2: Sequential Pipeline

Agents execute in a strict order. Each agent processes the accumulated conversation and hands off to the next. This pattern works well for staged processing like research, then draft, then review, then format.

ChatCompletionAgent researchAgent = new()
{
    Name = "Researcher",
    Instructions = "Gather and summarize relevant facts about the topic. Be thorough.",
    Kernel = kernelWithSearchPlugin
};

ChatCompletionAgent drafterAgent = new()
{
    Name = "Drafter",
    Instructions = "Using the researcher's findings, draft a well-structured article.",
    Kernel = kernel
};

ChatCompletionAgent factCheckerAgent = new()
{
    Name = "FactChecker",
    Instructions = "Verify the claims in the draft against the research. Flag inaccuracies. Say VERIFIED when satisfied.",
    Kernel = kernel
};

AgentGroupChat pipeline = new(researchAgent, drafterAgent, factCheckerAgent)
{
    ExecutionSettings = new()
    {
        SelectionStrategy = new SequentialSelectionStrategy(),
        TerminationStrategy = new ApprovalTerminationStrategy
        {
            Agents = [factCheckerAgent],
            MaximumIterations = 6
        }
    }
};

The SequentialSelectionStrategy ensures agents speak in the order they were added to the group chat. Predictable and easy to debug.

Pattern 3: Selector-Based Routing

For dynamic workflows where the next agent depends on context, use a KernelFunctionSelectionStrategy. The AI itself decides which agent should respond next.

KernelFunction selectionFunction = KernelFunctionFactory.CreateFromPrompt(
    """
    Given the conversation so far, determine which agent should respond next.
    Agents: {{$agents}}

    Rules:
    - If code needs to be written, select Coder
    - If code needs review, select Reviewer
    - If tests need to be written, select Tester

    Respond with ONLY the agent name.
    """);

AgentGroupChat dynamicChat = new(coderAgent, reviewerAgent, testerAgent)
{
    ExecutionSettings = new()
    {
        SelectionStrategy = new KernelFunctionSelectionStrategy(
            selectionFunction, kernel)
        {
            ResultParser = (result) => result.GetValue<string>()?.Trim() ?? "Coder"
        },
        TerminationStrategy = new MaxIterationTerminationStrategy(10)
    }
};

This pattern is powerful but harder to predict. Use it when the workflow genuinely requires dynamic routing, not as a default choice.

Built-In Observability

The Agent Framework inherits Semantic Kernel’s filter pipeline, giving you observability without bolting on custom middleware. You can intercept every function invocation, every prompt sent to the model, and every response received.

public class AgentObservabilityFilter : IFunctionInvocationFilter
{
    private readonly ILogger _logger;

    public AgentObservabilityFilter(ILogger<AgentObservabilityFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnFunctionInvocationAsync(
        FunctionInvocationContext context,
        Func<FunctionInvocationContext, Task> next)
    {
        _logger.LogInformation(
            "Agent invoking function {Plugin}.{Function}",
            context.Function.PluginName,
            context.Function.Name);

        var stopwatch = Stopwatch.StartNew();
        await next(context);
        stopwatch.Stop();

        _logger.LogInformation(
            "Function {Function} completed in {ElapsedMs}ms",
            context.Function.Name,
            stopwatch.ElapsedMilliseconds);
    }
}

Register the filter on each agent’s kernel:

var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(/* ... */);
builder.Services.AddSingleton<IFunctionInvocationFilter, AgentObservabilityFilter>();
Kernel observableKernel = builder.Build();

For production systems, pair this with OpenTelemetry to export traces, metrics, and logs to your monitoring infrastructure. Semantic Kernel emits traces following the OpenTelemetry gen_ai semantic conventions, so your agent interactions show up naturally in distributed tracing tools.

Single Agent vs Multi-Agent: Making the Decision

Not every problem needs multiple agents. In fact, most problems don’t. Here is a framework for deciding.

Use a single agent when:

  • The task has a single, well-defined objective
  • One system prompt can adequately describe the required behavior
  • The tools needed are cohesive (they all serve the same purpose)
  • Latency matters — each additional agent adds round trips to the AI service

Use multiple agents when:

  • You need genuinely different perspectives (writer vs editor, optimist vs critic)
  • Different stages require different tool sets that would confuse a single agent
  • You want a separation of concerns — each agent has a focused responsibility
  • The workflow benefits from iterative refinement across specialized roles

Real-World Trade-Offs

Multi-agent systems introduce complexity that single-agent setups avoid entirely.

Cost. Every agent turn is an API call. A three-agent pipeline with two iterations generates at least six LLM calls for a single user request. Budget accordingly.

Latency. Sequential agent conversations are inherently serial. If your writer/editor loop runs four turns, that’s four round-trip API calls before the user sees a result. Consider streaming the final output.

Debugging. When three agents collaborate and the output is wrong, identifying which agent introduced the error requires careful logging. The observability filters discussed above become essential, not optional.

Determinism. Multi-agent conversations are less predictable than single-agent interactions. The same input can produce different agent-to-agent dynamics. Build termination strategies with hard limits (MaximumIterations) to prevent runaway conversations.

Start with the simplest architecture that could work. Build a single agent with clear instructions and good tools. If you find that the instructions are becoming contradictory or the tool set is too large for the model to use reliably, that’s when decomposition into multiple agents earns its keep.

What’s Next

The Agent Framework continues to evolve rapidly within the Semantic Kernel ecosystem. Concepts like process orchestration (Microsoft.SemanticKernel.Process) and declarative agent definitions are on the roadmap.

For now, focus on mastering the core patterns covered here. Understand how Semantic Kernel’s architecture works, get comfortable with function calling and tool use, and read the official Agent Framework documentation to stay current with API changes.

The Microsoft.SemanticKernel.Agents.Core package on NuGet is the single dependency you need. If you’re new to the concept of agents, our practical definition of AI agents provides additional context on where the industry is heading.

Build agents that accomplish real tasks. Keep the architecture as simple as the problem allows. Add agents only when specialization genuinely improves outcomes.

AI-Friendly Summary

Summary

This article covers Microsoft's Agent Framework for .NET, built on Semantic Kernel. It explains the difference between chatbots and AI agents, introduces ChatCompletionAgent and AgentGroupChat, demonstrates single-agent and multi-agent patterns in C#, covers tool registration for agents, and discusses built-in observability. Developers will learn when to choose single vs multi-agent architectures and understand the real-world trade-offs involved.

Key Takeaways

  • Agents differ from chatbots — they maintain goals, use tools, and execute multi-step plans
  • ChatCompletionAgent wraps a Kernel with instructions and a name for identity
  • AgentGroupChat orchestrates conversations between multiple agents
  • Common patterns include writer/editor, sequential pipeline, and round-robin
  • Agents reuse Semantic Kernel plugins for tool registration
  • Built-in filters provide observability without custom instrumentation
  • Start with a single agent and add agents only when you have distinct roles

Implementation Checklist

  • Install Microsoft.SemanticKernel.Agents.Core NuGet package
  • Define each agent with a clear name, instructions, and dedicated kernel
  • Register tools as plugins on the agent's kernel
  • Choose a termination strategy for AgentGroupChat
  • Implement selection strategy for multi-agent routing
  • Add function invocation filters for observability
  • Test individual agents before combining into multi-agent workflows

Frequently Asked Questions

What is an AI agent vs a chatbot?

A chatbot responds to messages in a conversational loop with no persistent goals. An AI agent maintains objectives, can use tools, make decisions, and execute multi-step plans autonomously. Agents observe their environment, reason about next steps, and take actions — chatbots simply generate the next reply.

How does Microsoft Agent Framework relate to Semantic Kernel?

Microsoft Agent Framework is built directly on top of Semantic Kernel. It extends SK's kernel, plugins, and AI service connectors with agent-specific abstractions like ChatCompletionAgent and AgentGroupChat. You configure agents using the same Kernel builder, register the same plugins, and use the same AI connectors.

Can I build multi-agent systems in .NET?

Yes. The Microsoft.SemanticKernel.Agents.Core NuGet package provides AgentGroupChat, which enables multiple agents to collaborate in structured conversations. You can implement round-robin, sequential handoff, and selector-based orchestration patterns entirely in C#.

What are common multi-agent patterns?

The most widely used patterns are writer/editor (one agent drafts, another refines), sequential pipeline (agents execute in a fixed order), round-robin (agents take turns), and selector-based routing (a strategy selects which agent speaks next based on context). Each pattern suits different workflow requirements.

Related Articles

Was this article useful?

Feedback is anonymous and helps us improve content quality.

Discussion

Engineering discussion powered by GitHub Discussions.

#AI Agents #Microsoft Agent Framework #Multi-Agent #Semantic Kernel #.NET AI