AI EngineeringZero to ProductionHome·About·What’s new·Contact
Multi-Agent Orchestration · Chapter M2

Multi-Agent Systems with Microsoft AutoGen

CrewAI models a team as roles on an assembly line. AutoGen models it as a conversation: agents that talk to each other, in patterns you choose. It leans into the network topology from L1 — powerful for open-ended collaboration, and a sharp lesson in why free-form agent chatter needs firm termination rules.

⏱️ ~55 min🧪 3 labs🎯 Intermediate→Advanced
⚙️ To run this for realThe code here is complete and correct as written. To actually execute it you'll need:
  • an Anthropic API key (ANTHROPIC_API_KEY) + pip install anthropic
Reading and learning works without any of this — run when you're ready. IDs/ARNs in examples are placeholders; swap in your own.

Learning objectives

  • Explain AutoGen's conversation-based model and its core agent types.
  • Build a two-agent conversation with a clear termination condition.
  • Use a team pattern (round-robin / selector) for more than two agents.
  • Add tools and a human-in-the-loop participant.
  • Contrast AutoGen with CrewAI and LangGraph and pick deliberately.
📋 Before you startThis page builds on: Agent & tool foundations · Agent architectures. Jumping in? Skim those first — or open the recap.
30-second recap
  • Agent & tool foundations: The model asks to call a tool, your code runs it and returns the result, and the loop repeats until the model answers — with step caps so it can't run forever.
  • Agent architectures: Agents differ in who controls the flow: fixed workflows, a single tool-using loop, or graphs of steps with explicit state.
Builds on L1 & M1AutoGen is the clearest expression of L1's network topology (peers conversing) and also supports supervisor-style routing. Read it against M1's CrewAI: same goal — coordinate specialists — different metaphor (conversation vs assembly line).
AutoGen moves fast — learn the shapes, verify the APIAutoGen was substantially redesigned (the modern autogen-agentchat line differs from older autogen/pyautogen examples), and class names/imports shift across versions. The concepts here — conversation, termination, teams, human proxy — are stable; treat exact imports as illustrative and check them against the version you install.

The model: agents in conversation essential

Picture the difference between a factory and a meeting. In M1, CrewAI gave you a factory: work moves down an assembly line, each specialist does their station's job in a fixed order, and the output falls off the end. AutoGen throws that away and hands you a meeting room instead — you put a few experts around a table, give them a problem, and let them talk. Nobody's turn is pre-scripted; the conversation itself is the computation. That single shift in metaphor is why AutoGen feels so different, and why it can solve open-ended problems a rigid pipeline can't.

Here's why the meeting metaphor matters in practice: a pipeline can only do what you wired it to do, in the order you wired it. A conversation can double back — the reviewer sends the coder back to fix line 3, the coder revises, the reviewer looks again. That back-and-forth loop is trivial in AutoGen (it's just more messages) but awkward to express as a fixed line. When your problem is genuinely iterative — draft, critique, revise; brainstorm, challenge, refine — conversation is the natural shape.

The mistake almost everyone makes first is forgetting that a real meeting ends — someone eventually says "great, we're done." An AutoGen conversation has no such instinct. Two polite LLM agents will thank each other, agree with each other, and offer one more helpful suggestion… forever, quietly burning tokens the whole time. So the load-bearing idea to hold onto from the very first sentence is this: you don't just define who's in the room, you define when the meeting is over. That's the termination condition, and it's the piece beginners skip and regret.

In AutoGen, work happens through messages between agents. You define agents, put them in a team with a conversation pattern, give it a task, and the agents talk until a termination condition fires. The core participants:

Agent typeRole
Assistant agentAn LLM-backed agent — reasons, responds, can call tools. The workhorse.
User proxy / humanRepresents the human — can auto-reply, execute code, or ask a real person (human-in-the-loop).
TeamA group of agents plus a pattern (round-robin, selector) that decides who speaks next.
Termination conditionThe rule that ends the conversation — a keyword, a max-message count, or a custom check.
Coder Reviewer "here's the code" "fix line 3, then it's APPROVED" conversation ends when a termination condition matches (e.g. "APPROVED") Collaboration as dialogue. Agents exchange messages; each reads the shared conversation and responds. The catch: nothing stops the chatter unless you define when it's done. Termination is not optional — it's the load-bearing design decision.
🗺️ How to read this diagram

