C1 · lesson 3 · 10 min read
Orchestrator–worker and parallel subagents
Split a task across focused subagents that run in parallel, each with its own context and tools, and keep the fan-out bounded.
Հայերեն
Official video by Anthropic on YouTube. Pressing play loads it from youtube-nocookie.com. ·open on YouTube
In the orchestrator–worker pattern, one model breaks a task down at run time, hands the pieces to workers, and combines what they report back. The key words are “at run time”. If you know the subtasks in advance, a fixed parallel workflow is simpler (see lesson 6). Orchestrator–worker fits when the split depends on the input: which services a change touches, or which logs look suspicious.
What a subagent gives you
In the Agent SDK, workers are subagents: separate agent instances that Claude
starts through the Agent tool. Each one has:
- Its own context. It starts fresh, apart from forks. Its tool calls and file reads stay inside it, and only its final message goes back to the parent. A worker can read forty log files and return eight lines.
- Its own instructions. It gets its own system prompt (
prompt), not the parent’s. - Its own tools. If you list
tools, that’s all it gets. A tool you leave out isn’t in its session at all. - Parallelism. Independent workers run at the same time, so the fan-out takes as long as the slowest worker, not the sum of all of them.
Defining workers
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
WORKERS = {
"log-scanner": AgentDefinition(
description="Finds error bursts and stack traces in logs/. Use for any log question.",
prompt="Scan only the paths you are given. Return at most 10 lines: timestamp, file:line, error.",
tools=["Read", "Grep", "Glob"],
model="haiku",
),
"manifest-reviewer": AgentDefinition(
description="Reviews Kubernetes manifests for probe, resource and image changes.",
prompt="Report risky settings with file:line. Never propose kubectl apply.",
tools=["Read", "Grep", "Glob"],
),
}
async def main():
async for msg in query(
prompt="Checkout API returns 502 since 14:10. Triage.",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"],
agents=WORKERS,
max_budget_usd=2.0,
env={"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "1",
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "4"},
),
):
if hasattr(msg, "result"):
print(msg.result)
asyncio.run(main())
AgentDefinition requires only description and prompt. The optional fields
include tools, disallowedTools, model (an alias such as haiku, inherit,
or a full model ID), maxTurns, skills, mcpServers, effort, background
and permissionMode. Claude picks a worker by matching the task against its
description, so write descriptions like routing rules. To force a particular
worker, name it in the prompt.
Keeping the fan-out bounded
Claude decides how many subagents to start, and subagents can start their own. One prompt can grow into a tree of agents. Three limits keep it in check:
| Limit | How you set it | Default |
|---|---|---|
| Depth | CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH |
3 layers |
| Concurrency | CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS |
20 at once |
| Spend | max_budget_usd / maxBudgetUsd |
No limit |
Subagent spend counts toward the parent’s budget. When the cap is reached, new
spawns fail with Budget limit reached, background workers are stopped, and the
query ends with error_max_budget_usd.
When it’s worth it
Good fits: reviews that split by concern (security, cost, tests), incident triage across several independent sources, and research over many files where the parent only needs conclusions. Poor fits: steps that depend on each other (worker B needs A’s output), tiny tasks where spawning costs more than doing, and anything that writes to the same files or resources in parallel. Two workers editing one Terraform state is a race, not a speed-up.
Recap
- Orchestrator–worker means a model splits the work at run time, delegates it, and merges the results.
- Subagents give context isolation, their own prompt and tools, and parallel execution. Only the final message comes back.
- Define them with
agentsandAgentDefinition. Thedescriptiondrives delegation. - Bound the tree with depth, concurrency and
max_budget_usd, and never let workers write to shared state in parallel.
$ ./try-it.sh
Try it: parallel incident triage
- Save the orchestrator from this lesson as
triage.pyin a repo that has alogs/folder and some Kubernetes manifests. - Run it with the prompt "Checkout API returns 502 since 14:10. Triage." and print every
tool_useblock namedAgent(also matchTaskfor older versions) together with itssubagent_type. - Print each message that has a
parent_tool_use_id. Confirm that the workers' file reads appear there, not in the orchestrator's own turns. - Set
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTSto1inenvand compare the wall-clock time with the parallel run. - Lower
max_budget_usdto0.10and check that the run ends witherror_max_budget_usdinstead of spawning more workers.
Check yourself
5 questions. Original practice questions, not taken from the real exam.