想讓 coding agent 接得上一大堆工具,最乾脆的做法是只給模型一個工具。

聽起來像在抬槓。現在的 coding agent 大多是另一個方向:接一個 MCP server,工具清單長一截;再接一個,又長一截。工具越多,能做的事越多,這個直覺聽起來理所當然。

WhipCode 是反著做的。它在 GitHub 上的 repo 叫 context-labs/whip,README 第一句把自己定位成「A coding agent built for work that doesn’t fit in one context window.」。2026-10-19 我用 gh api 看的時候,它有 1,095 顆星、Apache-2.0 授權、主要語言是 Go,repo 建立於 2026-08-20,到今天剛好 60 天。最新的正式版是 9 月 25 日發的 v1.0.0,之後只有 prerelease,最後一個是 9 月 27 日的 v1.0.1-alpha.18。

它的設計文件把話講得很死:

RLM is whip’s execution model, not an optional mode. Every root and child model sees rlm_exec; its Starlark or JavaScript cells call daemon-hosted modules. There is no direct-tool agent mode and no mode field on a session.

—— docs/rlm-runtime.md

注意最後一句。沒有「預設一個、進階使用者可以切回來」這種退路,連切換的欄位都沒有。

一個工具,裡面裝的是一間總機

docs/tools.md 寫得更直白:「The model-facing catalog has one entry」,表格裡只有一列 rlm_exec,用途是「Evaluate one bounded Starlark cell against daemon-hosted modules」。root 是這樣,子 agent 也是這樣。

那讀檔、跑 shell、開瀏覽器怎麼辦?全部變成模組。files 模組底下有 list、search、read、write、patch;shell.run 是 blocking 呼叫,上限 120 秒;另外還有 browser、computer、agents、messages 這些。模型要做事,就寫一小段 Starlark(或 JavaScript,跑在 QuickJS 上,預設是 Starlark)丟進 rlm_exec,在 cell 裡呼叫這些模組。

打個比方。傳統做法像每次開會前,把公司每個部門的分機表從頭念一遍,念完才開始討論。WhipCode 的做法是只給你一支總機號碼,要找誰,自己撥進去轉。分機表還在,只是不再每次念給你聽。

MCP 也一樣。文件寫「MCP discovery does not add tools to a model request」,你在設定裡接了幾個 MCP server,模型的工具清單照樣只有一列,那些操作全收在 mcp 模組底下。

作者列的設計目標有四條,第一條是「keep large context available without repeatedly placing it in the prompt」,第四條是「keep all side effects behind one daemon-owned policy boundary」。從這兩條看得出它在意什麼:大東西不用每回合塞進 prompt,所有副作用都過同一道關卡。至於他們有沒有拿多工具方案做過對照,我在文件裡找不到這一段,就不替作者補動機了。

數字長什麼樣

repo 裡有一份 .ai-docs/research/rlm-context-and-limits.md,檔頭寫「Inspected 2026-09-08 UTC」,是作者對一個 live root 做的 context 盤點。先講清楚條件:這份是 41 天前的研究文件,跑的是開發版 daemon v0.5.13-101-g04b4c062-permission-fix,不是 v1.0.0。那次接了 12 個 MCP server,加起來 780 個工具。

項目 大小 模型看得到嗎
System prompt 37,920 bytes Yes
Model-facing tool schema 712 bytes, one tool Yes
MCP host schemas 639,798 bytes, 780 tools No automatic insertion

表格下面作者自己加了一句「These rows overlap and must not be summed as independent prompt costs.」,所以別把三列加起來當成一回合的成本。這張表真正能說的只有一件事:780 個工具的 schema 有六十幾萬 bytes,它們沒有自動進 prompt;模型看得到的工具 schema 是 712 bytes。同一份文件記錄,子 agent 第一次 provider 回應的 input 是 10,295 tokens。

到這裡,開頭那句話看起來站得住。工具清單被請出了 prompt。

然後它在 cell 裡炸了

