多代理:orchestrator 與 subagent
你已經在第 3 課用 Claude Agent SDK 跑出一個真正自主的 agent——它能呼叫工具、判斷下一步、在迴圈裡跑到任務完成。但當任務變大,你會撞到三道牆。
第一道:context 視窗爆炸。讓 agent 去研究五個競品,每個競品讀十幾份資料——才到第三個,視窗快滿,agent 開始「忘記」前面的發現。第二道:速度慢成線性。五個競品一個一個研究,等於五倍時間,用戶等到放棄。第三道:職責混亂。同一個 agent 既搜資料又分析又寫報告,system prompt 變成拼盤,什麼都做等於什麼都不精。
多 agent 架構就是為了解決這三個問題而存在的:拆開 context、並行執行、分工專業化。
這堂學什麼
- 何時需要多 agent:三個判斷標準——context 隔離、並行加速、專業分工
- orchestrator-worker 架構:orchestrator 的職責、subagent 的設計原則
- 傳話失真問題:資訊在 agent 間傳遞時為何失真,以及 fat prompt 對策
- 成本意識:多 agent 的 token 倍增邏輯,以及 Opus + Sonnet 混搭策略
- 實戰:用 Claude Agent SDK 搭出三 agent 研究分工系統
觀念一:何時需要多 agent
不是什麼任務都需要多 agent。Multi-agent 有固定代價:token 成本倍增、複雜度提高、出錯點增加。先搞清楚三個真正需要它的場景。

場景一:context 隔離
每個 LLM 呼叫都有 context 視窗上限(claude-sonnet-4-5 是 200K tokens)。聽起來很大,但一個讀大量文件的研究任務——讀 50 份 PDF、爬 30 個網頁、累積工具呼叫結果——很容易把視窗塞滿。
多 agent 的解法:讓每個 subagent 處理一個子任務,只有最終摘要回傳給 orchestrator,不是所有原始資料都塞進去。Anthropic 官方的多 agent 研究系統就是這樣設計的:subagent 讀幾十份文件,但 orchestrator 的 context 裡只有每個 subagent 的一段總結。
場景二:並行加速
如果任務能拆成獨立的子任務,並行跑比串行快得多。Anthropic 的實測結果:把原本線性執行改為並行派發 3–5 個 subagent,複雜研究任務的完成時間縮短了最多 90%。
前提是子任務之間沒有依賴——A 的結果不需要等 B 才能跑。如果 A 的輸出是 B 的輸入,那是串行,沒辦法並行。
場景三:專業分工
同一個 agent 又搜資料又分析又寫報告,system prompt 就會臃腫失焦。更好的做法:researcher 只管搜集整理、analyst 只管找模式和矛盾、writer 只管輸出漂亮格式。每個 subagent 的 prompt 更短更聚焦,品質通常更高。
觀念二:orchestrator-worker 架構
多 agent 系統的標準模式是 orchestrator-worker:一個統籌者負責規劃派發,多個執行者各自完成子任務。

