前面幾天一直在講「分工」,Day 5 講三角色分開才不會自己驗自己,Day 15 講平行 session 的隔離,Day 17 講 hooks 把規則變成會執行的機制。但有一個問題我一直沒有正面寫過:這些分工在 Claude Code 裡到底是怎麼設定出來的?今天就寫 subagent 的機制本身,包含一個跟早期版本不一樣的地方:舊版文件寫過「subagent 不能再派 subagent」,現行官方文件寫的已經不是這樣了。
先說明這篇的資料來源。下面講的行為都來自官方文件(code.claude.com 的 sub-agents、agent-teams 與環境變數頁),我是把文件原文抓下來逐段核對,不是憑印象寫。我沒有拿自己的 repo 做巢狀行為的壓力實測,所以凡是文件沒寫、只是我推論的地方,我都會明講是推論。本機的版本是 2.1.287,這個版本在後面講到的預設深度規則之後,所以文中的預設值對我的環境成立。
subagent 就是一個放在 .claude/agents/(專案)或 ~/.claude/agents/(使用者)底下的 markdown 檔,上面是 YAML frontmatter,下面是這個 agent 的 system prompt。最小可用版本只需要兩個欄位:
---
name: code-reader
description: 唯讀地閱讀程式碼並回報結構,不修改任何檔案。
---
你是一個唯讀的程式碼閱讀者。只回報你實際讀到的內容,
每個結論附檔案與行號,找不到就說找不到。
官方文件明講只有 name 和 description 是必填。description 的角色很重要,主對話的 Claude 就是靠它判斷「這件事該不該委派給你」,所以它要寫成「什麼情況該用我」,而不是這個 agent 的自我介紹。
其他欄位都是選填,我把比較常動到的整理成一張表:
| 欄位 | 用途 |
|---|---|
tools |
這個 agent 能用哪些工具,省略就是繼承全部 |
disallowedTools |
從繼承或指定的清單裡再拿掉某些工具 |
model |
sonnet、opus、haiku、fable、完整 model ID,或 inherit |
permissionMode |
權限模式,例如 plan、acceptEdits |
maxTurns |
最多跑幾輪,到了會回傳並標示未完成 |
skills |
啟動時把指定 skill 的全文預載進來 |
mcpServers |
這個 agent 專屬的 MCP 連接 |
hooks |
綁在這個 agent 生命週期上的 hooks |
isolation |
設成 worktree 就在暫時的 git worktree 裡跑 |
effort |
low 到 max,覆蓋主 session 的力度 |
background |
設成 true 強制背景執行 |
這張表裡有兩件事我特別想提醒。第一,欄位名稱是 camelCase,必須跟文件完全一樣,文件原話是「Claude Code ignores a field it doesn't recognize without reporting an error」。也就是你把 disallowedTools 打成 disallowed_tools,不會有任何錯誤訊息,這個限制就是默默沒生效。Day 16 講過把踩過的坑變成檢查,這種「拼錯不報錯」的設定就是典型要靠檢查才抓得到的東西。第二,effort 是定義檔的欄位,作用是覆蓋主 session 的力度,等於這個 agent 的出廠設定。至於派工呼叫本身有沒有對應的參數,我沒有查證,所以不在這裡下結論。
模型的選擇有明確的優先順序:每次呼叫時帶的 model 參數最優先,其次是定義檔的 model,再來是 CLAUDE_CODE_SUBAGENT_MODEL 環境變數,最後才是主對話的模型。這個順序決定了 Day 8 講的「模型分層」可以落在哪一層:我偏好把常駐 agent 的層級直接寫死在定義檔裡,臨時派工再用呼叫參數覆蓋。
定義檔放哪裡也有規則可循。專案的 .claude/agents/ 和使用者的 ~/.claude/agents/ 都會被掃描,而且會遞迴掃進子資料夾,身分只看 name,檔名不必一樣。同名的時候有優先序:專案的定義會蓋過使用者層的同名定義,而多層專案目錄之間,離目前工作目錄最近的那一份勝出。這個規則連內建的 Explore 也適用:你自己在專案裡放一個同名的 Explore,把 model 改成 haiku,預設情況下大範圍搜尋就會落在最便宜的一層(每次呼叫帶的 model 參數仍然優先),這正是 Day 8 講的資源分層的最小實作。還有一個小細節,改動 .claude/agents/ 底下既有的檔案,幾秒內就會熱重載生效,但如果是新建出來的 agents 目錄,或是用 --add-dir 加進來的目錄,就得重啟才會被看到。所以當你新建了目錄卻覺得「改了沒反應」,先確認是不是還沒重啟,再去檢查 YAML。
subagent 最核心的價值不是「多一個人幫忙」,而是「多一個乾淨的 context window」。文件對這點寫得很直接:每個 subagent 都從一個全新、隔離的 context 開始,看不到你的對話歷史,看不到主對話已經叫用過的 skill,也看不到主對話已經讀過的檔案。
那它到底拿到什麼?照文件列的順序是這樣:
~/.claude/CLAUDE.md 和 AGENTS.md),內建的 Explore 與 Plan 會跳過。skills 欄位預載的 skill 全文。它拿不到的有:主對話的歷史、output style、auto memory。文件還有一句很實用的提醒:如果有一條規則一定要讓它知道,例如「忽略 vendor/ 目錄」,就要把它重新寫進委派的 prompt 裡。這跟我在 Day 9 講的「規劃收斂完最容易鬆掉的是動手那一刻」是同一個道理:你以為它知道,其實它只知道你寫給它的那一頁。
回傳的方向也一樣窄。subagent 做完之後,主對話只會收到它最後那則訊息的摘要,過程中讀了哪些檔、跑了哪些指令,都留在它自己的 context 裡。長輸出不會灌進主對話,這是它能保護主對話 context 的原因,也是為什麼我在 Day 5 說審查要用 fresh context:審查者沒看過產出的過程,所以它沒辦法順著產出者的假設走。

