·15 min read

Microsoft Agent Framework: Microsoft's unified agent framework (MIT, 11.6k stars) that succeeds both Semantic Kernel and AutoGen

Microsoft's unified agent framework that succeeds both Semantic Kernel and AutoGen — with dedicated migration guides, 160+ contributors, and GA release v1.0.0.

The Problem

For two years, Microsoft maintained two separate AI agent frameworks under the same roof. Semantic Kernel, launched in 2023, was the enterprise play — deeply integrated with the .NET ecosystem, Azure, and Microsoft 365 Copilot. AutoGen, launched in 2024, was the research play — multi-agent conversations, group chat patterns, and academic flexibility. Both were MIT-licensed. Both had passionate communities. Both solved overlapping problems with incompatible APIs.

The cost of this split was real. Teams building on Semantic Kernel who wanted AutoGen’s multi-agent patterns had to rewrite their orchestration layer. Teams on AutoGen who needed Semantic Kernel’s enterprise middleware (telemetry, DI, Azure integration) had to stitch together two frameworks with no compatibility layer. Microsoft’s own documentation team maintained two separate doc sets, two sample galleries, and two migration guides. Contributors had to choose which repo to invest in.

Dimension Before (Two Frameworks) After (Unified Agent Framework)
Agent creation API Kernel + ChatCompletionAgent (SK) vs AssistantAgent (AutoGen) Single Agent class with chat_client + instructions
Tool registration [KernelFunction] attribute (SK) vs FunctionTool class (AutoGen) Unified @tool decorator with automatic schema inference
Multi-agent orchestration Manual Kernel wiring (SK) vs Team/GraphFlow (AutoGen) WorkflowBuilder graph API + @workflow functional API
Middleware Plugin pipeline (SK) Unified middleware system for agents and workflows
Checkpointing None built-in Superstep-level checkpointing with Cosmos DB, file, or in-memory storage
Observability OpenTelemetry via Kernel (SK) Zero-config OpenTelemetry via environment variables
Migration path None Dedicated migration guides for SK and AutoGen with compatibility layer
Package count 20+ NuGet/PyPI packages across two repos Single agent-framework package with optional extras
Contributors ~100 across two repos 160+ in one repo
GitHub stars ~20k SK + ~30k AutoGen (combined) 11.6k and growing (new unified repo)

Why this matters: The unification is not a rebranding exercise. Microsoft’s own benchmarks show the Agent Framework is 6x faster than AutoGen on multi-agent workflows and 4x more token-efficient than CrewAI. The framework ships with dedicated migration guides for both Semantic Kernel and AutoGen, a compatibility layer for existing KernelFunction instances, and the same team that built both predecessors. If you are starting a new agent project in 2026, this is the framework Microsoft wants you to use.

The Investigation

The decision to unify Semantic Kernel and AutoGen into a single Agent Framework was not made lightly. Microsoft’s internal investigation surfaced three root causes that made the split untenable.

Root cause 1: The kernel abstraction was the wrong boundary.

Semantic Kernel’s core abstraction was the Kernel object — a dependency injection container that held the model connection, plugin registry, memory store, and logging pipeline. Every agent needed a Kernel, and every Kernel carried the full weight of the SK ecosystem. This made sense for enterprise apps that needed all those services, but it was overkill for simple agent patterns. AutoGen took the opposite approach: no kernel, no DI, just agents and teams. The result was two frameworks that could not share code because their fundamental abstractions were incompatible.

What this means: The Agent Framework replaces the Kernel with a lightweight chat_client abstraction. An agent is just a chat client plus instructions plus optional tools. No DI container. No plugin registry. No memory store unless you add one. The framework’s Agent class is 50 lines of core logic — everything else is middleware.

Root cause 2: Multi-agent orchestration was bolted on, not designed in.

Semantic Kernel added multi-agent support in v1.12 (September 2024) with AgentGroupChat and ChatCompletionAgent. The API was a thin wrapper over the existing Kernel infrastructure — agents shared a kernel, communicated through a shared chat history, and had no formal orchestration model. AutoGen’s Team and GraphFlow were more sophisticated but tightly coupled to AutoGen’s agent model. Neither framework could run the other’s workflows.

What this means: The Agent Framework’s orchestration layer is built on a modified Pregel (Bulk Synchronous Parallel) model from the ground up. Workflows are directed graphs with typed edges, superstep-based execution, and deterministic checkpointing at every superstep boundary. The same graph API works for sequential pipelines, concurrent fan-out, conditional routing, and nested workflows.

Root cause 3: Two frameworks meant two communities, two doc sets, two backlogs.

Microsoft’s internal data showed that 40% of GitHub issues filed against Semantic Kernel were actually about multi-agent patterns that AutoGen already solved. Conversely, 30% of AutoGen issues were about enterprise features (telemetry, DI, Azure integration) that Semantic Kernel had for years. The community was fragmented — contributors had to pick a side, and users had to learn two frameworks to solve one problem.

What this means: The unified repo has 160+ contributors, 95 releases across Python and .NET packages, and a single documentation site at learn.microsoft.com/en-us/agent-framework/. The migration guides for Semantic Kernel and AutoGen are first-class documentation, not afterthoughts.

The Solution

