Lesson 4: Multi-Agent Systems
Course: Building AI Agents
Estimated time: 30 minutes
Difficulty: Intermediate-Advanced Builder
There's a ceiling on what a single agent can do well. Not because of capability - a single agent with the right tools can theoretically handle almost any task. The ceiling is quality and reliability. When you ask one agent to research a topic, write about it, edit the draft, and publish it, you get output that's mediocre at all four things. The agent is spreading its context across competing objectives.
The solution is the same one that works in human organizations: specialization. Multiple agents, each focused on one job, handing off to the next.
This is multi-agent architecture. It's more complex to build than a single agent, and that complexity is only worth it in specific situations. This lesson covers when to go multi-agent, how the patterns work, and how to actually build one.
When One Agent Isn't Enough
Single agents break down in three situations:
The task is too long for a single context window. A research agent that reads 20 web pages and synthesizes them will fill its context before it's halfway through the sources. A researcher agent reads and summarizes each page separately. An orchestrator agent combines the summaries. Neither runs out of context.
Different parts of the task require different expertise. Writing great content and editing great content are different skills. A writing agent with a system prompt focused on voice, flow, and engagement writes differently than an editing agent focused on logic, accuracy, and clarity. Separate prompts produce better outputs than one prompt trying to do both.
You need quality gates between steps. If you're producing content at scale, you want a checkpoint between "draft" and "publish." An editor agent that reads the draft and either approves it or returns specific revision notes is more reliable than hoping the writer agent gets it right every time.
If none of these conditions apply to your task - it fits in context, it doesn't require conflicting expertise, you don't need intermediate checkpoints - keep it as a single agent. Multi-agent adds real overhead.
The Orchestrator-Specialist Pattern
The most common multi-agent pattern. One orchestrator, multiple specialists.
The orchestrator's job is coordination: break the task into pieces, assign them to specialists, collect the outputs, handle failures, and assemble the final result. The orchestrator usually doesn't do the actual work - it manages who does.
Each specialist does one thing: research, writing, editing, publishing, fact-checking, code review, whatever the pipeline requires. A specialist has a focused system prompt, a specific tool set for its job, and a defined input/output contract.
The contract is the key part. When you build a specialist, define:
- What it receives as input (always)
- What it returns as output (always)
- What it returns if it fails (always)
Without consistent contracts, the orchestrator has to handle every specialist's quirks differently. With consistent contracts, adding a new specialist is just adding another handler in the orchestrator loop.
A simple content pipeline looks like this:
Orchestrator receives: topic + target audience + publish destination
-> Researcher receives: topic, returns: 5 source summaries + key facts
-> Writer receives: source summaries + key facts + target audience, returns: draft article
-> Editor receives: draft article, returns: APPROVED or REVISION_NEEDED + notes
-> (if REVISION_NEEDED) Writer receives: draft + revision notes, returns: revised draft
-> Publisher receives: approved article + destination, returns: published URL
Orchestrator returns: published URL + summary of what was published
Each agent in that pipeline has a clear job. The orchestrator's loop is just: call next agent, check output, decide what to do based on the result.
Agent Communication: How Specialists Talk to Each Other
Agents don't talk to each other directly. They pass messages through your code (or through the orchestrator). There are three common patterns:
Sequential handoff. Agent A finishes, returns output, orchestrator passes that output to Agent B. Simple, easy to debug, works for linear pipelines. The downside: if Agent A fails, the whole pipeline stops.
Parallel execution. Multiple agents run at the same time on different parts of the task. A research agent fetching five URLs simultaneously is faster than fetching them sequentially. Requires your orchestrator to spawn concurrent operations and collect results. In Python, asyncio or concurrent.futures handles this.
Feedback loops. Agent B reads Agent A's output and can send it back for revision. The editor-writer loop is the classic example: editor rejects the draft with notes, writer revises, editor approves. You need a maximum iteration count here, or you get a loop that never terminates.
Shared context vs. message passing. Two ways agents share state: you can write to a shared file or database that all agents read, or you can pass the relevant context in each agent's input message. Shared context is simpler to code but harder to debug (which agent wrote that? when?). Message passing is more explicit and easier to trace.
For most pipeline use cases, message passing with a shared results file works well. The orchestrator writes each specialist's output to a structured results file. Later specialists read what earlier ones produced. The orchestrator reads the final file to assemble the result.
Building a 2-Agent Research and Writing System
Let's make it concrete. A research-writer pipeline: one agent finds information, one agent writes about it.
The Researcher Agent
RESEARCHER_PROMPT = """You are a research specialist. Your job is to gather accurate, relevant information on a topic and return a structured summary.
You have access to:
- search_web: search for information
- fetch_url: read the full content of a specific page
When you receive a research request:
1. Search for the topic using 2-3 different search queries
2. Identify the 3-4 most relevant sources from search results
3. Fetch and read each source
4. Extract the key facts, quotes, and data points relevant to the topic
5. Return a structured research brief
Output format:
RESEARCH_BRIEF:
Topic: [topic]
Key Facts:
- [fact 1]
- [fact 2]
...
Sources:
- [source title] - [URL]
...
"""
def run_researcher(topic: str) -> str:
messages = [{"role": "user", "content": f"Research this topic: {topic}"}]
return run_agent_loop(RESEARCHER_PROMPT, messages, research_tools)
The Writer Agent
WRITER_PROMPT = """You are a content writer. You receive a research brief and write a clear, engaging article based on it.
When you receive a research brief:
1. Read through all the facts and sources
2. Identify the most important points to cover
3. Write an article of 600-800 words that explains the topic clearly
4. Cite specific facts from the research brief (not general claims)
5. End with a clear takeaway or conclusion
Output format:
TITLE: [article title]
---
[article body]
---
SOURCES: [list of sources you used]
"""
def run_writer(research_brief: str, target_audience: str) -> str:
messages = [
{
"role": "user",
"content": f"Write an article for {target_audience} based on this research:\n\n{research_brief}"
}
]
return run_agent_loop(WRITER_PROMPT, messages, []) # Writer has no tools - just context
The Orchestrator
def run_content_pipeline(topic: str, target_audience: str) -> dict:
print(f"Starting pipeline for: {topic}")
# Step 1: Research
print("Running researcher...")
research_brief = run_researcher(topic)
if research_brief.startswith("ERROR"):
return {"status": "failed", "stage": "research", "error": research_brief}
# Step 2: Write
print("Running writer...")
article = run_writer(research_brief, target_audience)
if article.startswith("ERROR"):
return {"status": "failed", "stage": "writing", "error": article}
# Return assembled result
return {
"status": "complete",
"topic": topic,
"research_brief": research_brief,
"article": article
}
The orchestrator is just a function. It calls specialists in sequence, checks their outputs, handles errors, and assembles the result. No magic. No framework required for this pattern.
Quality Gates
A quality gate is an agent (or check) that sits between two pipeline stages and decides whether to pass the output forward or reject it.
The simplest quality gate is a reviewer agent:
REVIEWER_PROMPT = """You are a content reviewer. You check articles against quality standards and return either APPROVED or REVISION_NEEDED.
Quality standards:
- Claims must be specific, not vague
- Each major point should reference a source
- No filler phrases or padding
- Clear structure with a logical flow
If the article meets standards, return:
APPROVED
If it needs revision, return:
REVISION_NEEDED
Issues:
- [specific issue 1]
- [specific issue 2]
...
"""
Wire it into the orchestrator between the writer and publisher stages:
# After writing:
review_result = run_reviewer(article)
if review_result.startswith("APPROVED"):
# Continue to publish
pass
elif review_result.startswith("REVISION_NEEDED"):
# Extract issues and send back to writer
revised_article = run_writer_revision(article, review_result)
# Review again (with max 2 rounds)
Quality gates slow the pipeline down and increase costs. They're worth it when the cost of bad output is high - publishing wrong information, sending a bad email, deploying broken code.
Practical Considerations
Cost adds up. Every agent in your pipeline is an API call. A 4-agent pipeline costs 4x what a single agent costs. For high-volume use, calculate this before committing to multi-agent architecture. Sometimes a single well-promped agent is cheaper and good enough.
Debugging is harder. When something goes wrong in a multi-agent pipeline, you need to know which agent failed and why. Log every agent's input, output, and any errors. Don't just log the final result.
Start with 2. If you're thinking about a 5-agent pipeline, build the 2-agent version first. Orchestrator + one specialist. Get that working and the patterns become obvious. Add specialists one at a time.
Failure modes compound. If each agent has a 90% success rate on its task, a 4-agent pipeline in sequence has roughly 65% end-to-end success. Design each specialist to be as reliable as possible. Error handling at the orchestrator level needs to account for partial failures.
Exercise: Build a 2-Agent System
Take the researcher and writer code from this lesson and wire them together.
Your orchestrator should:
- Accept a topic and target audience from the command line
- Run the researcher, log its output to a file
- Pass the research brief to the writer, log that output
- Return the final article to stdout
Then extend it: add one quality gate. It can be simple: does the article have a title? Does it cite at least two sources? Does it meet a minimum word count? Anything that catches an obviously bad output before it goes further.
Run the pipeline on 3 different topics. Read the research briefs and the articles. Is the writer using the research? Is the research actually relevant? What would you change about the prompts?
Key Takeaway
Multi-agent systems are the right tool for long tasks, tasks requiring conflicting expertise, and tasks that need quality gates. For everything else, a single agent is simpler and cheaper. Know the difference before you build.
What's Next
Your agents can do things and work together. But they still forget everything when they stop running. Lesson 5 is about memory and reliability - the two things that separate a demo agent from one you can trust in production.