In the previous lessons, you learned how to build powerful single agents by managing state, connecting external MCP servers, and creating custom tools. These techniques allow you to create agents that can handle complex tasks with specialized capabilities. However, some problems are so multifaceted that even a well-equipped single agent struggles to handle them optimally. Just as software development teams divide work among specialists — backend engineers, frontend developers, and database administrators — your agent systems can benefit from the same division of labor.
In this lesson, you'll learn how to design multi-agent systems in which specialized agents coordinate to solve complex problems. You'll discover how to define sub-agents with specific roles, configure a main orchestrator agent that delegates work, and observe how these agents communicate through the SDK's internal Task tool. By the end of this lesson, you'll understand how to build agent hierarchies that leverage each specialist's unique strengths, creating systems that are more capable than any single agent could be.
A sub-agent is a specialized agent designed to excel at a specific type of task. Rather than creating one generalist agent that tries to do everything, you create multiple focused agents, each optimized for its particular domain. The main agent, often called an orchestrator, coordinates these specialists by delegating appropriate tasks to each one.
Consider a code review workflow. A single agent could analyze code, fix bugs, and write documentation, but this approach has limitations. The agent might use an expensive model like Sonnet for all tasks, even simple documentation that could be handled by the faster, cheaper Haiku model. It might also struggle to maintain focus, mixing analysis concerns with implementation details. A multi-agent approach solves these problems by creating three specialists: an analyzer that uses Sonnet with Read and Grep tools to deeply understand code structure, a fixer that uses Sonnet with Read and Write tools to implement high-quality corrections, and a documenter that uses Haiku with Read and Write tools to efficiently generate clear documentation. Each agent focuses on what it does best, using the right model and tools for its specific role.
Multi-agent architectures shine when tasks have distinct phases that require different capabilities, when different subtasks benefit from different models or tool sets, when you want to optimize costs by using cheaper models for simpler tasks, or when you need clear separation of concerns for maintainability. The orchestrator agent doesn't need to know how to do everything — it just needs to know which specialist to call for each type of work.
Before you can create a multi-agent system, you need to define each sub-agent using the AgentDefinition class. This class allows you to specify everything that makes each agent unique: its role, its instructions, the tools it can use, and which model powers it.
The AgentDefinition takes four key parameters that shape the agent's behavior and capabilities:
description: A brief label that helps the orchestrator understand what this agent does — think of it as the agent's job titleprompt: The system instructions that define how this agent should approach its work, similar to thesystem_promptyou've used in previous lessons but specific to this sub-agent's roletools: A list of tool names this agent is allowed to use, giving you fine-grained control over each agent's capabilities. If omitted, the agent will inherit all tools available to the orchestratormodel: Which Claude model powers this agent. You can specify"haiku","sonnet", or"opus"just like you do for the main agent. There's also a special option that makes the sub-agent use whatever model the orchestrator is using.
The fixer and documenter agents complete our specialized team, each optimized for their specific roles in the code improvement workflow.
The fixer agent is designed to implement corrections:
- Focuses on identifying and fixing issues efficiently through its prompt
- Has access to
Readfor examining code andWritefor making changes - Uses
Sonnetbecause generating correct, high-quality code fixes requires sophisticated reasoning about code semantics and potential side effects
The documenter agent takes a different approach:
- Has
ReadandWritetools like the fixer - Uses the
Haikumodel instead ofSonnet - This is a strategic choice because documentation generation is primarily a text generation task that doesn't require the same level of complex reasoning as code analysis or bug fixing
Haikuis faster and more cost-effective, making it perfect for this role
This demonstrates one of the key advantages of multi-agent systems: you can optimize each agent's model choice based on the complexity of its specific task. Now that we have all three specialists defined, we can configure the orchestrator that will coordinate them.
With your sub-agents defined, you now configure the main orchestrator agent that will coordinate them. This happens through the agents parameter in ClaudeAgentOptions, which creates a registry of available specialists to which the orchestrator can delegate work.
The configuration establishes the orchestrator's capabilities and its team of specialists:
model="haiku": The orchestrator's job is primarily coordination and delegation, which doesn't require the reasoning power ofSonnet. The orchestrator needs to understand the task, decide which specialist to call, and synthesize their results — tasks thatHaikuhandles efficiently.max_turns=15: Allows for extended conversations since multi-agent workflows often require multiple back-and-forth exchanges as specialists complete their work and report backallowed_tools: Acts as a global allow-list for the entire multi-agent system — sub-agents can only use tools that appear in this list.permission_mode="acceptEdits": Set to automatically enable other file editing toolsagents: This dictionary maps string keys to objects, creating a registry of specialists. The keys you choose here become the identifiers the orchestrator uses when delegating work, so descriptive names like , , and make it clear which specialist handles which type of work.
The system_prompt in a multi-agent system defines the delegation strategy and workflow. Unlike previous lessons, where the system prompt simply guided a single agent's behavior, here it tells the orchestrator which specialists to call and in what order.
The system prompt establishes the orchestrator's identity as a Lead Developer and defines its goal: improving code quality. More importantly, it provides explicit instructions for the delegation workflow. The phrase "you MUST follow this strict process" creates a strong directive that the orchestrator should follow these steps in order. The numbered list specifies exactly which specialist to call at each stage, using the same keys defined in the agents dictionary. The instruction to "Report back after each step" ensures the orchestrator communicates progress to the user, making the multi-agent workflow transparent. This system prompt is deliberately prescriptive because orchestration requires clear coordination — without explicit instructions, the orchestrator might skip specialists, call them in the wrong order, or try to do the work itself instead of delegating. The prompt essentially defines a workflow in which analysis happens first to understand what needs fixing, then fixes are applied to resolve identified issues, and finally documentation is added to make the improved code maintainable. To understand how this delegation actually works, we need to look at the Task tool.
When the orchestrator delegates work to a sub-agent, the Claude Agent SDK uses an internal tool called Task to make the delegation happen. Understanding how this works helps you observe and debug multi-agent interactions.
This enhanced version of display_response checks if a tool being used is named "Task". When it detects a Task tool call, it prints the input parameters, which reveal exactly how the orchestrator is delegating work. The Task tool's input typically includes parameters like subagent_type (which specialist to call), description (a brief summary of what to do), and prompt (detailed instructions for the sub-agent). By displaying these parameters, you can see which specialist is being called, what task they're being given, and what specific instructions they're receiving. This visibility is valuable because it helps you verify that the orchestrator is following the delegation strategy you defined in the system prompt, shows you how the orchestrator translates high-level goals into specific instructions for each specialist, and helps you debug issues by revealing exactly what instructions each sub-agent received. Now let's see the complete multi-agent system in action.
With all the pieces in place — the three specialized sub-agents defined, the orchestrator configured with its delegation strategy, and the display_response helper ready to observe the workflow — you're ready to execute the multi-agent system. The agents will work on a Python file that contains several types of issues, making it an ideal candidate for demonstrating how different specialists handle different aspects of code improvement.
Here's the file that the agents will improve:
This code has no input validation, risks a ZeroDivisionError when the list is empty, uses an inefficient manual loop instead of the built-in sum() function, lacks type hints and docstrings, and has an unresolved TODO comment. These issues span multiple categories — bugs, code quality, and documentation problems — making it perfect for demonstrating how the analyzer identifies problems, the fixer resolves them, and the documenter adds comprehensive documentation.
To run the multi-agent system, you create the client with your configured options and issue a simple, high-level query:
The query is deliberately high-level — it doesn't specify how to improve the file or what steps to take because the orchestrator's system prompt handles that coordination. The orchestrator will ensure the work flows through the analyzer, fixer, and documenter in sequence, with each specialist using its specific tools and model to handle its part of the workflow. Let's observe how this unfolds.
When you run the complete example, the orchestrator begins by explaining its plan and using its own tools to locate and examine the file before delegating to specialists.
The orchestrator starts by explaining its approach and using its own tools to prepare for delegation. It uses Glob to search for the file in the filesystem and Read to examine the current contents. This demonstrates an important aspect of multi-agent systems: the orchestrator isn't just blindly delegating — it's intelligently preparing by gathering the information it needs. After confirming it found the file, the orchestrator explicitly states it will follow the strict three-step process defined in its system prompt. This shows the orchestrator is following its instructions to work methodically through analysis, fixing, and documentation phases. Now let's observe how it delegates to the analyzer.
With the file located and examined, the orchestrator delegates to the analyzer specialist with detailed instructions about what to investigate.
The orchestrator calls the Task tool to delegate to the analyzer. The Task tool input reveals the delegation details: subagent_type: analyzer specifies which specialist to call, the description provides a brief summary of the task, and the prompt contains comprehensive instructions organized as a bulleted list of what to analyze. This structured prompt ensures the analyzer knows exactly what to look for — error handling gaps, code style problems, performance issues, missing validation, missing documentation, logic errors, and best practice violations. By providing this level of detail, the orchestrator guides the analyzer to conduct a thorough investigation across all relevant quality dimensions. The analyzer will now use its Read and Grep tools to examine the code.
The analyzer specialist uses its tools to examine the code and produces a comprehensive report categorizing all the issues it found.
The analyzer uses its Read tool to examine the file contents and identifies issues across seven categories. The orchestrator reports back that the analysis is complete and summarizes the finding — seven categories of issues were discovered. While the detailed report isn't shown in full here, the orchestrator's response confirms that the analyzer successfully identified error handling gaps, code style problems, performance issues, missing validation, missing documentation, logic errors, and best practice violations. This comprehensive analysis provides the foundation for the next phase. The orchestrator explicitly announces it's moving to Step 2, demonstrating it's following the strict process defined in its system prompt. Now let's observe how it delegates the fixing work.
With the analysis complete, the orchestrator moves to the second phase by delegating to the fixer specialist with highly detailed instructions based on the analyzer's findings.
The orchestrator calls the Task tool with subagent_type: fixer and provides an exceptionally detailed prompt. The prompt translates the analyzer's findings into a numbered list of ten specific fixes to implement. Each item is concrete and actionable — add error handling for specific cases, replace the loop with sum(), add type hints, implement the TODO, and so on. The prompt prioritizes critical bugs like division by zero at the top and includes instructions about ensuring production-ready code quality. This demonstrates the orchestrator's coordination skill: it synthesizes information from the analyzer and translates it into clear, prioritized instructions for the fixer. The fixer doesn't need to re-analyze the code; it receives an explicit checklist of what to fix. Now the fixer will use its Read and Write tools to apply these corrections.
The fixer specialist reads the current code, applies all the necessary corrections, and writes the improved version back to the file.
The fixer uses Read to examine the current code, then Write to apply all ten fixes from the detailed checklist. The orchestrator confirms the fixes are complete and explicitly announces it's moving to Step 3, demonstrating it continues to follow the strict process. At this point, the code has been transformed — it now includes proper error handling for empty lists and division by zero, input type validation, the efficient sum() function instead of a manual loop, type hints throughout, comprehensive docstrings, resolved TODOs, enhanced functionality in process_data(), PEP 8 compliant style, and a module-level docstring. The code is functionally correct and well-structured. Now the orchestrator delegates to the documenter to add the final layer of comprehensive documentation.
With the code functionally correct and well-structured, the orchestrator moves to the final phase by delegating to the documenter specialist to enhance the documentation.
The orchestrator calls the Task tool with subagent_type: documenter and provides detailed instructions about what documentation to add. The prompt is organized as a numbered list covering module-level documentation, function docstrings, inline comments, type hint clarifications, usage examples, and any additional helpful documentation. Importantly, the prompt acknowledges that some documentation may already be present from the fixer's work (note the phrase "if not already present"), instructing the documenter to review and enhance rather than blindly overwrite. The goal statement at the end emphasizes making the code "self-documenting" — documentation so clear that developers can understand and maintain the code easily. This is where using Haiku for the documenter pays off, as documentation enhancement is primarily a text generation task that doesn't require Sonnet's reasoning capabilities. The documenter will now use its Read and Write tools to add comprehensive documentation.
The documenter specialist reads the fixed code, adds comprehensive documentation, writes the enhanced version, and the orchestrator verifies the results before providing a detailed summary of the entire workflow.
The documenter uses Read to examine the fixed code, then Write to add comprehensive documentation throughout. After the documentation is complete, the orchestrator performs a verification step by reading the final version one more time to confirm the results. This demonstrates good orchestration practice — verifying work completion before declaring success. The orchestrator then provides an impressive final summary organized into sections for each phase. For Step 1, it recaps the seven categories of issues found. For Step 2, it lists the six major fixes applied with checkmarks. For Step 3, it details the documentation enhancements including specific metrics like "55 lines" of module-level documentation. The summary concludes with key improvements that include a dramatic transformation metric: from 10 lines of problematic code to 325 lines of production-ready, well-documented code. This demonstrates the power of multi-agent coordination — each specialist focused on what it does best while the orchestrator ensured they worked together in the right sequence to achieve a remarkable transformation.
The multi-agent code review system you just built demonstrates how specialized agents coordinate through built-in tools like Read, Write, and Grep. But what if your sub-agents need capabilities from custom tools or external MCP servers? Perhaps your analyzer needs to search API documentation, or your documenter needs to query a knowledge base.
To enable sub-agents to use MCP tools, you must follow one critical rule: the orchestrator's allowed_tools must include any MCP tools that sub-agents will use. The orchestrator acts as a gatekeeper — if an MCP tool isn't in its allowed_tools, no sub-agent can use it, even if the tool is specified in the sub-agent's configuration.
Let's enhance our code review system with a documentation researcher that uses MCP tools:
The orchestrator's allowed_tools includes both its own tools (Read, Write, Grep) and the MCP tools that the researcher sub-agent needs. The researcher can now use these MCP tools to fetch API documentation and inform the code review process with authoritative information about best practices.
You've now mastered the art of designing sophisticated multi-agent systems that coordinate specialized agents to solve complex problems. The pattern is clear: define each sub-agent using AgentDefinition with its specific description, prompt, tools, and model; register these specialists in the agents parameter using descriptive keys; configure the orchestrator's delegation strategy through a detailed system_prompt; and observe the delegation through the Task tool to understand the workflow. Sub-agents can access built-in tools and MCP tools, allowing you to equip specialists with external capabilities. Multi-agent systems excel when tasks have distinct phases, when different subtasks benefit from different models or tool sets, when you want to optimize costs, and when you need clear separation of concerns.
In the upcoming practice exercises, you'll design your own multi-agent systems for different domains, experiment with orchestration strategies, and build workflows that leverage each agent's unique strengths to create solutions more powerful than any single agent could achieve alone!