Microsoft Agent Framework is an open-source (MIT), multi-language framework for building production-grade AI agents and multi-agent workflows. It supports Python and C#/.NET with consistent APIs, multiple LLM providers (Azure OpenAI, OpenAI, Microsoft Foundry, Anthropic Claude, Amazon Bedrock, Google Gemini, Ollama), and three execution models: single-agent, graph-based workflows, and declarative YAML workflows.

┌─────────────────────────────────────────────────────────────┐
│                    Microsoft Agent Framework                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────────┐   │
│  │  Single      │  │  Graph       │  │  Declarative     │   │
│  │  Agent       │  │  Workflows   │  │  YAML Workflows  │   │
│  │              │  │              │  │                  │   │
│  │  Agent       │  │  Workflow    │  │  SetVariable     │   │
│  │  + tools     │  │  Builder     │  │  InvokeAgent     │   │
│  │  + streaming │  │  + edges     │  │  SendActivity    │   │
│  │  + HITL      │  │  + supersteps│  │  Foreach/If     │   │
│  └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘   │
│         │                 │                    │              │
│  ┌──────┴─────────────────┴────────────────────┴──────────┐   │
│  │              Cross-Cutting Infrastructure              │   │
│  │                                                        │   │
│  │  Middleware Pipeline  │  Checkpointing  │  DevUI       │   │
│  │  OpenTelemetry       │  MCP Protocol   │  A2A Protocol │   │
│  │  Human-in-the-Loop   │  Agent Skills   │  Hosting      │   │
│  └────────────────────────────────────────────────────────┘   │
│                                                              │
│  ┌─────────────────────────────────────────────────────────┐  │
│  │  LLM Providers                                           │  │
│  │  Azure OpenAI │ OpenAI │ Foundry │ Anthropic │ Bedrock  │  │
│  │  Google Gemini │ Ollama │ GitHub Copilot SDK             │  │
│  └─────────────────────────────────────────────────────────┘  │
│                                                              │
│  ┌─────────────────────────────────────────────────────────┐  │
│  │  Languages                                              │  │
│  │  Python (50.9%) │ C#/.NET (45.9%) │ TypeScript (2.7%)  │  │
│  └─────────────────────────────────────────────────────────┘  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Here is what each piece does:

  • Agent — The core unit. A chat client plus instructions plus optional tools. No kernel, no DI container. Created with Agent(client=..., instructions=..., tools=...).
  • WorkflowBuilder — Graph-based orchestration. Define executors (agents or custom logic) and edges between them. Edges can have conditions for conditional routing. Builds a directed graph executed in supersteps.
  • Declarative YAML Workflows — Define orchestration logic in YAML with 20+ action types: SetVariable, If, Foreach, InvokeAgent, InvokeMcpTool, SendActivity, Question (human-in-the-loop). Uses PowerFx-like expressions prefixed with =.
  • Middleware — Request/response processing pipeline for cross-cutting concerns: logging, authentication, rate limiting, tool approval. Applied to agents and workflows.
  • Checkpointing — Saves full workflow state at every superstep boundary. Supports InMemoryCheckpointStorage (dev), FileCheckpointStorage (single-machine), and CosmosCheckpointStorage (production distributed).
  • DevUI — Browser-based interactive debugger. Run agents and workflows, inspect state, view traces. Install with pip install agent-framework-devui.
  • MCP Protocol — Model Context Protocol support for connecting to external tool servers. Available in DevUI, declarative workflows (InvokeMcpTool), and programmatic agents.
  • A2A Protocol — Agent-to-Agent hosting patterns for distributed multi-agent deployments.
  • OpenTelemetry — Zero-config observability. Set OTEL_EXPORTER_OTLP_ENDPOINT and traces flow automatically.

Production-Grade Code Walkthrough

Here is a complete multi-agent workflow that processes customer support tickets — triage, research, respond, and review:

import asyncio
import os
from typing import Annotated
from pydantic import Field
from agent_framework import Agent, tool, WorkflowBuilder, WorkflowViz
from agent_framework.openai import OpenAIChatClient
from agent_framework.checkpointing import FileCheckpointStorage

# ---------------------------------------------------------------------------
# Step 1: Define tools that agents will use
# ---------------------------------------------------------------------------

@tool(approval_mode="never_require")
def search_knowledge_base(
    query: Annotated[str, Field(description="The search query for the knowledge base.")],
) -> str:
    """Search the internal knowledge base for relevant documentation."""
    # In production, this would query a vector database or search index
    knowledge = {
        "refund": "Refund policy: Full refund within 30 days. Partial refund after 30 days.",
        "api_key": "API keys can be regenerated from the dashboard under Settings > API Keys.",
        "billing": "Billing cycles are monthly. Invoices are generated on the 1st of each month.",
    }
    for key, value in knowledge.items():
        if key in query.lower():
            return value
    return "No relevant documentation found for the query."

@tool(approval_mode="never_require")
def get_customer_history(
    customer_id: Annotated[str, Field(description="The customer ID to look up.")],
) -> str:
    """Retrieve customer account history and past interactions."""
    # In production, this would query a CRM or database
    return f"Customer {customer_id}: Premium tier, active since 2024-03-15, 3 past tickets (all resolved)."

@tool(approval_mode="never_require")
def escalate_to_human(
    ticket_id: Annotated[str, Field(description="The ticket ID to escalate.")],
    reason: Annotated[str, Field(description="The reason for escalation.")],
) -> str:
    """Escalate a ticket to a human support agent."""
    # In production, this would create a ticket in Zendesk, ServiceNow, etc.
    return f"Ticket {ticket_id} escalated: {reason}. Assigned to queue 'senior-support'."

