子代理是主代理可以生成的独立代理实例,用于处理专注的子任务。 使用子代理可以为专注的子任务隔离上下文、并行运行多个分析,以及应用专门的指令而不会使主代理的提示词膨胀。
本指南介绍如何使用 agents 参数在 SDK 中定义和使用子代理。
您可以通过三种方式创建子代理:
query() 选项中使用 agents 参数(TypeScript、Python).claude/agents/ 目录中将代理定义为 markdown 文件(参见将子代理定义为文件)general-purpose 子代理,无需您进行任何定义本指南重点介绍编程方式,这是 SDK 应用程序的推荐方法。
当您定义子代理时,Claude 会根据每个子代理的 description 字段决定是否调用它们。编写清晰的描述来说明何时应该使用该子代理,Claude 将自动委派适当的任务。您也可以在提示词中按名称显式请求子代理(例如,"使用 code-reviewer 代理来...")。
子代理与主代理保持独立的上下文,防止信息过载并保持交互的专注性。这种隔离确保专门的任务不会用无关的细节污染主对话上下文。
示例:research-assistant 子代理可以探索数十个文件和文档页面,而不会用所有中间搜索结果来干扰主对话,只返回相关的发现。
多个子代理可以并发运行,显著加速复杂的工作流程。
示例:在代码审查期间,您可以同时运行 style-checker、security-scanner 和 test-coverage 子代理,将审查时间从几分钟缩短到几秒钟。
每个子代理都可以拥有定制的系统提示词,包含特定的专业知识、最佳实践和约束条件。
示例:database-migration 子代理可以拥有关于 SQL 最佳实践、回滚策略和数据完整性检查的详细知识,这些在主代理的指令中会是不必要的噪音。
子代理可以被限制使用特定的工具,降低意外操作的风险。
示例:doc-reviewer 子代理可能只能访问 Read 和 Grep 工具,确保它可以分析但永远不会意外修改您的文档文件。
直接在代码中使用 agents 参数定义子代理。此示例创建了两个子代理:一个具有只读访问权限的代码审查器和一个可以执行命令的测试运行器。Task 工具必须包含在 allowedTools 中,因为 Claude 通过 Task 工具调用子代理。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Review the authentication module for security issues",
options=ClaudeAgentOptions(
# Task tool is required for subagent invocation
allowed_tools=["Read", "Grep", "Glob", "Task"],
agents={
"code-reviewer": AgentDefinition(
# description tells Claude when to use this subagent
description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
# prompt defines the subagent's behavior and expertise
prompt="""You are a code review specialist with expertise in security, performance, and best practices.
When reviewing code:
- Identify security vulnerabilities
- Check for performance issues
- Verify adherence to coding standards
- Suggest specific improvements
Be thorough but concise in your feedback.""",
# tools restricts what the subagent can do (read-only here)
tools=["Read", "Grep", "Glob"],
# model overrides the default model for this subagent
model="sonnet"
),
"test-runner": AgentDefinition(
description="Runs and analyzes test suites. Use for test execution and coverage analysis.",
prompt="""You are a test execution specialist. Run tests and provide clear analysis of results.
Focus on:
- Running test commands
- Analyzing test output
- Identifying failing tests
- Suggesting fixes for failures""",
# Bash access lets this subagent run test commands
tools=["Bash", "Read", "Grep"]
)
}
)
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())| 字段 | 类型 | 必需 | 描述 |
|---|---|---|---|
description | string | 是 | 描述何时使用此代理的自然语言描述 |
prompt | string | 是 | 代理的系统提示词,定义其角色和行为 |
tools | string[] | 否 | 允许的工具名称数组。如果省略,则继承所有工具 |
model | 'sonnet' | 'opus' | 'haiku' | 'inherit' | 否 | 此代理的模型覆盖。如果省略,默认使用主模型 |
子代理不能生成自己的子代理。不要在子代理的 tools 数组中包含 Task。
您也可以在 .claude/agents/ 目录中将子代理定义为 markdown 文件。有关此方法的详细信息,请参阅 Claude Code 子代理文档。以编程方式定义的代理优先于同名的基于文件系统的代理。
即使不定义自定义子代理,当 Task 在您的 allowedTools 中时,Claude 也可以生成内置的 general-purpose 子代理。这对于在不创建专门代理的情况下委派研究或探索任务非常有用。
Claude 会根据任务和每个子代理的 description 自动决定何时调用子代理。例如,如果您定义了一个 performance-optimizer 子代理,其描述为"用于查询调优的性能优化专家",当您的提示词提到优化查询时,Claude 将调用它。
编写清晰、具体的描述,以便 Claude 能够将任务匹配到正确的子代理。
要确保 Claude 使用特定的子代理,请在提示词中按名称提及它:
"Use the code-reviewer agent to check the authentication module"这将绕过自动匹配并直接调用指定的子代理。
您可以根据运行时条件动态创建代理定义。此示例创建了一个具有不同严格级别的安全审查器,对严格审查使用更强大的模型。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
# Factory function that returns an AgentDefinition
# This pattern lets you customize agents based on runtime conditions
def create_security_agent(security_level: str) -> AgentDefinition:
is_strict = security_level == "strict"
return AgentDefinition(
description="Security code reviewer",
# Customize the prompt based on strictness level
prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...",
tools=["Read", "Grep", "Glob"],
# Key insight: use a more capable model for high-stakes reviews
model="opus" if is_strict else "sonnet"
)
async def main():
# The agent is created at query time, so each request can use different settings
async for message in query(
prompt="Review this PR for security issues",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Task"],
agents={
# Call the factory with your desired configuration
"security-reviewer": create_security_agent("strict")
}
)
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())子代理通过 Task 工具调用。要检测子代理何时被调用,请检查 name: "Task" 的 tool_use 块。来自子代理上下文内的消息包含 parent_tool_use_id 字段。
此示例遍历流式消息,记录子代理何时被调用以及后续消息何时来自该子代理的执行上下文。
SDK 之间的消息结构有所不同。在 Python 中,内容块通过 message.content 直接访问。在 TypeScript 中,SDKAssistantMessage 包装了 Claude API 消息,因此内容通过 message.message.content 访问。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Use the code-reviewer agent to review this codebase",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Glob", "Grep", "Task"],
agents={
"code-reviewer": AgentDefinition(
description="Expert code reviewer.",
prompt="Analyze code quality and suggest improvements.",
tools=["Read", "Glob", "Grep"]
)
}
)
):
# Check for subagent invocation in message content
if hasattr(message, 'content') and message.content:
for block in message.content:
if getattr(block, 'type', None) == 'tool_use' and block.name == 'Task':
print(f"Subagent invoked: {block.input.get('subagent_type')}")
# Check if this message is from within a subagent's context
if hasattr(message, 'parent_tool_use_id') and message.parent_tool_use_id:
print(" (running inside subagent)")
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())子代理可以被恢复以从中断处继续。恢复的子代理保留其完整的对话历史,包括所有先前的工具调用、结果和推理。子代理会从停止的地方精确恢复,而不是重新开始。
当子代理完成时,Claude 会在 Task 工具结果中接收其代理 ID。要以编程方式恢复子代理:
session_idagentIdresume: sessionId,并在提示词中包含代理 ID您必须恢复同一会话才能访问子代理的记录。每次 query() 调用默认启动一个新会话,因此传递 resume: sessionId 以在同一会话中继续。
如果您使用的是自定义代理(而非内置代理),您还需要在两次查询的 agents 参数中传递相同的代理定义。
下面的示例演示了此流程:第一次查询运行子代理并捕获会话 ID 和代理 ID,然后第二次查询恢复会话以提出需要第一次分析上下文的后续问题。
import { query, type SDKMessage } from '@anthropic-ai/claude-agent-sdk';
// Helper to extract agentId from message content
// Stringify to avoid traversing different block types (TextBlock, ToolResultBlock, etc.)
function extractAgentId(message: SDKMessage): string | undefined {
if (!('message' in message)) return undefined;
// Stringify the content so we can search it without traversing nested blocks
const content = JSON.stringify(message.message.content);
const match = content.match(/agentId:\s*([a-f0-9-]+)/);
return match?.[1];
}
let agentId: string | undefined;
let sessionId: string | undefined;
// First invocation - use the Explore agent to find API endpoints
for await (const message of query({
prompt: "Use the Explore agent to find all API endpoints in this codebase",
options: { allowedTools: ['Read', 'Grep', 'Glob', 'Task'] }
})) {
// Capture session_id from ResultMessage (needed to resume this session)
if ('session_id' in message) sessionId = message.session_id;
// Search message content for the agentId (appears in Task tool results)
const extractedId = extractAgentId(message);
if (extractedId) agentId = extractedId;
// Print the final result
if ('result' in message) console.log(message.result);
}
// Second invocation - resume and ask follow-up
if (agentId && sessionId) {
for await (const message of query({
prompt: `Resume agent ${agentId} and list the top 3 most complex endpoints`,
options: { allowedTools: ['Read', 'Grep', 'Glob', 'Task'], resume: sessionId }
})) {
if ('result' in message) console.log(message.result);
}
}子代理记录独立于主对话持久存在:
cleanupPeriodDays 设置进行清理(默认:30 天)。子代理可以通过 tools 字段限制工具访问:
此示例创建了一个只读分析代理,可以检查代码但不能修改文件或运行命令。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def main():
async for message in query(
prompt="Analyze the architecture of this codebase",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Task"],
agents={
"code-analyzer": AgentDefinition(
description="Static code analysis and architecture review",
prompt="""You are a code architecture analyst. Analyze code structure,
identify patterns, and suggest improvements without making changes.""",
# Read-only tools: no Edit, Write, or Bash access
tools=["Read", "Grep", "Glob"]
)
}
)
):
if hasattr(message, "result"):
print(message.result)
asyncio.run(main())| 用例 | 工具 | 描述 |
|---|---|---|
| 只读分析 | Read、Grep、Glob | 可以检查代码但不能修改或执行 |
| 测试执行 | Bash、Read、Grep | 可以运行命令和分析输出 |
| 代码修改 | Read、Edit、Write、Grep、Glob | 完全读写访问,无命令执行 |
| 完全访问 | 所有工具 | 从父代理继承所有工具(省略 tools 字段) |
如果 Claude 直接完成任务而不是委派给您的子代理:
allowedTools 中在 .claude/agents/ 中定义的代理仅在启动时加载。如果您在 Claude Code 运行时创建了新的代理文件,请重启会话以加载它。
在 Windows 上,具有非常长提示词的子代理可能由于命令行长度限制(8191 个字符)而失败。保持提示词简洁或对复杂指令使用基于文件系统的代理。
Was this page helpful?