Microsoft Agent Framework Multi-Agent Workflow Tutorial

On April 3, 2026, Microsoft GA’d Agent Framework 1.0, the unified successor to Semantic Kernel and AutoGen โ€” two SDKs that had been doing overlapping work for two years. One framework, same combined teams, stable APIs, MIT license.

This tutorial walks you through building a Microsoft Agent Framework multi-agent workflow from scratch in Python: a single agent first, then a handoff orchestration between specialized agents, plus the first-hour gotchas Microsoft’s quickstart buries in footnotes.

One Framework to Replace Two: What Shipped on April 3

This is not a rebrand. Semantic Kernel and AutoGen remain available but are now positioned as the prior generation, and Microsoft has published migration guides for each community. The new package ships as agent-framework on PyPI and Microsoft.Agents.AI on NuGet, both published April 2, 2026.

Stable connectors cover Foundry, Azure OpenAI, OpenAI, Anthropic Claude, Amazon Bedrock, and Ollama out of the box. Gemini integration ships as a separate preview package. OpenTelemetry observability is baked in.

Two top-level primitives replace the scattered abstractions of both predecessors: Agents and Workflows. Internalize that split before you write a line of code โ€” it’s the contract the rest of the framework hangs on.

The Mental Model: When an Agent Earns Its Complexity

If you can write a function to handle the task, do that instead of using an AI agent.

Microsoft Learn, Agent Framework Overview

That sentence, tucked into Microsoft Learn’s Agent Framework overview, is the most honest line in any agent-framework doc published in 2026. Use it as your filter before every design decision.

The Agents-vs-Workflows split follows directly from it. Agents are LLM-backed units for open-ended, conversational tasks โ€” when a human would need to improvise, reach for an agent. Workflows are graph-based multi-agent orchestrations for well-defined steps where execution order matters. If your process has a clean flowchart, use a Workflow. If it has a vibe, use an Agent inside a Workflow.

The agent building blocks are model clients, agent sessions for state, context providers for memory, middleware to intercept actions, and MCP clients for tool interop. Workflows add graph-based routing, type-safe handoffs, checkpoints, and human-in-the-loop approval gates on top.

At 1.0, the framework ships five stable orchestration patterns: sequential, concurrent, handoff, group chat, and Magentic. All five support streaming, checkpointing, HITL approvals, and pause/resume. This tutorial covers Handoff because it’s the pattern most transferable to readers coming from the OpenAI Agents SDK.

Install and Build Your First Agent

Install with one line from PyPI. No separate connector packages needed for most providers:

pip install agent-framework==1.0.*

Pin the minor version. The .NET package went from 1.0.0 to 1.1.0 in eight days โ€” 1.0.0 published April 2, 1.1.0 on April 10 โ€” and Python is on the same cadence. Unpinned dependencies in production will bite you before the end of the month.

Before any client code, call load_dotenv(). Agent Framework does not auto-load .env files, and the docs say so plainly: “Agent Framework does not automatically load .env files. To use a .env file, call load_dotenv() at the start of your application, or set environment variables directly in your shell or IDE.” This is the single most common first-hour rage-quit trigger.

Here’s a minimal single agent using the Foundry connector, the path Microsoft’s quickstart foregrounds:

import asyncio
from dotenv import load_dotenv
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential

load_dotenv()  # MUST be before any client instantiation

async def main():
    credential = AzureCliCredential()
    client = FoundryChatClient(
        project_endpoint="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project",
        model="gpt-5.4-mini",
        credential=credential,
    )

    # as_agent() promotes the raw client into a named Agent with instructions
    agent = client.as_agent(
        name="HelloAgent",
        instructions="You are a friendly assistant. Keep your answers brief.",
    )

    result = await agent.run("What is the largest city in France?")
    print(f"Agent: {result}")

asyncio.run(main())

If You Don’t Have Azure Foundry

Most developers reading this don’t have a Foundry project provisioned, and the official quickstart will throw an auth error before you write a line of agent logic. Swap the import for a two-line fix:

from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient(model="gpt-5.4-mini")  # reads OPENAI_API_KEY from env
# ...then the .as_agent() call is identical

Want to run fully local? Replace OpenAIChatClient with the Ollama connector โ€” same interface, different import, no API key. This is the “works on any laptop” path the Microsoft quickstart refuses to foreground.

Illustration: Microsoft Agent Framework multi-agent workflow

Build a Microsoft Agent Framework Multi-Agent Workflow with Handoff

Scenario: a triage agent classifies an incoming request, then hands off to either a research agent or a drafting agent. It’s realistic, generalizable, and the shape maps directly to what most teams actually build in week one.

import asyncio
from dotenv import load_dotenv
from agent_framework import FileCheckpointStorage
from agent_framework.openai import OpenAIChatClient
from agent_framework.orchestrations import HandoffBuilder

load_dotenv()

async def main():
    client = OpenAIChatClient(model="gpt-5.4-mini")

    # Two specialized agents with distinct instructions
    researcher = client.as_agent(
        name="Researcher",
        instructions="You gather factual background. Return concise bullet points with sources.",
    )

    drafter = client.as_agent(
        name="Drafter",
        instructions="You write polished prose given an outline or facts. Never invent new facts.",
    )

    # The triage agent decides which specialist takes over
    triage = client.as_agent(
        name="Triage",
        instructions=(
            "Classify the user request. If it needs facts, hand off to Researcher. "
            "If it needs writing, hand off to Drafter."
        ),
    )

    # Wire them into a Handoff workflow with durable checkpointing
    workflow = (
        HandoffBuilder(
            name="research_and_draft",
            participants=[triage, researcher, drafter],
            checkpoint_storage=FileCheckpointStorage(storage_path="./checkpoints"),
        )
        .with_start_agent(triage)
        .with_autonomous_mode()
        .build()
    )

    result = await workflow.run("Write a 200-word briefing on fusion funding in 2025.")
    print(result)