# ---------------------------------------------------------------------------
# Step 2: Create agents
# ---------------------------------------------------------------------------

chat_client = OpenAIChatClient(
    model="gpt-4o",
    api_key=os.environ["OPENAI_API_KEY"],
)

triage_agent = Agent(
    client=chat_client,
    name="TriageAgent",
    instructions="""You are a support ticket triage agent. Your job is to:
1. Classify the ticket into one of: billing, technical, account, general
2. Determine priority: urgent (account locked, service down), high (billing error, broken feature), medium (how-to question), low (feature request)
3. Extract the customer ID if present
4. Pass the structured triage result to the next agent.
Output format: CLASSIFICATION: <type> | PRIORITY: <level> | CUSTOMER: <id or unknown> | SUMMARY: <one-line summary>""",
)

research_agent = Agent(
    client=chat_client,
    name="ResearchAgent",
    instructions="""You are a research agent. Given a triage result, you must:
1. Search the knowledge base for relevant documentation
2. Look up the customer's history
3. Compile your findings into a structured research brief.
Always use the search_knowledge_base and get_customer_history tools.""",
    tools=[search_knowledge_base, get_customer_history],
)

response_agent = Agent(
    client=chat_client,
    name="ResponseAgent",
    instructions="""You are a customer support response agent. Given a research brief and the original ticket:
1. Draft a professional, empathetic response
2. Include specific references to knowledge base articles
3. If the issue requires human intervention, use the escalate_to_human tool
4. Output the final response text.""",
    tools=[escalate_to_human],
)

review_agent = Agent(
    client=chat_client,
    name="ReviewAgent",
    instructions="""You are a quality review agent. Given the original ticket, research brief, and draft response:
1. Check that the response addresses the customer's actual question
2. Verify all claims are supported by the research
3. Flag any missing information or potential errors
4. Output either: APPROVED with the final response, or REVISION_REQUESTED with specific feedback.""",
)

# ---------------------------------------------------------------------------
# Step 3: Build the workflow graph
# ---------------------------------------------------------------------------

checkpointer = FileCheckpointStorage(path="./checkpoints/support-workflow")

workflow = (
    WorkflowBuilder(start_executor=triage_agent)
    .add_edge(triage_agent, research_agent)
    .add_edge(research_agent, response_agent)
    .add_edge(response_agent, review_agent)
    .build(checkpoint_storage=checkpointer)
)

# Visualize the workflow
viz = WorkflowViz(workflow)
print(viz.to_mermaid())
# Outputs:
# ```mermaid
# flowchart LR
#   TriageAgent --> ResearchAgent
#   ResearchAgent --> ResponseAgent
#   ResponseAgent --> ReviewAgent
# ```

# ---------------------------------------------------------------------------
# Step 4: Run the workflow
# ---------------------------------------------------------------------------

async def process_ticket(ticket_text: str) -> str:
    events = await workflow.run(ticket_text)
    outputs = events.get_outputs()
    for output in outputs:
        agent_name = output.messages[0].author_name
        text = output.text
        print(f"[{agent_name}]: {text[:100]}...")
    # Return the final output (from ReviewAgent)
    final = outputs[-1].text if outputs else "No output produced."
    return final

result = asyncio.run(
    process_ticket(
        "Customer ID: CUST-48291. I was charged twice for my monthly subscription "
        "and my API keys stopped working. Need immediate help."
    )
)
print(f"Final result: {result}")

Setup Instructions

# Install the core package (includes OpenAI/Azure OpenAI support)
pip install agent-framework

# For Azure AI Foundry support
pip install agent-framework-foundry

# For declarative YAML agents
pip install agent-framework-declarative

# For the DevUI debugger
pip install agent-framework-devui

# For Cosmos DB checkpointing (production)
pip install agent-framework-azure-cosmos

How to Use Effectively

1. Start with a single agent before adding orchestration

The simplest possible agent is a chat client plus instructions. Add tools only when the agent needs to interact with external systems.

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

agent = Agent(
    client=OpenAIChatClient(model="gpt-4o"),
    instructions="You are a helpful assistant. Answer concisely.",
)
result = await agent.run("What is the capital of France?")
print(result)  # "Paris"

2. Add tools with the @tool decorator

The @tool decorator automatically infers the JSON schema from type annotations and Pydantic Field descriptions. No manual schema definitions needed.

from typing import Annotated
from pydantic import Field
from agent_framework import tool

@tool(approval_mode="never_require")  # Options: always_require, never_require, auto
def calculate_shipping(
    weight_kg: Annotated[float, Field(description="Package weight in kilograms.", ge=0)],
    destination: Annotated[str, Field(description="Two-letter country code (ISO 3166-1 alpha-2).")],
    express: Annotated[bool, Field(description="Whether to use express shipping.")] = False,
) -> str:
    """Calculate shipping cost based on weight and destination."""
    base_rate = 10.0 if destination == "US" else 25.0
    weight_charge = weight_kg * 2.5
    express_surcharge = 15.0 if express else 0.0
    total = base_rate + weight_charge + express_surcharge
    return f"Shipping cost: ${total:.2f} ({'express' if express else 'standard'})"

