寫於 2026 年 8 月 20 至 21 日(補 8 月 19 日的排程),9 月才上線(部落格的發佈額度在 8 月中用完了,稿子積壓了三週)。文中對 pilotfish 的描述以 v1.3.10 的原始碼為準,你讀到時它可能已經改版。

一個多模型調度專案的價值,不在它幫你叫出幾個 subagent,在它擋掉幾次。

這句話聽起來像在唱反調,但 pilotfish 整份設計就是照這個方向長出來的。它是給 Claude Code 用的多模型調度政策,作者是台灣開發者(GitHub 帳號 Nanako0129),README、usage、research 都有 .zh-TW.md 版本,其中最值得讀的那份實地報告甚至只有繁中版。

先講一個容易誤會的地方。GitHub 把這個 repo 標成 Python,但它不是 Python 套件:沒有 pyproject.toml、沒有 setup.py、沒有任何可以 import 的模組。三個 .py 檔裡最大的那個是測試檔,測的還是「Markdown 政策文件的字串內容」。README 自己講得很白:It installs configuration, not a runtime service, and writes nothing into your projects.

它的交付物只有三樣:8 個 agent 定義的 Markdown、1 段要貼進 ~/.claude/CLAUDE.md 的政策文字、1 段 settings.json 片段。沒有 daemon,沒有 SDK,沒有任何外部 API 呼叫。

機制它一個都沒發明

Claude Code 早就給了你機制:Agent tool 有 model 參數、agent 定義檔可以綁 model、settings.json 可以設主模型。pilotfish 賣的是「什麼時候該用哪個機制」的那份規範,而且這份規範每個 session 自動載入。

公司給你一套會議室預約系統,跟「什麼案子該開會、誰該進來、誰有決定權」那份規範,不是同一件東西。後者才是讓開會不失控的原因。

從原始碼歸納,它跟原生功能的差別有四個。第一,模型選擇從「每次派工的臨場判斷」變成「一次性的角色契約」。Agent tool 的 model 是呼叫時才決定的,你每次都得自己想這個該用 haiku 還是 sonnet;pilotfish 改成 8 個具名角色,model 與 effort 寫死在 ~/.claude/agents/<role>.md 的 frontmatter,政策還明文要求呼叫既有角色時不要model

第二,補上 effort 這個維度。Agent tool 本身沒有 effort 參數,pilotfish 靠 agent 檔的 frontmatter 把它綁在角色上,於是同樣跑 Sonnet,mech-executor(low)跟 executor(medium)變成兩個不同成本、不同用途的角色。

第三,把「唯讀」從叮嚀變成強制。plan-verifiersecurity-reviewer 用 agent frontmatter 的 tools 白名單,讓 harness 直接排除寫入能力。安裝文件特別警告不要裝 prompt-only 的近似版本,原文是 plan-verifier and security-reviewer depend on enforced tool exclusion to preserve the pre-approval read-only boundary. 兩份唯讀角色檔裡都寫了同一句:pre-approval boundary enforced by capability, not prompt text

第四個最實際。Claude Code 內建的 Explore subagent 會繼承主 session 的模型,主 session 是 Opus,你最便宜的高量搜尋就被靜默升級成最貴的模型在燒。pilotfish 故意在 user level 放一個 name: Explore 的 agent 檔去蓋掉內建版本,把探索釘死在 Haiku。安裝手冊註明這是刻意的,不是衝突。

政策文字裡永遠不出現模型名

作者把這條標成 the single most important rule:政策說「把機械性工作派給 mech-executor」,不說「派給 Sonnet」。model 綁定只存在一個地方,就是每個 agent 檔的 frontmatter。

我 grep 驗證過這條有做到。政策範本全文 58 行,opussonnethaiku 一次都沒出現,只有八個角色名。

這條鐵律換來一條很乾淨的退化鏈:政策只認角色名,角色檔用 model alias,alias 由 Claude Code 追蹤到當前推薦版本,具體模型來來去去。作者拿一個真實事件當壓力測試:2026 年 6 月的 export-control 停用事件裡,用 alias 的帳號優雅降級,把完整 model ID 寫死的使用者直接吃到 404 硬錯誤。

他還把三種常被混為一談的失敗拆開,各配一個機制:主模型 overload 用 fallbackModel、模型被 deprecated 用 alias、你想換一個 frontier 取捨就明確下 /model。這三件事平常被統稱為「模型出問題」,但它們該動的層完全不同。

