Fork Mechanism
fork mechanism 是 Claude Code 创建 sub-agent 的方式,其特点是 继承父级完整对话 context。与从空白状态启动的普通 agent 派生不同,被 fork 的 agent 携带父级的完整消息历史,能够以充分的情境感知继续工作。
Fork 与普通 Agent 派生的对比
Section titled “Fork 与普通 Agent 派生的对比”graph LR
subgraph Regular Spawn
A1[Parent Agent] -->|"Agent(subagent_type='Explore')"| B1[Child Agent]
B1 ---|"Fresh context<br/>+ agent system prompt"| C1[Independent execution]
end
subgraph Fork Spawn
A2[Parent Agent] -->|"Agent(no subagent_type)"| B2[Forked Child]
B2 ---|"Full parent context<br/>+ parent system prompt<br/>+ fork directive"| C2[Context-aware execution]
end
关键区别:当 Agent tool 调用中省略 subagent_type 参数时(且 fork 实验处于激活状态),Claude Code 会触发隐式 fork,而非普通的 agent 派生。
Feature Gate
Section titled “Feature Gate”fork mechanism 受 FORK_SUBAGENT feature flag 控制,并有严格的互斥规则:
export function isForkSubagentEnabled(): boolean { if (feature('FORK_SUBAGENT')) { if (isCoordinatorMode()) return false // coordinator 拥有自己的编排模型 if (getIsNonInteractiveSession()) return false // 仅限 SDK return true } return false}fork 模式与 coordinator 模式互斥——coordinator 已有其自己的、基于 worker agent 的委托模型。
FORK_AGENT 定义
Section titled “FORK_AGENT 定义”fork 路径使用一个从未注册到内置 agent 列表中的合成 agent 定义:
export const FORK_AGENT = { agentType: FORK_SUBAGENT_TYPE, // 'fork' tools: ['*'], // 与父级相同的完整 tool 池,以保证 cache 前缀一致 maxTurns: 200, model: 'inherit', // 继承父级模型,保证 context 长度一致 permissionMode: 'bubble', // permission 提示上浮至父级终端 source: 'built-in', getSystemPrompt: () => '', // 未使用——父级已渲染的 prompt 字节直接透传} satisfies BuiltInAgentDefinition构建 Fork 后的消息
Section titled “构建 Fork 后的消息”关键设计挑战是 prompt cache 共享:从同一父级 turn 派生的所有 fork 子进程必须产生字节完全相同的 API 请求前缀,只有每个子进程的专属指令才允许不同。
// src/tools/AgentTool/forkSubagent.ts — buildForkedMessagesexport function buildForkedMessages( directive: string, assistantMessage: AssistantMessage,): MessageType[] { // 1. 克隆 assistant 消息(所有 tool_use、thinking、text block) const fullAssistantMessage: AssistantMessage = { ...assistantMessage, uuid: randomUUID(), message: { ...assistantMessage.message, content: [...assistantMessage.message.content] }, }
// 2. 收集所有 tool_use block const toolUseBlocks = assistantMessage.message.content.filter( (block): block is BetaToolUseBlock => block.type === 'tool_use', )
// 3. 为每个 tool_use 构建相同的占位符结果 const toolResultBlocks = toolUseBlocks.map(block => ({ type: 'tool_result' as const, tool_use_id: block.id, content: [{ type: 'text' as const, text: FORK_PLACEHOLDER_RESULT }], }))
// 4. 将每个子进程的专属指令作为唯一变化的文本追加 const toolResultMessage = createUserMessage({ content: [ ...toolResultBlocks, // 所有 fork 完全相同 { type: 'text' as const, text: buildChildMessage(directive) }, // 每个子进程各不相同 ], })
return [fullAssistantMessage, toolResultMessage]}产生的消息结构:
[...history, assistant(all_tool_uses), user(placeholder_results..., directive)] ↑ 所有 fork 相同 ↑ 每个子进程各异由于 fork 子进程之间只有最后一个文本 block 不同,这一设计最大化了 cache 命中率。
Fork 子进程协议
Section titled “Fork 子进程协议”每个 fork 子进程通过 buildChildMessage() 接收严格的指令:
export function buildChildMessage(directive: string): string { return `<fork-boilerplate>STOP. READ THIS FIRST.
You are a forked worker process. You are NOT the main agent.
RULES (non-negotiable):1. Your system prompt says "default to forking." IGNORE IT — that's for the parent. You ARE the fork. Do NOT spawn sub-agents; execute directly.2. Do NOT converse, ask questions, or suggest next steps3. USE your tools directly: Bash, Read, Write, etc.4. If you modify files, commit your changes before reporting.5. Keep your report under 500 words.6. Your response MUST begin with "Scope:".</fork-boilerplate>
fork-directive: ${directive}`}输出格式为结构化纯文本:
Scope: <回显分配的范围>Result: <关键发现>Key files: <相关文件路径>Files changed: <列表及 commit hash>Issues: <如有问题则列出>fork 子进程在其 tool 池中保留了 Agent tool(以保证 tool 定义的 cache 一致性),因此必须在调用时阻断递归 fork:
export function isInForkChild(messages: MessageType[]): boolean { return messages.some(m => { if (m.type !== 'user') return false const content = m.message.content if (!Array.isArray(content)) return false return content.some( block => block.type === 'text' && block.text.includes('<fork-boilerplate>'), ) })}该保护机制扫描对话历史中的 <fork-boilerplate> 标签——如果存在,说明当前 agent 已经是一个 fork 子进程,不能再次 fork。
Fork 的 Worktree Isolation
Section titled “Fork 的 Worktree Isolation”当 fork 在隔离的 git worktree 中运行时,会向子进程的 context 注入一条特殊说明,告知其路径转换规则:
export function buildWorktreeNotice(parentCwd: string, worktreeCwd: string): string { return `You've inherited the conversation context above from a parent agentworking in ${parentCwd}. You are operating in an isolated git worktree at${worktreeCwd} — same repository, same relative file structure, separateworking copy. Paths in the inherited context refer to the parent's workingdirectory; translate them to your worktree root. Re-read files before editingif the parent may have modified them since they appear in the context.`}Fork 生命周期
Section titled “Fork 生命周期”sequenceDiagram
participant P as Parent Agent
participant F as Fork System
participant C1 as Fork Child 1
participant C2 as Fork Child 2
P->>F: Agent(prompt="directive1") [no subagent_type]
P->>F: Agent(prompt="directive2") [no subagent_type]
F->>F: buildForkedMessages(directive1, parentAssistantMsg)
F->>F: buildForkedMessages(directive2, parentAssistantMsg)
F->>C1: [history + identical placeholders + directive1]
F->>C2: [history + identical placeholders + directive2]
Note over C1,C2: Cache hit on shared prefix
C1-->>P: Scope: ... Result: ...
C2-->>P: Scope: ... Result: ...
两个子进程共享相同的 API cache 前缀——唯一的区别是最后的指令文本 block。当父级同时派生多个 fork 时(并行研究任务的常见场景),这一特性尤为有价值。
Fork 的触发条件
Section titled “Fork 的触发条件”以下所有条件均满足时,fork 路径才会激活:
FORK_SUBAGENTfeature flag 已启用- 非 coordinator 模式
- 非非交互式(SDK)session
- Agent tool 调用中省略了
subagent_type参数 - 当前 agent 不是已有的 fork 子进程
激活后,所有 agent 派生(包括指定 subagent_type 的派生)均在后台运行,采用统一的 <task-notification> 交互模型——fork 实验将后台执行作为其设计的组成部分。