Tools can be placed at the agent level (available for all queries) or the run level (scoped to a single query):

# Agent-level: available for every run
agent = Agent(client=chat_client, tools=[calculate_shipping])

# Run-level: scoped to one query
result = await agent.run("Ship 2kg to DE express", tools=[calculate_shipping])

# Both: merged at runtime

3. Build multi-agent workflows with the graph API

Use WorkflowBuilder for fixed topologies with typed edges. The builder validates that message types are compatible between connected executors at build time.

from agent_framework import WorkflowBuilder

# Sequential pipeline
workflow = (
    WorkflowBuilder(start_executor=agent_a)
    .add_edge(agent_a, agent_b)
    .add_edge(agent_b, agent_c)
    .build()
)

# Concurrent fan-out
workflow = (
    WorkflowBuilder(start_executor=dispatcher)
    .add_edge(dispatcher, worker_1)
    .add_edge(dispatcher, worker_2)
    .add_edge(dispatcher, worker_3)
    .add_edge(worker_1, aggregator)
    .add_edge(worker_2, aggregator)
    .add_edge(worker_3, aggregator)
    .build()
)

# Conditional routing
workflow = (
    WorkflowBuilder(start_executor=classifier)
    .add_edge(classifier, billing_agent, condition=lambda msg: "billing" in msg.text.lower())
    .add_edge(classifier, tech_agent, condition=lambda msg: "technical" in msg.text.lower())
    .add_edge(classifier, default_agent)  # fallback
    .build()
)

4. Use the functional API for dynamic workflows

For workflows where the execution path depends on runtime data, use the @workflow and @step decorators. This lets you write plain Python with native control flow.

from agent_framework import workflow, step

@workflow
class DocumentProcessor:
    def __init__(self, chat_client):
        self.client = chat_client

    @step
    async def extract(self, document: str) -> dict:
        agent = Agent(client=self.client, instructions="Extract key fields from this document.")
        result = await agent.run(document)
        return {"raw": document, "extracted": result}

    @step
    async def validate(self, data: dict) -> dict:
        agent = Agent(client=self.client, instructions="Validate the extracted data. Flag any inconsistencies.")
        result = await agent.run(str(data["extracted"]))
        data["validation"] = result
        return data

    @step
    async def summarize(self, data: dict) -> str:
        if "error" in data.get("validation", "").lower():
            return f"Validation failed: {data['validation']}"
        agent = Agent(client=self.client, instructions="Summarize the validated document.")
        return await agent.run(str(data["extracted"]))

processor = DocumentProcessor(chat_client)
result = await processor.run("Annual report 2025: Revenue $2.1B, up 18% YoY.")

5. Add checkpointing and resume for long-running workflows

Checkpoints save the full workflow state at every superstep boundary. If the process crashes, you resume from the last checkpoint — no work is lost.

from agent_framework.checkpointing import FileCheckpointStorage, CosmosCheckpointStorage

# Development: local file storage
storage = FileCheckpointStorage(path="./checkpoints/my-workflow")

# Production: Cosmos DB for distributed resilience
# storage = CosmosCheckpointStorage(
#     connection_string=os.environ["COSMOS_CONNECTION_STRING"],
#     database_name="agent-checkpoints",
#     container_name="workflow-state",
# )

workflow = WorkflowBuilder(start_executor=agent_a).add_edge(agent_a, agent_b).build(
    checkpoint_storage=storage
)

# Run with automatic checkpointing
events = await workflow.run("Process this long document.")

# Resume from last checkpoint after a crash
events = await workflow.resume()  # Restores all executor state and pending messages

Use Cases

1. Customer support ticket processing

Automate the full ticket lifecycle: triage, research, respond, review, and escalate.

When you would use this: You have a support team drowning in tickets. You want AI to handle the first response for common issues while escalating complex cases to humans.

Why Microsoft Agent Framework fits: The graph-based workflow maps naturally to a support pipeline. Each stage is a separate agent with its own instructions and tools. The escalate_to_human tool provides a clean handoff boundary. Checkpointing ensures no ticket is lost mid-processing. The DevUI lets your support team monitor live ticket flow.

2. Content generation pipeline

Generate, review, and publish content through a multi-stage editorial workflow.

When you would use this: You produce blog posts, marketing copy, or documentation at scale. You need a consistent quality bar enforced by automated review.

Why Microsoft Agent Framework fits: Sequential workflows with conditional edges let you route content through writer, reviewer, and editor agents. The @tool decorator integrates with your CMS API. Declarative YAML workflows let non-engineers define simple editorial pipelines without writing code.

3. Code review and analysis

Automated code review with multi-perspective analysis: security, style, correctness, and performance.

When you would use this: Your team gets 50+ PRs a day. Human reviewers cannot catch every issue. You want AI to flag problems before human review.

Why Microsoft Agent Framework fits: Concurrent fan-out lets you run security, style, and performance reviews in parallel. The aggregator agent synthesizes findings into a single review. MCP tools connect to your code analysis toolchain. The framework’s 93-second average latency (vs 506s for LangGraph) keeps review turnaround fast.

4. Data extraction and enrichment pipeline

Extract structured data from unstructured documents, validate it, and enrich it with external sources.

When you would use this: You process invoices, contracts, or medical records. You need structured output with validation and enrichment from multiple data sources.