現在進到這篇真正想講的部分。官方文件現行的原文是:
By default, a subagent can spawn subagents of its own, up to three layers below the main conversation.
意思是預設情況下,主對話派出去的 subagent,自己手上也有 Agent 工具,可以再派下一層,最多往下三層。到了深度上限的那一層,Claude Code 會把 Agent 工具從該層的 subagent 身上拿掉(fork 是例外,後面會講),所以最後一層的 subagent 只能自己把被委派的工作做完,再回傳一份摘要。
這個行為在版本間改過幾次,這是整篇最容易寫錯的地方,所以我把文件記載的版本史單獨列出來:
| 版本 | 預設行為 |
|---|---|
| v2.1.172 到 v2.1.216 | 預設可巢狀,最深五層,不可調整 |
| v2.1.217 到 v2.1.218 | 預設上限降為一層,等於預設不能巢狀 |
| v2.1.219 起 | 預設三層,可用環境變數調整 |
要調整深度,用 CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH。設成 1 就是完全關掉巢狀,設成 2 就是 subagent 可以再派第二層,但第二層不能再往下。官方 issue 裡有人回報過某個版本(v2.1.225)設成 1 仍多出一層的 off-by-one。文件只給了版本號,沒有給各版本的發布日期,所以我在這裡也只寫版本號,不編日期。

另外有幾個行為細節,對實際設計很有影響:
-p)與 Agent SDK 不一樣。 文件寫的是啟動它的 subagent 不會等,所以一個比發起者晚結束的巢狀背景 subagent,會直接回報給主對話。如果你的流程是用 -p 跑腳本,這個差異不只是順序不同,結果會繞過發起它的 subagent,直接回到主對話。Concurrent subagent limit reached,而且錯誤訊息會叫 Claude 不要重試。可以用 CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS 調整。巢狀加上並行,很容易比想像中更快碰到這個數字。Agent 工具,但呼叫會報錯。這一節是我今天最想讓人帶走的一點。tools 欄位省略的意思不是「沒有工具」,而是「繼承 subagent 可用的所有工具」。而 Agent 就在那個工具池裡。所以一個定義檔如果沒有寫 tools,在深度還允許的時候,它就預設有能力再派下一層。
我檢查了自己專案裡的四個 agent,分別負責研究、規劃、撰寫和審查。它們的 frontmatter 都只有 name、model、description 三個欄位,沒有一個寫了 tools。照上面的規則,這四個全部都能再派 subagent。
這件事對其中一個特別不對勁,就是審查那一個。它的職責是「以 fresh context 獨立檢查」,本質上是一個唯讀的審查者。但依照現在的設定,它不只可以寫檔,還可以自己再派出別的 agent 去幫它做事。審查者再往下派工,這會讓「誰看過什麼、誰驗了什麼」變得很難追,也就是 Day 5 想避免的那種責任模糊。我目前沒有任何證據顯示它真的這樣做過,這是一個「設定上允許,所以風險存在」的發現,不是「已經出事」的紀錄。
文件給了兩種擋法,說法是:想讓某個 subagent 在巢狀開啟的情況下仍不往下派,例如一個應該保持唯讀的審查者,就在它的 tools 清單裡省略 Agent,或是把 Agent 放進 disallowedTools。以審查者來說,最乾淨的寫法是直接用白名單:
---
name: reviewer
description: 以 fresh context 獨立檢查文件,只讀不寫,不往下派工。
model: opus
tools: Read, Grep, Glob
---
用白名單的好處是「以後新增的工具預設都不會進來」,比黑名單安全。不過白名單要依實際職責來列,唯讀不等於不能跑檢查,如果審查需要執行檢查指令,就得把 Bash 加進來,再用別的方式收窄。如果你想保留大部分工具、只擋掉派工能力,用黑名單也行:
---
name: writer
description: 依 brief 與已驗證資料撰寫初稿。
model: sonnet
disallowedTools: Agent
---
還有一個很容易誤解的語法。在主對話層(用 claude --agent 啟動的主執行緒)可以寫 Agent(worker, researcher),限定它只能派這兩種 agent。但官方文件明講:在 subagent 的定義檔裡,把 Agent 列進 tools,只代表「允許它再派」,括號裡的類型清單會被忽略。也就是說,你沒辦法在定義檔裡寫「這個 orchestrator 只能派 researcher」,它要嘛能派所有類型,要嘛不能派。要限制類型,可以用 permissions.deny 加上 Agent(類型名) 這類規則全域擋掉特定類型,或是靠它的 system prompt 去約束,但後者不是機制,是請求。

