docs: 审校 Agent 文档术语与架构描述准确性(docs/agent) (#231)

基于源码验证和官方文档对照,修正三份 Agent 文档的内容准确性:

coordinator-and-swarm.mdx:
- 对齐官方术语:Leader→Team Lead, teammate→Teammate, 引入 Mailbox 消息系统
- 修正 Swarm 门控为 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1(原文"默认启用"不准确)
- 拓扑描述改为"星型+P2P混合"(官方有 Team Lead 角色,非纯网状)
- 新增架构组件表、Mailbox 消息系统、Hook 事件(TeammateIdle/TaskCreated/TaskCompleted)、限制说明
- 新增持久化存储路径(~/.claude/teams/, ~/.claude/tasks/)
- 移除虚构的"高水位标记"(claimTask 仅使用文件锁)
- 修正 Scratchpad 描述(Workers 获得,非 Coordinator 拥有)
- 修复引号不匹配

sub-agents.mdx:
- 修正为三路径路由(命名 Agent / Fork / GP 回退),原文仅两种
- 补充 isForkSubagentEnabled() 三前提(feature flag + 非Coordinator + 非非交互式)
- 新增模型解析优先级(4级链 + inherit 运行时解析)
- 新增内置 Agent 表(Explore/Plan/GP/statusline-setup/claude-code-guide)
- 新增 Hook 事件表(frontmatter + settings.json 双级别)
- 补充异步生命周期完整链路(finalizeAgentTool → getWorktreeResult)
- 补充 transcript 存储路径和结果格式细节

worktree-isolation.mdx:
- 区分 createWorktreeForSession vs createAgentWorktree 两种函数
- 修正清理机制为 cleanupWorktreeIfNeeded(非 ExitWorktreeTool 手动操作)
- 补充 usedSparsePaths 字段
This commit is contained in:
CyberScrubber
2026-04-10 18:00:42 +08:00
committed by GitHub
parent ff03fe7fcb
commit 81073135e2
3 changed files with 166 additions and 55 deletions

View File

@@ -14,8 +14,8 @@ keywords: ["子 Agent", "AgentTool", "任务委派", "forkSubagent", "子进程
AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
AgentTool.call() ← 入口AgentTool.tsx:239
├── 解析 effectiveTypefork vs 命名 agent
├── filterDeniedAgents() ← 权限过滤
├── 解析 effectiveTypefork vs 命名 agent vs GP 回退
├── filterDeniedAgents() ← 仅命名 Agent 路径执行:权限过滤
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
├── createAgentWorktree() ← 可选 worktree 隔离
@@ -26,26 +26,30 @@ runAgent() ← 核心执行runAgent.ts:248
├── executeSubagentStartHooks() ← Hook 注入
├── query() ← 进入标准 agentic loop
│ ├── 消息流逐条 yield
│ └── recordSidechainTranscript() ← JSONL 持久化
│ └── recordSidechainTranscript() ← JSONL 持久化~/.claude/projects/{project}/{session}/subagents/
finalizeAgentTool() ← 结果汇总
├── 提取文本内容 + usage 统计
└── mapToolResultToToolResultBlockParam() ← 格式化为 tool_result
```
## 两种子 Agent 路径:命名 Agent vs Fork
## 子 Agent 的三种路径
`AgentTool.call()` 根据是否提供 `subagent_type` 走两条完全不同的路径(`AgentTool.tsx:322-356`
`AgentTool.call()` 根据 `subagent_type` 参数和 Fork 实验开关,走三条不同的路径
| 维度 | 命名 Agent`subagent_type` 指定) | Fork 子进程(`subagent_type` 省略) |
|------|-------------------------------------|--------------------------------------|
| **触发条件** | `subagent_type` 有值 | `isForkSubagentEnabled()` && 未指定类型 |
| **System Prompt** | Agent 自身的 `getSystemPrompt()` | 继承父 Agent 的完整 System Prompt |
| **工具池** | `assembleToolPool()` 独立组装 | 父 Agent 的原始工具池(`useExactTools: true` |
| **上下文** | 仅任务描述 | 父 Agent 的完整对话历史(`forkContextMessages` |
| **模型** | 可独立指定 | 继承父模型(`model: 'inherit'` |
| **权限模式** | Agent 定义的 `permissionMode` | `'bubble'`(上浮到父终端) |
| **目的** | 专业任务委派 | Prompt Cache 命中率优化 |
| 维度 | 命名 Agent`subagent_type` 指定) | Fork 子进程(Fork 启用 + 类型省略) | General-purpose 回退Fork 关闭 + 类型省略) |
|------|-------------------------------------|--------------------------------------|---------------------------------------------|
| **触发条件** | `subagent_type` 有值 | `isForkSubagentEnabled() === true` 且未指定类型 | `isForkSubagentEnabled() === false` 且未指定类型 |
| **System Prompt** | Agent 自身的 `getSystemPrompt()` | 继承父 Agent 的完整 System Prompt | General-purpose Agent 的 `getSystemPrompt()` |
| **工具池** | `assembleToolPool()` 独立组装 | 父 Agent 的原始工具池(`useExactTools: true` | `assembleToolPool()` 独立组装 |
| **上下文** | 仅任务描述 | 父 Agent 的完整对话历史(`forkContextMessages` | 仅任务描述 |
| **模型** | 可独立指定 | 继承父模型(`model: 'inherit'` | 可独立指定 |
| **权限模式** | Agent 定义的 `permissionMode` | `'bubble'`(上浮到父终端) | Agent 定义的 `permissionMode` |
| **目的** | 专业任务委派 | Prompt Cache 命中率优化 | 通用任务处理 |
<Note>
Fork 实验的门控函数 `isForkSubagentEnabled()` 需要同时满足三个前提:`FORK_SUBAGENT` feature flag 已启用、当前不在 Coordinator 模式中、且不是非交互式会话。任一条件不满足时,省略 `subagent_type` 会静默降级为 General-purpose Agent而非触发 Fork。
</Note>
Fork 路径的设计核心是 **Prompt Cache 共享**:所有 fork 子进程共享父 Agent 的完整 `assistant` 消息(所有 `tool_use` 块),用相同的占位符 `tool_result` 填充,只有最后一个 `text` 块包含各自的指令。这使得 API 请求前缀字节完全一致,最大化缓存命中。
@@ -64,9 +68,41 @@ Fork 子进程保留 Agent 工具(为了 cache-identical tool defs但通
1. **`querySource` 检查**(压缩安全):`context.options.querySource === 'agent:builtin:fork'`
2. **消息扫描**(降级兜底):检测 `<fork-boilerplate>` 标签
## 工具池的独立组装
### 模型解析优先级
子 Agent 不继承父 Agent 的工具限制——它的工具池完全独立组装(`AgentTool.tsx:573-577`
子 Agent 的模型选择遵循严格的优先级链(`src/utils/model/agent.ts`
```
1. CLAUDE_CODE_SUBAGENT_MODEL 环境变量 ← 全局覆盖
↓(未设置时)
2. 每次调用的 model 参数 ← AgentTool 入参
↓(未指定时)
3. Agent 定义的 model frontmatter ← 如 "sonnet", "haiku", "inherit"
↓(未定义时)
4. 继承父对话模型conversation model ← getDefaultSubagentModel() 返回 "inherit"
```
其中 `inherit` 不是简单的模型传递——它经过 `getRuntimeMainLoopModel()` 解析,确保 plan mode 下的 `opusplan→Opus` 等运行时映射正确生效。当 Agent 指定的模型族(如 `haiku`)与父模型同族时,直接复用父模型的精确 ID避免跨 provider 降级。
## 命名 Agent 的工具池独立组装
### 内置 Agent
系统预定义了几个内置 Agent`src/tools/AgentTool/builtinAgents.ts`),各有明确的职责和模型配置:
| Agent | 模型 | 权限 | 用途 |
|-------|------|------|------|
| **Explore** | Haiku轻量快速 | 只读Read/Grep/Glob | 代码库搜索与探索 |
| **Plan** | 继承父模型 | 只读 | 为 Plan Mode 收集研究信息 |
| **General-purpose** | 继承父模型 | 全部工具 | 复杂的通用任务处理 |
| **statusline-setup** | 继承父模型 | 受限 | 配置状态栏设置 |
| **claude-code-guide** | 继承父模型 | 受限 | 解答 Claude Code 使用问题 |
用户还可通过 `.claude/agents/` 目录或 settings 定义自定义 Agent作用域优先级为managed settings > CLI `--agents` > 项目级 `.claude/agents/` > 用户级 `~/.claude/agents/` > plugin。
命名 Agent包括 General-purpose 回退)不继承父 Agent 的工具限制——它的工具池完全独立组装。Fork 子进程则通过 `useExactTools: true` 直接继承父 Agent 的原始工具池,以保持 Prompt Cache 中工具定义的字节一致性。
命名 Agent 的工具池组装逻辑:
```typescript
const workerPermissionContext = {
@@ -93,6 +129,17 @@ const resolvedTools = useExactTools
`resolveAgentTools()` 会根据 Agent 定义中的 `tools` 字段过滤可用工具,将 `['*']` 映射为全量工具。
### Hook 事件
子 Agent 支持 Agent 定义 frontmatter 和全局 settings.json 两种级别的 Hook
| 来源 | 事件 | 说明 |
|------|------|------|
| Agent frontmatter `hooks` | `PreToolUse` / `PostToolUse` | 工具调用前后拦截 |
| Agent frontmatter `hooks` | `Stop` | 自动转换为 `SubagentStop``registerFrontmatterHooks` 传入 `isAgent=true` |
| settings.json | `SubagentStart` | 子 Agent 启动时触发(`executeSubagentStartHooks()` |
| settings.json | `SubagentStop` | 子 Agent 停止时触发 |
## Worktree 隔离机制
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:590-593`
@@ -115,19 +162,23 @@ Worktree 生命周期:
### 异步 Agent后台运行
当 `run_in_background=true``selectedAgent.background=true` Agent 立即返回 `async_launched` 状态`AgentTool.tsx:686-764`
当 `run_in_background=true``selectedAgent.background=true`、或系统判定应强制异步(如 `assistantForceAsync`、`proactiveModule` 激活)Agent 立即返回 `async_launched` 状态:
```
registerAsyncAgent(agentId, ...) ← 注册到 AppState.tasks
↓ (void — 火后不管)
runAsyncAgentLifecycle() ← 后台执行
runAsyncAgentLifecycle() ← 后台执行agentToolUtils.ts
├── runAgent().onCacheSafeParams ← 进度摘要初始化
├── 消息流迭代
├── completeAsyncAgent() ← 标记完成
├── classifyHandoffIfNeeded() ← 安全检查
├── finalizeAgentTool() 结果汇总(提取文本 + usage 统计)
├── completeAsyncAgent() ← 标记完成(先于通知,确保 TaskOutput 尽快解除阻塞)
├── classifyHandoffIfNeeded() ← 安全分类(需 TRANSCRIPT_CLASSIFIER feature
├── getWorktreeResult() ← Worktree 清理(如有隔离)
└── enqueueAgentNotification() ← 通知主 Agent
```
如果异步 Agent 提供了 `name` 参数,还会注册到 `agentNameRegistry`,支撑 `SendMessage` 工具通过名称路由到该 Agent。
异步 Agent 获得独立的 `AbortController`,不与父 Agent 共享——用户按 ESC 取消主线程不会杀掉后台 Agent。
### 同步 Agent前台运行
@@ -154,16 +205,16 @@ const raceResult = await Promise.race([
## 结果回传格式
`mapToolResultToToolResultBlockParam()` 根据状态返回不同格式`AgentTool.tsx:1298-1375`
`mapToolResultToToolResultBlockParam()` 根据状态返回不同格式:
| 状态 | 返回内容 |
|------|---------|
| `completed` | 内容 + `<usage>` 块token/tool_calls/duration |
| `async_launched` | agentId + outputFile 路径 + 操作指引 |
| `completed` | 内容 + `<usage>` 块token/tool_calls/duration;无内容时插入占位文本 `"(Subagent completed but returned no output.)"` 防止模型误判为空 |
| `async_launched` | agentId + outputFile 路径 + 操作指引(指引内容取决于 `canReadOutputFile`:有读取权限时提示通过 Read/Bash 查看进度,否则仅告知已启动) |
| `teammate_spawned` | agent_id + name + team_name |
| `remote_launched` | taskId + sessionUrl + outputFile |
对于一次性内置 AgentExplore、Plan`<usage>` 块被省略——每周节省约 1-2 Gtok 的上下文窗口
对于一次性内置 AgentExplore、Plan当**不存在** worktree 隔离时,`<usage>` 块和 agentId 尾部被省略——每周节省约 1-2 Gtok 的上下文窗口。存在 worktree 时仍需返回 `worktreePath` 和 `worktreeBranch` 信息
## MCP 依赖的等待机制
@@ -174,7 +225,7 @@ const MAX_WAIT_MS = 30_000 // 最长等 30 秒
const POLL_INTERVAL_MS = 500 // 每 500ms 轮询
```
早期退出条件:任何必需服务器进入 `failed` 状态时立即停止等待。工具可用性通过 `mcp__` 前缀工具名解析(`mcp__serverName__toolName`)判断。
早期退出条件:任何必需服务器进入 `failed` 状态时立即停止等待。工具可用性通过 `mcp__` 前缀工具名解析(`mcp__serverName__toolName`)判断。等待结束后如果仍有必需服务器未就绪,`call()` 会抛出错误并明确列出缺失的服务器名称。
## 适用场景