Claude Code Agent SDK 扩展

Claude Code Agent SDK 扩展

SDK 中的子智能体

11 分钟阅读

SDK 中的子智能体

定义和调用子智能体以隔离上下文、并行运行任务,以及在 Claude Agent SDK 应用程序中应用专门的指令。

子智能体是您的主智能体可以生成的独立智能体实例,用于处理专注的子任务。 使用子智能体来隔离上下文、并行运行多个分析,以及应用专门的指令,而不会增加主智能体的提示词。

本指南说明如何使用 agents 参数在 SDK 中定义和使用子智能体。

概述

您可以通过三种方式创建子智能体:

  • 以编程方式:在您的 query() 选项中使用 agents 参数。请参阅 TypeScriptPython 参考文档
  • 基于文件系统:在 .claude/agents/ 目录中将智能体定义为 markdown 文件。请参阅将子智能体定义为文件
  • 内置通用智能体:Claude 可以随时通过 Agent 工具调用内置的 general-purpose 子智能体,无需您定义任何内容

本指南重点介绍编程方法,这是 SDK 应用程序的推荐方法。

定义子智能体时,Claude 根据每个子智能体的 description 字段确定是否调用它。编写清晰的描述,说明何时应使用子智能体,Claude 将自动委派适当的任务。您也可以在提示词中按名称显式请求子智能体,例如"使用代码审查员智能体来..."。

使用子智能体的好处

上下文隔离

每个子智能体在其自己的新对话中运行。中间工具调用和结果保留在子智能体内部;只有其最终消息返回到父智能体。请参阅子智能体继承的内容以了解子智能体上下文中的确切内容。

示例: research-assistant 子智能体可以探索数十个文件,而这些内容都不会在主对话中累积。父智能体收到的是简洁的摘要,而不是子智能体读取的每个文件。

并行化

多个子智能体可以并发运行,因此独立的子任务完成时间为最慢的一个,而不是所有任务的总和。

示例: 在代码审查期间,您可以同时运行 style-checkersecurity-scannertest-coverage 子智能体,而不是按顺序运行。

专门的指令和知识

每个子智能体都可以有定制的系统提示词,具有特定的专业知识、最佳实践和约束。

示例: database-migration 子智能体可以具有关于 SQL 最佳实践、回滚策略和数据完整性检查的详细知识,这些在主智能体的指令中将是不必要的噪音。

工具限制

子智能体可以限制为特定工具,降低意外操作的风险。

示例: doc-reviewer 子智能体可能只能访问 Read 和 Grep 工具,确保它可以分析但永远不会意外修改您的文档文件。

创建子智能体

使用 agents 参数直接在代码中定义子智能体。Claude 通过 Agent 工具调用子智能体,因此在 allowedTools 中包含 Agent 以自动批准子智能体调用,无需权限提示。

本页面上的大多数示例仅打印最终结果。要确认 Claude 委派给了子智能体而不是直接回答,请参阅检测子智能体调用

此示例创建两个子智能体:一个具有只读访问权限的代码审查员和一个可以执行命令的测试运行器。

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(
            # Auto-approve these tools, including Agent for subagent invocation
            allowed_tools=["Read", "Grep", "Glob", "Agent"],
            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())

AgentDefinition 配置

字段类型必需描述
descriptionstring何时使用此智能体的自然语言描述
promptstring智能体的系统提示词,定义其角色和行为
toolsstring[]允许的工具名称数组。如果省略,继承所有工具
disallowedToolsstring[]要从智能体的工具集中移除的工具名称数组。MCP 服务器级别的模式也被接受:mcp__servermcp__server__* 移除来自该服务器的每个工具,mcp__* 移除来自任何服务器的每个 MCP 工具
modelstring此智能体的模型覆盖。接受别名,如 'fable''opus''sonnet''haiku''inherit',或完整的模型 ID。如果省略,默认为主模型
skillsstring[]在启动时预加载到智能体上下文中的 skills 名称列表。未列出的 skills 仍可通过 Skill 工具调用
memory'user' | 'project' | 'local'此智能体的内存源
mcpServers(string | object)[]此智能体可用的 MCP 服务器,按名称或内联配置
initialPromptstring当此智能体作为主线程智能体运行时自动提交为第一个用户轮次。当智能体作为子智能体调用时忽略
maxTurnsnumber智能体停止前的最大智能体轮数
backgroundboolean调用时将此智能体作为非阻塞后台任务运行
effort'low' | 'medium' | 'high' | 'xhigh' | 'max' | number此智能体的推理工作量级别
permissionModePermissionMode此智能体内工具执行的权限模式