Why Microsoft Agent Framework fits: The functional API (@workflow/@step) lets you express extraction logic as plain Python with native control flow. Checkpointing is critical for long-running batch jobs. The framework’s 7,006 tokens per run (vs 27,684 for CrewAI) means lower costs at scale.

5. Multi-agent research and synthesis

Deploy a team of research agents that explore different angles of a question and synthesize findings.

When you would use this: You need a comprehensive research report on a complex topic. Different agents research different aspects (market data, technical feasibility, competitive landscape) and a synthesis agent combines the results.

Why Microsoft Agent Framework fits: Concurrent fan-out with conditional aggregation is a first-class pattern. The MagenticBuilder provides group collaboration patterns out of the box. OpenTelemetry tracing lets you see exactly which agent contributed which finding. The framework’s 0.10 consistency standard deviation (lowest among all frameworks tested) means reliable output quality.

Cheat Sheet

Aspect Detail
Repository github.com/microsoft/agent-framework
License MIT
Stars 11,600+
Contributors 160+
Latest Python release v1.9.0 (June 18, 2026)
Latest .NET release v1.1.0
Languages Python (50.9%), C#/.NET (45.9%), TypeScript (2.7%)
Install pip install agent-framework
Core abstraction Agent(client, instructions, tools)
LLM providers Azure OpenAI, OpenAI, Foundry, Anthropic, Bedrock, Gemini, Ollama, GitHub Copilot SDK
Orchestration APIs Graph API (WorkflowBuilder), Functional API (@workflow/@step), Declarative (YAML)
Execution model Modified Pregel (Bulk Synchronous Parallel) with supersteps
Middleware Request/response pipeline for agents and workflows
Checkpointing InMemory, File, Cosmos DB — automatic at superstep boundaries
Observability Zero-config OpenTelemetry via environment variables
Human-in-the-loop Question action in declarative workflows, RequestInfoExecutor in graph workflows
MCP support DevUI, declarative workflows (InvokeMcpTool), programmatic agents
A2A support Hosting samples for Python and .NET
DevUI pip install agent-framework-devui, browser-based debugger
Declarative agents YAML/JSON agent definitions via agent-framework-declarative
Migration from SK chatClient.AsAIAgent(instructions=...) replaces Kernel + ChatCompletionAgent
Migration from AutoGen Agent replaces AssistantAgent, WorkflowBuilder replaces Team/GraphFlow
Benchmark quality 9.87/10 (highest among 5 frameworks tested)
Benchmark latency 93s average (fastest among 5 frameworks tested)
Benchmark tokens/run 7,006 (most token-efficient among 5 frameworks tested)
Benchmark consistency 0.10 std dev (most consistent among 5 frameworks tested)
Cost at 10k runs/mo ~$600 (GPT-4o pricing)
CodeAct improvement 52.4% faster, 63.9% fewer tokens vs traditional tool-calling

Vibe Coding Projects

Project 1: Personal research assistant

What it does: A multi-agent system that takes a research question, dispatches parallel searches across web, academic, and internal sources, then synthesizes a structured report with citations.

What you will learn: Concurrent fan-out patterns, conditional aggregation, tool integration with search APIs, and the DevUI debugger for inspecting agent outputs.

Effort: 2-3 hours. Core logic is ~80 lines of Python. The hard part is defining good agent instructions and handling edge cases in the synthesis step.

Project 2: Automated PR reviewer

What it does: A workflow that takes a GitHub PR diff, runs concurrent reviews (security, style, correctness, performance), aggregates findings, and posts a review comment back to the PR.

What you will learn: MCP tool integration (connecting to GitHub API), conditional routing (skip style review for docs-only PRs), and the @tool decorator for custom analysis tools.

Effort: 4-6 hours. The workflow graph is straightforward. The complexity is in the analysis tools — you need good prompts for each review dimension and a robust aggregation strategy.

Project 3: Document processing pipeline with human review

What it does: A workflow that ingests documents (PDFs, emails, scanned forms), extracts structured data, validates it against business rules, flags anomalies for human review, and writes approved records to a database.

What you will learn: Checkpointing and resume for long-running batch jobs, human-in-the-loop patterns with Question actions, Cosmos DB checkpoint storage for production resilience, and the declarative YAML workflow API for non-engineer configuration.

Effort: 8-12 hours. The extraction and validation agents are straightforward. The human-in-the-loop integration and production checkpointing add complexity. The YAML workflow definition is a nice bonus for team members who prefer configuration over code.

Problems Solved Efficiently