This picture is the whole idea of AutoGen in one image: instead of a fixed assembly line, two agents talk to each other. Each one reads the shared conversation and writes the next message, back and forth, until a rule says "stop".

  • The left box (Coder) and the right box (Reviewer) are two agents — each is an LLM given a job by its system message.
  • The top arrow is the Coder sending its work ("here's the code"); the bottom arrow is the Reviewer answering ("fix line 3, then it's APPROVED"). They take turns — this is the conversation.
  • The red line at the bottom is the load-bearing part: the chat only ends when a termination condition matches — here, when the word APPROVED appears. Without such a rule the two would keep messaging forever.

In short: AutoGen coordinates agents by letting them converse. Your one non-negotiable job as the designer is to decide when the conversation is finished — everything else flows from that.

Lab M2.1 · A two-agent conversation essential

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Lab M2.1
shellpip install -U "autogen-agentchat" "autogen-ext[anthropic]"
pair.pyimport asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination
from autogen_ext.models.anthropic import AnthropicChatCompletionClient

model = AnthropicChatCompletionClient(model="claude-opus-4-8")

coder = AssistantAgent("coder", model_client=model,
    system_message="Write Python. Revise based on review feedback.")
reviewer = AssistantAgent("reviewer", model_client=model,
    system_message="Review the code. When it's correct, reply exactly 'APPROVED'.")

# stop when 'APPROVED' appears OR after 8 messages — always bound it two ways
term = TextMentionTermination("APPROVED")
team = RoundRobinGroupChat([coder, reviewer], termination_condition=term)

async def main():
    await team.run(task="Write a function that returns the nth Fibonacci number.")

asyncio.run(main())
▶ How this works

This is the smallest complete AutoGen program: two agents that talk until one says the magic word. A coder writes code and a reviewer checks it; they trade messages until the reviewer is satisfied. It shows every moving part you need — agents, a team, and a termination rule.

  1. The import lines pull in the pieces: AssistantAgent (an LLM-backed agent), RoundRobinGroupChat (a team where agents speak in a fixed rotation), a termination condition, and the Anthropic model client that connects to Claude.
  2. model = AnthropicChatCompletionClient(...) is the shared brain — both agents use the same Claude model; only their system messages (their instructions) differ.
  3. The two AssistantAgent(...) lines create the players. The coder is told to write and revise; the reviewer is told to reply exactly APPROVED when the code is correct — that exact word is what will end the chat.
  4. TextMentionTermination("APPROVED") is the stop rule: end the conversation as soon as the text APPROVED appears. RoundRobinGroupChat([coder, reviewer], ...) puts both agents in a team that alternates turns.
  5. team.run(task=...) kicks it off with a real job. It's async (note await and asyncio.run(main())) because agent calls happen over the network — await just means "wait here for the reply, don't freeze the program".

What the output means: Behind the scenes the coder writes a Fibonacci function, the reviewer critiques it, the coder revises, and the reviewer eventually replies APPROVED — which trips the termination condition and the run returns.

Try this: Change the reviewer's done-word to something the coder would never accidentally type, and give the pair a harder task. Watch how many back-and-forth turns it takes before APPROVED shows up.

A conversation with no termination is a runaway billTwo agents will happily thank each other forever. Always set a termination condition — and back it with a max-message cap as a second brake, exactly like the recursion limit on a LangGraph cycle (L5). The "APPROVED" keyword is the primary stop; the message cap is the safety net.

Lab M2.2 · Teams of three+ (who speaks next?) essential