在 Python SDK 中,多字词字段名称(如 disallowedToolsmcpServers)保持其 camelCase 拼写以匹配线路格式,而不是遵循 Python 的 snake_case 约定。有关详细信息,请参阅 AgentDefinition 参考

Claude Code v2.1.198 中的两个子智能体行为发生了变化:

  • 子智能体默认在后台运行。省略 run_in_background 输入的 Agent 工具调用会启动后台子智能体,当 Claude 需要结果后才继续时,它会设置 run_in_background: false。在 v2.1.198 之前,省略 run_in_background 会同步运行子智能体。设置 background 字段为 true 以强制特定智能体进行后台执行,无论 Claude 请求什么。
  • 子智能体继承主会话的扩展思考配置。在早期版本中,无论主会话的设置如何,扩展思考在子智能体内被禁用。

自 Claude Code v2.1.172 起,子智能体可以生成自己的子智能体。位于主智能体下方五个级别的子智能体无法生成进一步的子智能体,无论其是在前台还是后台运行。要防止子智能体生成其他子智能体,请从其 tools 数组中省略 Agent 或将其添加到 disallowedTools。有关完整的深度规则,请参阅嵌套子智能体

基于文件系统的定义(替代方案)

您也可以在 .claude/agents/ 目录中将子智能体定义为 markdown 文件。有关此方法的详细信息,请参阅 Claude Code 子智能体文档。以编程方式定义的智能体优先于具有相同名称的基于文件系统的智能体。

即使不定义自定义子智能体,Claude 也可以生成内置的 general-purpose 子智能体。这对于委派研究或探索任务而无需创建专门的智能体很有用。在 allowedTools 中包含 Agent 以便这些调用自动批准,无需权限提示。

子智能体继承的内容

子智能体的上下文窗口从新开始,没有父对话,但不是空的。从父智能体到子智能体的唯一内容是 Agent 工具的提示词字符串,因此请直接在该提示词中包含子智能体需要的任何文件路径、错误消息或决策。

具有 SendMessage 工具的子智能体会从会话中运行的其他命名智能体列表开始,因此它知道可以向哪些名称发送消息。Claude Code 会自动在子智能体的第一轮中添加该列表。fork 不会获得该列表,因为它继承了父对话。该列表需要 Claude Code v2.1.206 或更高版本。

子智能体接收子智能体不接收
其自己的系统提示词(AgentDefinition.prompt)和 Agent 工具的提示词父智能体的对话历史或工具结果
项目 CLAUDE.md(通过 settingSources 加载)预加载的 skill 内容,除非在 AgentDefinition.skills 中列出
工具定义(从父智能体继承,或 tools 中的子集)父智能体的系统提示词

父智能体逐字接收子智能体的最终消息作为 Agent 工具结果,但可能在其自己的响应中总结它。要在面向用户的响应中逐字保留子智能体输出,请在您传递给主 query() 调用的提示词或 systemPrompt 选项中包含一条指令。

结束子智能体早期的 API 错误(例如速率限制)永远不会作为其结果传递。如果速率限制、过载或服务器错误中断了已经产生文本输出的前台子智能体,Agent 工具会返回该部分输出并注明子智能体未完成。未产生任何内容的子智能体,或其唯一输出仅为工具调用且没有文本的子智能体,会失败并显示错误消息 Agent terminated early due to an API error,后跟错误详情。有关前台和后台行为,请参阅 API errors in subagents

这种部分输出处理需要 Claude Code v2.1.199 或更高版本。在 v2.1.199 中,速率限制、过载或服务器错误会导致仅工具调用的形状出现空的部分结果,仅包含中断注记。

