Microsoft Agent Framework (MAF) is a dual-language SDK for building multi-agent systems that need to survive restarts, hand off work between agents, and deploy to both local dev boxes and cloud infrastructure. It ships with Python and .NET implementations that share the same orchestration primitives, state model, and deployment patterns.
The framework has 13,008 stars and is trending at #6 on GitHub for Python. It targets teams moving from prototype notebooks to production workloads where durability, observability, and governance matter.
Why Multi-Language Matters for Agent Infrastructure
Most agent frameworks pick a single runtime. MAF maintains API parity across Python (asyncio) and .NET (Task-based async). This is not a cosmetic choice. It means:
- Data teams can prototype in Python while platform teams deploy in .NET.
- The same orchestration graph runs on Azure Functions (Python), AWS Lambda (.NET), or Kubernetes (either).
- You can swap runtimes without rewriting agent logic or state management.
The framework does not abstract away language differences. Instead, it provides equivalent primitives: Agent, Workflow, State, and Message exist in both SDKs with matching semantics.
Graph-Based Orchestration: What It Actually Means
MAF uses directed acyclic graphs (DAGs) to model agent workflows. Each node is an agent or a decision point. Edges define message flow and handoff conditions.
Four core patterns:
- Sequential: Agent A completes, passes output to Agent B.
- Concurrent: Multiple agents run in parallel, results merge downstream.
- Handoff: Agent A delegates to Agent B based on runtime conditions (user input, tool call result, timeout).
- Group collaboration: Multiple agents share a message bus, each reacts to relevant events.
The graph is not a static config file. You define it in code, and the framework compiles it into an execution plan. State checkpoints happen at node boundaries, so a crash mid-workflow can resume from the last completed agent.
Example: Sequential Handoff
from agent_framework import Agent, Workflow, State
research_agent = Agent(
name="researcher",
tools=[web_search, summarize],
system_prompt="Find and summarize relevant sources."
)
writer_agent = Agent(
name="writer",
tools=[draft_content, format_markdown],
system_prompt="Write a technical article from research notes."
)
workflow = Workflow()
workflow.add_agent(research_agent)
workflow.add_agent(writer_agent)
workflow.add_edge("researcher", "writer", condition=lambda state: state.research_complete)
state = State(topic="agent orchestration patterns")
result = await workflow.run(state)
The condition lambda determines when the handoff fires. If research_complete is false, the workflow pauses and waits for external input or a retry signal.
State Persistence and Durability Model
MAF separates state storage from execution. The framework defines a StateStore interface with pluggable backends:
- In-memory (dev/test)
- Redis (low-latency, ephemeral)
- Azure Cosmos DB (durable, globally distributed)
- PostgreSQL (relational, transactional)
State snapshots happen automatically at agent boundaries. If a workflow crashes, the next invocation loads the last checkpoint and resumes. This requires agents to be idempotent: running the same agent twice with the same input should produce the same output (or at least not corrupt state).
State Schema
Each workflow instance has:
-
workflow_id: unique identifier -
current_node: which agent is active -
messages: conversation history -
context: arbitrary JSON blob for tool outputs, intermediate results, user metadata
The framework does not enforce a schema for context. You define what goes in there. This flexibility is useful but dangerous: if Agent A writes {"user_id": 123} and Agent B expects {"userId": "123"}, the handoff breaks.
Observability and Debugging Hooks
MAF emits structured events at every state transition:
- Agent start/complete
- Tool call (request, response, latency)
- Handoff decision (which edge fired, why)
- Error (exception type, stack trace, retry count)
Events flow to OpenTelemetry-compatible collectors. You can pipe them to Azure Monitor, Datadog, or a local Jaeger instance.
The framework also exposes a trace_id that propagates through the entire workflow. If a user reports a bug, you can grep logs for that trace and see every agent invocation, tool call, and state mutation.
Human-in-the-Loop Gates
You can inject approval steps into the graph:
workflow.add_edge("researcher", "writer", condition=lambda state: state.approved_by_human)
The workflow pauses at the edge, emits a human_approval_required event, and waits. An external service (Slack bot, web UI, approval queue) sets state.approved_by_human = True and resumes execution.
This is not a built-in UI. MAF provides the plumbing (pause, resume, state mutation). You build the approval interface.
Deployment Patterns: Local, Serverless, and Kubernetes
MAF workflows run anywhere Python or .NET runs. The framework does not dictate deployment shape, but the docs show three common patterns:
| Pattern | Runtime | State Backend | Scaling | Latency | Cost |
|---|---|---|---|---|---|
| Local dev | Python/Jupyter | In-memory | Single process | <100ms | Free |
| Serverless | Azure Functions, AWS Lambda | Redis or Cosmos DB | Auto-scale, cold start penalty | 200ms-2s | Pay-per-invocation |
| Kubernetes | Docker + K8s | PostgreSQL or Cosmos DB | Horizontal pod autoscaler | <500ms | Fixed cluster cost |
Serverless Gotchas
Cold starts kill multi-agent workflows. If your workflow has five sequential agents, and each invocation cold-starts a new function, you pay 5x the latency penalty. Solutions:
- Keep functions warm with scheduled pings.
- Use a single long-running function that executes the entire workflow.
- Switch to Kubernetes for predictable latency.
Kubernetes Shape
A typical deployment:
- One deployment per agent type (researcher, writer, reviewer).
- Shared Redis for state (fast, ephemeral).
- PostgreSQL for durable checkpoints (slow, survives crashes).
- Ingress controller routes requests to the workflow orchestrator pod.
The orchestrator loads the graph, dispatches work to agent pods via internal service calls, and checkpoints state after each step.
Security Boundaries and Governance
MAF does not enforce security. It provides hooks:
- Tool authorization: Each agent declares which tools it can call. The framework checks this list before executing a tool.
-
State encryption: You can encrypt the
contextblob before persisting it. The framework does not do this automatically. - Audit logs: Every state mutation emits an event. You can pipe these to a SIEM.
The framework assumes you trust all agents in a workflow. If Agent A can write to state.context, Agent B can read it. There is no row-level security or capability-based isolation.
For multi-tenant deployments, you must partition workflows by tenant (separate workflow_id namespace) and ensure state backends enforce tenant isolation.
Failure Modes and Recovery
Common failure scenarios:
- Agent timeout: Agent runs longer than max duration. Framework kills it, marks the node as failed, and optionally retries.
- Tool call error: LLM requests a tool that does not exist or returns malformed JSON. Framework logs the error, optionally retries with a corrective prompt.
- State corruption: Two concurrent workflows mutate the same state. Framework uses optimistic locking (version numbers) to detect conflicts and abort one transaction.
- Deployment rollback: New agent version breaks the workflow. Framework can resume old workflows with the old agent code if you version your agent containers.
The framework does not handle cascading failures. If Agent A depends on an external API that goes down, the workflow pauses indefinitely unless you add a timeout or circuit breaker.
When to Use MAF vs. Alternatives
Use MAF if:
- You need multi-agent orchestration with durable state.
- You want to prototype in Python and deploy in .NET (or vice versa).
- You care about observability, governance, and human-in-the-loop control.
- You expect to run agents in production for months or years.
Avoid MAF if:
- You are building a single-agent chatbot with no handoffs.
- You need sub-100ms latency (the state checkpoint overhead is non-trivial).
- You want a fully managed service with no infrastructure decisions (MAF is a framework, not a platform).
- You need capability-based security or multi-tenant isolation out of the box.
Technical Verdict
MAF is production-grade plumbing for teams that need to run multi-agent workflows reliably. The dual-language support is rare and valuable if you have polyglot teams or want deployment flexibility. The state model is sound but requires discipline: you must design idempotent agents and handle state schema evolution.
The framework does not hide complexity. You still need to choose a state backend, configure observability, and build approval UIs. But it gives you the primitives to do those things without fighting the framework.
If you are moving from a prototype notebook to a production deployment, MAF is worth evaluating. If you are building a simple chatbot, it is overkill.
Top comments (0)