Skip to content

Commit 8b63e54

Browse files
docs: 文档更新
1 parent 7d5271e commit 8b63e54

3 files changed

Lines changed: 547 additions & 146 deletions

File tree

docs/agent/sub-agents.mdx

Lines changed: 169 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,194 @@
11
---
2-
title: "子 Agent 机制 - AI 分身术与任务委派"
3-
description: "深入解析 Claude Code 子 Agent 机制:主 Agent 如何通过 AgentTool 委派子任务,子 Agent 的生命周期管理、工具继承和结果回传"
4-
keywords: ["子 Agent", "Agent 分身", "任务委派", "AgentTool", "多 Agent"]
2+
title: "子 Agent 机制 - AgentTool 的执行链路与隔离架构"
3+
description: "从源码角度解析 Claude Code 子 Agent:AgentTool.call() 的完整执行链路、Fork 子进程的 Prompt Cache 共享、Worktree 隔离、工具池独立组装、以及结果回传的数据格式"
4+
keywords: ["子 Agent", "AgentTool", "任务委派", "forkSubagent", "子进程隔离"]
55
---
66

7-
{/* 本章目标:解释子 Agent 机制的设计和应用场景 */}
7+
{/* 本章目标:从源码角度揭示子 Agent 的完整执行链路、工具隔离、通信协议和生命周期管理 */}
88

9-
## 为什么需要子 Agent
9+
## 执行链路总览
1010

11-
有些任务太大,一个 AI 实例忙不过来
11+
一条 `Agent(prompt="修复 bug")` 调用的完整路径
1212

13-
- "在 5 个不同的文件中分别找到并修复同类 bug"
14-
- "一边重构后端 API,一边更新前端调用"
15-
- "研究这个库的用法,同时修改我们的代码"
13+
```
14+
AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
15+
16+
AgentTool.call() ← 入口(AgentTool.tsx:239)
17+
├── 解析 effectiveType(fork vs 命名 agent)
18+
├── filterDeniedAgents() ← 权限过滤
19+
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s)
20+
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
21+
├── createAgentWorktree() ← 可选 worktree 隔离
22+
23+
runAgent() ← 核心执行(runAgent.ts:248)
24+
├── getAgentSystemPrompt() ← 构建 agent 专属 system prompt
25+
├── initializeAgentMcpServers() ← agent 级 MCP 服务器
26+
├── executeSubagentStartHooks() ← Hook 注入
27+
├── query() ← 进入标准 agentic loop
28+
│ ├── 消息流逐条 yield
29+
│ └── recordSidechainTranscript() ← JSONL 持久化
30+
31+
finalizeAgentTool() ← 结果汇总
32+
├── 提取文本内容 + usage 统计
33+
└── mapToolResultToToolResultBlockParam() ← 格式化为 tool_result
34+
```
1635

17-
## 分身术的运作方式
36+
## 两种子 Agent 路径:命名 Agent vs Fork
1837