调用子智能体

自动调用

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", "Agent"],
            agents={
                # Call the factory with your desired configuration
                "security-reviewer": create_security_agent("strict")
            },
        ),
    ):
        if hasattr(message, "result"):
            print(message.result)


asyncio.run(main())

检测子智能体调用

Claude 通过 Agent 工具调用子智能体。要检测何时调用子智能体,请检查 tool_use 块,其中 name"Agent"。来自子智能体上下文内的消息包含 parent_tool_use_id 字段。

工具名称在 Claude Code v2.1.63 中从 "Task" 重命名为 "Agent"。当前 SDK 版本在 tool_use 块中发出 "Agent",但在 system:init 工具列表和 result.permission_denials[].tool_name 中仍使用 "Task"。检查 block.name 中的两个值可确保跨 SDK 版本的兼容性。

消息结构在 SDK 之间有所不同。在 Python 中,内容块直接通过 message.content 访问。在 TypeScript 中,SDKAssistantMessage 包装 Claude API 消息,因此内容通过 message.message.content 访问。

此示例遍历流式消息,记录何时调用子智能体以及后续消息何时源自该子智能体的执行上下文。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ToolUseBlock


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", "Agent"],
            agents={
                "code-reviewer": AgentDefinition(
                    description="Expert code reviewer.",
                    prompt="Analyze code quality and suggest improvements.",
                    tools=["Read", "Glob", "Grep"],
                )
            },
        ),
    ):
        # Check for subagent invocation. Match both names: older SDK
        # versions emitted "Task", current versions emit "Agent".
        if hasattr(message, "content") and message.content:
            for block in message.content:
                if isinstance(block, ToolUseBlock) and block.name in (
                    "Task",
                    "Agent",
                ):
                    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())

恢复子智能体

您可以恢复子智能体以继续中断的地方,而不是重新开始。恢复的子智能体保留其完整的对话历史,包括所有先前的工具调用、结果和推理。

当子智能体完成时,Agent 工具结果包含一个包含 agentId: <id> 的文本块。内置的 ExplorePlan 智能体 是一次性的,不返回 agentId,因此当您需要恢复时,请使用自定义智能体或 general-purpose。要以编程方式恢复子智能体:

  1. 捕获会话 ID:在第一个查询期间从消息中提取 session_id
  2. 提取智能体 ID:从 Agent 工具结果文本中解析 agentId
  3. 恢复会话:在第二个查询的选项中传递 resume: sessionId,并在您的提示词中包含智能体 ID

您必须恢复同一会话以访问子智能体的记录。默认情况下,每个 query() 调用都会启动一个新会话,因此请传递 resume: sessionId 以在同一会话中继续。

使用自定义智能体时,在两个查询的 agents 参数中传递相同的智能体定义。

下面的示例定义了一个自定义 endpoint-finder 智能体。第一个查询运行它并从 Agent 工具结果中捕获会话 ID 和智能体 ID,然后第二个查询恢复会话以提出需要来自第一个分析的上下文的后续问题。

import asyncio
import re
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition, ToolResultBlock

AGENTS = {
    "endpoint-finder": AgentDefinition(
        description="Locates and catalogs API endpoints in a codebase.",
        prompt="You find and document API endpoints. Report each endpoint's path, method, and handler.",
        tools=["Read", "Grep", "Glob"],
    )
}


def extract_agent_id(block: ToolResultBlock) -> str | None:
    """Extract agentId from an Agent tool result's text content."""
    parts = block.content if isinstance(block.content, list) else [{"text": block.content}]
    for part in parts:
        if match := re.search(r"agentId:\s*([\w-]+)", part.get("text") or ""):
            return match.group(1)
    return None


