claude code book · 第三部分:高级模式篇 · 第 9 部分
第 9 章 子智能体与 Fork 模式
深入理解子智能体的生成机制和完整生命周期管理,掌握 Fork 模式的缓存共享策略
第9章:子智能体与 Fork 模式
学习目标:
- 深入理解子智能体的生成机制和完整生命周期管理
- 掌握 Fork 模式的缓存共享策略与字节级继承原理
- 学会设计和创建自定义智能体以应对特定场景需求
- 理解对抗性验证 Agent 的设计哲学及其工程意义
Claude Code 的真正威力不仅在于单轮对话的能力,更在于它可以将复杂任务委派给专门的子智能体(Subagent)并行处理。想象一个大型交响乐团:指挥家(主智能体)不需要亲自演奏每一种乐器,而是将不同声部的演奏交给各自领域的专家(子智能体)。本章将深入解析 AgentTool 的完整架构,揭示内置智能体的设计哲学,并重点拆解 Fork 模式如何通过精巧的缓存策略实现真正的并行执行而不浪费 token。
9.1 AgentTool 架构
目录结构与模块职责
AgentTool 的代码位于智能体工具目录下,由以下核心模块组成:
| 职责 | 说明 | 设计考量 |
|---|---|---|
| 工具主入口 | 处理输入 schema、路由策略、同步/异步分支 | 单一入口点降低调用复杂度 |
| 智能体运行器 | 管理生命周期、上下文构建、MCP 初始化 | 生命周期与业务逻辑解耦 |
| Fork 模式实现 | 构建缓存安全的消息前缀 | 缓存策略独立演进 |
| 内置智能体注册表 | 根据 feature gate 动态加载 | 按需加载减少启动开销 |
| 自定义智能体加载 | 解析 Markdown/JSON 定义 | 用户友好的声明式配置 |
| 工具函数集 | 工具过滤、解析、结果格式化等 | 可复用的基础设施 |
这套架构的核心设计原则是关注点分离:工具主入口负责”决定做什么”,运行器负责”怎么做”,Fork 模块负责一种特定的”怎么做得更高效”的策略。
架构洞察:为什么不将路由逻辑和执行逻辑合并?
在很多简单系统中,路由和执行是同一个模块。但 Claude Code 的场景远比简单系统复杂——一个工具调用可能走同步路径(主线程等待)、异步路径(后台执行)或 Fork 路径(缓存并行)。将路由决策与执行过程分离,意味着当需要新增一种执行策略(比如未来的”流式子智能体”)时,只需要在路由层增加一个分支,而不需要修改运行器核心逻辑。这是经典的策略模式在系统架构层面的应用。
BaseAgentDefinition 类型定义
所有智能体都基于智能体加载模块中定义的 BaseAgentDefinition 类型,包含以下关键字段:智能体唯一标识符(agentType)、使用场景描述(whenToUse)、允许/禁止的工具列表、预加载的 skill 名称、智能体专属 MCP 服务器、生命周期钩子、UI 显示颜色、模型指定、推理努力程度、权限模式、最大轮次限制、是否后台运行、隔离模式、是否省略 CLAUDE.md 上下文等。
这些字段并非随意堆砌,而是可以按照功能维度归类为四大类:
graph LR
subgraph 身份与描述
A1["agentType"]
A2["whenToUse"]
A3["color"]
end
subgraph 工具与能力
B1["allowedTools /<br/>disallowedTools"]
B2["skills"]
B3["mcpServers"]
end
subgraph 执行控制
C1["model / effort"]
C2["maxTurns"]
C3["permissionMode /<br/>background"]
end
subgraph 上下文管理
D1["omitClaudeMd /<br/>isolationMode"]
D2["hooks"]
D3["memory"]
end
classDef category fill:#e8f4fd,stroke:#2196F3,stroke-width:2px,color:#1565C0
class A1,A2,A3,B1,B2,B3,C1,C2,C3,D1,D2,D3 category
从这个类型衍生出三种具体的智能体定义:
- BuiltInAgentDefinition(内置智能体):通过
getSystemPrompt()动态生成系统提示。内置智能体的提示在编译时固定,但运行时通过函数动态构建,这样可以在不同环境(feature gate 开关状态不同)下生成不同的提示内容。 - CustomAgentDefinition(自定义智能体):来自用户/项目/策略设置。用户通过 Markdown 文件声明式定义,系统自动解析。
- PluginAgentDefinition(插件智能体):插件提供的智能体,带有 plugin 元数据。插件智能体可以携带额外的版本信息和依赖声明。
交叉引用: 插件智能体的加载机制与第11章的插件系统紧密相关。插件不仅可以提供技能(Skill),还可以提供智能体(Agent),两者共享相同的插件发现和加载基础设施。
三种智能体来源
智能体的加载优先级由加载合并函数决定:
graph TD
A["内置智能体<br/>(开箱即用,默认选项)"]
B["插件智能体<br/>(可覆盖同名内置智能体)"]
C["用户设置<br/>~/.claude/settings.json"]
D["项目设置<br/>.claude/settings.json"]
E["Flag 设置<br/>CLI 启动参数"]
F["策略设置<br/>(企业管理配置,最高优先级)"]
A -->|"被覆盖"| B
B -->|"被覆盖"| C
C -->|"被覆盖"| D
D -->|"被覆盖"| E
E -->|"被覆盖"| F
style F fill:#ff6b6b,stroke:#c0392b,color:#fff
style E fill:#f39c12,stroke:#d68910,color:#fff
style D fill:#3498db,stroke:#2471a3,color:#fff
style C fill:#2ecc71,stroke:#27ae60,color:#fff
style B fill:#9b59b6,stroke:#7d3c98,color:#fff
style A fill:#bdc3c7,stroke:#7f8c8d,color:#333
- 内置智能体(built-in):优先级最低,作为默认选项
- 插件智能体(plugin):可以覆盖内置智能体
- 用户设置(userSettings) -> 项目设置(projectSettings) -> Flag 设置(flagSettings) -> 策略设置(policySettings):优先级依次升高
这意味着策略级别的自定义智能体可以覆盖同名内置智能体,为企业部署提供了灵活性。
实际使用场景:企业安全审计覆盖
假设一个金融科技公司需要所有代码审查都遵循内部安全规范。管理员可以在策略设置中定义一个名为
code-review的智能体,覆盖内置的同名智能体。这个自定义版本会在系统提示中加入公司特定的安全检查清单(如 PCI-DSS 合规检查),并接入内部的漏洞扫描 MCP 服务器。这样无论开发者如何配置个人设置,企业级的安全审查标准始终生效。
9.2 内置智能体
内置智能体在注册模块中通过 getBuiltInAgents() 函数返回当前环境下可用的智能体列表。每个智能体都被精心设计为特定领域的专家。它们的组合覆盖了软件工程中最常见的四种工作模式:探索、规划、执行和验证。
graph LR
subgraph 只读层
E["Explore<br/>(搜索)"]
P["Plan<br/>(规划)"]
end
subgraph 读写层
G["General<br/>(执行)"]
V["Verification<br/>(验证)"]
end
style E fill:#3498db,stroke:#2471a3,color:#fff
style P fill:#2ecc71,stroke:#27ae60,color:#fff
style G fill:#f39c12,stroke:#d68910,color:#fff
style V fill:#e74c3c,stroke:#c0392b,color:#fff
Explore Agent:只读代码探索专家
Explore Agent 是一个高速只读搜索智能体。它的定义中声明了严格的禁止操作列表:禁止 Agent、ExitPlanMode、FileEdit、FileWrite、NotebookEdit 等工具,并可选择使用 haiku 模型以降低成本,同时省略 CLAUDE.md 以节省 token。
其核心设计理念体现在两个关键决策上:
第一,严格的只读约束。 系统提示中声明了严格的禁止操作列表(不能创建文件、修改文件、运行任何改变系统状态的命令),并且在工具层面通过 disallowedTools 物理禁止了 Edit、Write 等工具。这种”双重锁”设计——提示层面的软约束加上工具层面的硬约束——确保了即使模型”幻觉”中想要修改文件,也会因工具不可用而被物理阻止。
第二,omitClaudeMd 优化。 Explore 是只读搜索智能体,不需要 commit/PR/lint 规则。CLAUDE.md 通常包含项目的编码规范、commit 消息格式、PR 模板等内容,这些对于一个只做搜索的智能体完全无用。省略 CLAUDE.md 不仅减少了 token 消耗,更重要的是减少了系统提示的噪声,让模型更聚焦于搜索任务本身。这一优化据估计每周可节省 5-15 Gtoken。
最佳实践:如何有效利用 Explore Agent
Explore Agent 最适合以下场景:
- 代码考古:快速定位某个 bug 的根因,追踪调用链路
- 依赖分析:理解某个函数被哪些模块引用
- 架构探索:梳理项目的目录结构和模块关系
- 知识检索:查找某个特定配置项或常量的定义位置
使用提示:给 Explore Agent 足够具体的搜索目标,比如”找到处理用户认证的所有中间件函数”比”看看代码”效果好得多。
Plan Agent:结构化规划智能体
Plan Agent 复用了 Explore Agent 的工具集(同样禁止 Edit、Write 等修改类工具),但承担不同角色——它是软件架构师和规划专家。Plan Agent 使用 inherit 模型并省略 CLAUDE.md,其系统提示要求它输出结构化的实现计划,并以”关键文件”列表结尾,为后续的实现阶段提供清晰指引。
Plan Agent 的输出通常包含以下结构化信息:
| 输出要素 | 说明 | 对后续阶段的价值 |
|---|---|---|
| 问题分析 | 对当前需求的理解和拆解 | 确保实现方向正确 |
| 实施步骤 | 按优先级排列的修改步骤 | General Agent 可按步骤逐一执行 |
| 关键文件 | 需要阅读和修改的文件列表 | 减少后续阶段的搜索开销 |
| 风险评估 | 可能的副作用和注意事项 | Verification Agent 的检查重点 |
| 依赖关系 | 步骤之间的前后依赖 | 决定是否可以并行执行 |
为什么 Plan Agent 要省略 CLAUDE.md?
这与 Explore Agent 的原因相同但又有微妙的区别。Explore 省略是因为”不需要”,Plan 省略是因为”不应该”。规划阶段的目的是理解代码结构并制定计划,不应该受限于项目特定的编码风格约束。如果 Plan Agent 看到了”本项目使用 camelCase 命名”这样的规则,它可能会在规划阶段就考虑命名细节,而这些细节应该在实现阶段处理。省略 CLAUDE.md 让 Plan Agent 更关注”做什么”而非”怎么做”。
General Purpose Agent:通用智能体
General Purpose Agent 是最灵活的智能体,拥有全部工具权限(使用通配符 '*' 允许所有工具)。但实际可用工具仍受全局过滤函数的约束。它是实际干活的”执行者”——前面 Explore 负责侦查,Plan 负责制定计划,General Purpose 负责动手修改。
General Purpose Agent 的设计哲学是”默认信任,边界后移”。它不做工具层面的预设限制,而是依赖全局安全层(权限系统、工具过滤函数)来确保安全。这种设计的好处是灵活性最大化——不同的任务可以动态组合不同的工具集,而不需要为每种任务预定义工具白名单。
反模式警告:不要用 General Purpose Agent 做只读任务
虽然 General Purpose Agent 技术上可以执行只读搜索,但这会带来不必要的成本和安全风险。只读任务应该使用 Explore Agent:它的 haiku 模型更便宜、省略 CLAUDE.md 节省 token、工具限制消除了意外修改的风险。这是一个典型的最小权限原则的应用。
Verification Agent:验证智能体
Verification Agent 是一个独特的”对抗性”智能体,设计目标是尽可能破坏被验证的代码,而不是确认它能工作。它使用红色 UI 标识强调对抗性质,始终后台运行,禁止修改项目文件,使用 inherit 模型。
对抗性设计的深层哲学
Verification Agent 的系统提示明确警告了两种失败模式:
-
验证回避(Verification Avoidance):模型倾向于找借口不运行测试,比如”这段代码逻辑上看起来正确”或”测试环境配置复杂”。系统提示列出了常见的自我合理化借口,要求 Agent 必须实际执行验证而非口头确认。
-
前 80% 的表面正确性(Surface Correctness Trap):代码可能通过了基本的 happy path 测试,但在边界条件、并发场景、错误路径上存在隐患。系统提示要求 Agent 特别关注”不那么明显”的失败模式。
这种对抗性设计借鉴了软件工程中的红队测试(Red Teaming)理念。传统测试验证”代码按照预期工作”,而红队测试验证”代码在非预期情况下不会崩溃”。将这两种思维应用到同一个 LLM 的不同实例上,形成了一种”左手打右手”的自我对抗机制。
flowchart TD
A["主智能体完成了代码修改"] --> B["Verification Agent 被激活(后台)"]
B --> C["尝试运行测试,寻找失败的用例"]
B --> D["检查边界条件和异常路径"]
B --> E["寻找遗漏的 import 或类型错误"]
B --> F["验证并发安全性"]
B --> G["检查是否有遗漏的文件需要修改"]
C --> H["输出:发现的问题列表 + 建议修复方案"]
D --> H
E --> H
F --> H
G --> H
style A fill:#3498db,stroke:#2471a3,color:#fff
style B fill:#e74c3c,stroke:#c0392b,color:#fff
style H fill:#2ecc71,stroke:#27ae60,color:#fff
设计智慧:为什么 Verification Agent 要后台运行?
Verification Agent 始终在后台运行,这是一个深思熟虑的设计决策。原因有三:第一,验证是一个”可以等待”的任务——用户不需要实时看到验证过程,只需要看到最终结果;第二,后台运行释放了主线程,让用户可以继续与主智能体交互;第三,后台模式强制验证 Agent 独立工作,不会因为等待用户输入而中断验证流程。
9.3 Fork 模式:缓存安全的并行执行
Fork 模式是 Claude Code 中最精巧的架构创新之一。它允许主智能体将同一时刻的完整上下文”分叉”给多个并行子任务,同时利用 Anthropic API 的 prompt cache 机制避免重复传输大量 token。
Fork 模式的直觉理解
如果你熟悉 Unix 系统的 fork() 系统调用,Claude Code 的 Fork 模式与其有异曲同工之妙:
| 类比维度 | Unix fork() | Claude Code Fork 模式 |
|---|---|---|
| 触发时机 | 父进程调用 fork() | 主智能体调用 Agent 工具 |
| 继承内容 | 内存映像、文件描述符 | 系统提示、工具定义、对话历史 |
| 分叉后关系 | 父子进程独立运行 | 主/子智能体并行运行 |
| 通信方式 | pipe/shared memory | 任务通知 XML |
| 资源节省 | Copy-on-Write 内存共享 | Prompt Cache token 共享 |
| 递归防护 | 进程层级限制 | querySource 检查 |
forkSubagent 模块的核心设计
Fork 模式的激活需要满足三个条件:feature gate 开启、非 Coordinator 模式、非非交互模式。当 Coordinator 模式激活时,Fork 模式会被自动禁用,因为 Coordinator 已经拥有自己的任务委派模型。
交叉引用: Coordinator 模式与 Fork 模式的互斥关系将在第10章详细讨论。简言之,Coordinator 是一种更重量的编排模式,适合需要精细任务管理的场景;Fork 是一种轻量级的并行模式,适合将同一上下文分发到多个独立子任务。
在工具主入口的路由逻辑中,当用户省略子智能体类型且 Fork 模式开启时,系统会触发 Fork 路径。
继承父对话的完整上下文和系统提示
Fork 子智能体的核心原则是字节级继承——子智能体必须与父智能体共享完全相同的 API 请求前缀,才能命中 prompt cache。
在 Fork 路径中,系统会优先使用父智能体已渲染的系统提示字节,避免因重建导致的缓存失效。只有在无法获取已渲染提示时才降级到重建路径。重建可能因特性开关的冷热状态不同而偏离,导致缓存失效,因此传递渲染后的字节是保证精确匹配的关键。
为什么是字节级而非语义级?
Prompt cache 是 Anthropic API 的底层基础设施优化,它通过字节前缀匹配来复用已处理的 token。这意味着即使两段文本”语义完全相同”,只要在字节层面有任何一个字符的差异(哪怕是一个多余的空格或换行符),缓存就不会命中。这就是为什么 Fork 模式需要传递已渲染的原始字节而非重新构建——重新构建可能在空白字符、属性顺序等细微之处产生差异,而这些差异足以导致整个缓存前缀失效。
一个形象的比喻:prompt cache 就像 HTTP 缓存中的 ETag,它比对的是内容的精确哈希值。即使你把一段 HTML 重新格式化了缩进,虽然浏览器渲染结果相同,但 ETag 已经变了,缓存就失效了。
CacheSafeParams 五个维度
缓存安全参数类型定义了缓存命中的关键契约,包含五个维度:系统提示(systemPrompt)、用户上下文(userContext)、系统上下文(systemContext)、工具定义和模型(toolUseContext)、对话前缀消息(forkContextMessages)。
graph TD
A["systemPrompt<br/>系统提示必须字节一致"]
B["userContext<br/>用户上下文(CLAUDE.md 等)"]
C["systemContext<br/>系统注入的上下文信息"]
D["toolUseContext<br/>工具定义 + 模型选择"]
E["forkContextMessages<br/>对话历史前缀消息"]
A --> B --> C --> D --> E
F["五维度全部一致<br/>缓存命中,节省 token 和延迟"]
G["任一维度偏离<br/>缓存失效,完整重新处理"]
E -.->|"命中"| F
E -.->|"偏离"| G
style A fill:#e74c3c,stroke:#c0392b,color:#fff
style B fill:#e67e22,stroke:#d35400,color:#fff
style C fill:#f1c40f,stroke:#f39c12,color:#333
style D fill:#2ecc71,stroke:#27ae60,color:#fff
style E fill:#3498db,stroke:#2471a3,color:#fff
style F fill:#27ae60,stroke:#1e8449,color:#fff
style G fill:#c0392b,stroke:#922b21,color:#fff
这五个维度对应 Anthropic API 缓存键的组成:system prompt、tools、model、messages prefix、thinking config。只有这五个维度完全一致时,子请求才能命中父请求的缓存。
Fork 路径通过 useExactTools 标志确保工具定义不变——当 Fork 模式激活时,直接使用父工具池而非重新解析智能体工具,从而保持工具定义的字节一致性。
buildForkedMessages 的巧妙设计
Fork 消息构建函数是缓存共享的核心。它的处理逻辑如下:首先保留完整的父 assistant 消息(包含所有 tool_use 块、thinking、text,但分配新的 UUID);然后收集所有 tool_use 块;为每个 tool_use 生成统一的固定字符串占位符 tool_result;最后构建单条 user 消息,包含占位符结果和子任务指令。
关键点在于占位符结果是所有 Fork 子智能体共享的固定字符串 “Fork started — processing in background”。这意味着:
- 所有 Fork 子智能体共享相同的前缀:
[...历史, assistant(所有 tool_use), user(占位符结果..., 指令)] - 只有最后的指令文本不同:最大化缓存命中面积
- 结果结构为:
[...history, assistant(all_tool_uses), user(placeholder_results..., directive)]
缓存效率分析示例
假设一个对话场景:父对话积累了 50,000 token 的历史消息,父智能体的 assistant 消息包含 3 个 tool_use 块(约 2,000 token),系统提示和工具定义约 10,000 token。
不使用 Fork 模式(传统子智能体):
- 每个子智能体独立构建请求:50,000 + 2,000 + 10,000 = 62,000 token/次
- 3 个子智能体总计:186,000 input token
使用 Fork 模式:
- 父请求:62,000 token(首次,建立缓存)
- 3 个子智能体共享前缀:仅各自的”指令文本”部分是新的(约 200 token/个)
- 3 个子智能体总计:3 * 200 = 600 新增 input token
- 总计:62,600 input token(其中 62,000 命中缓存)
节省比例: (186,000 - 62,600) / 186,000 = 约 66% 的 token 节省
这种节省在大规模使用(比如一个会话中产生数十个 Fork 调用)时会更加显著。
递归 Fork 防护
Fork 子智能体保留了 Agent 工具以维持缓存一致的工具定义,因此必须防止递归 Fork。系统实现了双重检测策略:
-
querySource 检查(主防线):设置在上下文选项上,不受自动压缩影响。这是一个运行时标记,在 Fork 子智能体的上下文中被设置,标识”我是被 Fork 出来的”。由于它不是对话历史的一部分,不会被上下文压缩机制清除,因此即使在长对话中也能可靠地防止递归。
-
消息扫描(后备):检测特定标签,应对 querySource 未传递的边界情况。这是一种防御性编程策略,处理那些 querySource 可能在序列化/反序列化过程中丢失的极端情况。
子任务指令生成函数会生成包含严格行为规范的指令,核心内容包括:声明自己是 Fork 工作进程而非主智能体、禁止生成子智能体、禁止提问或建议下一步操作等。
为什么不让 Fork 子智能体也拥有 Fork 能力?
表面上看,递归 Fork 可以实现”无限并行”的嵌套执行。但实际上这会带来严重的工程问题:
- 资源爆炸:每个 Fork 都会创建一个新的 API 连接和工具实例,递归 Fork 会导致资源使用指数级增长
- 结果聚合困难:嵌套的 Fork 结果需要多层聚合,增加了结果丢失的风险
- 调试困难:递归结构使得错误追踪和性能分析变得极其复杂
- 缓存失效:每一层 Fork 的上下文都会与上一层略有不同,递归层级的缓存命中率会急剧下降
9.4 自定义智能体
.claude/agents/ 目录中的定义文件
自定义智能体通过加载合并函数加载。加载流程如下:
flowchart TD
A["扫描 agents 目录"] --> B["解析每个 .md 文件"]
B --> C["提取 YAML frontmatter → 智能体配置"]
B --> D["提取 Markdown 正文 → 系统提示"]
C --> E["并行加载插件智能体和内存快照"]
D --> E
E --> F["合并所有来源,按优先级去重"]
F --> G["注册到智能体注册表"]
- 扫描 agents 目录中的 Markdown 文件
- 对每个 Markdown 文件解析智能体定义
- 并行加载插件智能体和内存快照
- 合并所有来源,按优先级去重
Markdown frontmatter 格式
一个完整的自定义智能体 Markdown 文件示例如下:
---
name: security-auditor
description: 分析代码中的安全漏洞并生成审计报告
tools:
- Bash
- Read
- Grep
- Glob
disallowedTools:
- Write
model: haiku
effort: high
permissionMode: default
maxTurns: 30
color: orange
background: true
memory: project
skills:
- /commit
mcpServers:
- slack
hooks:
PreToolUse:
- matcher: Bash
command: "audit-log.sh"
---
你是一位代码安全审计专家。你的职责是:
1. 分析代码中潜在的安全漏洞
2. 检查常见的攻击向量(XSS、SQL 注入、CSRF 等)
3. 生成结构化的审计报告
4. 提供修复建议
报告格式:
- **漏洞级别**:Critical / High / Medium / Low
- **影响范围**:受影响的文件和函数
- **修复建议**:具体的代码修改方案
parseAgentFromMarkdown() 函数解析这个 frontmatter,将 name 映射为 agentType,将 Markdown 正文作为系统提示。如果 memory 字段启用,系统还会自动注入文件读写工具以支持持久化记忆。
更多自定义 Agent 案例
案例一:文档生成 Agent
---
name: doc-writer
description: 为代码生成 API 文档和使用指南
tools:
- Read
- Grep
- Glob
- Write
disallowedTools:
- Bash
model: haiku
effort: medium
maxTurns: 20
color: blue
background: true
---
你是一位技术文档专家。你的职责是:
1. 阅读代码中的函数签名、类型定义和注释
2. 生成结构化的 API 文档
3. 为复杂逻辑编写使用示例
4. 保持文档风格与现有文档一致
输出要求:
- 使用 Markdown 格式
- 包含参数说明、返回值说明、使用示例
- 标注 deprecated 的 API
案例二:测试生成 Agent
---
name: test-generator
description: 为指定模块自动生成单元测试和集成测试
tools:
- Read
- Write
- Grep
- Glob
- Bash
model: inherit
effort: high
maxTurns: 40
color: green
background: true
---
你是一位测试工程师。你的任务是:
1. 阅读目标模块的源码,理解其公开接口和内部逻辑
2. 识别所有需要测试的代码路径
3. 为每个路径编写测试用例
4. 确保测试覆盖:正常路径、边界条件、错误处理
测试原则:
- 测试应该独立、可重复
- 使用有意义的测试描述
- Mock 外部依赖,不 Mock 被测模块内部函数
案例三:性能分析 Agent
---
name: perf-analyzer
description: 分析代码的性能瓶颈并给出优化建议
tools:
- Read
- Grep
- Glob
- Bash
disallowedTools:
- Write
- Edit
model: inherit
effort: high
maxTurns: 25
color: yellow
---
你是一位性能分析专家。你的任务是:
1. 识别代码中的性能热点(O(n^2) 循环、不必要的内存分配、频繁的 I/O)
2. 分析数据库查询效率(N+1 查询、缺失索引)
3. 评估并发和异步模式的使用是否合理
4. 给出具体的优化建议和预估的性能提升
报告格式:
- **问题编号**和严重程度(Critical/High/Medium/Low)
- **代码位置**:文件名:行号
- **问题描述**:为什么这里慢
- **优化建议**:具体的修改方案
- **预期提升**:量化的性能改善预估
交叉引用: 自定义智能体的 hooks 配置与第8章的 Hook 系统使用相同的格式。你可以为智能体定义 PreToolUse、PostToolUse 等生命周期钩子,实现诸如审计日志、自动格式化等横切关注点。
9.5 智能体工具隔离
全局禁止列表
系统定义了全局禁止列表,所有子智能体都不能使用这些工具,包括:防止递归获取输出的工具、Plan mode 相关工具(仅主线程可用)、Agent 工具(防止递归嵌套)、向用户提问的工具、任务停止工具(需要主线程任务状态)。
graph TD
subgraph 第一层["第一层:全局禁止列表(所有子智能体)"]
G1["Agent, ExitPlanMode"]
G2["TaskOutput, TaskStop"]
G3["AskUserQuestion"]
end
subgraph 第二层["第二层:异步白名单(后台智能体)"]
W1["Read, Write, Edit, Bash"]
W2["Grep, Glob"]
W3["WebSearch, WebFetch + MCP 工具"]
end
subgraph 第三层["第三层:filterToolsForAgent(最终仲裁)"]
F1["MCP 工具 → 始终可用"]
F2["全局禁止 → 一律排除"]
F3["异步智能体 → 白名单过滤"]
F4["进程内队友 → 部分豁免"]
end
第一层 --> 第二层 --> 第三层
style 第一层 fill:#ff6b6b,stroke:#c0392b,color:#fff
style 第二层 fill:#f39c12,stroke:#d68910,color:#fff
style 第三层 fill:#3498db,stroke:#2471a3,color:#fff
异步智能体白名单
异步(后台)智能体的工具集进一步受限为白名单集合,包括 Read、Write、Edit、Bash、Grep、Glob、WebSearch 等核心工具,但不包括 TaskOutput、Agent 等可能导致递归的工具。
filterToolsForAgent
工具过滤函数是最终的仲裁者,实现三层过滤机制:MCP 工具始终可用;全局禁止工具一律排除;异步智能体只能使用白名单工具,但进程内队友可以豁免部分限制。
设计考量:为什么 MCP 工具始终可用?
MCP 工具是由用户显式配置的外部服务,它们经过了用户授权(通过权限配置)。与内置工具不同,MCP 工具的行为由外部服务器定义,Claude Code 无法预知其功能范围。因此,系统选择不限制子智能体使用 MCP 工具,而是将安全控制交给 MCP 的权限系统(参见第12章)。
9.6 子智能体生命周期管理
子智能体从创建到销毁经历完整的生命周期。理解这个生命周期对于设计高效的多智能体协作流程至关重要。
生命周期状态转换
stateDiagram-v2
[*] --> Created: Agent 工具被调用
Created --> ContextBuilt: 解析智能体定义
ContextBuilt --> Running: 构建系统提示、工具池、MCP 连接
Running --> WaitingForTool: 等待工具调用结果
WaitingForTool --> Running: 工具结果返回
Running --> Completed: maxTurns 达到 / 任务完成 / 错误发生
Completed --> CleanedUp: 生成任务通知
CleanedUp --> [*]: 清理 MCP 连接、释放资源
Running --> Killed: 用户中断
Running --> AsyncComplete: 后台模式完成
AsyncComplete --> [*]: 通知主智能体
note right of Created
智能体定义被解析
end note
note right of ContextBuilt
构建系统提示、工具池、
MCP 连接初始化
end note
note right of CleanedUp
清理 MCP 连接、释放资源
end note
上下文构建阶段
在子智能体开始执行之前,运行器会构建完整的执行上下文。这个过程包括:
- 系统提示渲染:根据智能体定义生成系统提示,注入工具描述、权限信息等
- 工具池组装:根据 allowedTools/disallowedTools 和全局过滤函数确定可用工具集
- MCP 服务器初始化:为智能体专属的 MCP 服务器建立连接
- Skill 预加载:加载智能体定义中指定的技能
- 内存注入:如果启用了 memory 选项,注入持久化记忆内容
资源清理阶段
子智能体完成或失败后,运行器负责清理资源:
- 关闭智能体专属的 MCP 服务器连接(注意:非专属连接由父级管理)
- 释放工具实例引用
- 将结果格式化为任务通知传递给调用者
- 如果是后台智能体,将通知排队等待主智能体下一轮处理
最佳实践:合理设置 maxTurns
maxTurns 控制子智能体的最大执行轮次。设置过低可能导致任务未完成就被中断;设置过高可能浪费 token 在无效的循环中。推荐的经验值:
- 只读搜索任务:10-15 轮
- 代码生成任务:20-30 轮
- 复杂重构任务:30-50 轮
- 探索性任务:15-20 轮
对于后台运行的智能体,建议设置较低的 maxTurns 作为安全阀,防止失控的智能体持续消耗资源。
实战练习
练习 1:创建一个自定义代码审查智能体
在项目的 .claude/agents/ 目录下创建 code-reviewer.md,定义一个专门用于代码审查的智能体。要求:
- 只读权限(禁止 Write、Edit)
- 使用
haiku模型以降低成本 - 输出结构化的审查报告
- 设置 maxTurns 为 15(审查任务不需要太多轮次)
- 启用 background 模式(审查不需要阻塞主流程)
扩展挑战: 为代码审查智能体添加一个 MCP 服务器连接,接入一个外部的代码质量检测服务。
练习 2:分析 Fork 模式的缓存效率
假设父对话有 100 条消息(约 80,000 token),父智能体一次性发出了 3 个 Fork 调用。根据 Fork 消息构建机制,计算:
- 第一个 Fork 子智能体的缓存前缀长度
- 第二、第三个 Fork 子智能体能复用多少缓存
- 如果不使用 Fork 模式,3 个子智能体需要传输多少 input token
思考题: 如果 3 个 Fork 子智能体的任务描述分别是 200、350、180 token,它们各自的新增 token 是多少?缓存命中率分别是多少?
练习 3:探索 Verification Agent 的对抗策略
查阅 Verification Agent 的系统提示设计理念,列出其中定义的所有”自我合理化借口”,并思考为什么这些对 LLM 验证任务是必要的。
深度思考: 如果你要设计一个”代码审查 Agent”,应该采用 Verification Agent 的对抗性风格,还是采用更协作的风格?在什么场景下对抗性更合适?在什么场景下协作性更合适?
练习 4:设计一个多智能体协作流程
设计一个完整的软件开发流程,使用以下内置智能体组合:
- 用户提出需求:“为用户认证模块添加 OAuth2 支持”
- 使用 Explore Agent 调查现有的认证代码结构
- 使用 Plan Agent 制定实施计划
- 使用 General Purpose Agent 执行实施
- 使用 Verification Agent 验证实现
写出每一步的输入和预期输出。
关键要点
-
AgentTool 的三层智能体体系(内置、自定义、插件)提供了从开箱即用到深度定制的完整光谱,优先级机制允许企业级覆盖。自定义智能体通过 Markdown 文件定义,降低了创建门槛。
-
内置智能体各司其职:Explore 专注高速只读搜索,Plan 做结构化规划,General Purpose 是万能选手,Verification 采取对抗性验证策略。四者组合覆盖了软件工程的核心工作流。
-
Fork 模式的核心创新是通过字节级继承实现 prompt cache 共享:相同的系统提示、工具定义、消息前缀加上统一的占位符结果,最大化缓存命中面积。在大规模并行场景下可节省 60%+ 的 token。
-
CacheSafeParams 的五个维度(system prompt、user context、system context、tool context、messages)是缓存命中的充分必要条件,任何维度的偏离都会导致缓存失效。这就是为什么 Fork 路径优先传递已渲染的原始字节。
-
工具隔离的三层防线(全局禁止列表、异步白名单、
filterToolsForAgent过滤)确保子智能体不会产生递归、不会越权、不会在后台模式下阻塞用户交互。 -
子智能体生命周期管理从创建到清理经过完整的状态转换。合理设置 maxTurns、选择合适的 model、正确配置工具集,是高效使用子智能体的关键。
-
对抗性验证哲学不仅是一种技术选择,更是一种工程文化的体现——与其相信代码”应该能工作”,不如主动尝试证明它不能工作。