講完怎麼擋,也要講什麼時候我會刻意開著。我的判準是:這個 agent 的工作本身就是「拆解再收斂」,並且拆出來的子任務彼此獨立、各自需要乾淨的 context。例如一個負責「查證一篇文章所有引用」的 agent,它可以把每個來源分給下一層去核對,自己只負責把結果彙整成一份表。
---
name: citation-checker
description: 逐一核對一篇文章的所有外部引用,拆給下一層平行查證,彙整成核對表。
model: sonnet
tools: Read, Grep, Glob, Agent
maxTurns: 30
---
你負責核對引用。先列出文章中所有外部引用,
把每個來源交給下一層的 subagent 單獨查證,
自己不做查證。最後輸出核對表:引用、來源網址、
是否一致、查不到的標「未查到」。
這裡有三個刻意的設計。Agent 明確寫在 tools 裡,代表這是我有意開的,不是繼承來的。maxTurns 限制它自己的輪數,但它擋不住一輪之內並行派出很多個,真正限制展開量的是深度和並行數上限。而且它的 prompt 直接規定「自己不做查證」,讓最上層只做彙整,下一層才做事,這樣每一層的責任是清楚的。
整體的深度我會在環境層再壓一層。三層對我來說多半太深了,如果只需要一層拆解,我會在 settings.json 的 env 區塊把深度調成 2:
{
"env": {
"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "2"
}
}
設成 2 的意思是:主對話派出的 subagent 可以再派一層,但那一層不能再派。我的理由是推論而不是文件的結論:每個 subagent 都會自己發送請求,這些請求都算在同一份用量額度裡(這句是文件說的),所以巢狀越深,同一件事消耗的用量就可能越多(這句是我的推論,文件沒有專門的成本警語)。把深度壓低,是用機制去限制一個我無法在事前精確估算的成本。
反過來,下面這幾種情況我會直接關掉巢狀,或是根本不開 Agent:
這四點跟 Day 11 講除錯時一次只驗證一個假設,是同一個方向:不確定的時候,先縮小範圍,再慢慢放寬。
最後一個常被混淆的概念。agent teams 是另一個機制,官方標示為實驗功能,預設關閉,要設 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 才會啟用。兩者的差別,官方文件歸納得很清楚:
| subagent | agent teams | |
|---|---|---|
| 溝通方式 | 結果回報給呼叫者 | teammate 之間可以直接互傳訊息 |
| 協調方式 | 由主 agent 管理 | 靠訊息加共享的 task list |
| 適用情境 | 只在乎結果的聚焦任務 | 需要討論與協作的複雜工作 |
| 成本 | 較低,結果以摘要回到主 context | 較高,每個 teammate 是獨立的 Claude instance |
文件的結論是:要快速、聚焦、做完回報的工人,用 subagent;要成員互相分享發現、互相挑戰、自己協調,用 agent teams。另外,agent teams 有自己的限制:teammate 不能再生出 teammate,只有 lead 能管理團隊。這跟 subagent 的巢狀是兩回事,不要把兩者搞混。
有一個坑值得單獨提。啟用 agent teams 之後,當 Claude 替一個 subagent 取名字,它就會變成 teammate,而不是一般的 subagent,而且不需要你確認(fork 和呼叫時帶了 isolation 的情況除外)。文件的說法是「teams can form even when you didn't ask for one」。如果你的流程依賴的是 subagent 單向回報的行為,又不小心開了這個實驗旗標,行為會悄悄變掉。要避免就把該環境變數設成 0。
把今天的內容收斂成幾件可以立刻動手的事:
claude --version 確認自己的版本。預設三層是 v2.1.219 起的行為,版本比這舊,預設可能是五層或一層,v2.1.172 以前則根本不能巢狀。.claude/agents/*.md,逐一看有沒有寫 tools。沒寫的,就是能再派下一層的。tools 白名單,或加上 disallowedTools: Agent。Agent 寫進 tools,配 maxTurns,並用 CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH 把整體深度壓在你能接受的範圍。我越來越確定一件事:分工能不能成立,不取決於你想了多漂亮的角色設計,而是取決於那條「誰能做什麼、誰不能做什麼」的線,有沒有被寫成機制。Day 17 的 hooks 是在事件層把這條線寫死,今天的 tools 欄位是在角色層把這條線寫死。省略 tools 這件事讓我意識到,沒有被明確寫下來的邊界,在機制眼裡就是「全部允許」。