Two agents never have to wonder whose turn it is — with three or more, that question is suddenly the whole game. Think about a two-person conversation: it can only ping-pong, A then B then A, so "who speaks next" answers itself. Now put five people in a meeting. If everyone talks in a rigid clockwise circle, the person with nothing useful to add still burns everyone's time when their slot comes around. If instead a good chair watches the discussion and calls on whoever's expertise the moment needs, the meeting moves twice as fast. AutoGen makes you pick which kind of meeting you're running — and that choice is called speaker selection.

Why does this deserve its own section rather than being an afterthought? Because speaker selection is exactly where AutoGen touches the topologies you met in L1. A fixed rotation is the simplest possible coordination — sequential collaboration, no brains required. Letting a model choose the next speaker is the supervisor/router pattern: an LLM reading the room and dispatching to the right specialist. The same abstract shapes you drew as boxes-and-arrows in L1 show up here as one constructor argument. Getting this choice right is the difference between a team that converges and one that flails.

The trap here is subtle and it bites people who reach for the "smart" option too early. A model-driven selector sounds strictly better than a dumb rotation — let the AI decide! — but it can only choose well if the agents have clearly distinguishable roles. Give it a "planner", a "coder", and a "reviewer" with sharp, non-overlapping system messages and it routes beautifully. Give it three vaguely-worded generalists and it picks more or less at random, wasting turns and money while looking sophisticated. The rule of thumb: a selector is only as good as the role descriptions you feed it, so earn the complexity — start with round-robin, and upgrade to a selector only once your roles are genuinely distinct.

Beyond two agents, the key question is speaker selection: who talks next? AutoGen gives you patterns for this — the difference between L1's network (free-for-all) and supervisor (a chooser) topologies.

Round-robin — fixed rotation planner coder reviewer always the same order, whoever is next in the circle Selector — a model chooses selectormodel planner coder reviewer reads the conversation, picks whoever fits now selector only picks well when the roles are clearly distinct Two ways to answer "who speaks next?" Round-robin cycles the same fixed order every time; a selector team asks a model to read the room and hand the floor to the agent that fits the moment.
🗺️ How to read this diagram

Compare the two halves left-to-right: the left is a fixed loop, the right is a chooser sitting above the same agents.

  • On the left, the blue arrows form a circle — planner → coder → reviewer → back to planner. The order never changes; each agent gets an equal, predictable turn regardless of whether it has anything useful to add.
  • On the right, the purple selector model box sits above the three agents and fans arrows down to all of them. Each turn it reads the conversation and picks one — the solid arrow is the agent it chose this turn, the dashed arrows are the ones it could have chosen.
  • The red caption is the catch: that smart routing only pays off when the three roles are sharply distinct. Give the selector three vague generalists and it picks almost at random.

In short: Round-robin = a fixed circle (simple, sequential); selector = a model dispatching to the right specialist (supervisor/dynamic routing) — earn the complexity with distinct roles.

Team patternHow the next speaker is chosenMaps to (L1)
Round-robinFixed rotation — each agent in turnSimple sequential collaboration
SelectorA model picks the best next speaker for the momentSupervisor / dynamic routing
Swarm / handoffAgents explicitly hand off to a named peerNetwork with directed edges
Lab M2.2

Requires: pip install autogen-agentchat

selector.pyfrom autogen_agentchat.teams import SelectorGroupChat
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination

planner  = AssistantAgent("planner",  model_client=model, system_message="Break the task into steps.")
coder    = AssistantAgent("coder",    model_client=model, system_message="Implement the current step.")
reviewer = AssistantAgent("reviewer", model_client=model, system_message="Review; say 'DONE' when complete.")

# selector uses a model to choose the next speaker each turn (supervisor-style)
term = TextMentionTermination("DONE") | MaxMessageTermination(20)   # combine conditions
team = SelectorGroupChat([planner, coder, reviewer], model_client=model,
                         termination_condition=term)
▶ How this works

