Multi-Agent Lab
Hands-on: build a research team with a supervisor, researcher, and writer agent
Overview
Throughout Module 10, you learned why multi-agent systems exist, how to orchestrate them, how they communicate, and how they reach consensus. Each lesson introduced a pattern in isolation. But real multi-agent systems combine all of these patterns into a cohesive whole -- a team of agents that coordinate, delegate, and produce results no single agent could achieve alone.
In this hands-on lab, you will build a research team from scratch: a Supervisor agent that decomposes research questions, a Researcher agent that gathers and analyzes information, and a Writer agent that synthesizes findings into a polished report. You will build this system progressively, adding one component at a time, so you can see how the pieces fit together.
By the end of this lab, you will have a working multi-agent system that takes a research question and produces a structured report -- and you will understand every moving part.
Architecture
Here is the complete system you will build. Study this diagram before diving into code -- it shows every agent, how they communicate, and what data flows between them.
graph TD
User["User: Research Question"] --> Sup["Supervisor Agent"]
subgraph ResearchTeam["Research Team"]
Sup -->|"assigns subtasks"| R1["Researcher Agent 1"]
Sup -->|"assigns subtasks"| R2["Researcher Agent 2"]
R1 -->|"findings"| State["Shared State"]
R2 -->|"findings"| State
State -->|"all findings"| Sup
Sup -->|"synthesize brief"| Writer["Writer Agent"]
Writer -->|"draft report"| Sup
end
Sup -->|"final report"| User
style ResearchTeam fill:#ebf8ff,stroke:#3182ce
style Sup fill:#fefcbf,stroke:#b7791f
style R1 fill:#c6f6d5,stroke:#38a169
style R2 fill:#c6f6d5,stroke:#38a169
style Writer fill:#fed7d7,stroke:#c53030
style State fill:#e9d8fd,stroke:#805ad5
The Supervisor is the orchestrator. It receives the user's question, breaks it into subtasks, dispatches those subtasks to Researcher agents, collects findings into shared state, and then hands everything to the Writer agent for synthesis. This is the supervisor pattern from lesson 02 combined with the shared-state communication model from lesson 03.
Step 1: Define the Shared State and Message Types
Before writing any agent logic, you need the data structures that agents use to communicate. In a multi-agent system, clear message contracts are the foundation that everything else builds on. Without them, agents cannot understand each other's output.
The shared state is a central data store that all agents can read from and write to. It holds the research question, the subtasks, each researcher's findings, and the final report. This is the blackboard pattern from lesson 03 -- a shared memory that agents coordinate through.
A few design decisions to notice:
ResearchStateis a single object that gets passed to every agent. This is simpler than point-to-point messaging for a small team.message_logrecords every inter-agent message. This is critical for debugging -- when something goes wrong in a multi-agent system, the message log is your primary diagnostic tool.log_messageprints a summary to the console so you can watch the agents communicate in real time.
Step 2: Build the Base Agent
All three agents share common behavior: they each have a role, a system prompt, and the ability to call the LLM. Rather than duplicating this logic, define a base agent class that encapsulates the shared functionality. Each specialized agent will extend this base.
The call_llm_json method handles a common pattern in multi-agent systems: agents that need to return structured data rather than free-form text. The Supervisor uses JSON to define subtasks, the Researcher uses JSON to structure findings, and only the Writer returns prose.
Step 3: Implement the Researcher Agent
The Researcher agent is a specialist. It receives a focused subtask question, analyzes it thoroughly, and returns structured findings. It does not decide what to research -- that is the Supervisor's job. This separation of concerns is what makes multi-agent systems powerful: each agent does one thing well.
Notice how the Researcher writes its findings into the shared state (state.findings[subtask.id]). It also logs messages back to the Supervisor so the Supervisor knows when a subtask completes or fails. This is the blackboard pattern in action -- agents communicate by writing to shared state rather than sending direct messages.
Step 4: Implement the Writer Agent
The Writer agent has a completely different skill set from the Researcher. It does not analyze questions -- it takes raw findings and transforms them into a coherent, well-structured report. In multi-agent terminology, this is a pipeline handoff: the Researcher's output becomes the Writer's input.
The Writer receives a brief constructed from all the Researcher findings in the shared state. It does not know which Researcher produced which finding -- it sees them as a unified body of research to synthesize. This loose coupling means you can add more Researchers without changing the Writer at all.
Step 5: Build the Supervisor Loop
The Supervisor is the brain of the system. It implements the orchestration loop you studied in lesson 02: decompose the task, dispatch subtasks to workers, monitor progress, and aggregate results. This is where all the patterns from this module converge.
flowchart TD
A["Receive research question"] --> B["Decompose into subtasks"]
B --> C["Assign subtasks to Researchers"]
C --> D{"All subtasks complete?"}
D -->|"No"| E["Wait for Researcher results"]
E --> D
D -->|"Yes"| F["Hand findings to Writer"]
F --> G["Review draft report"]
G --> H{"Report acceptable?"}
H -->|"No"| I["Request revisions"]
I --> F
H -->|"Yes"| J["Deliver final report"]
style A fill:#fefcbf,stroke:#b7791f
style B fill:#fefcbf,stroke:#b7791f
style C fill:#c6f6d5,stroke:#38a169
style F fill:#fed7d7,stroke:#c53030
style G fill:#fefcbf,stroke:#b7791f
style J fill:#c6f6d5,stroke:#38a169
The Supervisor's run method implements a four-phase pipeline:
- Decompose -- the Supervisor uses its own LLM call to break the question into subtasks
- Dispatch -- subtasks are assigned to Researchers in a round-robin pattern
- Monitor -- the Supervisor checks how many subtasks completed versus failed
- Synthesize -- all findings are handed to the Writer for report generation
This is a simplified version of the orchestration loop. In production, Phase 2 would run Researchers in parallel (using asyncio), and Phase 3 would include retry logic for failed subtasks. But the sequential version here makes the flow easier to follow and debug.
Step 6: Run the System
With all the pieces in place, running the multi-agent team is a single function call.
When you run this, you will see the console output trace every inter-agent message:
The message log at the bottom gives you full observability into the agent interactions. This is not just useful for debugging -- it is essential for understanding how information flows through the system and where bottlenecks or failures occur.
Making It Production-Ready
The system you just built works, but a production multi-agent system needs several additional capabilities. Here is a map of what to add and where each concern fits.
graph LR
subgraph Core["What You Built"]
A["Supervisor Loop"]
B["Researcher Agent"]
C["Writer Agent"]
D["Shared State"]
end
subgraph Production["Production Additions"]
E["Async Execution"]
F["Retry / Fallback"]
G["Token Budget Tracking"]
H["Evaluation Harness"]
end
A --> E
B --> F
C --> G
D --> H
style Core fill:#c6f6d5,stroke:#38a169
style Production fill:#ebf8ff,stroke:#3182ce
Async execution is the most impactful improvement. In the current system, Researchers run sequentially -- each one waits for the previous to finish. With asyncio and the Anthropic async client, all Researchers can run in parallel, cutting total latency dramatically.
Retry and fallback logic handles the reality that LLM calls sometimes fail -- rate limits, malformed JSON, network errors. Wrap each agent call in a retry loop with exponential backoff.
Token budget tracking prevents runaway costs. Each LLM call returns usage.input_tokens and usage.output_tokens. Track these across all agents and enforce a budget ceiling on the entire research run.
Evaluation harness is the topic of Module 11. Once your multi-agent system produces reports, you need systematic ways to measure whether those reports are accurate, complete, and well-written.
Key Takeaways
This lab brought together every pattern from Module 10 into a working system:
- Supervisor pattern (lesson 02): the Supervisor decomposes tasks, dispatches them, and aggregates results
- Shared state (lesson 03): agents communicate through a central
ResearchStateobject rather than direct point-to-point messaging - Task decomposition (lesson 05): the Supervisor uses the LLM to break questions into independently researchable subtasks
- Separation of concerns: Researchers analyze, the Writer synthesizes, and the Supervisor coordinates -- no agent tries to do everything
The most important lesson is not any single pattern -- it is how the patterns compose. A multi-agent system is not a collection of independent agents. It is a carefully designed pipeline where each agent's output feeds the next agent's input, and the Supervisor ensures the whole process moves toward a useful result.
What Comes Next
You have built a multi-agent research team that decomposes questions, distributes work, and synthesizes findings into a report. It coordinates agents, passes messages, and produces results. But how do you know the results are any good?
Right now, you are evaluating the system by reading its output and making a judgment call. That works for a demo, but it does not scale. What if the Researcher hallucinates a finding? What if the Writer drops an important detail? What if the Supervisor decomposes the question poorly? You need systematic, repeatable ways to measure quality.
You have built complex agent systems -- but how do you know they work correctly? Module 11: Evaluation and Testing covers exactly this: metrics, benchmarks, test harnesses, and evaluation pipelines that tell you whether your agents are actually doing their job.
Summary
In this lab, you built a complete multi-agent research system with three specialized agents working together:
- Shared state and message types provide the communication foundation that all agents build on -- without clear contracts, agents cannot coordinate
- A base agent class encapsulates the common LLM interaction logic so specialized agents focus only on their unique behavior
- The Researcher agent takes focused subtask questions and returns structured findings to the shared state
- The Writer agent reads all findings from shared state and synthesizes them into a coherent report, demonstrating pipeline handoff
- The Supervisor loop ties everything together -- decomposing questions, dispatching to Researchers round-robin, monitoring completion, and triggering synthesis
- Production readiness requires async execution, retry logic, token budget tracking, and the evaluation techniques covered in Module 11