Problem Type Why Microsoft Agent Framework Fits When to Look Elsewhere
Single-agent chat with tools Minimal API: Agent(client, instructions, tools). No kernel, no DI, no boilerplate. You need a simple chatbot with no tool calling — use the LLM provider’s SDK directly.
Multi-agent sequential pipeline WorkflowBuilder with typed edges. Deterministic execution. Built-in checkpointing. Your workflow is a single LLM call with no orchestration — use a single agent.
Concurrent fan-out with aggregation First-class pattern in the graph API. Type-validated message routing. You need event-driven, reactive workflows — consider Temporal or Durable Functions.
Human-in-the-loop workflows Question action, RequestInfoExecutor, tool approval modes. Your workflow has no human involvement — skip the HITL middleware.
Long-running batch processing Superstep-level checkpointing with Cosmos DB. Resume from crash. Your workflow completes in under 5 seconds — in-memory checkpointing is sufficient.
Declarative workflow configuration YAML workflows with 20+ action types. PowerFx expressions. Non-engineer friendly. Your workflow logic is highly dynamic — use the functional API instead.
Multi-provider LLM switching 8+ providers supported with consistent API. Switch providers by changing the chat_client. You only use one provider and never plan to switch — use that provider’s SDK.
Production observability Zero-config OpenTelemetry. Trace every agent call and workflow step. You are prototyping — skip telemetry until you need it.
Migration from Semantic Kernel Dedicated migration guide. Compatibility layer for KernelFunction instances. You are happy with SK and have no multi-agent needs — SK v1.x is still supported.
Migration from AutoGen Dedicated migration guide. Agent replaces AssistantAgent. WorkflowBuilder replaces Team. You are happy with AutoGen and have no enterprise needs — AutoGen still works.

Architectural Tradeoffs

What we gained

  • Unified API surface. One Agent class, one @tool decorator, one WorkflowBuilder — regardless of whether you are building a single-agent chatbot or a 20-agent research pipeline. The learning curve is steep at first but pays off across every project.
  • Deterministic execution. The Pregel superstep model guarantees that for the same inputs, the same workflow produces the same outputs. No race conditions, no non-deterministic agent scheduling. This is critical for testing, debugging, and audit trails.
  • Production infrastructure built in. Checkpointing, OpenTelemetry, middleware, and human-in-the-loop are not afterthoughts — they are first-class APIs. You do not need to bolt on observability or resilience after the fact.
  • Best-in-class benchmarks. 9.87/10 quality score, 93s average latency, 7,006 tokens per run, 0.10 consistency standard deviation — all best among the five frameworks tested in the 45-run controlled benchmark.
  • Microsoft ecosystem integration. Azure OpenAI, Azure AI Foundry, GitHub Copilot SDK, Cosmos DB, and Azure Functions are all first-class citizens. If your stack is Microsoft, this framework fits naturally.

What we sacrificed

  • Framework size. The Agent Framework is the heaviest framework in the ecosystem. The Python package pulls in multiple dependencies. The learning curve is steeper than OpenAI Agents SDK or simple LangChain chains. For a single-agent chatbot with no tools, the framework is overkill.
  • Documentation maturity. As of v1.0.0 (April 2026), the documentation is improving but still catching up to the code. The migration guides are excellent, but the conceptual documentation (how the Pregel model works, how to design good agent instructions) is thinner than the community would like.
  • Community fragmentation. The unification means the old Semantic Kernel and AutoGen communities are now one. But the old repos still exist, and users searching for help may find outdated SK or AutoGen answers. The migration guides help, but the SEO battle is ongoing.
  • TypeScript support. TypeScript is listed at 2.7% of the codebase and is clearly a secondary concern. If you are a Node.js shop, you will find better TypeScript support in LangChain or Vercel AI SDK.
  • Non-Microsoft cloud deployment. While the framework supports multiple LLM providers, the hosting patterns (Azure Functions, Durable Tasks) are Azure-centric. Deploying on AWS or GCP requires more manual setup.

Real lesson from production: “We migrated a 12-agent AutoGen workflow to the Agent Framework and saw a 4x latency improvement — from 380s to 95s on the same model. But the migration took two weeks, not two days. The APIs look similar but the execution model is fundamentally different. AutoGen’s event-driven scheduling does not map one-to-one to the Pregel superstep model. We had to rethink our agent boundaries and message flow. The performance gain was worth the migration cost, but we should have budgeted for a rewrite, not a port.” — Engineering lead at a fintech company, after migrating a credit underwriting pipeline.

Course-Style Deep Dive

Under the hood: The Pregel execution model

The Agent Framework’s workflow engine is a modified Pregel (Bulk Synchronous Parallel) model, originally developed at Google for large-scale graph processing. Here is how it works:

  1. Superstep N begins. The engine collects all pending messages from the previous superstep’s output queues.
  2. Message routing. Each message is routed to its target executor based on the edge definitions in the workflow graph. Edges can have conditions — messages that do not match any condition are dropped.
  3. Parallel execution. All executors that received messages run concurrently within the superstep. Each executor processes its messages and produces output messages.
  4. Synchronization barrier. The engine waits for all executors to complete before advancing to the next superstep. This ensures deterministic execution and clean checkpoint boundaries.
  5. Checkpoint. If a CheckpointStorage is configured, the full workflow state (executor states, pending messages, shared state) is persisted.
  6. Superstep N+1 begins. Repeat until no executors produce output messages and no messages are pending.

The key property of this model is determinism: for the same inputs and the same workflow graph, the execution trace is identical across runs. This is not true of event-driven frameworks (AutoGen, LangGraph) where agent scheduling depends on timing and message arrival order.

Advanced Pattern 1: Conditional routing with type-validated edges

Edges in the workflow graph can have conditions that route messages based on content. The condition function receives the message and returns a boolean. Multiple edges from the same executor are evaluated in order — the first matching edge wins.

from agent_framework import WorkflowBuilder, Agent
from agent_framework.openai import OpenAIChatClient
from pydantic import BaseModel

class TicketMessage(BaseModel):
    text: str
    priority: str  # "urgent", "high", "medium", "low"
    category: str  # "billing", "technical", "account", "general"

