跳转到内容

子 Agent 机制

一条 Agent(prompt="修复 bug") 调用的完整路径:

AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
AgentTool.call() ← 入口(AgentTool.tsx:239)
├── 解析 effectiveType(fork vs 命名 agent)
├── filterDeniedAgents() ← 权限过滤
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s)
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
├── createAgentWorktree() ← 可选 worktree 隔离
runAgent() ← 核心执行(runAgent.ts:248)
├── getAgentSystemPrompt() ← 构建 agent 专属 system prompt
├── initializeAgentMcpServers() ← agent 级 MCP 服务器
├── executeSubagentStartHooks() ← Hook 注入
├── query() ← 进入标准 agentic loop
│ ├── 消息流逐条 yield
│ └── recordSidechainTranscript() ← JSONL 持久化
finalizeAgentTool() ← 结果汇总
├── 提取文本内容 + usage 统计
└── mapToolResultToToolResultBlockParam() ← 格式化为 tool_result

两种子 Agent 路径:命名 Agent vs Fork

Section titled “两种子 Agent 路径:命名 Agent vs Fork”

AgentTool.call() 根据是否提供 subagent_type 走两条完全不同的路径(AgentTool.tsx:322-356):

维度命名 Agent(subagent_type 指定)Fork 子进程(subagent_type 省略)
触发条件subagent_type 有值isForkSubagentEnabled() && 未指定类型
System PromptAgent 自身的 getSystemPrompt()继承父 Agent 的完整 System Prompt
工具池assembleToolPool() 独立组装父 Agent 的原始工具池(useExactTools: true
上下文仅任务描述父 Agent 的完整对话历史(forkContextMessages
模型可独立指定继承父模型(model: 'inherit'
权限模式Agent 定义的 permissionMode'bubble'(上浮到父终端)
目的专业任务委派Prompt Cache 命中率优化

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

// forkSubagent.ts:142 — 所有 fork 子进程的占位结果
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
// buildForkedMessages() 构建:
// [assistant(全量 tool_use), user(placeholder_results..., 子进程指令)]

Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork(AgentTool.tsx:332):

  1. querySource 检查(压缩安全):context.options.querySource === 'agent:builtin:fork'
  2. 消息扫描(降级兜底):检测 <fork-boilerplate> 标签

子 Agent 不继承父 Agent 的工具限制——它的工具池完全独立组装(AgentTool.tsx:573-577):

const workerPermissionContext = {
...appState.toolPermissionContext,
mode: selectedAgent.permissionMode ?? 'acceptEdits'
}
const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools)

关键设计决策:

  • 权限模式独立:子 Agent 使用 selectedAgent.permissionMode(默认 acceptEdits),不受父 Agent 当前模式的限制
  • MCP 工具继承appState.mcp.tools 包含所有已连接的 MCP 工具,子 Agent 自动获得
  • Agent 级 MCP 服务器runAgent() 中的 initializeAgentMcpServers() 可以为特定 Agent 额外连接专属 MCP 服务器

runAgent.ts:500-502 在工具组装后进一步过滤:

const resolvedTools = useExactTools
? availableTools // Fork: 直接使用父工具
: resolveAgentTools(agentDefinition, availableTools, isAsync).resolvedTools

resolveAgentTools() 会根据 Agent 定义中的 tools 字段过滤可用工具,将 ['*'] 映射为全量工具。

isolation: "worktree" 参数让子 Agent 在独立的 git worktree 中工作(AgentTool.tsx:590-593):

const slug = `agent-${earlyAgentId.slice(0, 8)}`
worktreeInfo = await createAgentWorktree(slug)

Worktree 生命周期:

  1. 创建:在 .git/worktrees/ 下创建独立工作副本
  2. CWD 覆盖runWithCwdOverride(worktreePath, fn) 让所有文件操作在 worktree 中执行
  3. 路径翻译:Fork + worktree 时注入路径翻译通知(buildWorktreeNotice
  4. 清理cleanupWorktreeIfNeeded):
    • Hook-based worktree → 始终保留
    • 有变更 → 保留,返回 worktreePath
    • 无变更 → 自动删除

run_in_background=trueselectedAgent.background=true 时,Agent 立即返回 async_launched 状态(AgentTool.tsx:686-764):

registerAsyncAgent(agentId, ...) ← 注册到 AppState.tasks
↓ (void — 火后不管)
runAsyncAgentLifecycle() ← 后台执行
├── runAgent().onCacheSafeParams ← 进度摘要初始化
├── 消息流迭代
├── completeAsyncAgent() ← 标记完成
├── classifyHandoffIfNeeded() ← 安全检查
└── enqueueAgentNotification() ← 通知主 Agent

异步 Agent 获得独立的 AbortController,不与父 Agent 共享——用户按 ESC 取消主线程不会杀掉后台 Agent。

同步 Agent 的关键特性是 可后台化AgentTool.tsx:818-833):

const registration = registerAgentForeground({
autoBackgroundMs: getAutoBackgroundMs() || undefined // 默认 120s
})
backgroundPromise = registration.backgroundSignal.then(...)

在 agentic loop 的每次迭代中,系统用 Promise.race 竞争下一条消息和后台化信号:

const raceResult = await Promise.race([
nextMessagePromise.then(r => ({ type: 'message', result: r })),
backgroundPromise // 超过 autoBackgroundMs 触发
])

后台化后,前台迭代器被终止(agentIterator.return()),新的 runAgent()isAsync: true 重新启动,当前台的输出文件继续写入。

mapToolResultToToolResultBlockParam() 根据状态返回不同格式(AgentTool.tsx:1298-1375):

状态返回内容
completed内容 + <usage> 块(token/tool_calls/duration)
async_launchedagentId + outputFile 路径 + 操作指引
teammate_spawnedagent_id + name + team_name
remote_launchedtaskId + sessionUrl + outputFile

对于一次性内置 Agent(Explore、Plan),<usage> 块被省略——每周节省约 1-2 Gtok 的上下文窗口。

如果 Agent 声明了 requiredMcpServerscall() 会等待这些服务器连接完成(AgentTool.tsx:371-410):

const MAX_WAIT_MS = 30_000 // 最长等 30 秒
const POLL_INTERVAL_MS = 500 // 每 500ms 轮询

早期退出条件:任何必需服务器进入 failed 状态时立即停止等待。工具可用性通过 mcp__ 前缀工具名解析(mcp__serverName__toolName)判断。

并行研究

多个 fork 子进程并行搜索不同方向,共享 Prompt Cache 前缀,只有指令不同

专业委派

使用命名 Agent(Explore/Plan/verification)执行专业任务,受限工具集 + 独立权限

隔离实验

isolation: "worktree" 在独立工作副本中尝试方案,不影响主分支

后台构建

run_in_background: true 启动长时间构建/测试任务,主 Agent 继续工作