Advanced

Multi-agent systems

When a task is too big for one agent, split it. Several specialists, each focused on its role, often do better than one agent trying to be everything.

What you will see

researcher ran (agent_calls=1) writer ran (agent_calls=2) reviewer ran (agent_calls=3)

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

When one agent cannot carry the whole task, I split it into specialists that pass their work forward. A supervisor routes each job, and every exchange stays bounded by a step limit.

1

I create three agents, one for research, one for writing, one for review, hand the research output to the writer, and print what each specialist holds.

class Agent: def __init__(self, role): self.role = role self.output = None def plan(self): self.output = self.role + " plan" def assign(self, ctx): self.output = self.role + " on: " + ctx def check(self, ctx): return self.role + " checked: " + ctx researcher = Agent("research") writer = Agent("write") reviewer = Agent("review") researcher.plan() researcher_out = "Found three sources on refunds." writer.assign(researcher_out) reviewer.check(writer.output) print("Agents: researcher, writer, reviewer") print("Research output handed to writer: " + researcher_out)

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Agents: researcher, writer, reviewer Research output handed to writer: Found three sources on refunds.
Splitting work keeps each agent simple and focused. Give every specialist one clear role instead of asking one agent to be everything.
2

A supervisor never does the deep work; it routes each task to the right specialist. I run the router and print which specialist gets the job.

def supervisor(task_type): tasks = ["write", "debug"] routed = [t for t in tasks if task_type != t] routed.append(task_type) return routed for task in supervisor("write"): print("Delegating to: " + task)

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Delegating to: debug Delegating to: write
A supervisor routes, specialists execute. If the lead starts doing the work itself, the team loses its focus.
3

Agents talking to each other can loop forever, so I cap the number of exchanges and stop the pipeline once the limit is hit, printing each run along the way.

agent_calls = 0 for agent in ["researcher", "writer", "reviewer"]: agent_calls += 1 if agent_calls > 20: print("Stopping loop: step limit reached") break print(agent + " ran (agent_calls=" + str(agent_calls) + ")")

Before you run it, say out loud what it will print. Then open the answer.

What it prints
researcher ran (agent_calls=1) writer ran (agent_calls=2) reviewer ran (agent_calls=3)
Every agent loop needs a hard step limit. Without it, agents handing tasks to one another can run forever and burn tokens for nothing.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A supervisor routes tasks to specialists; each agent acts and hands results along the pipeline.
1 Supervisor receives the goal
↓
2 Route to the right specialist agent
↓
3 Specialist runs and returns its result
↓
4 Collect and finish, with a step limit
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

class Agent: sets nothing on screen
def __init__(self, role): sets nothing on screen
self.role = role sets nothing on screen
self.output = None sets nothing on screen
def plan(self): sets nothing on screen
self.output = self.role + " plan" sets nothing on screen
def assign(self, ctx): sets nothing on screen
self.output = self.role + " on: " + ctx sets nothing on screen
def check(self, ctx): sets nothing on screen
return self.role + " checked: " + ctx sets nothing on screen
researcher = Agent("research") sets nothing on screen
writer = Agent("write") sets nothing on screen
reviewer = Agent("review") sets nothing on screen
researcher.plan() sets nothing on screen
researcher_out = "Found three sources on refunds." sets nothing on screen
writer.assign(researcher_out) sets nothing on screen
reviewer.check(writer.output) sets nothing on screen
print("Agents: researcher, writer, reviewer") Agents: researcher, writer, reviewer
print("Research output handed to writer: " + researcher_out) Research output handed to writer: Found three sources on refunds.
Why this works
Common multi-agent patterns
PatternHow it works
SupervisorOne agent routes to others
PipelineEach agent hands off to the next
DebateAgents argue, a judge decides

Supervisor for routing, pipeline for stages, debate for quality.

Pick the pattern to match how the work flows.

Avoiding runaway loops

Agents talking to agents can loop forever. Guard it:

if agent_calls > 20:
    force_stop()
    escalate()

Set a hard step limit on every agent loop.

Also give each agent a budget (tokens or cost) and stop when it runs low.

Examples

Assigning roles

One agent can't do it all

researcher = Agent("research")
writer = Agent("write")
reviewer = Agent("review")

researcher.plan()
writer.from(researcher.output)
reviewer.check(writer.output)

Each agent has a role and talks to the others. This is a multi-agent system.

Why it works: splitting work lets each agent stay focused and simpler.

A supervisor pattern

A lead agent delegates

if task.type == "write":
    delegate(writer)
elif task.type == "debug":
    delegate(debugger)
else:
    ask_human()

A supervisor routes work to the right specialist. It never does the deep work itself.

Supervisor: route. Specialists: execute.

Passing context between agents

Pass context between agents

researcher = Agent('research')
writer = Agent('write')
context = researcher.run(topic)
writer.from(context)   # writer sees the research

Agents share information by passing their output forward. Each agent only sees what it needs, but the team builds on shared knowledge.

Why it works: clear hand-offs keep specialists focused without redundant work.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Tool calling
Previous Lesson
Tool calling
Next Lesson
Reasoning & planning
Reasoning & planning →