client = OpenAIChatClient(model="gpt-4o")

triage = Agent(client=client, name="Triage", instructions="Classify the ticket.")
billing = Agent(client=client, name="Billing", instructions="Handle billing issues.")
tech = Agent(client=client, name="Tech", instructions="Handle technical issues.")
urgent = Agent(client=client, name="Urgent", instructions="Handle urgent issues with priority.")

workflow = (
    WorkflowBuilder(start_executor=triage)
    # Urgent issues skip the queue and go to the urgent handler
    .add_edge(triage, urgent, condition=lambda msg: "urgent" in msg.text.lower())
    # Non-urgent issues route by category
    .add_edge(triage, billing, condition=lambda msg: "billing" in msg.text.lower())
    .add_edge(triage, tech, condition=lambda msg: "technical" in msg.text.lower())
    # Fallback: no condition = catch-all
    .add_edge(triage, billing)
    .build()
)

Production consideration: Edge conditions are evaluated for every message in every superstep. Keep conditions cheap — avoid LLM calls or network requests in condition functions. If you need LLM-based routing, make it an executor, not a condition.

Advanced Pattern 2: Custom middleware for cross-cutting concerns

Middleware runs before and after every agent invocation. Use it for logging, authentication, rate limiting, tool approval, or content filtering.

from agent_framework import Agent, Middleware, NextMiddleware
from agent_framework.openai import OpenAIChatClient
import time
import logging

logger = logging.getLogger("agent-framework")

class TimingMiddleware(Middleware):
    """Log the duration of every agent invocation."""

    async def __call__(self, context, next_middleware: NextMiddleware):
        start = time.monotonic()
        try:
            result = await next_middleware(context)
            duration = time.monotonic() - start
            logger.info(
                "Agent %s completed in %.2fs with %d tool calls",
                context.agent.name,
                duration,
                len(context.tool_calls),
            )
            return result
        except Exception as e:
            duration = time.monotonic() - start
            logger.error("Agent %s failed after %.2fs: %s", context.agent.name, duration, str(e))
            raise

class RateLimitMiddleware(Middleware):
    """Enforce a rate limit on agent invocations per user."""

    def __init__(self, max_calls: int = 10, window_seconds: int = 60):
        self.max_calls = max_calls
        self.window_seconds = window_seconds
        self._calls: dict[str, list[float]] = {}

    async def __call__(self, context, next_middleware: NextMiddleware):
        user_id = context.metadata.get("user_id", "anonymous")
        now = time.monotonic()
        window_start = now - self.window_seconds

        # Prune old entries
        self._calls[user_id] = [t for t in self._calls.get(user_id, []) if t > window_start]

        if len(self._calls[user_id]) >= self.max_calls:
            raise RuntimeError(f"Rate limit exceeded for user {user_id}")

        self._calls[user_id].append(now)
        return await next_middleware(context)

# Apply middleware to an agent
agent = Agent(
    client=OpenAIChatClient(model="gpt-4o"),
    instructions="You are a helpful assistant.",
    middleware=[TimingMiddleware(), RateLimitMiddleware(max_calls=30, window_seconds=60)],
)

Production consideration: Middleware order matters. Middleware is applied in the order it appears in the list — the first middleware wraps the second, which wraps the third, and so on. Put authentication and rate limiting first (they fail fast), observability second (it captures everything), and business logic middleware last.

Advanced Pattern 3: Human-in-the-loop with checkpointing

For workflows that require human approval at specific decision points, use the RequestInfoExecutor or the Question action in declarative workflows. Combined with checkpointing, the workflow pauses at the human-in-the-loop boundary and resumes when the human responds.

from agent_framework import WorkflowBuilder, Agent, RequestInfoExecutor
from agent_framework.openai import OpenAIChatClient
from agent_framework.checkpointing import FileCheckpointStorage

client = OpenAIChatClient(model="gpt-4o")

extractor = Agent(client=client, name="Extractor", instructions="Extract data from the document.")
validator = Agent(client=client, name="Validator", instructions="Validate the extracted data.")

# The human review step pauses the workflow and waits for input
human_review = RequestInfoExecutor(
    name="HumanReview",
    request_message="Please review the extracted data and approve or reject.",
    response_schema={
        "type": "object",
        "properties": {
            "approved": {"type": "boolean"},
            "comments": {"type": "string"},
        },
        "required": ["approved"],
    },
)

workflow = (
    WorkflowBuilder(start_executor=extractor)
    .add_edge(extractor, human_review)
    .add_edge(human_review, validator, condition=lambda msg: msg.get("approved", False))
    .build(checkpoint_storage=FileCheckpointStorage(path="./checkpoints/hitl-workflow"))
)

# Run the workflow
events = await workflow.run("Invoice #12345: $1,500 for consulting services.")

# The workflow pauses at HumanReview, waiting for input.
# In production, you would serve this via an API endpoint:
# POST /workflow/{id}/respond with {"approved": true, "comments": "Looks good"}

# Resume after human response
events = await workflow.resume(response={"approved": True, "comments": "Verified against PO #789"})

Production consideration: Human-in-the-loop workflows need a persistence layer for pending requests. The checkpoint storage handles this — the workflow state is saved at the superstep boundary before the human review, and the pending request is stored in the checkpoint. When the human responds, the workflow resumes from the checkpoint. This means the human can take hours or days to respond without losing workflow state.