asyncio.run(main())

Handoff mechanics are simple once you’ve seen them. The active agent returns a HandoffResult naming the target agent; the workflow routes control accordingly; the receiving agent inherits the full conversation context. No prompt gymnastics required.

The checkpoint_storage=FileCheckpointStorage(...) argument is the feature that punches above its weight. Pass it to HandoffBuilder and state is persisted to disk automatically โ€” the kind of thing you only appreciate at 2 a.m. when a long-running agent crashes after 40 minutes of work. The checkpoint and resume long-running workflows docs cover the hydration model in full.

One design caveat: the handoffs in this example are in-process only. Agent-to-Agent (A2A) protocol support โ€” the thing that would let this workflow hand off to, say, a Google ADK agent running in a different runtime โ€” ships as a separate preview package (agent-framework-a2a). It’s available now, though still pre-release. Design your handoff interfaces cleanly today so cross-framework interop is a swap, not a rewrite.

If you’ve used the OpenAI Agents SDK before, the mental model here will feel familiar โ€” the API shape differs but the Handoff pattern is the same. For a direct comparison of implementations, see our OpenAI Agents SDK multi-agent tutorial.

First-Hour Gotchas the Microsoft Docs Don’t Emphasize

Gotcha 1 โ€” .env files don’t auto-load. Worth saying twice. Add from dotenv import load_dotenv; load_dotenv() before any credential or client instantiation. Miss it, and you’ll chase an auth error that’s actually an empty environment variable.

Gotcha 2 โ€” the official quickstart assumes Foundry. The Microsoft Learn quickstart uses a your-foundry-service.services.ai.azure.com endpoint. If you don’t have a Foundry project, you’ll hit an import or auth failure before writing agent logic. Swap to the OpenAI or Ollama path first, then go back to Foundry once you’ve got something working.

Gotcha 3 โ€” version churn is real. .NET 1.1.0 dropped on April 10, eight days after the 1.0.0 packages published. Python is on the same rapid cadence. Pin your versions and re-test before upgrading anything touching production.

Gotcha 4 โ€” A2A is pre-release. Cross-framework agent interop via A2A is available now through the agent-framework-a2a preview package, but it’s not stable yet. If you need production-grade cross-framework interop today, MCP remains the safer bridge until A2A hits 1.0.

Gotcha 5 โ€” third-party systems are on you. Microsoft’s Transparency FAQ explicitly notes that non-Azure models and external MCP servers are used at the developer’s own risk for data handling. Relevant the moment you’re piping user data through a third-party endpoint in production.

Agent Framework vs LangGraph, CrewAI, and OpenAI Agents SDK

Quick and opinionated. Readers who want the full matrix can follow our AI Agent Framework Comparison 2026.

  • vs LangGraph: closest architectural cousin. Both are graph-based with explicit control. Agent Framework adds simpler agent abstractions on top and native Azure telemetry; LangGraph still wins on time-travel debugging and LangSmith observability.
  • vs OpenAI Agents SDK: same Handoff mental model. Agent Framework adds Group Chat and Magentic orchestration for more complex coordination; OpenAI’s SDK wins for fastest first prototype if you’re already on OpenAI.
  • vs CrewAI: CrewAI is faster to a first prototype with its role-based crew model. Agent Framework wins on type safety, production telemetry, and Azure-native integration.
  • vs Google ADK: ADK for Java 1.0 shipped March 30, 2026 โ€” days before Agent Framework’s April 3 announcement. Both target A2A interoperability, but Agent Framework’s A2A support is currently a preview package while ADK has had more time to stabilize. One concrete reason to watch the next few releases.

Bottom line: Microsoft-ecosystem teams running on Azure, Foundry, or Entra should default to Agent Framework. Neutral or polyglot teams still have LangGraph as the strongest alternative.

What to Build Next

The most underrated feature in Agent Framework 1.0 isn’t the five orchestration patterns or checkpoint hydration โ€” it’s the declarative YAML workflow definition, which turns multi-agent topologies into version-controllable artifacts you can diff in a pull request. Code-as-config for agent graphs is what separates a toy from a production system, and it’s sitting in the microsoft/agent-framework repository waiting for you to find it.

The open question hanging over the whole space: Microsoft and Google shipped agent framework 1.0s four days apart โ€” ADK for Java on March 30, Agent Framework on April 3 โ€” both naming A2A interoperability as a stated goal. Until Agent Framework’s A2A package exits preview, “cross-vendor agent interop” is a roadmap bullet, not a runtime guarantee. Readers weighing the other early-2026 contender can work through our Google ADK multi-agent tutorial and decide which side of that bet to stand on.

Get the Daily Pulse

Sharp analysis on what's actually moving in AI. No hype, no filler, no weekly digest.

Get the Daily Pulse

Sharp AI analysis, daily. Two minutes, every morning.

Get the Daily PulseTwo minutes, every morning