With more than two agents the big question becomes who talks next? A round-robin just rotates, but a selector team asks a model to pick the best next speaker for the moment — like a supervisor handing the floor to whoever is most useful right now.

  1. Three agents are defined — a planner (breaks the task into steps), a coder (implements a step), and a reviewer (says DONE when complete). Distinct jobs matter here: the selector can only choose well if the roles are clearly different.
  2. TextMentionTermination("DONE") | MaxMessageTermination(20) combines two stop rules with | (meaning "either one ends it"): stop when someone says DONE, or after 20 messages no matter what. The second is a safety cap for when the agents get stuck.
  3. SelectorGroupChat([...], model_client=model, ...) builds the team. Unlike round-robin, it passes a model_client because a model does the choosing — reading the conversation and deciding who should speak next each turn.

Try this: Give the three agents vague, overlapping system messages and then very sharp, distinct ones. The selector's choices get noticeably smarter with clear roles — that's the whole point of letting a model pick the speaker.

Termination conditions composeNotice TextMentionTermination(...) | MaxMessageTermination(20) — conditions combine with |. Ship at least two: a semantic stop ("DONE") for the happy path and a hard cap for when the agents get stuck. This is the belt-and-suspenders pattern from every loop in this course.

Lab M2.3 · Tools & a human in the loop intermediate

A conversation, no matter how clever, is still just talk until someone can actually do something. Up to now our agents have been debating in a sealed room — they can reason and critique, but they can't look up today's stock price or run the code they just wrote. Two additions break the agents out of that room: tools (plain Python functions an agent may call) turn talk into action, and a human participant puts a real person in the loop as one of the speakers. Together they're what separate a chatty demo from something you'd trust to touch the real world.

The tool idea should feel familiar — it's the same tool-calling you built in the agents chapter, just happening inside a multi-agent conversation now. Here's the mental model that makes it click: an agent's tools are like the reference books and instruments an expert keeps within reach at the meeting. When the analyst needs a number it doesn't have, it doesn't guess and bluff — it reaches for the get_stock function, reads off the real value, and continues. The function's docstring is how the model knows when to reach for that particular book, so a vague or missing docstring is the most common reason a tool never gets called.

The human participant is the part worth slowing down on, because it's where safety lives. AutoGen's UserProxyAgent is genuinely two-faced: it can be a real person prompted at the keyboard, or an automated stand-in that auto-replies and even executes code on the agents' behalf. That second mode is exactly why AutoGen earned its reputation for code-writing loops — an assistant proposes code, the proxy runs it and reports the result, and they iterate. But it's also the trap: an auto-executing proxy with no sandbox will cheerfully run whatever the model dreamed up. The discipline is the same one this whole course repeats — a person is one of the speakers precisely so that anything irreversible pauses for a human "yes" first. This is L5's human-in-the-loop gate, expressed here as "a human is simply one of the participants in the chat."

AutoGen agents take tools (Python functions), and a human participant can join the conversation — the human-in-the-loop gate from L5, expressed as "a person is one of the speakers."

tools turn talk into action · a human gates the finish analyst agent LLM in the chat get_stock() calls for real data "$123.45" back proposes human(UserProxy) one of the speakers approves finalized anything irreversible pauses here for a human "yes" Tools + a human, inside the conversation. The analyst calls a plain Python function for a real number instead of guessing, then a human participant must approve before the result is finalized — the L5 gate, expressed as "a person is one of the speakers."
🗺️ How to read this diagram

Follow the flow left-to-right, with one detour down to the tool: the agent fetches real data, proposes, and the human decides.

  • The analyst agent (left) is an LLM in the chat. The downward arrow to get_stock() is a tool call — instead of inventing a price it calls a plain Python function and reads the real value back (the dashed return arrow).
  • The arrow across to the human (UserProxy) box is the agent proposing its answer. The human is not outside the system — it is one of the participants, taking a turn like any agent.
  • The green arrow to finalized only fires after the human approves. The red line is the payoff: anything irreversible waits here for a human "yes", which is exactly L5's human-in-the-loop gate.

In short: A tool lets the agent act on real data; the human participant is the gate that keeps an acting agent supervised before anything is committed.

Lab M2.3

Requires: pip install autogen-agentchat

tools_human.pyfrom autogen_agentchat.agents import AssistantAgent, UserProxyAgent

def get_stock(ticker: str) -> str:
    """Get the latest price for a ticker."""
    return f"{ticker}: $123.45"