同一份研究文件,緊接著記了一次很難看的失敗。

父 agent 呼叫 agents.spawn 開子 agent 的時候,沒寫 capabilities 也沒寫 mcp_tools。文件說省略 capabilities 的子 agent 會繼承父的,於是子 agent 繼承了全部 780 個 MCP selector。spawn 的回傳值裡有一個 effective_mcp_tools,就是那整份清單。父 agent 在 cell 裡把它 print 出來,超過了 cell 的 65,536-byte 輸出上限。

然後是最麻煩的那句:「The cell returned an error after the spawn effect had committed.」

子 agent 已經生出來了,cell 卻回報錯誤。父 agent 重試。重試途中先寫出兩個 Starlark 語法錯誤,接著撞上一個文件稱為「truthful duplicate-name error」的同名錯誤(同一個父 agent 底下,子 agent 名稱必須唯一,而第一次 spawn 其實已經生效),最後靠 agents.list() 才把狀況搞清楚。

工具清單請出 prompt 之後,它沒有消失,它變成了一個函式的回傳值。回傳值大到超過輸出上限,錯誤就從「context 太長」變成「程式的副作用已經生效、但呼叫端拿到錯誤」,這是寫分散式系統的人最怕的那種失敗:你不知道剛剛那一下到底算不算數。模型這時候得像工程師一樣去查狀態,而它順手又寫錯了兩次語法。

一個工具的代價,是把工具問題翻譯成程式問題。程式問題有程式問題的爛法。

作者自己承認:第一版不 scale

真正讓我覺得這份 repo 值得寫的,是另一份文件。.ai-docs/plans/mcp-discovery/PLAN.md 標著「Status: implemented 2026-09-16」,Why 段第一段是這樣寫的:

The model discovers MCP tools by pulling a server’s whole catalog into a cell and narrowing it with code it writes on the spot. That was the right first shape and it does not scale to the servers actually configured.

他們列了 2026-09-13 那次 acceptance run 的規模:Catalyst 427 個工具、ahrefs 135、chrome-devtools 29、firecrawl 26、exa 3。

第一版的流程是:mcp.list_tools(server) 回傳每個工具和完整 input schema。文件說一個 cell 能 print 64 KB,所以「a naive print truncates and the model learns nothing about the tools that did not fit」。daemon 端什麼都不排序、不過濾,模型只能自己猜一個子字串,在 Starlark 或 JavaScript 裡跑迴圈比對描述,把留下來的印出來,而且「pays those tokens on every turn that needs MCP」。

這是一條很完整的死路。工具清單從 prompt 拿掉了,模型改成每次需要 MCP 時自己去翻整本目錄,翻不完的部分還被截斷。省下的 prompt 空間,又在 cell 輸出裡一點一點付回去。

他們最後的解法,文件寫得毫不扭捏:

「This is the pattern Claude Code solves with deferred tools and ToolSearch: the catalog stays out of context until a name is known; a keyword search returns candidates; selecting one loads its schema.」

於是有了 mcp.search 先找候選、mcp.describe 再拿單一工具的 schema。我 6 月寫過 Claude Code 的 MCP Tool Search。放在一起看,一邊是多工具 agent 為了不塞爆 context 而延遲載入,一邊是單工具 agent 為了不撐爆 cell 而延遲載入,繞了不同的路,停在同一個地方。

同一份計畫的 Decisions 第 1 點也值得抄下來:「No server names in the base prompt. The prompt is message 0; a change there invalidates the whole cached prefix.」連 server 名字都不放進 prompt,因為一動就毀掉整段 prompt cache。這個在乎快取前綴的程度,跟「只給一個工具」其實是同一種性格。

那開頭那句話還成立嗎

我還是站在開頭那一邊,但要把它講得更精確:只給一個工具,換到的是「工具清單什麼時候進場」的控制權,不是免費的 context。

