A single AI agent has limits — it can only do so much on its own. But what happens when you put multiple specialized agents together, each with a distinct role, and let them collaborate on complex tasks? That’s the core idea behind AutoGen, Microsoft’s open-source framework for building multi-agent AI systems.
This guide walks through AutoGen’s architecture, shows you how to build agent teams that collaborate autonomously, and covers real-world patterns for code generation, analysis, and human-in-the-loop workflows.
Why multi-agent?
A single LLM call is stateless and limited by its context window. Multi-agent systems break complex problems into specialized roles:
- Specialization — each agent has a focused system prompt and tools, making it better at its specific job.
- Verification — one agent generates, another reviews. Errors that slip past a single agent get caught by a critic.
- Decomposition — complex tasks split into subtasks that agents handle in parallel or sequence.
- Human-in-the-loop — agents can pause, ask for approval, and incorporate human feedback before proceeding.
AutoGen (now in version 0.4, rewritten as AutoGen AgentChat) provides the primitives to build these systems: agents, teams, and conversation patterns.
Architecture overview
AutoGen agents communicate through messages. A team defines how agents take turns (round-robin, selector-based, or custom). A termination condition decides when the conversation is done — after a keyword, a maximum number of turns, or when an agent signals completion.
Setting up AutoGen
pip install autogen-agentchat autogen-ext[openai,azure]
AutoGen 0.4 is a complete rewrite. If you’ve used the older pyautogen package, the API has changed significantly — the new version is more modular and production-ready.
Configure the model client
from autogen_ext.models.openai import AzureOpenAIChatCompletionClient
from azure.identity import DefaultAzureCredential
model_client = AzureOpenAIChatCompletionClient(
azure_deployment="gpt-4o",
azure_endpoint="https://your-resource.openai.azure.com/",
credential=DefaultAzureCredential(),
model="gpt-4o",
api_version="2025-01-01-preview",
)
Your first multi-agent team
Let’s build a code-generation team with three agents: a coder, a reviewer, and an executor.
from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination
from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor
coder = AssistantAgent(
name="coder",
model_client=model_client,
system_message="""You are an expert Python developer.
Write clean, well-documented code to solve the task.
Always include error handling and type hints.
When the code is approved and tested, respond with TERMINATE.""",
)
reviewer = AssistantAgent(
name="reviewer",
model_client=model_client,
system_message="""You are a senior code reviewer.
Review the code for:
- Correctness and edge cases
- Security vulnerabilities
- Performance concerns
- Code style and readability
Provide specific, actionable feedback.
If the code is production-ready, say APPROVED.""",
)
executor = CodeExecutorAgent(
name="executor",
code_executor=LocalCommandLineCodeExecutor(work_dir="./workspace"),
)
Assemble the team
termination = TextMentionTermination("TERMINATE")
team = RoundRobinGroupChat(
participants=[coder, reviewer, executor],
termination_condition=termination,
max_turns=12,
)
# Run the team on a task
result = await team.run(
task="Write a Python function that validates email addresses using regex. Include unit tests."
)
for message in result.messages:
print(f"[{message.source}]: {message.content[:200]}")
The flow: the coder writes the code, the reviewer checks it, the executor runs it. If the reviewer finds issues, the loop continues. When the coder says “TERMINATE,” the conversation ends.
Selector-based teams
Round-robin is predictable but rigid. For more dynamic conversations, use SelectorGroupChat — a model decides which agent should speak next based on the conversation context:
from autogen_agentchat.teams import SelectorGroupChat
planner = AssistantAgent(
name="planner",
model_client=model_client,
system_message="""You are a project planner.
Break tasks into clear steps and delegate to the right agent.
Coordinate the team's work and track progress.""",
)
researcher = AssistantAgent(
name="researcher",
model_client=model_client,
system_message="""You are a research specialist.
Gather information, find best practices, and provide context
for the team's decisions.""",
)
team = SelectorGroupChat(
participants=[planner, coder, reviewer, researcher],
model_client=model_client, # used to select the next speaker
termination_condition=termination,
)
The selector model analyzes each message and picks the most appropriate next agent. The planner might start by breaking down the task, then the researcher gathers requirements, the coder implements, and the reviewer verifies — all orchestrated dynamically.
Tools and function calling
Agents become much more powerful when they can call external tools. Define tools as Python functions:
from autogen_agentchat.agents import AssistantAgent
async def search_database(query: str, limit: int = 10) -> str:
"""Search the product database for matching items."""
# In production, query your actual database
results = await db.search(query, limit=limit)
return json.dumps(results)
async def send_notification(recipient: str, message: str) -> str:
"""Send a notification to a user via email."""
await email_service.send(recipient, message)
return f"Notification sent to {recipient}"
support_agent = AssistantAgent(
name="support",
model_client=model_client,
tools=[search_database, send_notification],
system_message="""You are a customer support agent.
Use the available tools to look up product information
and send notifications when needed.""",
)
Human-in-the-loop
For sensitive operations, you’ll want human approval before agents take action. AutoGen supports this through the UserProxyAgent or by adding a handoff mechanism:
from autogen_agentchat.agents import UserProxyAgent
human = UserProxyAgent(
name="human",
description="A human user who provides approvals and feedback.",
)
team = RoundRobinGroupChat(
participants=[planner, coder, human],
termination_condition=termination,
)
# When running in a console, the human agent prompts for input
# In a web app, you'd connect it to your UI
result = await team.run(
task="Refactor the authentication module to use OAuth 2.0"
)
DockerCommandLineCodeExecutor instead of LocalCommandLineCodeExecutor in production to isolate code execution.
Conversation patterns
| Pattern | Implementation | Best for |
|---|---|---|
| Round-robin | RoundRobinGroupChat | Predictable pipelines (write → review → test) |
| Dynamic selection | SelectorGroupChat | Complex tasks where the next speaker depends on context |
| Two-agent chat | Direct agent.run() | Simple back-and-forth between two agents |
| Nested teams | Teams as participants in other teams | Hierarchical workflows with sub-teams |
| Swarm | Swarm with handoffs | Customer service routing, escalation flows |
Real-world scenario: data analysis pipeline
Here’s a practical example — a team that analyzes a CSV dataset and produces a report:
analyst = AssistantAgent(
name="analyst",
model_client=model_client,
system_message="""You are a data analyst.
When given a dataset, write Python code using pandas
to explore the data: shape, dtypes, missing values,
key statistics, and notable patterns.""",
)
visualizer = AssistantAgent(
name="visualizer",
model_client=model_client,
system_message="""You are a data visualization expert.
Take the analyst's findings and create matplotlib charts
that tell a clear story. Save charts as PNG files.
Use a clean, professional style.""",
)
writer = AssistantAgent(
name="writer",
model_client=model_client,
system_message="""You are a technical writer.
Synthesize the analysis and visualizations into a
concise executive summary. Include key findings,
recommendations, and the charts.
End with TERMINATE when the report is complete.""",
)
executor = CodeExecutorAgent(
name="executor",
code_executor=LocalCommandLineCodeExecutor(work_dir="./analysis"),
)
pipeline = RoundRobinGroupChat(
participants=[analyst, executor, visualizer, executor, writer],
termination_condition=TextMentionTermination("TERMINATE"),
max_turns=20,
)
result = await pipeline.run(
task="Analyze the sales data in ./data/sales_2024.csv. Find trends, seasonality, and anomalies."
)
AutoGen vs. other frameworks
| Feature | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| Multi-agent patterns | Round-robin, selector, swarm, nested | Sequential, hierarchical | State machine graphs |
| Code execution | Built-in (local + Docker) | Via tools | Via tools |
| Human-in-the-loop | Native UserProxyAgent | Human input tool | Interrupt nodes |
| Language | Python, .NET (preview) | Python | Python, JavaScript |
| Model support | OpenAI, Azure, Ollama, etc. | OpenAI, Azure, Ollama | Via LangChain |
| Best for | Research, complex collaboration | Role-based agent teams | Deterministic workflows |
AutoGen excels at open-ended collaboration where agents need to iterate and refine. LangGraph is stronger for workflows where you need precise control over every state transition. CrewAI offers a simpler API for straightforward role-based teams.
Production considerations
- Sandbox code execution — always use Docker-based executors in production. Never let agents run arbitrary code on your host machine.
- Set turn limits — agents can get stuck in loops. Always configure
max_turnsand combine with timeout-based termination. - Monitor token usage — multi-agent conversations consume tokens fast. Each agent turn is a full API call. Track costs per conversation.
- Log everything — save the full conversation transcript for debugging. AutoGen supports structured logging out of the box.
- Start simple — begin with two agents (generator + reviewer) and add complexity only when needed. More agents means more coordination overhead.
- Test termination conditions — the most common production issue is agents that never terminate. Test edge cases where the task can’t be completed.
The .NET preview
AutoGen also has a .NET version (Microsoft.AutoGen) in preview. It follows the same multi-agent concepts but integrates with the .NET ecosystem:
using Microsoft.AutoGen.AgentChat;
using Microsoft.AutoGen.Contracts;
var coder = new AssistantAgent(
name: "coder",
modelClient: azureClient,
systemMessage: "You are an expert C# developer...");
var reviewer = new AssistantAgent(
name: "reviewer",
modelClient: azureClient,
systemMessage: "You are a senior code reviewer...");
var team = new RoundRobinGroupChat(
[coder, reviewer],
terminationCondition: new MaxMessageTermination(10));
var result = await team.RunAsync("Build a REST API for a todo app");
Next steps
- Clone the repo: github.com/microsoft/autogen — check the
/python/samplesfolder for patterns. - Start with two agents — a coder and a reviewer working on a real task in your codebase.
- Add code execution — let the executor run the generated code and feed errors back for automatic debugging.
- Explore the docs: microsoft.github.io/autogen
Leave a Reply