analyst = AssistantAgent("analyst", model_client=model,
    tools=[get_stock],                       # a Python function becomes a tool
    system_message="Use tools to get real data; never guess prices.")

# the human joins as a participant — asked to weigh in before finalizing
human = UserProxyAgent("human", input_func=input)   # prompts a real person at the console

team = RoundRobinGroupChat([analyst, human], termination_condition=term)
▶ How this works

Two upgrades in one lab: give an agent a tool (a plain Python function it can call for real data) and add a human as a participant in the conversation. Together these turn a chatty demo into something that can act and be supervised.

  1. get_stock(ticker) is an ordinary Python function — it just returns a price string here. The docstring ("""Get the latest price...""") is important: the model reads it to understand when to call the tool.
  2. AssistantAgent("analyst", ..., tools=[get_stock], ...) hands that function to the agent as a tool. Now instead of guessing a price, the analyst can call get_stock and use the real result — the system message even forbids guessing.
  3. UserProxyAgent("human", input_func=input) adds a real person to the chat. input is Python's built-in "ask at the keyboard" function, so when it's this agent's turn, the program pauses and waits for you to type — a human-in-the-loop gate.
  4. RoundRobinGroupChat([analyst, human], ...) puts the analyst and the human in a team that alternates: the analyst proposes, the human responds, and so on until the shared termination condition fires.

What the output means: At runtime the analyst calls get_stock to fetch a price instead of inventing one, then the console pauses for the human to weigh in before anything is finalized.

Try this: Add a second tool (say a function that looks up a company name) and watch the analyst decide which tool to call. This is the same tool-calling idea from the agents chapter, now inside a multi-agent conversation.

Two flavors of "user proxy"A UserProxyAgent can be a real human (prompts for input) or an automated stand-in that auto-replies or executes code. That dual nature is why AutoGen became known for code-writing agents: an assistant proposes code, the proxy executes it and reports results, and they iterate — a reflection loop (L1) between two participants. Same safety rule as always: gate anything that executes code or takes irreversible action.

AutoGen vs CrewAI vs LangGraph intermediate

CrewAI — assembly line A B C fixed order, one pass AutoGen — conversation A B talk back and forth LangGraph — state machine s1 s2 gate explicit edges + gates same models, same tools — only the coordination differs Three metaphors, one job. CrewAI moves work down a fixed line, AutoGen lets agents converse to a solution, and LangGraph wires an explicit state machine you can gate — the difference is how much you trust free-form chat versus a guaranteed path.
🗺️ How to read this diagram

Read the three panels left-to-right; each shows the shape of how that framework coordinates the same specialists.

  • The left panel (CrewAI) is a one-way line — A → B → C, fixed order, a single pass. Simple and readable, but it can't easily double back.
  • The middle panel (AutoGen) is two boxes with arrows both ways — agents talk back and forth, doubling back as needed. Powerful for open-ended work, but it needs a termination rule or it never stops.
  • The right panel (LangGraph) is an explicit state machine: labelled states with drawn edges and a red gate node. You get a guaranteed path and can pause risky steps — at the cost of more upfront wiring.

In short: Pick the coordination shape by how much you trust free-form chat: a line (CrewAI), a conversation (AutoGen), or a wired-and-gated machine (LangGraph). The intelligence underneath is the same.

FrameworkMetaphorBest atWatch out for
CrewAI (M1)Roles on an assembly lineQuick, readable role-based teamsLess exact control
AutoGen (this)Agents in conversationOpen-ended collaboration, code-gen loops, research chatRunaway chatter without firm termination; API churn
LangGraph (L4/L5)Explicit state machineControl, persistence, human-in-the-loop, auditMore upfront wiring
Pick by how much you trust free-form chatReach for AutoGen when the problem genuinely benefits from agents conversing to a solution (brainstorming, iterative code, debate). Reach for LangGraph when you need to guarantee the path and gate risky steps. CrewAI sits between for role pipelines. All three can call the same Claude models and the same tools — the difference is the coordination model, not the intelligence.