Orchestrator 的職責:
- 接收高層任務,分析並拆成子任務
- 決定哪些子任務可以並行、哪些必須串行
- 為每個 subagent 準備清晰的任務 prompt(含所有它需要的上下文)
- 等待 subagent 回傳,驗證結果品質
- 彙整所有結果,處理衝突,輸出最終答案
Subagent 的職責:
- 專注在一個明確定義的子任務
- 使用受限的工具集(只拿它真正需要的工具)
- 輸出摘要性結果,不是原始資料的全部
Anthropic 官方的生產級研究系統印證了這個分工:claude-opus-4-5 當 orchestrator 負責規劃和彙整,claude-sonnet-4-5 當 subagent 負責具體的搜尋和閱讀。測評結果:這個組合比純用 Opus 單 agent 效能高 90.2%,而且成本更低。品質重要的地方用強模型,量大的地方用快又便宜的模型——這是最重要的成本控制策略。
在 Claude Agent SDK 裡,AgentDefinition 的 model 參數讓你為每個 subagent 獨立指定模型:
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
import asyncio
async def main():
async for msg in query(
prompt="研究台灣 AI 新創生態,重點:估值前三名公司、主要投資人、競爭格局",
options=ClaudeAgentOptions(
model="claude-opus-4-5", # orchestrator 用 Opus 做規劃
allowed_tools=["Agent"], # orchestrator 只負責派發,不自己搜尋
agents={
"researcher": AgentDefinition(
description="負責網路搜尋和資料蒐集。用來查詢公司資訊、新聞、財報。",
prompt="""你是一個資料蒐集專家。
針對指定主題搜尋相關事實和數據,整理成結構化摘要。
輸出格式:每個主題一段,包含來源說明。只要事實,不要分析。""",
tools=["WebSearch", "WebFetch"],
model="claude-sonnet-4-5", # subagent 用 Sonnet 省成本
),
"analyst": AgentDefinition(
description="負責分析和比較數據。用來找模式、矛盾、洞察。",
prompt="""你是一個商業分析師。
根據提供的資料找出規律、比較差異、提出洞察。
輸出格式:重點條列,每點附支持資料。""",
tools=["Read"], # analyst 只需要讀資料,不需要上網
model="claude-sonnet-4-5",
),
},
),
):
if hasattr(msg, "result"):
print(msg.result)
asyncio.run(main())
三個關鍵細節:
allowed_tools裡放"Agent"— 讓 orchestrator 呼叫 subagent 時不被權限擋住- 每個
AgentDefinition的description是告訴 orchestrator「什麼情況下用這個 subagent」,這個欄位寫清楚,自動委派才準確 tools裡只放它真正需要的工具——analyst不需要上網,給它 Read 就夠了,工具最小化是安全最佳實踐
觀念三:傳話失真——最容易被忽略的問題
多 agent 系統有一個隱藏的敵人:subagent 的 context 是全新的、隔離的。
官方文件說得很清楚:
「父 agent 和 subagent 之間的唯一通道,就是 Agent tool 的 prompt 字串。subagent 的 context 視窗從零開始,沒有父對話的任何歷史。」
這意味著:orchestrator 知道的事情,subagent 不會自動知道。如果你沒有在派發 prompt 裡明確寫出來,subagent 就是從零開始工作。

這就是傳話失真的根本原因——不是資訊在傳遞中扭曲,而是沒傳到:orchestrator 知道某個重要背景,但在派發任務時忘記告訴 subagent。
對策:寫 fat prompt,不要省字
壞的 orchestrator 派發 prompt:
幫我研究 TeraPower 這間公司。
好的 orchestrator 派發 prompt:
任務背景:使用者想分析台灣 AI 新創生態,重點是三家估值最高的公司。
你的子任務:研究 TeraPower 公司。
需要回傳的資訊:
1. 最新估值(如有)
2. 主要投資人和輪次
3. 核心產品/服務
4. 最近 6 個月的重大新聞(最多 3 則)
輸出格式:結構化 JSON,欄位名稱英文,值繁中。
注意:只要事實,不要推測;如果找不到某項資訊,填 null 並說明原因。
差別在哪?好的版本給了 subagent 任務背景(它為什麼要做這件事)、明確的輸出規格(orchestrator 才能可靠地解析結果)、邊界條件(找不到怎麼辦)。三樣都有,傳話失真的機率就大幅降低。
觀念四:成本意識不能省略
Anthropic 工程部落格寫得很直白:
多 agent 系統消耗的 token 大約是聊天的 15 倍。 單 agent 相比聊天大約是 4 倍。
換算成成本:如果一個 claude-sonnet-4-5 聊天問答花你 NT$1,一個 agent 跑同樣的任務可能要 NT$4,一個多 agent 系統可能要 NT$15 以上——而且是每次呼叫。

這不是要你別用多 agent,而是要在架構設計時主動思考兩件事:
一、任務的價值值得這個成本嗎? 生成一份賣 NT$500 的客製報告?值得。幫使用者找一篇文章?直接 WebSearch 就好,不需要三個 agent。
二、Orchestrator 用強模型、subagent 用弱模型。 Anthropic 的數據:Opus 4 orchestrator + Sonnet 4 subagent,比純 Opus 4 單 agent 效能高 90.2%,同時成本更低。Opus 做規劃和判斷,Sonnet 做大量的執行工作——分工讓預算花在刀口上。
另一個成本陷阱:不必要的串行。如果五個 subagent 都能並行跑,但你的 prompt 讓 orchestrator 一個一個等,token 總量不變,但等待時間乘以五。並行不省 token,但並行省時間,這兩件事要分開想清楚。
手把手實戰:三 agent 研究分工系統
我們來實作一個三 agent 的研究系統:給定一個主題,researcher 蒐集資料,critic 找盲點和爭議,最後 orchestrator 彙整出平衡的報告。