The Results

The controlled benchmark (45 runs across 5 multi-agent frameworks using the same model, prompts, and tools) tells a clear story:

Metric Before (AutoGen) Before (Semantic Kernel) After (Agent Framework) Improvement
Quality score (1-10) 9.63 ~9.0 (estimated) 9.87 +2.5% vs AutoGen
Average latency 572s ~300s (estimated) 93s 6.2x faster vs AutoGen
Tokens per run 10,793 ~12,000 (estimated) 7,006 35% fewer vs AutoGen
Consistency (std dev) 0.45 ~0.30 (estimated) 0.10 4.5x more consistent vs AutoGen
Cost per 10k runs (GPT-4o) ~$900 ~$800 (estimated) ~$600 33% cheaper vs AutoGen
CodeAct latency N/A N/A 13.23s 52.4% faster than traditional tool-calling
CodeAct tokens N/A N/A 2,489 63.9% fewer than traditional tool-calling
LLM providers 4 5 8+ 2x more providers
Contributors ~80 ~60 160+ 2x more contributors
Releases (2025-2026) ~30 ~25 95 3x more releases

What this means for you: If you are building a new agent system in 2026, the Microsoft Agent Framework is the performance leader across every metric that matters — quality, speed, cost, and consistency. The 6x latency improvement over AutoGen is not theoretical: it comes from the Pregel superstep model that eliminates the scheduling overhead of event-driven frameworks. The 35% token reduction means real cost savings at scale. And the 160+ contributors and 95 releases mean the framework is actively maintained and rapidly improving.

If you are migrating from Semantic Kernel or AutoGen, the dedicated migration guides and compatibility layer make the transition manageable. Budget for a rewrite, not a port — the execution model is fundamentally different — but the performance and cost improvements justify the investment.

What to Watch Out For

Beginner advice

  1. Do not start with multi-agent workflows. Start with a single agent. Add tools. Add middleware. Only then add orchestration. The framework’s power is in multi-agent patterns, but the complexity compounds quickly. A single agent with good instructions and a few tools solves 80% of use cases.

  2. Write good agent instructions before writing code. The quality of your agent’s output is determined more by the instructions parameter than by the model choice. Spend 30 minutes iterating on instructions before you write a single line of orchestration code. Test instructions with the DevUI before building the workflow.

  3. Use the DevUI for debugging. pip install agent-framework-devui and run devui ./agents --port 8080. The browser-based debugger shows you every agent invocation, tool call, and message in the workflow. It is significantly faster than debugging through print statements.

  4. Set up OpenTelemetry from day one. Set OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_SERVICE_NAME in your environment. The framework emits traces for every agent call and workflow step automatically. You will thank yourself when you need to debug a production issue.

  5. Choose the right checkpoint storage for your stage. Use InMemoryCheckpointStorage for prototyping (zero setup), FileCheckpointStorage for local development (survives process restarts), and CosmosCheckpointStorage for production (distributed resilience). Do not use Cosmos DB for development — the latency and cost are unnecessary.

  6. Understand the Pregel model before building complex workflows. The superstep model means all executors in a superstep run concurrently, then the workflow synchronizes. This is different from event-driven frameworks where agents react to messages as they arrive. If your workflow has tight feedback loops between agents, you may need to restructure it for the superstep model.

  7. Pin your dependency versions. The framework releases frequently (95 releases in ~14 months). Pin agent-framework==1.9.0 in your requirements.txt or pyproject.toml. The changelog documents breaking changes, but they happen.

Lesson learned: “We did not pin our dependencies and a minor version bump broke our workflow. The as_agent method signature changed between 1.8.0 and 1.9.0 — the function_invocation_configuration kwarg was renamed. It took us two hours to find the issue because the error message was opaque. Pin your versions.” — Production engineer at a SaaS company.

Lesson learned: “The DevUI is not just a debugger — it is the fastest way to prototype agent instructions. We saved three days of development time by iterating on agent prompts in the DevUI before wiring them into our workflow. The ability to see raw LLM responses, tool call arguments, and timing information in one view is invaluable.” — AI engineer at an e-commerce platform.

Lesson learned: “We tried to port our AutoGen group chat pattern directly to the Agent Framework and it failed. The Pregel model does not support the free-form agent-to-agent messaging that AutoGen’s GroupChat allows. We had to redesign our workflow as a sequential pipeline with a shared state object. The result was faster and more reliable, but the migration took twice as long as we estimated.” — Engineering lead at a fintech company.

Getting Started

# 1. Install the framework
pip install agent-framework agent-framework-devui

# 2. Set your API key
export OPENAI_API_KEY="sk-..."

# 3. Create your first agent
cat > hello_agent.py << 'EOF'
import asyncio
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

async def main():
    agent = Agent(
        client=OpenAIChatClient(model="gpt-4o"),
        instructions="You are a helpful assistant. Answer concisely.",
    )
    result = await agent.run("What is the Microsoft Agent Framework?")
    print(result)

asyncio.run(main())
EOF

# 4. Run it
python hello_agent.py

# 5. Open the DevUI
devui . --port 8080
# Opens browser to http://localhost:8080

Next in the Open-Source AI Tools Mastery series: Google AX

NL

Written by Nivant Labs Team

Engineer at Nivant Labs

Share this post