八個角色長這樣:

角色 model effort 用途
scout haiku low 唯讀偵查,回 file:line
Explore haiku low 廣域唯讀搜尋,刻意覆蓋內建版
plan-verifier opus medium 核准前挑戰 Plan,只回 READY 或結構化 REVISE
security-reviewer opus high 核准前的安全證據蒐集
mech-executor sonnet low 完全指定的機械性重複工作
executor sonnet medium 已核准、需要局部判斷的實作
verifier opus medium fresh-context 否證式驗證
security-executor opus high 已核准的安全敏感實作

兩種封鎖手法要分清楚。唯讀角色用 tools: 白名單(等於把 Bash、Write、Edit、Agent、Workflow 全排除),執行角色用 disallowedTools: 黑名單(保留 Bash 但拿掉 Agent 跟 Workflow)。八個角色全部禁止再往下派工,每一個都是 leaf。

security-executor 為什麼要獨立成一個 Opus 角色,理由我第一次看到:frontier 模型的 safety classifier 可能在任務中途拒絕做良性的防禦性安全工作,所以安全任務從一開始就 route 到 Opus,讓「被拒絕」這條路徑根本走不到,而不是等它發生再補救。

它花最多力氣在講不要派工

多數 orchestration 專案在推銷「多派工」。pilotfish 反過來,用一整組 benchmark 證明自己能抑制過度派工。README 那句 Risk, not file count, triggers independent review. 就是整套政策的軸。

幾條最有價值的規則。有界的 task-local 搜尋預設留在主 session,即使跨目錄也一樣,只要拆分會重複付出啟動與整合成本。單一未知 bug 全程留在主 session,根因調查、trace 除錯、patch 設計、第一個最小修復、live 驗證都不派工,政策原文是 Never build sequential scout→executor pipeline.

論證我很喜歡:把這條緊耦合的鏈中段丟給一個 fresh executor,executor 得重建 context 而 orchestrator 在乾等,然後 orchestrator 又得重建足夠 context 才能整合它的答案。兩邊都付重建成本。

大家都遇過 AI 把一個簡單 bug 拆成三個 subagent、然後每個都在重讀同一批檔案。這條規則就是在治這個。

還有兩條值得抄進自己的規則檔。read scope 暫時獨佔:agent 的讀取範圍在結果收回前歸它獨佔,主 session 不得讀同一範圍。以及 Broad initial request is not approval of unseen Plan.,意思是你一開始那句籠統的需求,不算是對還沒看過的 Plan 的核准。

觸發獨立審查的條件全是風險:使用者明確要求、安全與信任、破壞性或不可逆或外部異動、資料與 schema 與遷移、release、實質的跨元件驗收。檔案數量不觸發,對模型的擔心不觸發,例行文件或 UI 不觸發。

最好的東西是一份承認自己失敗的報告

作者寫了一份繁中實地報告,記錄兩個真實長 session(合計約 101 小時 wall-clock、61.8 小時活躍)的時間與 token 歸因。結論對任何在用 AI agent 寫程式的人都值得一讀:

「機器時間的大頭不在跑測試、不在等 agent,而在主模型自己『想+寫』——佔活躍時間的 58%~79%,吃掉全部 output tokens 的 92%。政策的每一個單獨決策都對,聚合起來卻讓 orchestrator 悄悄變成了 implementer。」

設計意圖是把量產工作路由到便宜的執行角色。實際分流比例 8%。

作者指出主因不是路由壞掉,而是量產工作根本沒被辨識成「可委派的形狀」。Session B 的主 session 自己執行了 1,356 次 Edit/Write,連續 24 小時維持每小時 40 到 110 次自我編輯:

「每一條 review finding 的修正,context 都已經熱在主 session 手上,『直接修比派工快』的判斷單看每一次都成立。但這類小修是逐條到達的,任何一次決策當下都不長得像 batch——於是沒有任何一條規則會觸發『同型小任務已經連續直接做了 N 次,該改派了』。26 小時下來,1,267 次直接編輯對 12 次派工。」

這段講的其實不只是 AI。任何一個人在被小事逐條打斷的時候,都會做出同樣的選擇,而且每一次都是對的。