19-
Claude Code 中的 Agent 工具让 AI 能够**启动另一个 AI 实例**来处理子任务
38+
`AgentTool.call()` 根据是否提供 `subagent_type` 走两条完全不同的路径(`AgentTool.tsx:322-356`
2039

21-
<Steps>
22-
<Step title="主 Agent 分析任务">
23-
主 Agent 判断任务可以被拆解为独立的子任务
24-
</Step>
25-
<Step title="启动子 Agent">
26-
通过 Agent 工具创建一个或多个子 Agent,每个子 Agent 收到一个清晰的子任务描述
27-
</Step>
28-
<Step title="并行执行">
29-
多个子 Agent 可以同时工作,互不干扰
30-
</Step>
31-
<Step title="结果汇总">
32-
子 Agent 完成后,结果返回给主 Agent,主 Agent 汇总并呈现给用户
33-
</Step>
34-
</Steps>
40+
| 维度 | 命名 Agent(`subagent_type` 指定) | Fork 子进程(`subagent_type` 省略) |
41+
|------|-------------------------------------|--------------------------------------|
42+
| **触发条件** | `subagent_type` 有值 | `isForkSubagentEnabled()` && 未指定类型 |
43+
| **System Prompt** | Agent 自身的 `getSystemPrompt()` | 继承父 Agent 的完整 System Prompt |
44+
| **工具池** | `assembleToolPool()` 独立组装 | 父 Agent 的原始工具池(`useExactTools: true`|
45+
| **上下文** | 仅任务描述 | 父 Agent 的完整对话历史(`forkContextMessages`|
46+
| **模型** | 可独立指定 | 继承父模型(`model: 'inherit'`|
47+
| **权限模式** | Agent 定义的 `permissionMode` | `'bubble'`(上浮到父终端) |
48+
| **目的** | 专业任务委派 | Prompt Cache 命中率优化 |
3549

36-
## Agent 的边界
50+
Fork 路径的设计核心是 **Prompt Cache 共享**:所有 fork 子进程共享父 Agent 的完整 `assistant` 消息(所有 `tool_use` 块),用相同的占位符 `tool_result` 填充,只有最后一个 `text` 块包含各自的指令。这使得 API 请求前缀字节完全一致,最大化缓存命中。
3751

38-
子 Agent 不是和主 Agent 完全一样的——它有明确的能力边界:
52+
```typescript
53+
// forkSubagent.ts:142 — 所有 fork 子进程的占位结果
54+
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
3955

40-
| 特性 | 主 Agent | 子 Agent |
41-
|------|---------|---------|
42-
| 可用工具 | 全部工具 | 受限子集(不能再启动子 Agent 等) |
43-
| 上下文 | 完整的会话历史 | 只有主 Agent 给的任务描述 |
44-
| 权限 | 用户设定 | 继承主 Agent 的权限,或更严格 |
45-
| 状态 | 可修改全局状态 | 隔离的状态空间 |
56+
// buildForkedMessages() 构建:
57+
// [assistant(全量 tool_use), user(placeholder_results..., 子进程指令)]
58+
```
4659

47-
## 通信方式
60+
### Fork 递归防护
4861

49-
Agent 和子 Agent 之间通过**消息邮箱**通信
62+
Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork(`AgentTool.tsx:332`
5063

51-
- 主 Agent 通过 `Agent` 工具启动子 Agent
52-
- 子 Agent 通过 `SendMessage` 工具向主 Agent 报告进度
53-
- 这种松耦合的通信方式让 Agent 可以异步协作
64+
1. **`querySource` 检查**(压缩安全):`context.options.querySource === 'agent:builtin:fork'`
65+
2. **消息扫描**(降级兜底):检测 `<fork-boilerplate>` 标签
66+
67+
## 工具池的独立组装
68+
69+
子 Agent 不继承父 Agent 的工具限制——它的工具池完全独立组装(`AgentTool.tsx:573-577`):
70+
71+
```typescript
72+
const workerPermissionContext = {
73+
...appState.toolPermissionContext,
74+
mode: selectedAgent.permissionMode ?? 'acceptEdits'
75+
}
76+
const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools)
77+
```
78+
79+
关键设计决策:
80+
- **权限模式独立**:子 Agent 使用 `selectedAgent.permissionMode`(默认 `acceptEdits`),不受父 Agent 当前模式的限制
81+
- **MCP 工具继承**`appState.mcp.tools` 包含所有已连接的 MCP 工具,子 Agent 自动获得
82+
- **Agent 级 MCP 服务器**`runAgent()` 中的 `initializeAgentMcpServers()` 可以为特定 Agent 额外连接专属 MCP 服务器
83+
84+
### 工具过滤的 resolveAgentTools
85+
86+
`runAgent.ts:500-502` 在工具组装后进一步过滤:
87+
88+
```typescript
89+
const resolvedTools = useExactTools
90+
? availableTools // Fork: 直接使用父工具
91+
: resolveAgentTools(agentDefinition, availableTools, isAsync).resolvedTools
92+
```
93+
94+
`resolveAgentTools()` 会根据 Agent 定义中的 `tools` 字段过滤可用工具,将 `['*']` 映射为全量工具。
95+
96+
## Worktree 隔离机制
97+
98+
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:590-593`):
99+
100+
```typescript
101+
const slug = `agent-${earlyAgentId.slice(0, 8)}`
102+
worktreeInfo = await createAgentWorktree(slug)
103+
```
104+
105+
Worktree 生命周期:
106+
1. **创建**:在 `.git/worktrees/` 下创建独立工作副本
107+
2. **CWD 覆盖**`runWithCwdOverride(worktreePath, fn)` 让所有文件操作在 worktree 中执行
108+
3. **路径翻译**:Fork + worktree 时注入路径翻译通知(`buildWorktreeNotice`
109+
4. **清理**`cleanupWorktreeIfNeeded`):
110+
- Hook-based worktree → 始终保留
111+
- 有变更 → 保留,返回 `worktreePath`
112+
- 无变更 → 自动删除
113+
114+
## 生命周期管理:同步 vs 异步
115+
116+
### 异步 Agent(后台运行)
117+
118+
`run_in_background=true``selectedAgent.background=true` 时,Agent 立即返回 `async_launched` 状态(`AgentTool.tsx:686-764`):
119+
120+
```
121+
registerAsyncAgent(agentId, ...) ← 注册到 AppState.tasks
122+
↓ (void — 火后不管)
123+
runAsyncAgentLifecycle() ← 后台执行
124+
├── runAgent().onCacheSafeParams ← 进度摘要初始化
125+
├── 消息流迭代
126+
├── completeAsyncAgent() ← 标记完成
127+
├── classifyHandoffIfNeeded() ← 安全检查
128+
└── enqueueAgentNotification() ← 通知主 Agent
129+
```
130+
131+
异步 Agent 获得独立的 `AbortController`,不与父 Agent 共享——用户按 ESC 取消主线程不会杀掉后台 Agent。
132+
133+
### 同步 Agent(前台运行)
134+
135+
同步 Agent 的关键特性是 **可后台化**`AgentTool.tsx:818-833`):
136+
137+
```typescript
138+
const registration = registerAgentForeground({
139+
autoBackgroundMs: getAutoBackgroundMs() || undefined // 默认 120s
140+
})
141+
backgroundPromise = registration.backgroundSignal.then(...)
142+
```
143+
144+
在 agentic loop 的每次迭代中,系统用 `Promise.race` 竞争下一条消息和后台化信号:
145+
146+
```typescript
147+
const raceResult = await Promise.race([
148+
nextMessagePromise.then(r => ({ type: 'message', result: r })),
149+
backgroundPromise // 超过 autoBackgroundMs 触发
150+
])
151+
```
152+
153+
后台化后,前台迭代器被终止(`agentIterator.return()`),新的 `runAgent()``isAsync: true` 重新启动,当前台的输出文件继续写入。
154+
155+
## 结果回传格式
156+
157+
`mapToolResultToToolResultBlockParam()` 根据状态返回不同格式(`AgentTool.tsx:1298-1375`):
158+
159+
| 状态 | 返回内容 |
160+
|------|---------|
161+
| `completed` | 内容 + `<usage>` 块(token/tool_calls/duration) |
162+
| `async_launched` | agentId + outputFile 路径 + 操作指引 |
163+
| `teammate_spawned` | agent_id + name + team_name |
164+
| `remote_launched` | taskId + sessionUrl + outputFile |
165+
166+
对于一次性内置 Agent(Explore、Plan),`<usage>` 块被省略——每周节省约 1-2 Gtok 的上下文窗口。
167+
168+
## MCP 依赖的等待机制
169+
170+
如果 Agent 声明了 `requiredMcpServers``call()` 会等待这些服务器连接完成(`AgentTool.tsx:371-410`):
171+
172+
```typescript
173+
const MAX_WAIT_MS = 30_000 // 最长等 30 秒
174+
const POLL_INTERVAL_MS = 500 // 每 500ms 轮询
175+
```
176+
177+
早期退出条件:任何必需服务器进入 `failed` 状态时立即停止等待。工具可用性通过 `mcp__` 前缀工具名解析(`mcp__serverName__toolName`)判断。
54178

55179
## 适用场景
56180

57181
<CardGroup cols={2}>
58182
<Card title="并行研究" icon="magnifying-glass">
59-
多个子 Agent 同时搜索不同方向的信息
183+
多个 fork 子进程并行搜索不同方向,共享 Prompt Cache 前缀,只有指令不同
60184
</Card>
61-
<Card title="分治修改" icon="code-branch">
62-
把大规模修改拆分到多个子 Agent 并行执行
63-
</Card>
64-
<Card title="前后台配合" icon="layer-group">
65-
一个子 Agent 在后台运行测试,主 Agent 继续写代码
185+
<Card title="专业委派" icon="code-branch">
186+
使用命名 Agent(Explore/Plan/verification)执行专业任务,受限工具集 + 独立权限
66187
</Card>
67188
<Card title="隔离实验" icon="flask">
68-
在 worktree 中启动子 Agent 尝试一个方案,不影响主分支
189+
`isolation: "worktree"` 在独立工作副本中尝试方案,不影响主分支
190+
</Card>
191+
<Card title="后台构建" icon="layer-group">
192+
`run_in_background: true` 启动长时间构建/测试任务,主 Agent 继续工作
69193
</Card>
70194
</CardGroup>

0 commit comments

Comments
 (0)