Claude Code 詳解:SubAgent 六種執行模式
整理版優先睇
Claude Code 嘅 SubAgent 六種執行模式,由同步到遠端,逐個拆解設計取捨
呢篇文章係深入分析 Claude Code 裡面子 Agent(SubAgent)嘅六種執行模式。背景係主 Agent 因為上下文窗口限制,遇到複雜多步任務時會自動啟動一個子 Agent,等佢喺乾淨嘅上下文入面專注執行。作者從源碼入手,由 AgentTool 嘅 call() 函數開始,逐步拆解每種模式嘅觸發條件、內部實現同限制。
六種模式包括:Sync(同步阻塞,快靚正)、Async(後台非阻塞,適合長時間任務)、Fork(繼承父級上下文,慳 tokens)、Coordinator(協調者模式,將任務拆解畀多個 Worker 並行)、Teammate(多 Agent 協作,互相通信)同 Remote(遠程執行,隔離環境)。文章最後對比各模式嘅差異,仲講咗子 Agent 嵌套嘅限制——預設情況下唔畀創建子 Agent,深度防止遞歸爆炸。
- 子 Agent 嘅核心起因係上下文窗口限制,主 Agent 上下文過長時容易「逃逸」,所以需要乾淨嘅子上下文專注執行。
- 六種模式各有適用場景:Sync 最快但阻塞,Async 唔阻塞適合背景任務,Fork 慳 tokens 但唔可以再 Fork,Coordinator 並行拆大 Project,Teammate 多 Agent 協作,Remote 隔離執行。
- Fork 模式最特別,佢復用父級上下文(包括 Prompt Cache),令子 Agent 唔使由頭建立上下文,但有限制:唔可以再 Fork。
- 預設情況下,子 Agent 唔可以再創建子 Agent(靜態過濾禁止 Agent 工具),只有內部用戶、Fork child 同 In-process teammate 等例外,而且有運行時 Guard 進一步限制。
- 實戰選擇:簡單快速任務用 Sync,耗時獨立任務用 Async,需要保留父級上下文用 Fork,大型項目用 Coordinator,多 Agent 協作用 Teammate,安全隔離用 Remote。
內容結構
// src/tools/AgentTool/prompt.tsWhen NOT to use the Agent tool:
- If you want to read a specific file path, use FileRead or Glob instead- If you are searching for a specific class definition like "class Foo", use Glob instead- If you are searching for code within 2-3 files, use FileRead instead
點解要有 SubAgent?
Claude Code 嘅主 Agent 會根據任務複雜度自動決定係直接回應定係啟動一個子 Agent。例如你問「呢個係咩模型」,佢直接答;但如果你話「幫我重構呢個模塊再跑 Test」,佢可能會背後開一個子 Agent 處理。呢個決策唔需要你手動觸發,全由 LLM 自主判斷。
核心原因係上下文窗口限制。當對話累積大量歷史,主 Agent 嘅上下文會變得好長,帶來兩個問題:tokens 成本上升,同埋 LLM 容易「逃逸」——忽略目標直接話完成。
子 Agent 以乾淨嘅上下文啟動,只關注分配嘅具體任務,執行完交返結果,可以更專注完成長時間任務。
六種執行模式
所有模式最終都行一個共享執行引擎 runAgent(),只係喺調用前後包裝唔同。以下逐一睇每種模式嘅分別。
- 1 Sync(同步模式):預設模式,當 subagent_type 存在且冇指定 background 時行呢條路。父 Agent 阻塞等子 Agent 做完,適合快速明確嘅任務。
- 2 Async(異步/後台模式):觸發條件係 run_in_background=true 或 agent 定義 background: true。父 Agent 唔等結果,立即繼續,適合長時間任務例如全量測試。
- 3 Fork 模式:省略 subagent_type 並啟用 FORK_SUBAGENT feature flag。佢複用父級上下文,透過 Prompt Cache 共享慳 tokens,但唔可以再 Fork。
- 4 Coordinator 模式:啟動時設 CLAUDE_CODE_COORDINATOR_MODE=1,主 Agent 變身協調者,拆解任務畀多個 Worker 並行做,適合複雜多步驟項目。
- 5 Teammate 模式:傳入 team_name 並啟用 ENABLE_AGENT_SWARMS,多 Agent 之間可以互相通信協作,例如一個負責實現、一個負責測試。
- 6 Remote 模式:agent 定義設定 isolation: 'remote',子 Agent 被發送到遠程機器執行,提供物理隔離,但僅限 Anthropic 內部使用。
共享執行引擎 runAgent()
runAgent() 係一個 AsyncGenerator,每產生一個消息就 yield 畀調用方,邊執行邊輸出。進入執行前有六個準備階段,包括生成系統提示詞、組合工具列表等。
核心係一個 Query Loop:調 LLM → LLM 返回消息(可能含 tool_use)→ 執行工具 → 結果反饋畀 LLM → 重複,直到 LLM 停止或達到最大循環次數。
成本優化方面:普通子 Agent 關閉 thinking(因為具體任務唔需要深度推理),但 Fork 例外,需要繼承父級 thinking 配置嚟保持 Prompt Cache 命中。
子 Agent 嵌套限制
子 Agent 可唔可以再創建子 Agent?答案係預設唔得。Claude Code 有兩層保護:靜態過濾同運行時 Guard。
靜態過濾:所有子 Agent 預設禁止池入麪包含 Agent 工具,佢哋根本睇唔到呢個工具,自然無法嵌套。
例外情況包括:Anthropic 內部用戶、Fork child(因為要繼承完整工具定義)、In-process Teammate(swarms 模式下代碼特判)。但即使有工具,運行時仲有 Guard 攔截,例如 Teammate 唔可以再開 Teammate,Fork child 唔可以再 Fork。
靜態過濾決定邊啲場景可以拎到 Agent 工具
- 普通子 Agent(外部用戶):❌ 被過濾,根本冇 Agent 工具。
- 子 Agent(Anthropic 內部):✅ 保留,因為禁止列表對 ant 用戶為空。
- Async/background 子 Agent:❌ 被過濾,行白名單模式。
- In-process Teammate:✅ 特殊放行。
- Fork child:✅ 保留,因為繼承父級完整工具定義(Cache 一致性)。
模式對比同總結
決定用邊種模式,要睇任務特性:是否阻塞、是否需要共享上下文、是否需要並行、是否需要多 Agent 協作。
總括嚟講,Sync 適合簡單快速任務,Async 適合耗時獨立任務,Fork 適合需要保持上下文但唔想佔主線程嘅場景,Coordinator 適合大型項目,Teammate 適合多 Agent 協作,Remote 適合需要隔離環境。
每一種模式都係取捨:Sync 快但阻塞,Fork 慳 tokens 但限制嵌套,Coordinator 並行度高但管理複雜,Teammate 協作能力強但需要 feature flag。
用緊Claude Code嗰陣,你可能會留意到,有啲任務佢會自己搞掂,但有啲任務佢就會「派個agent去做」。例如你問「呢個係咩模型」,佢就直接答你;但如果你話「幫我重構呢個module再跑通個測試」,佢就可能會開一個子智能體喺後台處理。
呢個決定唔需要用戶主動觸發。Claude Code嘅主agent手上有一個叫Agent嘅工具,佢嘅描述係:
Launch a new agent to handle complex, multi-step tasks autonomously.LLM會根據任務複雜程度自行決定要唔要呼叫呢個工具,喺源碼入面,都清楚話畀LLM知幾時不該用它:
// src/tools/AgentTool/prompt.ts
When NOT to use the Agent tool:
- If you want to read a specific file path, use FileRead or Glob instead
- If you are searching for a specific class definition like "class Foo", use Glob instead
- If you are searching for code within 2-3 files, use FileRead instead咁點解要引入子智能體,而唔係俾主agent自己做曬所有嘢?核心原因係上下文窗口嘅限制。當對話累積咗好多歷史訊息之後,主agent嘅上下文會變得好長,呢個會帶嚟兩個問題:第一係tokens成本上升,第二係LLM喺太長嘅上下文入面容易「逃逸」——即係忽略目標直接話完成。子智能體用乾淨嘅上下文啟動,淨係專注喺分配咗嘅具體任務,做完之後將結果交返嚟,咁就可以更專注咁完成長時間嘅任務。
下面由源碼入口AgentTool.tsx嘅call()函數開始,睇嚇子智能體由誕生到結束嘅完整鏈路。
入口:AgentTool.tsx — call()
先睇輸入,AgentTool接受啲咩參數?
// src/tools/AgentTool/AgentTool.tsx
inputSchema = z.object({
prompt: z.string(), // 給子 agent 的任務描述
subagent_type: z.string(), // agent 類型名稱(可選,fork 時省略)
run_in_background: z.boolean(), // 是否後台執行(可選)
})其中subagent_type係由一個註冊表入面揀一個角色。呢個註冊表喺啟動嗰陣由loadAgentsDir.ts由多個來源收集:代碼內置嘅agent(general-purpose、Explore、Plan、Verification等)、用戶自定義嘅~/.claude/agents/*.md、項目級嘅.claude/agents/*.md。每個agent定義包括:可以用咩工具、系統提示詞、最大循環次數、模型選擇。
call()攞到參數之後,核心工作係路由分發——根據參數同環境條件,決定行邊條執行路徑:

下面逐個詳細講每種執行模式嘅內部實現。
執行模式詳解
喺詳細講各模式之前,先睇佢哋共享嘅執行引擎——runAgent()。無論係 Sync、Async 定係 Fork,最終都係行呢個函數。
runAgent():共享執行引擎
runAgent() 係一個 AsyncGenerator——每產生一條訊息就 yield 畀調用方,邊執行邊輸出:
// src/tools/AgentTool/runAgent.ts
async function* runAgent({
agentDefinition, // Agent 配置(工具、提示詞、模型等)
promptMessages, // 初始消息(用戶的 prompt)
toolUseContext, // 工具執行上下文
isAsync, // 是否後台
useExactTools, // fork 路徑標記
maxTurns, // 最大循環次數
...
}): AsyncGenerator<StreamEvent>進入執行之前,先經過6個準備階段:

然後進入核心嘅 Query Loop:
// src/tools/AgentTool/runAgent.ts
for await (const msg of query(...)) {
yield msg // 流式輸出給調用方
recordSidechainTranscript() // 持久化到磁盤(崩潰可恢復)
}循環邏輯:call LLM → LLM 返返訊息(可能包含 tool_use)→ 執行工具 → 結果反饋俾 LLM → 重複,直至 LLM 停止或者達到 maxTurns。
成本優化點:普通子 agent 嘅 thinking 被關閉(thinkingConfig: { type: 'disabled' }),因為執行具體任務唔需要深度推理,可以慳 tokens。但 Fork 路徑例外——佢需要繼承父級嘅 thinking 配置嚟保證 Prompt Cache 命中。
理解了 runAgent() 之後,各模式嘅分別就清楚曬:佢哋只係喺呼叫 runAgent() 前後做咗唔同嘅包裝。
Sync(同步模式)
Sync 係默認模式——當 subagent_type 存在而且冇指定後台執行嗰陣就行呢條路。用戶話「幫我揾嚇呢個函數嘅定義」、「檢查呢個文件有冇語法錯誤」呢類明確、快速嘅任務嗰陣,LLM 通常會揀同步執行。
父 agent 呼叫 runAgent() 之後阻塞消費成個 generator,直到子 agent 行完。完成之後透過 finalizeAgentTool() 提取結果:
// src/tools/AgentTool/agentToolUtils.ts
function finalizeAgentTool(agentMessages, agentId, metadata): AgentToolResult {
// 取最後一條 assistant 消息的文本內容
const content = lastAssistantMessage.message.content.filter(_ => _.type === 'text')
// 統計 token 消耗和工具調用次數
const totalTokens = getTokenCountFromUsage(...)
const totalToolUseCount = countToolUses(agentMessages)
// 發送 cache 驅逐信號——子 agent 的 cache 鏈可以釋放了
logEvent('tengu_cache_eviction_hint', { scope: 'subagent_end' })
return { agentId, agentType, content, totalDurationMs, totalTokens, totalToolUseCount }
}返回的 AgentToolResult 包含子 agent 嘅文本輸出、耗時、token 消耗等資訊,父 agent 攞到之後繼續自己嘅邏輯。
Async(異步/後台模式)
觸發條件:run_in_background=true,或者 agent 定義入面 background: true,或者 Coordinator 模式。用戶話「幫我喺後台行一次全量測試」、「後台分析一下呢個日誌文件」嗰陣就行呢條路。
同 Sync 嘅分別在於外面包咗一層異步註冊:
// src/tools/AgentTool/AgentTool.tsx
registerAsyncAgent(agentId, ...) // 註冊到後台任務框架
// 立即返回 { status: 'async_launched', agentId }
// 子 agent 在後台獨立執行 runAgent()
// 完成後:
completeAgentTask(agentId)
enqueueAgentNotification(...) // 通知父級"我做完了"父 agent 唔等結果,即刻繼續工作。子 agent 完成之後透過通知機制通知。
Async 模式底下嘅隔離更徹底:
• isNonInteractiveSession = true(唔可以互動)• shouldAvoidPermissionPrompts = true(自動拒絕權限彈窗)• 上下文完全隔離(唔共享父級嘅 appState)
Fork 模式
觸發條件:省略 subagent_type + FORK_SUBAGENT feature flag 開咗。Claude Code 嘅 prompt 入面話畀 LLM 知:「當中間工具輸出唔值得保留喺你嘅上下文嘅時候,fork 自己」——例如開放式嘅調研問題、需要多步編輯嘅實現任務。
Fork 解決嘅問題:當父級上下文已經好長嗰陣,建立一個全新子 agent 需要由零開始構建上下文,浪費 tokens。Fork 令子 agent 重用父級已經有嘅上下文——類似 Unix 嘅 fork() 系統呼叫。
// src/tools/AgentTool/forkSubagent.ts
const FORK_AGENT = {
agentType: 'fork',
tools: ['*'], // 繼承所有工具
maxTurns: 200,
model: 'inherit', // 繼承父模型
getSystemPrompt: () => '', // 不用自己的——直接用父級的
}Fork 嘅核心優化係Prompt Cache 共享。Claude API 對請求前綴做咗緩存,前綴字節級相同就可以命中。Fork 點樣做到嘅:
1. useExactTools = true→ 工具定義同父級完全一致2. override.systemPrompt = parent.renderedSystemPrompt→ 系統 prompt 字節相同3. 所有歷史 tool_result替換成統一 placeholder → 消除內容差異4. thinking 配置繼承父級 → 保持請求結構一致
遞歸防護:
// src/tools/AgentTool/forkSubagent.ts:78-89
function isInForkChild(messages): boolean {
return messages.some(m => m.content.includes('<fork_boilerplate>'))
}每個 fork 子 agent 嘅初始訊息會注入一個 <fork_boilerplate> 標籤。再次 fork 嗰陣掃描歷史發現呢個標籤 → 拒絕。
限制條件:同 Coordinator 互斥,非互動模式唔支援。
Coordinator 模式
觸發條件:環境變數 CLAUDE_CODE_COORDINATOR_MODE=1。用戶用 --coordinator 啟動 Claude Code 嗰陣進入呢個模式。
本質上 Coordinator 係 Async 模式嘅特化應用——所有 Worker 都行 Async 路徑,但加咗專屬嘅系統提示詞同工具限制。啟用之後 Claude Code 唔再自己執行任務,而係拆解任務之後派俾多個 Worker 同時執行:
// src/coordinator/coordinatorMode.ts
export function isCoordinatorMode(): boolean {
if (feature('COORDINATOR_MODE')) {
return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
}
return false
}Coordinator 模式底下嘅行為變化:
• 所有子 agent 強制異步( AgentTool.tsx:567)——派完嘢即刻回覆用戶• 唔指定模型參數( AgentTool.tsx:252)——Worker 用默認模型• 有專屬系統提示詞,指示「你係協調者,將任務分配俾 Worker」 • Worker 只能用 Bash/Read/Edit 等有限工具,唔可以再創建 agent • 同 Fork 互斥
Coordinator 嘅系統提示詞入面定義咗完整嘅工作流程:
研究階段(Worker 並行調研)→ 綜合階段(Coordinator 自己理解)
→ 實施階段(Worker 執行修改)→ 驗證階段(Worker 測試)Worker 完成之後用 <task-notification> XML 格式匯報結果。Coordinator 消化結果之後向用戶匯報,或者透過 SendMessage 繼續指揮同一個 Worker。
Teammate 模式
觸發條件:傳入 team_name 參數 + ENABLE_AGENT_SWARMS feature flag 開咗。當 LLM 需要多個 agent 之間互相協作(例如一個負責實現、一個負責測試、互相通信反饋結果)嗰陣用。
Teammate 同普通子 agent 嘅分別:有名字、喺團隊花名冊入面註冊、可以透過 SendMessage 同其他 Teammate 互相通信。
兩種後端實現:
| Pane (tmux) | ||
| In-Process |
Pane 後端嘅啟動流程:
1. ensureSession() — 確保 tmux session 存在
2. buildInheritedCliFlags() — 繼承權限模式/模型/插件
3. buildInheritedEnvVars() — 繼承環境變量
4. createTeammatePaneInSwarmView() — 創建新 pane
5. sendCommandToPane() — 在 pane 中啓動 teammate權限繼承嘅安全設計:plan 模式下不繼承 bypassPermissions。就算父級跳過咗權限檢查,創建嘅 teammate 都唔會自動獲得呢個特權。
Teammate 名稱保證唯一——衝突嗰陣追加數字後綴(tester → tester-2)。
Remote 模式
觸發條件:agent 定義入面設定 isolation: 'remote',而且滿足遠程執行嘅前提條件(僅 Anthropic 內部可用)。
子 agent 透過 teleportToRemote() 被發送到遠程機器執行,任務、工具配置、權限一齊打包發出。執行完成之後結果回傳。
橫向對比
子Agent嵌套:子 Agent 可唔可以再創建子 Agent
子 agent 可唔可以再創建子 agent,本質取決於兩個條件:工具池入面有冇保留 Agent 工具 + 運行時 guard 有冇放行。
靜態過濾:邊個可以攞到 Agent 工具
Claude Code 設計咗分層嘅工具過濾機制。對於子 agent 嘅工具池:
// src/constants/tools.ts:41
const ALL_AGENT_DISALLOWED_TOOLS = new Set([
AGENT_TOOL_NAME, // Agent 工具本身——防遞歸
ASK_USER_QUESTION_TOOL_NAME, // 向用戶提問
TASK_STOP_TOOL_NAME, // 停止任務
// ...
])默認情況下,Agent 工具被放進所有子 agent 嘅禁止列表——子 agent 根本睇唔到呢個工具,自然冇得嵌套。
例外:
運行時 Guard:就算有工具都會攔截
通過靜態過濾之後,call() 入面仲有運行時檢查:
// src/tools/AgentTool/AgentTool.tsx
// teammate 不能再 spawn teammate(第 272 行)
if (isTeammate() && teamName && name) {
throw new Error('Teammates cannot spawn other teammates — the team roster is flat.');
}
// in-process teammate 不能創建 background agent(第 278 行)
if (isInProcessTeammate() && run_in_background === true) {
throw new Error('In-process teammates cannot spawn background agents.');
}
// fork child 不能再 fork(第 332-333 行)
if (querySource === 'agent:builtin:fork' || isInForkChild(messages)) {
throw new Error('Fork is not available inside a forked worker.');
}
設計邏輯入面,用靜態過濾管「廣度」,對大部分場景直接砍咗 Agent 工具,防止子Agent遞歸。運行時 guard 管「深度」,就算開咗子agent創建子agent,都會限制特定行為,防止遞歸爆炸。
在使用Claude Code時,你可能注意到,有些任務它會自己處理,而有些任務它會"派一個agent去做"。比如你問"這是什麼模型",它直接回答;但你說"幫我把這個模塊重構一下並跑通測試",它可能會啓動一個子智能體在後台處理。
這個決策不需要用戶顯式觸發。Claude Code的主agent手裏有一個名為Agent的工具,它的描述是:
Launch a new agent to handle complex, multi-step tasks autonomously.LLM根據任務複雜度自主決定是否調用這個工具,在源碼中,也明確告訴LLM什麼時候不該用它:
// src/tools/AgentTool/prompt.ts
When NOT to use the Agent tool:
- If you want to read a specific file path, use FileRead or Glob instead
- If you are searching for a specific class definition like "class Foo", use Glob instead
- If you are searching for code within 2-3 files, use FileRead instead那為什麼要引入子智能體,而不是讓主agent自己做完所有事?核心原因是上下文窗口的限制。當對話積累了大量歷史消息後,主agent的上下文會變得很長,這會帶來兩個問題:一是tokens成本升高,二是LLM在過長上下文中容易"逃逸"——忽略目標直接宣佈完成。子智能體以乾淨的上下文啓動,只關注被分配的具體任務,執行完把結果交回來,會能更專注的完成長時任務。
下面從源碼入口AgentTool.tsx的call()函數開始,來看子智能體從誕生到結束的完整鏈路。
入口:AgentTool.tsx — call()
先看輸入,AgentTool接受什麼參數?
// src/tools/AgentTool/AgentTool.tsx
inputSchema = z.object({
prompt: z.string(), // 給子 agent 的任務描述
subagent_type: z.string(), // agent 類型名稱(可選,fork 時省略)
run_in_background: z.boolean(), // 是否後台執行(可選)
})其中subagent_type是從一個註冊表裏選一個角色。這個註冊表在啓動時由loadAgentsDir.ts從多個來源收集:代碼內置的agent(general-purpose、Explore、Plan、Verification等)、用戶自定義的~/.claude/agents/*.md、項目級的.claude/agents/*.md。每個agent定義包含:能用什麼工具、系統提示詞、最大循環次數、模型選擇。
call()拿到參數後,核心工作是路由分發——根據參數和環境條件,決定走哪條執行路徑:

下面逐一展開每種執行模式的內部實現。
執行模式詳解
在展開各模式之前,先看它們共享的執行引擎——runAgent()。無論 Sync、Async 還是 Fork,最終都走這個函數。
runAgent():共享執行引擎
runAgent() 是一個 AsyncGenerator——每產生一條消息就 yield 給調用方,邊執行邊輸出:
// src/tools/AgentTool/runAgent.ts
async function* runAgent({
agentDefinition, // Agent 配置(工具、提示詞、模型等)
promptMessages, // 初始消息(用戶的 prompt)
toolUseContext, // 工具執行上下文
isAsync, // 是否後台
useExactTools, // fork 路徑標記
maxTurns, // 最大循環次數
...
}): AsyncGenerator<StreamEvent>進入執行前,先經過 6 個準備階段:

然後進入核心的 Query Loop:
// src/tools/AgentTool/runAgent.ts
for await (const msg of query(...)) {
yield msg // 流式輸出給調用方
recordSidechainTranscript() // 持久化到磁盤(崩潰可恢復)
}循環邏輯:調 LLM → LLM 返回消息(可能含 tool_use)→ 執行工具 → 結果反饋給 LLM → 重複,直到 LLM 停止或達到 maxTurns。
成本優化點:普通子 agent 的 thinking 被關閉(thinkingConfig: { type: 'disabled' }),因為執行具體任務不需要深度推理,省 tokens。但 Fork 路徑例外——它需要繼承父級的 thinking 配置來保證 Prompt Cache 命中。
理解了 runAgent() 後,各模式的區別就清楚了:它們只是在調用 runAgent() 前後做了不同的包裝。
Sync(同步模式)
Sync 是默認模式——當 subagent_type 存在且沒有指定後台執行時走這條路。用戶說"幫我搜一下這個函數的定義"、"檢查這個文件有沒有語法錯誤"這類明確、快速的任務時,LLM 通常選擇同步執行。
父 agent 調用 runAgent() 後阻塞消費整個 generator,直到子 agent 跑完。完成後通過 finalizeAgentTool() 提取結果:
// src/tools/AgentTool/agentToolUtils.ts
function finalizeAgentTool(agentMessages, agentId, metadata): AgentToolResult {
// 取最後一條 assistant 消息的文本內容
const content = lastAssistantMessage.message.content.filter(_ => _.type === 'text')
// 統計 token 消耗和工具調用次數
const totalTokens = getTokenCountFromUsage(...)
const totalToolUseCount = countToolUses(agentMessages)
// 發送 cache 驅逐信號——子 agent 的 cache 鏈可以釋放了
logEvent('tengu_cache_eviction_hint', { scope: 'subagent_end' })
return { agentId, agentType, content, totalDurationMs, totalTokens, totalToolUseCount }
}返回的 AgentToolResult 包含子 agent 的文本輸出、耗時、token 消耗等信息,父 agent 拿到後繼續自己的邏輯。
Async(異步/後台模式)
觸發條件:run_in_background=true,或 agent 定義中 background: true,或 Coordinator 模式。用戶說"幫我在後台跑一下全量測試"、"後台分析一下這個日誌文件"時走這條路。
和 Sync 的區別在於外面包了一層異步註冊:
// src/tools/AgentTool/AgentTool.tsx
registerAsyncAgent(agentId, ...) // 註冊到後台任務框架
// 立即返回 { status: 'async_launched', agentId }
// 子 agent 在後台獨立執行 runAgent()
// 完成後:
completeAgentTask(agentId)
enqueueAgentNotification(...) // 通知父級"我做完了"父 agent 不等待結果,立即繼續工作。子 agent 完成後通過通知機制告知。
Async 模式下的隔離更徹底:
• isNonInteractiveSession = true(不可交互)• shouldAvoidPermissionPrompts = true(自動拒絕權限彈窗)• 上下文完全隔離(不共享父級的 appState)
Fork 模式
觸發條件:省略 subagent_type + FORK_SUBAGENT feature flag 開啓。Claude Code 的 prompt 中告訴 LLM:"當中間工具輸出不值得保留在你的上下文中時,fork 自己"——比如開放式的調研問題、需要多步編輯的實現任務。
Fork 解決的問題:當父級上下文已經很長時,創建一個全新子 agent 需要從零構建上下文,浪費 tokens。Fork 讓子 agent 複用父級已有的上下文——類似 Unix 的 fork() 系統調用。
// src/tools/AgentTool/forkSubagent.ts
const FORK_AGENT = {
agentType: 'fork',
tools: ['*'], // 繼承所有工具
maxTurns: 200,
model: 'inherit', // 繼承父模型
getSystemPrompt: () => '', // 不用自己的——直接用父級的
}Fork 的核心優化是Prompt Cache 共享。Claude API 對請求前綴做了緩存,前綴字節級相同就能命中。Fork 怎麼做到的:
1. useExactTools = true→ 工具定義和父級完全一致2. override.systemPrompt = parent.renderedSystemPrompt→ 系統 prompt 字節相同3. 所有歷史 tool_result替換為統一 placeholder → 消除內容差異4. thinking 配置繼承父級 → 保持請求結構一致
遞歸防護:
// src/tools/AgentTool/forkSubagent.ts:78-89
function isInForkChild(messages): boolean {
return messages.some(m => m.content.includes('<fork_boilerplate>'))
}每個 fork 子 agent 的初始消息會注入一個 <fork_boilerplate> 標籤。再次 fork 時掃描歷史發現這個標籤 → 拒絕。
限制條件:與 Coordinator 互斥,非交互模式不支持。
Coordinator 模式
觸發條件:環境變量 CLAUDE_CODE_COORDINATOR_MODE=1。用戶以 --coordinator 啓動 Claude Code 時進入此模式。
本質上 Coordinator 是 Async 模式的特化應用——所有 Worker 都走 Async 路徑,但加了專屬的系統提示詞和工具限制。啓用後 Claude Code 不再自己執行任務,而是拆解任務後派給多個 Worker 並行執行:
// src/coordinator/coordinatorMode.ts
export function isCoordinatorMode(): boolean {
if (feature('COORDINATOR_MODE')) {
return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
}
return false
}Coordinator 模式下的行為變化:
• 所有子 agent 強制異步( AgentTool.tsx:567)——派完活立即回覆用戶• 不指定模型參數( AgentTool.tsx:252)——Worker 用默認模型• 有專屬系統提示詞,指示"你是協調者,把任務分配給 Worker" • Worker 只能用 Bash/Read/Edit 等有限工具,不能再創建 agent • 與 Fork 互斥
Coordinator 的系統提示詞裏定義了完整的工作流程:
研究階段(Worker 並行調研)→ 綜合階段(Coordinator 自己理解)
→ 實施階段(Worker 執行修改)→ 驗證階段(Worker 測試)Worker 完成後以 <task-notification> XML 格式回報結果。Coordinator 消化結果後向用戶彙報,或通過 SendMessage 繼續指揮同一個 Worker。
Teammate 模式
觸發條件:傳入 team_name 參數 + ENABLE_AGENT_SWARMS feature flag 開啓。當 LLM 需要多個 agent 之間互相協作(比如一個負責實現、一個負責測試、互相通信反饋結果)時使用。
Teammate 和普通子 agent 的區別:有名字、在團隊花名冊中註冊、可以通過 SendMessage 與其他 Teammate 互相通信。
兩種後端實現:
| Pane (tmux) | ||
| In-Process |
Pane 後端的啓動流程:
1. ensureSession() — 確保 tmux session 存在
2. buildInheritedCliFlags() — 繼承權限模式/模型/插件
3. buildInheritedEnvVars() — 繼承環境變量
4. createTeammatePaneInSwarmView() — 創建新 pane
5. sendCommandToPane() — 在 pane 中啓動 teammate權限繼承的安全設計:plan 模式下不繼承 bypassPermissions。即使父級跳過了權限檢查,創建的 teammate 也不會自動獲得這個特權。
Teammate 名稱保證唯一——衝突時追加數字後綴(tester → tester-2)。
Remote 模式
觸發條件:agent 定義中設置 isolation: 'remote',且滿足遠程執行的前提條件(僅 Anthropic 內部可用)。
子 agent 通過 teleportToRemote() 被髮送到遠程機器執行,任務、工具配置、權限一起打包發出。執行完成後結果回傳。
橫向對比
子Agent嵌套:子 Agent 能否再創建子 Agent
子 agent 能否再創建子 agent,本質取決於兩個條件:工具池裏是否保留了 Agent 工具 + 運行時 guard 是否放行。
靜態過濾:誰能拿到 Agent 工具
Claude Code 設計了分層的工具過濾機制。對於子 agent 的工具池:
// src/constants/tools.ts:41
const ALL_AGENT_DISALLOWED_TOOLS = new Set([
AGENT_TOOL_NAME, // Agent 工具本身——防遞歸
ASK_USER_QUESTION_TOOL_NAME, // 向用戶提問
TASK_STOP_TOOL_NAME, // 停止任務
// ...
])默認情況下,Agent 工具被放進所有子 agent 的禁止列表——子 agent 根本看不到這個工具,自然無法嵌套。
例外:
運行時 Guard:即使有工具也攔截
通過靜態過濾後,call() 裏還有運行時檢查:
// src/tools/AgentTool/AgentTool.tsx
// teammate 不能再 spawn teammate(第 272 行)
if (isTeammate() && teamName && name) {
throw new Error('Teammates cannot spawn other teammates — the team roster is flat.');
}
// in-process teammate 不能創建 background agent(第 278 行)
if (isInProcessTeammate() && run_in_background === true) {
throw new Error('In-process teammates cannot spawn background agents.');
}
// fork child 不能再 fork(第 332-333 行)
if (querySource === 'agent:builtin:fork' || isInForkChild(messages)) {
throw new Error('Fork is not available inside a forked worker.');
}
設計邏輯中,用靜態過濾管"廣度",對大部分場景直接砍掉 Agent 工具,防止子Agent遞歸。運行時 guard 管"深度",即使開了子agent創建子agent,也會限制特定行為,防止遞歸爆炸。