驗證那邊有兩個並存的結論。verifier 的 REFUTED 率是 41% 到 42%,證明 fresh-context 驗證真的在抓問題、不是橡皮圖章,這個 gate 不能砍。但粒度太細:201 次 verifier 呼叫、共 14.1 agent-hours、2.35M frontier tokens,大多在重驗一個測試已經覆蓋的小修正。plan-verifier 更誇張,24 次呼叫對 2 個 Plan、71% 打回率,變成規劃迴圈的 churn。

這些觀察直接催生了後續版本的「兩次 REVISE 就停」與「五次修復上限」。五個發現都指回一條具體的政策修正,這種可追溯性在開源專案裡很少見。

有個重要限定要跟著抄下來:兩個 session 都跑在作者另一個專案上,Claude Code 當介面與 agent runtime,模型經 gateway 路由到 OpenAI GPT-5.6,角色 roster 是 pilotfish 八角色政策的鏡像。所以政策語意是 pilotfish 的、模型檔位是同構配置,但絕對數字不可外推到原生 Claude。作者自己在侷限那段講得很清楚。

幾個實際的坑

安裝方式是「one-prompt install」,意思不是 pip,也不是 curl | bash:你 clone 特定 tag、在該目錄啟動 Claude Code,貼一段 prompt 叫它自己讀安裝 runbook 並照做。runbook 第一行就寫明它的讀者是 AI。刻意要你 clone 固定 tag 而不是抓會變動的 raw URL,原文警告是 Do not bypass WebFetch prompt-injection protection to install from a mutable raw URL.

最關鍵的坑:CLAUDE_CODE_SUBAGENT_MODEL 一旦被設,會靜默覆蓋每個 agent 的 model frontmatter,整套分層設計直接失效。同型的還有 availableModels allowlist 缺項,角色會安靜地繼承主模型,不會報錯。安裝手冊要求把這兩件事標進 plan,但不得未經核准自行 unset。

另一個要有心理準備的:自動派工不保證會發生。README 原文 Higher-priority Claude Code instructions can suppress Agent dispatch, and user-level CLAUDE.md cannot override them. ~/.claude/CLAUDE.md 是 user-level memory,優先序低於 Claude Code 自己的 system prompt,比它優先的指令可以直接壓掉 Agent 派工。官方的解法是你自己在 prompt 裡明講,或者用它附的 skill 手打 /pilotfish。那個 skill 的 frontmatter 有 disable-model-invocation: true,刻意讓 Claude 不能自己觸發它。

成本那邊有兩個反直覺的點。驗證不省錢:兩個 verification 角色都跑 Opus,而且在 fresh session 重讀 context,主 session 是 Opus 時這是同層的品質邊界,不是成本節省。每個 agent 都付重建成本:Every agent starts a fresh context and pays reconstruction plus integration cost; dispatch only when the combined benefit is positive.

它也沒有 enforcement hook。除了 toolsdisallowedTools 這兩個真的由 harness 強制的能力邊界,整套是靠模型自願遵守的政策文字。作者把 spawn guard 與 stop guard 明列為刻意不做,理由是 policy-only 在加機器之前就已經運作得不錯。

我沒驗證的部分

這篇是逐檔讀 v1.3.10 的原始碼寫出來的。我沒有在任何機器上安裝或執行過 pilotfish,也沒跑它的 benchmark,文中所有「實測」都是作者記錄的實測。research 文件裡引用的第三方社群數字我也沒有回原始 URL 逐項核對。

作者對自己專案的誠實程度值得一提。有一次 benchmark 因為受測帳號上一份更高優先序的 operator contract 禁止了 Agent 呼叫,導致中性 prompt 直接改了 fixture,他把那次結果自己判為不合格證據,還把花掉的錢留在 results.json 裡。他也在設計文件裡直接寫明這個架構不是原創。這種寫法在 600 多 star 的專案裡不常見。

回到開頭那句。你如果只想從這個 repo 拿一樣東西走,不要拿那張角色表——那張表照你自己的工作習慣重畫一次會更好用。拿那份實地報告裡的機制:同型小任務逐條到達的時候,每一次「直接做比派工快」都是對的,而它們加起來會讓你變成自己不想當的那個角色。 這件事跟你用哪個模型無關,跟你有沒有一條「連續 N 次就改派」的規則有關。

相關連結