async def main():
    agent_id = None
    session_id = None

    # First invocation - run the endpoint-finder subagent
    try:
        async for message in query(
            prompt="Use the endpoint-finder agent to find all API endpoints in this codebase",
            options=ClaudeAgentOptions(allowed_tools=["Read", "Grep", "Glob", "Agent"], agents=AGENTS),
        ):
            # Capture session_id from ResultMessage (needed to resume this session)
            if hasattr(message, "session_id"):
                session_id = message.session_id
            # Search tool results for the agentId trailer
            for block in getattr(message, "content", None) or []:
                if isinstance(block, ToolResultBlock):
                    agent_id = extract_agent_id(block) or agent_id
            # Print the final result
            if hasattr(message, "result"):
                print(message.result)
    except Exception as error:
        # A single-shot query() raises after yielding an error result,
        # so session_id and agent_id have already been captured by the loop above.
        print(f"Session ended with an error: {error}")

    # Second invocation - resume and ask follow-up
    if agent_id and session_id:
        async for message in query(
            prompt=f"Resume agent {agent_id} and list the top 3 most complex endpoints",
            options=ClaudeAgentOptions(
                allowed_tools=["Read", "Grep", "Glob", "Agent"], agents=AGENTS, resume=session_id
            ),
        ):
            if hasattr(message, "result"):
                print(message.result)
    else:
        print("No agentId found in the first query, so there is no subagent to resume.")


asyncio.run(main())

子智能体记录独立于主对话而持久存在:

  • 主对话压缩:当主对话压缩时,子智能体记录不受影响。它们存储在单独的文件中。
  • 会话持久性:子智能体记录在其会话内持久存在。您可以通过恢复同一会话在重启 Claude Code 后恢复子智能体。
  • 自动清理:记录根据 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", "Agent"],
            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())

常见工具组合

用例工具描述
只读分析ReadGrepGlob可以检查代码但不能修改或执行
测试执行BashReadGrep可以运行命令并分析输出
代码修改ReadEditWriteGrepGlob完整的读/写访问,无命令执行
完全访问所有工具从父智能体继承所有工具(省略 tools 字段)

使用动态工作流进行扩展

子智能体适用于每轮委派的几个任务。对于协调数十到数百个智能体的运行,请使用 Workflow 工具,它将编排移到运行时在对话上下文外执行的脚本中。请参阅动态工作流以了解工作流与逐轮子智能体委派的区别。

Workflow 工具在 TypeScript Agent SDK v0.3.149 及更高版本中可用。在 allowedTools 中包含 Workflow 以自动批准工作流运行。工具输入和输出架构列在 TypeScript 参考中。

故障排除

Claude 不委派给子智能体

如果 Claude 直接完成任务而不是委派给您的子智能体:

  • 检查 Agent 调用是否被批准:在 allowedTools 中包含 Agent 以自动批准子智能体调用。如果没有它,Agent 调用将转到您的 canUseTool 回调,或在 dontAsk 模式下被拒绝
  • 使用显式提示:在您的提示词中按名称提及子智能体,例如"使用代码审查员智能体来..."
  • 编写清晰的描述:准确解释何时应使用子智能体,以便 Claude 可以适当地匹配任务

基于文件系统的智能体未加载

Claude Code 监视 ~/.claude/agents/.claude/agents/,并在几秒内拾取新的或编辑的智能体文件,无需重启。如果定义从未出现,请排查这些原因:

  • 新的 agents 目录:监视程序仅覆盖会话启动时存在的目录,因此新目录中的第一个文件需要会话重启。这是最常见的原因。
  • 无效的 frontmatter 或重复的 name:检查文件的 YAML,以及现有智能体是否已使用该 name
  • --disable-slash-commands:使用此标志启动的会话不监视这些目录,始终需要重启以加载新文件。
  • 具有相同名称的程序化智能体:传递给 query()agents 会覆盖具有相同名称的文件系统智能体。

有关文件格式,请参阅如何编写子智能体文件

Windows 上的长提示词失败

在 Windows 上,具有非常长提示词的子智能体可能因命令行长度限制(8191 个字符)而失败。保持提示词简洁或使用基于文件系统的智能体来处理复杂指令。

博极客AI是专业人工智能学习平台,提供通俗易懂的AI入门教程、大模型应用、实战项目与行业动态,全站内容免费阅览,零基础也能轻松学AI,适配学生、职场新人及技术爱好者。

© 版权所有 2026 博极客AI,保留一切权利。