確認環境
確認第 3 課的環境已就緒:
pip show claude-agent-sdk # 確認已安裝,應看到版本號
# 確認 API 金鑰
echo $ANTHROPIC_API_KEY # 應看到 sk-ant-... 開頭的值
如果沒裝,回頭看第 3 課。這堂課從已有 SDK 的環境繼續。
定義三個角色的 system prompt
新建 research_agents.py,先定義每個 subagent 的 prompt:
# research_agents.py
RESEARCHER_PROMPT = """你是一個資料蒐集和整理專家。
工作原則:
- 針對指定主題,搜尋並整理相關事實、數據、現況
- 優先找一手資料(官方報告、新聞報導、研究機構)
- 每個重要主張都要附來源(網址或出版品名稱)
- 只回報事實,不做主觀評價
輸出格式:
## 主要發現
(條列,每點一個事實+來源)
## 關鍵數據
(數字、統計資料整理)
## 相關資源
(可信來源列表)"""
CRITIC_PROMPT = """你是一個批判性思考專家。
工作原則:
- 針對指定主題,搜尋反例、爭議、被忽略的角度
- 找出主流觀點的盲點或過度簡化的地方
- 平衡呈現:不是否定,而是補充另一面
- 每個反例或爭議點都要有依據
輸出格式:
## 主要爭議點
(條列,每點含反例或質疑+依據)
## 被低估的風險/問題
(補充說明)
## 不同立場的觀點
(對比呈現)"""
兩個 prompt 的設計原則:職責不重疊、輸出格式明確。Orchestrator 才能可靠地解析兩者的結果並彙整。
組裝 orchestrator 和 subagent
繼續在 research_agents.py 加入主要邏輯:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
async def run_research(topic: str) -> None:
"""
三 agent 研究系統:
- Orchestrator (claude-opus-4-5): 規劃、彙整
- Researcher (claude-sonnet-4-5): 蒐集資料
- Critic (claude-sonnet-4-5): 找反例和爭議
"""
print(f"\n=== 開始研究:{topic} ===\n")
async for msg in query(
prompt=f"""請針對以下主題進行深度研究,產出一份平衡的分析報告。
主題:{topic}
研究步驟:
1. 同時派發 researcher 和 critic 兩個 subagent 並行工作——不要等一個完成再叫另一個:
- researcher:蒐集主題的主要事實、數據、現況
- critic:找出主流觀點的盲點、爭議、被低估的風險
2. 等兩個 subagent 都完成後,彙整成報告
3. 報告結構:執行摘要 → 主要發現 → 爭議與不同觀點 → 結論
派發給每個 subagent 的 prompt 必須包含:完整的研究主題、你希望它聚焦的面向、輸出格式。
不要假設 subagent 知道背景,它們的 context 從零開始。
如果兩個 subagent 的資料有衝突,在報告中明確標注並說明你的判斷依據。""",
options=ClaudeAgentOptions(
model="claude-opus-4-5", # orchestrator 用 Opus 做規劃和彙整
allowed_tools=["Agent"], # 只允許呼叫 subagent,不讓 orchestrator 自己搜尋
agents={
"researcher": AgentDefinition(
description="資料蒐集專家。當需要搜尋事實、數據、現況時使用。",
prompt=RESEARCHER_PROMPT,
tools=["WebSearch", "WebFetch"],
model="claude-sonnet-4-5", # subagent 用 Sonnet 省成本
maxTurns=20, # 限制最大輪次,防止 subagent 無限循環
),
"critic": AgentDefinition(
description="批判性分析專家。當需要找反例、爭議、不同立場時使用。",
prompt=CRITIC_PROMPT,
tools=["WebSearch", "WebFetch"],
model="claude-sonnet-4-5",
maxTurns=15,
),
},
),
):
# 偵測 subagent 被呼叫的時機(方便 debug)
if hasattr(msg, "content") and msg.content:
for block in msg.content:
if hasattr(block, "name") and block.name in ("Agent", "Task"):
subagent_name = block.input.get("subagent_type", "unknown")
print(f"[派發 subagent: {subagent_name}]")
# 輸出最終結果
if hasattr(msg, "result"):
print(msg.result)
if __name__ == "__main__":
topic = "台灣電動車產業的現況與挑戰"
asyncio.run(run_research(topic))
幾個設計決策值得說明:
allowed_tools=["Agent"] — orchestrator 自己不搜尋,只派發。這讓架構更清晰:orchestrator 是指揮官,不是士兵。如果你讓 orchestrator 既搜尋又派發,職責就混亂了。
maxTurns=20 — 限制 subagent 最多跑幾輪。沒有設這個的話,迷路的 subagent 可能跑幾十輪才停止,token 費用失控。
工具偵測的 block.name in ("Agent", "Task") — SDK v2.1.63 之前工具叫 "Task",之後改成 "Agent"。寫兩個名字都檢查,兼容新舊版本。
執行並觀察
python research_agents.py
正常執行時,你會看到類似這樣的流程:
=== 開始研究:台灣電動車產業的現況與挑戰 ===
[派發 subagent: researcher]
[派發 subagent: critic]
... (兩個 subagent 各自執行)
# 執行摘要
台灣電動車產業正處於關鍵轉型期...
# 主要發現
- 台灣電動機車市場佔有率...
# 爭議與不同觀點
- 政策補貼退場時間表的爭議...
# 結論
...
兩個 [派發 subagent: ...] 訊息幾乎同時出現,代表並行成功。如果一個接一個出現,代表 orchestrator 沒有正確理解「並行」——這時候把 orchestrator prompt 裡的說明強調得更清楚,例如加上:「必須同時呼叫 researcher 和 critic,在任一個完成之前不要等待。」
加入基本的錯誤處理
生產環境裡,subagent 可能因為 API 限流或其他問題提前中止。SDK v2.1.199+ 的行為:
- 若 subagent 有產出文字:回傳部分輸出 + 說明「subagent 未完成」
- 若 subagent 什麼都沒產出:拋出錯誤
Agent terminated early due to an API error
加入基本的重試處理:
async def run_research_with_retry(topic: str, max_retries: int = 2) -> None:
for attempt in range(max_retries + 1):
try:
async for msg in query(
prompt=f"研究主題:{topic}\n請並行派發 researcher 和 critic,再彙整報告。",
options=ClaudeAgentOptions(
model="claude-opus-4-5",
allowed_tools=["Agent"],
agents={
"researcher": AgentDefinition(
description="資料蒐集專家",
prompt=RESEARCHER_PROMPT,
tools=["WebSearch", "WebFetch"],
model="claude-sonnet-4-5",
maxTurns=20,
),
"critic": AgentDefinition(
description="批判性分析專家",
prompt=CRITIC_PROMPT,
tools=["WebSearch", "WebFetch"],
model="claude-sonnet-4-5",
maxTurns=15,
),
},
),
):
if hasattr(msg, "result"):
print(msg.result)
return # 成功,結束
except Exception as e:
err = str(e)
if "Agent terminated early due to an API error" in err:
print(f"[第 {attempt + 1} 次] subagent 提前中止: {err}")
if attempt < max_retries:
print("等待 5 秒後重試...")
await asyncio.sleep(5)
else:
print("已達最大重試次數。")
raise
else:
raise # 其他錯誤直接往上拋
常見坑
坑 1:Agent 沒加進 allowed_tools,orchestrator 自己回答不委派
症狀:執行後看不到任何 [派發 subagent: ...],orchestrator 直接產出結果,完全沒有用到你定義的 subagent。
原因:allowed_tools 裡缺少 "Agent",SDK 把所有 Agent tool 呼叫視為需要手動批准。自動化腳本裡通常靜默拒絕,orchestrator 就改自己處理。
# 錯誤寫法:orchestrator 想呼叫 subagent 時會被拒絕
options=ClaudeAgentOptions(
allowed_tools=["WebSearch", "WebFetch"], # 忘記加 "Agent"
agents={...},
)
# 正確寫法
options=ClaudeAgentOptions(
allowed_tools=["Agent"], # orchestrator 不自己搜尋,只派發
agents={...},
)
坑 2:subagent 拿到的 prompt 太模糊,輸出品質差
症狀:orchestrator 派發任務後,subagent 回傳的結果偏離主題或過於空泛。Debug 方式:在 orchestrator prompt 裡加一句「在派發給 subagent 之前,先輸出你準備傳給它的完整 prompt」,然後看那段 prompt 到底寫了什麼。
根本原因:subagent 的 context 從零開始,它不知道外面發生了什麼事。Orchestrator prompt 沒有明確告訴它任務背景、聚焦面向、輸出格式、找不到資料怎麼辦——subagent 只能靠猜。
修正原則:每次派發給 subagent 的 prompt 必須包含四樣東西:任務背景(為什麼要做)、具體要找什麼、輸出格式、邊界條件(找不到怎麼處理)。
坑 3:subagent 巢狀過深,執行時間爆增
症狀:執行時間遠超預期,token 消耗爆炸,有時 subagent 靜默停止。
原因:SDK v2.1.172 之後,subagent 可以自己再召喚 subagent,最多五層。如果你沒有限制工具,orchestrator 呼叫 researcher,researcher 又自己召喚 sub-researcher……就產生無意間的多層巢狀。
解法:在 subagent 的 AgentDefinition 裡不要把 "Agent" 放進 tools,或者用 disallowedTools 明確禁止:
AgentDefinition(
description="資料蒐集專家",
prompt=RESEARCHER_PROMPT,
tools=["WebSearch", "WebFetch"], # 不含 "Agent" → 無法召喚 subagent
# 或者更明確:
# disallowedTools=["Agent"],
model="claude-sonnet-4-5",
)
坑 4:兩個 subagent 的結果有矛盾,orchestrator 兩個都塞進報告
症狀:researcher 說某數據是 A,critic 找到資料說是 B。最後報告把兩個都列出來,沒有說明矛盾,讀者一頭霧水。
這不是 bug,是 orchestrator prompt 設計遺漏。要明確告訴 orchestrator 衝突處理原則:
如果 researcher 和 critic 提供了相互矛盾的資訊:
1. 明確標注衝突:「對於 X,researcher 引用來源 A 說是○○;
critic 引用來源 B 說是●●,兩者矛盾。」
2. 如果能判斷哪個更可信(更新、更一手的來源),說明原因並採用
3. 如果無法判斷,兩者都呈現,標注「資料來源存在分歧,建議查閱原始資料」
坑 5:把多 agent 用在不需要的場景,成本白燒
症狀:跑完了,結果沒比單 agent 好,但 token 帳單多了好幾倍。
多 agent 的固定開銷是真實的:每多一個 subagent,就多一個完整的 LLM 對話,包括 system prompt、工具定義、所有工具呼叫的紀錄。如果任務可以由一個 agent 在 20 輪內完成,硬拆成三個 agent 反而增加協調成本、傳話失真風險、還有 token 費用。
判斷標準:如果任務無法清楚地拆成獨立的子任務,或子任務之間資訊耦合非常緊密(A 需要隨時知道 B 在做什麼),就用單 agent。多 agent 的價值在「獨立、可並行、可隔離 context」——三個條件至少要符合兩個才值得上多 agent。
作業
跑起來:執行
research_agents.py,用你自己感興趣的主題。觀察兩個 subagent 的呼叫順序——是幾乎同時出現還是一個接一個?如果是後者,調整 orchestrator prompt 讓它真正並行。加第三個 subagent:在 researcher 和 critic 之後,加一個
summarizer,職責是把前兩者的原始輸出濃縮成 300 字以內的執行摘要。注意這個 subagent 必須串行執行——orchestrator 要等 researcher 和 critic 都完成後,才把它們的輸出傳給 summarizer。思考一下你要怎麼在 orchestrator prompt 裡表達這個「先並行、再串行」的流程。成本紀錄:到 Anthropic 後台的 Usage 頁面,記錄今天這幾次執行花了多少 token。和第 3 課的單 agent 任務對比——多 agent 的 token 倍數大約是多少?跟 Anthropic 文件說的 15 倍相比,你的實測結果接近嗎?
下一課預告
你現在已經能架出一個多 agent 系統,讓它並行研究、彙整結果。但有一天 agent 會讓你失望:它走錯方向、工具呼叫出錯、輸出格式亂掉——而你完全不知道為什麼。
第 5 課「評估與除錯:agent 為什麼失敗」教你拆解 agent 的執行路徑:怎麼讀懂工具呼叫的 trace、怎麼設計評估指標、怎麼寫回歸測試讓你改完一個問題不會引發另一個。從「能跑」到「可以信任」,是這門課最重要的一跨。