Common pitfalls advanced

PitfallFix
No termination conditionAlways set one; combine a semantic stop with a max-message cap
Copying old pyautogen tutorialsUse the modern autogen-agentchat API; verify imports
Agents that flatter instead of finishingGive a concrete, checkable done-signal ("reply 'APPROVED'")
Auto-executing code with no sandboxSandbox the executor; gate destructive actions (T1)
Selector team with vague agent rolesDistinct system messages so the selector can choose well
Using conversation when you need controlUse LangGraph for guaranteed paths & audit

Exercises advanced

Exercise M2.1 — Coder + reviewer loop

Context: Removing the termination condition and watching the team run to the cap is the fastest way to feel why termination is load-bearing.

Your task: Build a coder + reviewer pair, give it a real task, confirm it terminates on "APPROVED", then remove the termination condition and watch it run to the message cap.

Requirements:

  • A coder and a reviewer agent on a real task
  • Confirm it terminates on the agreed stop word
  • Remove the termination condition and observe it running to the cap
  • Note why termination is load-bearing

💡 Hint: With no stop word the only thing that ends the run is the hard message cap — which is the backstop, not the intended exit.

Exercise M2.2 — Selector team

Context: A selector team only picks well when the roles are distinct — comparing it to round-robin shows what clear roles buy you.

Your task: Build a three-agent selector team (planner, coder, reviewer), read the transcript to judge whether the selector picks sensible speakers, and compare to a round-robin version.

Requirements:

  • Three agents with distinct roles
  • A selector picks the next speaker by content
  • Read the transcript and judge the choices
  • Tighten the system messages until routing is sensible
  • Compare to a round-robin version of the same three

💡 Hint: Round-robin is predictable but wastes turns; the selector adapts only as well as the roles are distinguished — vague roles make it choose poorly.

Show what to look for

