Subagents Reference
Subagents let you define specialized agents for parallel or focused work.
AgentDefinition
@dataclass
class AgentDefinition:
description: str # When to use this agent
prompt: str # Agent's system prompt
tools: list[str] | None = None # Allowed tools (inherits if None)
model: Literal["sonnet", "opus", "haiku", "inherit"] | None = None
Fields
| Field | Required | Description |
|---|---|---|
description | Yes | Natural language description for routing |
prompt | Yes | System prompt defining behavior |
tools | No | Tool allowlist (inherits parent if None) |
model | No | Model override (sonnet, opus, haiku, inherit) |
Defining Subagents
from claude_agent_sdk import ClaudeAgentOptions, AgentDefinition
options = ClaudeAgentOptions(
agents={
"researcher": AgentDefinition(
description="Research topics using web search",
prompt="You are a research assistant. Search the web for accurate, up-to-date information.",
tools=["WebSearch", "WebFetch"],
model="sonnet"
),
"coder": AgentDefinition(
description="Write and edit code files",
prompt="You are an expert programmer. Write clean, well-documented code.",
tools=["Read", "Write", "Edit", "Bash"],
model="sonnet"
),
"reviewer": AgentDefinition(
description="Review code for bugs and improvements",
prompt="You are a code reviewer. Focus on bugs, security issues, and best practices.",
tools=["Read", "Glob", "Grep"],
model="haiku" # Faster for reviews
)
}
)
How Subagents Work
- Task Tool - The main agent uses the
Tasktool to spawn subagents - Isolated Context - Each subagent has its own conversation
- Result Return - Subagent returns a result to the main agent
- Parallel Execution - Multiple subagents can run simultaneously
Task Tool Schema
{
"description": str, # 3-5 word summary
"prompt": str, # Task for the subagent
"subagent_type": str # Agent name from your definitions
}
Use Cases
Parallelization
Run multiple searches or analyses simultaneously:
options = ClaudeAgentOptions(
agents={
"web-searcher": AgentDefinition(
description="Search the web for information",
prompt="Search the web and return relevant findings.",
tools=["WebSearch", "WebFetch"]
),
"file-analyzer": AgentDefinition(
description="Analyze files in the codebase",
prompt="Analyze code files and report findings.",
tools=["Read", "Glob", "Grep"]
)
}
)
# Main agent can spawn both in parallel
Context Management
Subagents have isolated context - useful for:
- Processing large documents without bloating main context
- Running focused analyses
- Returning only relevant summaries
options = ClaudeAgentOptions(
agents={
"summarizer": AgentDefinition(
description="Summarize long documents",
prompt="Read the document and provide a concise summary of key points.",
tools=["Read"]
)
}
)
Specialized Tools
Give different agents different capabilities:
options = ClaudeAgentOptions(
agents={
"read-only": AgentDefinition(
description="Analyze without making changes",
prompt="Analyze the codebase. Do not make any changes.",
tools=["Read", "Glob", "Grep"] # No write tools
),
"writer": AgentDefinition(
description="Create and edit files",
prompt="Create or edit files as needed.",
tools=["Read", "Write", "Edit", "Bash"]
)
}
)
Complete Example
import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AgentDefinition,
AssistantMessage,
TextBlock
)
async def main():
options = ClaudeAgentOptions(
agents={
"researcher": AgentDefinition(
description="Research topics on the web",
prompt="""You are a research assistant.
Search for accurate, current information.
Return a summary with key facts and sources.""",
tools=["WebSearch", "WebFetch"],
model="sonnet"
),
"writer": AgentDefinition(
description="Write documents and reports",
prompt="""You are a technical writer.
Create clear, well-structured documents.
Use proper formatting and citations.""",
tools=["Read", "Write"],
model="sonnet"
),
"reviewer": AgentDefinition(
description="Review and improve content",
prompt="""You are an editor.
Review content for clarity, accuracy, and completeness.
Suggest improvements.""",
tools=["Read"],
model="haiku"
)
},
allowed_tools=["Task", "Read", "Write"] # Main agent tools
)
async with ClaudeSDKClient(options=options) as client:
# Complex task - main agent will coordinate subagents
await client.query("""
Research the latest developments in AI agents,
write a summary report, and review it for quality.
""")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
asyncio.run(main())
Built-in Agent Types
Claude Code includes these built-in subagent types:
| Type | Model | Purpose |
|---|---|---|
| Main Agent | sonnet/opus | Primary orchestrator |
| Plan Subagent | sonnet | Planning and design |
| Explore Subagent | haiku | Fast codebase navigation |
The Explore Subagent is particularly useful for quick searches:
# Explore uses Haiku for fast, cost-effective navigation
options = ClaudeAgentOptions(
agents={
"explorer": AgentDefinition(
description="Quick codebase exploration",
prompt="Find relevant files and return summaries.",
tools=["Glob", "Grep", "Read"],
model="haiku" # Fast navigation
)
}
)
Parallel Execution
Multiple Perspectives Pattern
Spawn multiple subagents with different viewpoints:
# Parallel code review with specialized perspectives
options = ClaudeAgentOptions(
agents={
"security-reviewer": AgentDefinition(
description="Security and vulnerability analysis",
prompt="Analyze code for security vulnerabilities, injection risks, data leaks.",
tools=["Read", "Glob", "Grep"],
model="haiku"
),
"performance-reviewer": AgentDefinition(
description="Performance analysis",
prompt="Identify performance bottlenecks, memory issues, inefficient algorithms.",
tools=["Read", "Glob", "Grep"],
model="haiku"
),
"maintainability-reviewer": AgentDefinition(
description="Code quality review",
prompt="Review for code smells, duplication, complexity, naming.",
tools=["Read", "Glob", "Grep"],
model="haiku"
)
}
)
# Main agent spawns all three, then synthesizes findings
Research Coordination Pattern
Coordinate multiple researchers on subtopics:
options = ClaudeAgentOptions(
agents={
"researcher": AgentDefinition(
description="Research a specific topic",
prompt="""Research the assigned topic thoroughly.
Save findings to research_output/notes/{topic}.md
Include key facts, statistics, and sources.""",
tools=["WebSearch", "WebFetch", "Write", "Read"],
model="sonnet"
),
"synthesizer": AgentDefinition(
description="Synthesize research into reports",
prompt="""Read all research notes and create a comprehensive report.
Structure with Executive Summary, Key Findings, Analysis.""",
tools=["Glob", "Read", "Write"],
model="sonnet"
)
}
)
# Main agent: 1) spawn researchers in parallel, 2) spawn synthesizer
Background Agents
Run agents asynchronously without blocking:
options = ClaudeAgentOptions(
agents={
"background-checker": AgentDefinition(
description="Run background checks and analysis",
prompt="Perform analysis and report when complete.",
tools=["Read", "Glob", "Grep", "Bash"],
model="haiku"
)
}
)
# Main agent can spawn background agent and continue working
# Background agent sends wake message when complete
Agent Resumption
Resume previously spawned subagents for stateful interactions:
# TypeScript V2 Session API
const sessionId = session.id; // Save this
// Later: resume the session
await using session = unstable_v2_resumeSession(sessionId, {
model: 'sonnet'
});
// Context is preserved
await session.send('What was that file you found earlier?');
This is useful for:
- Multi-turn subagent conversations
- Continuing interrupted work
- Building on previous analysis
Subagent Hooks
Track subagent lifecycle with hooks:
SubagentStart
Called when a subagent is spawned:
async def subagent_start_hook(input_data, tool_use_id, context):
agent_type = input_data.get("agent_type", "unknown")
task = input_data.get("task", "")
print(f"Spawning {agent_type}: {task[:100]}")
# Track spawned agents
spawned_agents[tool_use_id] = {
"type": agent_type,
"started_at": datetime.now().isoformat()
}
return {}
SubagentStop
Called when a subagent completes:
async def subagent_stop_hook(input_data, tool_use_id, context):
# Access the subagent's transcript
transcript_path = input_data.get("agent_transcript_path")
if tool_use_id in spawned_agents:
agent = spawned_agents[tool_use_id]
agent["ended_at"] = datetime.now().isoformat()
agent["transcript"] = transcript_path
return {}
Hook Registration
hooks = {
"SubagentStart": [HookMatcher(hooks=[subagent_start_hook])],
"SubagentStop": [HookMatcher(hooks=[subagent_stop_hook])]
}
options = ClaudeAgentOptions(hooks=hooks)
Skills Auto-Loading
Subagents can declare skills to load via YAML frontmatter:
# In agent definition or skill file
---
skills:
- code-review
- security-analysis
---
The agent will automatically have access to these skills.
Best Practices
- Clear descriptions - Help the main agent route tasks correctly
- Focused prompts - Each subagent should have a specific role
- Appropriate tools - Only give tools needed for the task
- Model selection - Use faster models for simple tasks
- Context isolation - Use subagents to manage large contexts
- Parallel when possible - Spawn multiple subagents for independent tasks
- Track lifecycle - Use SubagentStart/SubagentStop hooks for monitoring
- File coordination - Use shared directories for multi-agent output