多工具的 agent,清單在每一回合的 prompt 裡,你沒得選。WhipCode 把清單放到程式碼後面,模型要的時候才去拿;拿的方式從「整本印出來」演進到「先搜尋、再描述」。這個控制權是真的,而且所有副作用都得經過同一個 daemon,政策只要寫在一個地方。

代價也是真的。cell 有 65,536-byte 的輸出上限、每個 cell 有 30,000 ms 的 wall time、1,000,000 個 Starlark steps 的上限,這些邊界是把工具改成程式碼之後才多出來的。spawn 回傳值撐爆輸出上限那次,就是這些邊界咬人的樣子。

會讓我改變想法的條件是這樣:如果之後的文件或 issue 顯示,模型花在繞過 cell 上限、修自己寫壞的 Starlark 的回合,比它省下的工具 schema 還貴,那「只給一個工具」就只是把帳從一個帳戶轉到另一個帳戶。目前我手上沒有這種數據,作者的文件也沒有。

作者的文件還記了另一個同類型的失敗。docs/agent-loop.md 說舊的 compaction 規則永遠保留最新那一回合的完整內容,結果一回合裡塞了幾十次工具往返就永遠摺不起來,「one agent spent 40 minutes and 52 model calls making six tool calls of progress」,後來才加了兩道 guard。同一類問題換一層又冒出來;他們的做法是每冒出一次就寫下來。

作者對自己的圖表比誰都保留

README 裡有一張 benchmark 圖,WhipCode 那個點標著 66.7% 和 2.60 美元。光看圖,很容易就拿去跟其他 coding agent 比。

docs/benchmarks.md 把那張圖拆開來講:

項目 文件怎麼寫
66.7% 20 successes out of 30 planned tasks,one attempt per task:Terminal-Bench 17/21、code-repository 3/9
執行環境 WhipCode 用 Modal 上的 Frontier adaptation,走 Inference.net 的 Kimi K3 Fast route;對照的已發佈結果用的是 Kimi K3 through Fireworks on Runta
2.60 美元 has not been traced to a reproducible accounting definition or source run
報告記錄的成本 $132.945764 known aggregate cost,30 趟中 29 趟帳目完整,1 次 unknown-usage call

結論那句是作者自己下的:「not evidence of a matched cost advantage, statistical superiority, or an official WhipCode leaderboard rank. Do not infer general performance from this small task set.」兩邊環境也不同,文件寫「These are different environments, not a controlled paired comparison.」

所以 2.60 美元這個數字,連作者都說追不到來源,我不會拿它當任何結論的根據。30 題、每題跑一次,樣本也小到說明不了什麼。我反而因為這張表更相信這個 repo 的其他文件:願意把自己圖上的數字標成追不到來源的團隊,記錄失敗的那幾段大概也沒怎麼修飾。

我讀了什麼、沒做什麼

這篇的所有內容,來自我用 gh api 讀的 repo metadata、release 列表、README,以及 docs/ 和 .ai-docs/ 底下的文件。我只讀了文件,沒有實際裝起來跑過,也沒有 clone 下來對照程式碼,所以不知道實際用起來的手感。

幾個要注意的時間點:context 盤點和 spawn 那次失敗出自 9 月 8 日的研究文件,對應開發版 daemon;MCP 探索計畫標示 9 月 16 日實作完成,我沒有逐個 commit 驗證實作跟文件一致。.ai-docs 那批文件看起來有 AI 協作的成分,我也沒拿它們去跟 issue 或 PR 的討論交叉比對。

工具清單住過的三個地方

工具清單放在 prompt 裡,每一回合都付。放在程式碼後面整本印出來,付在 cell 的輸出上限,還會被截斷。放在搜尋後面,只有在知道名字的那一刻才付。WhipCode 一開始就沒走第一種,9 月中又從第二種改成第三種。

所以再看到哪個 agent 標榜接得上幾百個工具,我會先找它的文件裡有沒有寫:模型是在哪一刻、用多少 bytes 第一次認識這些工具。工具有幾個,反而是最不重要的那個數字。