Round-robin is predictable but wastes turns (the reviewer speaks even when there's nothing to review). Selector adapts but only as well as the roles are distinguished — vague roles make it pick poorly. Clear roles are what make dynamic selection pay off.

Exercise M2.3 — Human approval gate

Context: Both AutoGen's user-proxy and LangGraph's interrupt pause for a human — but one gives a more durable, resumable gate, and knowing which matters.

Your task: Add a UserProxyAgent (a real human) that must approve before an analyst's recommendation is finalized, then compare it to LangGraph's interrupt (L5).

Requirements:

  • A user-proxy requires human approval before finalizing the recommendation
  • Run it and exercise the approval step
  • Compare to LangGraph's interrupt-based gate
  • State which gives a more durable, resumable pause and why

💡 Hint: A conversational approval lives in the running chat; a checkpointed interrupt persists the pause and can resume a different process — that durability is the difference.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Agents in conversationBeginner

Context: AutoGen frames collaboration as a dialogue: work happens as messages between agents, unlike CrewAI's assembly line.

Your task: Model a two-agent exchange: each agent appends a message, run a few turns, and print the transcript.

Requirements:

  • Represent each message with its sender and content
  • Two agents append messages in turn
  • Run a few turns of back-and-forth
  • Print the transcript in order

💡 Hint: A list of message dicts that the agents append to; the collaboration is the dialogue, not a fixed pipeline.

Show solution

Work happens as a message exchange. Runnable stdlib:

def turn(agent_name, msg):
    return {"from": agent_name, "content": msg}

conversation = []
conversation.append(turn("assistant", "Here is a draft plan."))
conversation.append(turn("reviewer",  "Looks good, tighten step 2."))
conversation.append(turn("assistant", "Revised step 2."))

for m in conversation:
    print(f"{m['from']:10}: {m['content']}")

Unlike CrewAI's assembly line, AutoGen frames collaboration as a dialogue — agents talk until a rule stops them.

Exercise 2 · A termination condition (or it never stops)Intermediate

Context: Free-form agent chatter needs a firm stop, or it burns tokens forever — so a conversation ends on a stop word OR a message cap, two independent brakes.

Your task: Model a conversation loop that ends on a keyword ('TERMINATE') or a max-message cap.

Requirements:

  • Stop when the last message contains the stop word
  • Also stop when the message count hits the cap
  • Report which brake ended the conversation
  • Show the stop word ending a run before the cap

💡 Hint: Check both conditions each turn; the cap is the backstop for when the stop word never comes — the same two-brake idea as the L5 cycle.

Show solution

Two brakes, exactly like the L5 cycle. Runnable:

def should_stop(messages, max_messages=6, stop_word="TERMINATE"):
    if len(messages) >= max_messages:
        return "hit message cap"
    if messages and stop_word in messages[-1]["content"]:
        return "stop word seen"
    return None

def run(scripted_replies, max_messages=6):
    msgs = []
    for reply in scripted_replies:
        msgs.append({"content": reply})
        reason = should_stop(msgs, max_messages)
        if reason:
            return msgs, reason
    return msgs, "ran out of scripted turns"

msgs, why = run(["work...", "more work...", "done TERMINATE", "never reached"])
print(len(msgs), "messages; stopped:", why)   # 3 messages; stop word seen

Without a termination condition, a conversation team burns tokens forever — the message cap is the backstop when the stop word never comes.

Exercise 3 · Round-robin team: who speaks nextAdvanced

Context: A team pattern decides speaking order. Round-robin is the simplest: cycle through the agents in turn until termination.

Your task: Model round-robin over N agents, cycling through them until a turn cap.

Requirements:

  • Cycle through the agents in a fixed order
  • Wrap around after the last agent (modulo the team size)
  • Stop at a max-turns cap
  • Show the rotation over more turns than there are agents

💡 Hint: A rotating index (i % len(agents)) gives every agent an equal, predictable turn — the baseline before a smarter selector.

Show solution

Round-robin is a rotating index over the team. Runnable:

def round_robin(agents, max_turns=5):
    transcript = []
    for i in range(max_turns):
        speaker = agents[i % len(agents)]      # cycle through the team
        transcript.append((speaker, f"turn {i+1}"))
    return transcript

for who, what in round_robin(["planner", "coder", "reviewer"]):
    print(who, "->", what)
# planner, coder, reviewer, planner, coder ...

Round-robin gives every agent an equal, predictable turn — the simplest team pattern before you reach for a smarter selector.

Exercise 4 · Selector pattern: pick the next speaker by contentExpert

Context: A selector chooses the next speaker based on the conversation, not a fixed rotation — L1's network topology with brains.

Your task: Model a selector that routes to the agent whose skill the last message needs.

Requirements:

  • Each agent advertises keywords for its skill
  • The selector inspects the last message and picks the matching agent
  • Fall back to a default agent when nothing matches
  • Show messages routing to different agents by content

💡 Hint: Match the last message against each agent's keywords; whoever's skill fits speaks next, enabling open-ended collaboration.

Show solution

The selector routes on need, not order. Runnable:

AGENTS = {"coder": ["code", "bug", "implement"],
          "researcher": ["find", "source", "data"],
          "reviewer": ["review", "check", "approve"]}

def select_next(last_message):
    low = last_message.lower()
    for agent, keywords in AGENTS.items():
        if any(k in low for k in keywords):
            return agent
    return "reviewer"          # default when unsure

print(select_next("please implement the fix"))   # coder
print(select_next("find data on latency"))         # researcher
print(select_next("does this look done?"))          # reviewer

A selector team is L1's network topology with brains: whoever's skill fits the current message speaks next, enabling open-ended collaboration.

Exercise 5 · Add a human-in-the-loop participantProfessional

Context: A user-proxy agent represents the human inside the conversation — auto-replying for routine turns and interrupting for risky ones, the AutoGen version of L5's approval gate.

Your task: Model a proxy that pauses for human approval on risky proposals and auto-approves the rest.

Requirements:

  • Define which actions are risky
  • A risky proposal returns an ask-human decision (with a reason)
  • A safe proposal auto-approves
  • Show a safe action auto-approved and a risky one escalated

💡 Hint: The proxy is just another participant with a gate: branch on whether the proposed action is on the risky list.

Show solution

The human proxy is just another participant with a gate. Runnable:

RISKY = {"deploy", "delete", "spend"}

def user_proxy(proposal, auto_approve_safe=True):
    action = proposal.get("action", "")
    if action in RISKY:
        return {"decision": "ASK_HUMAN", "why": f"'{action}' is risky"}
    if auto_approve_safe:
        return {"decision": "AUTO_APPROVE"}
    return {"decision": "ASK_HUMAN"}

print(user_proxy({"action": "summarize"}))   # AUTO_APPROVE
print(user_proxy({"action": "deploy"}))       # ASK_HUMAN

The user-proxy lets a human sit inside the conversation — auto-replying for routine turns and interrupting for the risky ones, the AutoGen version of L5's approval gate.

Exercise 6 · The real AutoGen team (needs the framework)Industry scenario

Context: The production version uses the modern autogen-agentchat line: two AssistantAgents in a RoundRobinGroupChat with a termination condition — the conversation + brakes you modeled offline.

Your task: Write the real AutoGen team using documented APIs. (Needs the framework installed.)

Requirements:

  • Two AssistantAgents with a model client and system messages
  • Assemble them in a RoundRobinGroupChat
  • Combine two independent brakes: a TextMentionTermination (stop word) OR a MaxMessageTermination (cap)
  • Show the team being run on a task
  • Note the modern autogen-agentchat API differs from older pyautogen; label it as needing the framework + a model client

💡 Hint: The team rotates speakers and stops on the stop word or the message cap — the same two-brake pattern; verify imports against your installed version.

Show solution

Correct modern AutoGen. Needs pip install autogen-agentchat autogen-ext + a model client:

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination
# from autogen_ext.models.openai import OpenAIChatCompletionClient  # or another client

# model_client = OpenAIChatCompletionClient(model="...")  # your provider

writer = AssistantAgent("writer", model_client=model_client,
                        system_message="Draft a short answer.")
reviewer = AssistantAgent("reviewer", model_client=model_client,
                          system_message="Critique, then say TERMINATE when good.")

# two independent brakes: a stop word OR a hard message cap
termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(8)
team = RoundRobinGroupChat([writer, reviewer], termination_condition=termination)

# import asyncio; asyncio.run(team.run(task="Explain vLLM in two sentences."))

Same conversation + termination you modeled offline: the team rotates speakers and stops on the stop word or the message cap. The modern autogen-agentchat API differs from older pyautogen — verify imports against your version.

✓ Checkpoint — you can move on when you can…

  • Explain AutoGen's conversation model and its agent types.
  • Build a two-agent team with a termination condition.
  • Choose round-robin vs selector and say what each maps to in L1.
  • Add a tool and a human participant.
  • Choose between AutoGen, CrewAI, and LangGraph for a given problem.
🏗️ Toward the capstoneAn AutoGen coder+reviewer loop is a great way to generate and vet the Terraform the DevOps agent proposes — a debate that catches mistakes before a human ever sees the plan. But the moment that plan touches prod, control moves to the LangGraph safety gate (L5): conversation for ideation, a state machine for the irreversible step. See the safety-gate build →

Knowledge check check yourself

✓ Knowledge check

AutoGen models multi-agent work as a conversation; why is a termination condition described as the load-bearing design decision?

Show answer
Agents exchange messages until a termination condition fires — nothing stops the chatter on its own, so two agents will happily message forever and run up a bill. You must define when the conversation is done, and the guidance is to combine a semantic stop (e.g. the word "APPROVED") with a hard max-message cap as a second brake.
✓ Knowledge check

How does a SelectorGroupChat choose the next speaker differently from a RoundRobinGroupChat, and what does each map to in L1's topologies?

Show answer
Round-robin rotates speakers in a fixed order (simple sequential collaboration). A selector team uses a model to pick the best next speaker each turn (dynamic routing) — but it only chooses well when the agents have clearly distinct roles. Round-robin maps to sequential collaboration; selector maps to the supervisor/dynamic-routing topology.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in