同一支 agent,把工具包成 JSON schema 讓模型照著填,或者包成一支模型能 import 的 Python stub 讓它自己寫程式呼叫,兩條路徑消耗的 LLM inference 次數完全一樣。但在 14 個模型上,誰贏誰輸的分界線落在廠牌內部的世代交界——OpenAI 舊款全數落後、新款全數超前,Anthropic 全系列則沒有這條線。
別再填 JSON,讓模型寫程式
把工具暴露成 JSON schema 還是 typed Python stub,決定了 agent 每一次呼叫工具時模型要輸出什麼。Ishan Patel、Sahil Sen、Elias Lumer、Vamse Kumar Subbiah 四人在 2026 年 8 月 6 日發布的論文《The Bitter Lesson of Tool Calling》,把這個選擇放到 14 個語言模型上,用 BFCL v4 這個公開 benchmark 做系統性比較,而且是「橫跨兩個模型家族、20 個月發布間隔」的比較,不是單一世代的快照。論文自己把兩種介面講得很白:JSON tool calling 是「模型在每次函式呼叫時輸出一個結構化 JSON 物件」;programmatic tool calling(下面簡稱 PTC)則是把工具包成「typed Python stub,模型透過寫程式呼叫,執行與取得結果都在同一個 agent turn 內完成」。摘要給的頭條數字很乾脆:PTC 在 14 個模型裡有 11 個持平或超越 JSON 基準,GPT-5.6 家族改善達 10.6%,平行扇出下 13 之 14 個模型持平或超越,情境腐化下 JSON 基準平均退化 2.3%而 PTC 穩定。下面把這些數字拆成軸線,逐一核對。
先把量尺講清楚,比較才有意義。論文用的評測集是「BFCL v4 提供 309 筆具代表性的樣本,取自八個任務類別:simple_python、multiple、parallel、parallel_multiple、live_simple、live_multiple、live_parallel、live_parallel_multiple」。從類別命名合理推測,這八類大致對應單一函式呼叫、同一輪要在多個候選函式裡選對一個、同一輪要平行發出多個呼叫,以及字首帶 live 的、從真實流量抽樣而來的版本——不是只測「模型認不認得一個函式」這種最基本的情境,而是把串接、平行、多選一這幾種真實 agent 工作量都塞進同一套 309 筆的評測集裡,PTC 與 JSON 兩邊跑的是完全相同的 309 筆題目。論文列出的完整名單是:Anthropic 這邊 Claude Haiku 4.5、Claude Sonnet 4.5、Claude Sonnet 4.6、Claude Opus 4.8、Claude Sonnet 5;OpenAI 這邊 GPT-4o、GPT-4.1、GPT-5-nano、GPT-5、GPT-5.4-mini、GPT-5.4、GPT-5.6-Luna、GPT-5.6-Sol、GPT-5.6-Terra——湊起來正好 14 個模型,論文自己也沒有解釋為什麼只挑這兩個家族,沒有涵蓋其他供應商。
| 維度 | JSON tool calling | Programmatic tool calling |
|---|---|---|
| 主評測整體勝率 | 基準線 | 14 個模型中 11 個持平或超越基準 |
| 平行扇出勝率 | 基準線 | 14 個模型中 13 個持平或超越基準 |
| 串接消融最大單一模型變化 | Claude Sonnet 5 為 80.8% GPT-4.1 為 98.1% |
Claude Sonnet 5 拉到 96.2% GPT-4.1 崩到 40.4% |
| 情境腐化平均變化 | 平均退化 2.3% | 平均進步 5.5% |
| token 成本(串接消融) | 基準 1 倍 | 約 1.5 倍 input token,扇出數超過 N≈26 後反而更便宜 |
| 執行環境需求 | API 端輸出結構化物件,無額外執行環境 | 需要一個能跑模型寫出來的腳本的 shell subprocess,論文未著墨沙盒或隔離設計 |
呼叫介面本身:一段 JSON 物件,還是一支腳本
兩種介面的差別,論文寫得很具體。JSON tool calling 這邊,「模型接收一個包含函式 schema、格式化成 JSON 工具定義的 system prompt,以及一個呼叫工具端點的 API call;模型在每次函式呼叫時輸出一個結構化 JSON 物件」。PTC 這邊,工具「以 typed Python stub 的形式暴露,模型透過寫程式呼叫,執行與取得結果都在同一個 agent turn 內完成」,而且「agent loop 在一個原生 shell subprocess 裡執行這段腳本」。最容易被誤解的一點,是很多人以為改成程式化呼叫會多耗一輪對話——論文特別點出「subprocess 回傳之後不會再產生任何一次額外的 inference turn,一個 stop middleware 會攔截下一次模型呼叫並終止這個 agent loop」,兩種介面消耗的 LLM 呼叫次數因此完全相同,差別只在「模型這一次輸出的是一個物件,還是一段可以被直接執行的程式」。
PTC 要能被公平計分,還得靠兩個技術細節撐住。第一個細節在 stub 本身:「每一支 stub 函式捕捉自己的參數,並把參數以結構化 dict 的形式回傳」——stub 不會真的去打外部 API,只是把模型傳進來的參數原封不動包成一個可比對的資料結構,這樣 PTC 跟 JSON 兩邊在「呼叫是否正確」這件事上才能用同一把尺量,不會因為 PTC 那邊多了一層真實網路呼叫而混進額外的變因。第二個細節在計分那一端:「計分器從印出來的輸出裡擷取函式名稱與參數值」——PTC 那邊的正確性判斷,看的是這段腳本執行完、印到 stdout 的內容裡,函式名稱與參數對不對得上題目要的答案,程式碼本身寫得漂不漂亮跟計分無關。腳本因為 \n 逸出序列連跑都跑不完、印不出任何東西讓計分器去擷取——GPT-4.1 因此被判零分,跟「模型是不是懂這個工具」完全無關,單純是輸出格式踩到 subprocess 的地雷。
這個設計選擇聽起來抽象,拆成兩邊實際會長什麼樣,一比就清楚——拖動下面的分隔線,左邊是 JSON 版本的呼叫,右邊是 PTC 版本的呼叫,兩邊描述的是同一次「查詢天氣」動作。
拖曳分隔線比較兩種呼叫介面 · JSON 與 PTC 各一版
左:JSON tool calling,一次呼叫對應一個結構化物件
JSON 版本每次呼叫只回傳一個結構化物件,PTC 版本讓模型寫一段程式,一次呼叫多個 typed stub,兩者消耗的 LLM 呼叫次數完全相同。
主要準確率:11 之 14,但世代切得比廠牌準
把 11 之 14 這個總數拆開看,方向完全不是隨機分佈。Anthropic 的五支模型——「從 Claude Haiku 4.5 到 Claude Sonnet 5」——「全部持平或超越 JSON 基準,變化幅度落在 0.0 到 6.5 個百分點之間,而且這個型態橫跨所有模型世代都成立」。OpenAI 這邊分裂成兩群:「三支最新的 GPT-5.6 變體全部是正值,改善幅度介於 4.2% 到 10.6%之間」,其中「GPT-5.6-Sol 與 GPT-5.6-Terra 兩支各自達到 10.6%的絕對提升」;但「三支較舊的模型(GPT-4o、GPT-4.1、GPT-5.4-mini)反而全部低於基準,落後幅度在 19.7% 到 26.9%之間」。論文自己下的結論是「PTC 相對 JSON 的準確率差距,跟著模型世代走,不是跟著模型家族走」——這正是標題「bitter lesson」的具體所指:不是某個廠牌的架構選擇決定了誰適合 PTC,而是模型本身寫多行程式的可靠度到了哪個世代才夠格。
有一點容易被誤讀,得先說清楚:主評測(不是消融)裡,個別模型的差距其實沒有消融那麼戲劇化。論文給的一個例子是「Claude Sonnet 5:JSON 84.5 ± 4.0,PTC 86.1 ± 3.9」——兩邊差距只有 1.6 個百分點,信賴區間本身就將近 4 個百分點寬,重疊面積很大。真正把差距拉開、讓 GPT-4.1 崩到 40.4%、讓 Claude Sonnet 5 衝到 96.2% 的,是後面的串接與平行扇出消融,不是主評測本身。11 之 14 這個總數,量的是「方向對不對」,幅度多大要看是哪一種工作量。
把主評測的 11 之 14 拆開,三個群組的方向完全不同: GPT-5.6 家族三支最新模型 三支最新的 GPT-5.6 變體全部正值,改善幅度介於 4.2% 到 10.6%之間;其中 Sol 與 Terra 兩支各自達到 10.6%的絕對提升。 全部正值, Anthropic 全系列五支模型 從 Claude Haiku 4.5 到 Claude Sonnet 5,五支模型全部持平或超越 JSON 基準,變化幅度 0.0 到 6.5 個百分點,橫跨所有模型世代都成立。 沒有一支落後, 三支較舊的 OpenAI 模型 GPT-4o、GPT-4.1、GPT-5.4-mini 三支較舊的模型全部低於基準,落後幅度在 19.7% 到 26.9%之間。 卻全部低於基準——三條線加起來,正好就是論文說的「跟著世代走,不跟著家族走」。
資料來源:《The Bitter Lesson of Tool Calling》主評測結果段落。
串接與平行扇出:規模一放大,差距就現形
世代差距在單一呼叫上還不明顯,一旦把工作量換成多步驟串接,差距就被放大到肉眼可見的程度。串接消融(n=52,鏈長 2 到 20)裡,「Claude Sonnet 5 拿到最大的 PTC 增益(80.8%到96.2%),其次是 Claude Opus 4.8(80.8%到94.2%),六個模型維持在 5% 絕對值以內的近乎持平;GPT-4.1 是唯一的離群值,它 98.1% 的基準準確率在 PTC 下崩到 40.4%」。崩潰的原因是一個具體到近乎荒謬的機械故障:「這三個模型的失敗模式一致——它們在多行腳本裡輸出字面上的 \n 逸出序列而不是真正的換行,導致 subprocess 在任何需要超過一行腳本的題目上直接語法錯誤」。「跟 GPT-5 同期發布的 GPT-5-nano 沒有這個毛病,暗示這個修正是在 GPT-5.4-mini 與 GPT-5 之間的訓練資料裡補上的」——論文自己也用「suggesting」這個字,這是一個推測,不是確認。
平行扇出消融(n=32,扇出數 7 到 48)下,13 之 14 個模型持平或超越基準,「最大的 PTC 增益是 GPT-5,從 71.9% 拉到 96.9%」。針對 Claude Sonnet 5,論文另外用 N∈{60,70,72,75,100} 探出一個具體的崩潰點:「列舉準確率在 N≤70 時是 100%,N=72 時掉到 75%,N=100 時掉到 0%」;同一個扇出區間,「PTC 則在 N=72 與 N=100 兩個扇出數下都維持 100% 列舉準確率」——JSON 基準從滿分直接崩到掛零,PTC 完全沒有波動。但這不是 JSON tool calling 的通病:「這個不對稱現象沒有出現在 GPT-5.6-Sol 身上,它的基準列舉準確率一路撐到 N=100 都還是 100%,暗示這個結構性極限跟 Anthropic 模型序列化平行工具呼叫區塊的方式有關」。拖動下面圖表上方的切換,可以在「串接消融」跟「平行扇出」這兩組真實數字之間切換,看同一套機制在不同壓力條件下的漲跌幅度長什麼樣。
切換串接消融與平行扇出兩組真實數字 · 2 個視角
token 成本與情境腐化:便宜是有條件的
PTC 不是天生比較省——「在串接消融裡,programmatic tool calling 用掉的 input token 是 JSON tool calling 的 1.5 倍」,原因是每次呼叫都要把整份 typed stub 的原始碼塞進 system prompt,這是一筆固定 overhead。這筆 overhead 只有在扇出數夠大的時候才划算:「token 成本在 N≈26 交叉,低於這個門檻,programmatic tool calling 因為固定的 system-prompt overhead 反而比較貴」。工作量是單次呼叫或低扇出時,PTC 的固定成本沒有機會被攤平;只有工作量本身就是高扇出或長串接,PTC 的成本優勢才會浮現。這筆固定成本從哪裡來,論文講得很直接:「PTC 相對 JSON tool calling 帶有固定的 input token overhead:system prompt 把完整的指令樣板以散文形式嵌進去,而 JSON tool calling 則是透過 API 的 tools 參數傳遞函式 schema」。JSON 那邊的函式定義走的是 API 專門開的結構化欄位,天生比較精簡;PTC 這邊得把整份 typed stub 的原始碼當成一段文字塞進 system prompt,不管這次呼叫用不用得到,這份原始碼的 token 成本都得先付,扇出數夠大、後續呼叫次數夠多,才能把這筆先付的固定成本攤平。
情境腐化(context rot,每個條件 n=31,把大量無關內容灌進上下文再看準確率怎麼變)這組消融是摘要裡最反直覺的一段:「從過濾情境到灌爆情境,平均準確率變化是 JSON tool calling 絕對值 −2.3%,programmatic tool calling 則是 +5.5%」,而且拿來當額外對照的「一個以檔案系統為基礎的內部條件,在灌爆情境下平均退化了 32.0%」——JSON 基準的退化不是這組實驗裡最慘的一個。但論文自己踩了煞車:「消融的樣本數小(n=31 到 52),信賴區間很寬,個別模型結果應該當方向性訊號讀,不是定論」。評測本身還有一個盲點:「輸出檢查顯示,模型經常直接從參數化的世界知識給出正確的聚合答案(例如直接講出人口前三名的國家),而沒有真的執行列舉呼叫」——某些「PTC 表現完美」的分數,可能只是模型背答案背得準,不是工具呼叫機制真的更穩。所以這組數字能說的是 PTC 在灌爆情境下沒有退化,不能說 PTC 在情境腐化下全面勝出。
灌爆情境(flood)具體是怎麼做出來的,論文也講得很技術:「flood 條件把 decoy 函式 schema 注入到上下文裡,跟這筆題目真正相關的 schema 放在一起,讓總 schema 數量增加到 128 個;兩種條件分別是 filtered(只放這筆查詢真正會用到的函式)與 flood(總共 128 個 schema,包含真正相關的函式,加上從不相關領域抽出來的 corpus decoy)」。兩邊比較的是同一組真正有用的工具定義,差別只在灌爆版多塞了一堆這次用不到、來自完全不相關領域的工具描述,把上下文撐到 128 個 schema,貼近真實 agent 系統裡「工具箱裡塞了一堆這次用不到的工具」的常態。
除錯與執行風險:誰在收拾模型寫壞的程式碼
錯誤處理這一項,論文的規則很直白:「當 shell subprocess 拋出語法或執行錯誤時,這筆記錄計為零分,沒有任何記錄被跳過或排除在統計之外」。這個規則本身就是 GPT-4.1 那次崩潰能被看見的原因——如果計分器把失敗的記錄悄悄丟掉,98.1%崩到40.4%這種數字根本不會出現在報告裡。合理的推測是,PTC 的錯誤模式因此跟 JSON 不一樣:JSON 呼叫失敗通常是「欄位型別不對」或「函式名稱拼錯」這種局部、容易定位的錯誤;PTC 失敗則是一整段腳本的語法或執行錯誤,像 \n 逸出序列這種問題,診斷起來要先分清楚是模型輸出格式的問題,還是 subprocess 環境的問題,這兩種除錯路徑對維運團隊要求的技能未必相同。
論文完整讀過一輪,找不到一句話討論執行模型寫出來的程式碼需要什麼樣的沙盒或隔離機制——這是論文的比較框架本身沒有把這塊算進去。合理的推測是:一旦決定採用 PTC,把模型寫的腳本丟進真正能執行任意程式碼的 shell subprocess,就必須自己補上這塊——用什麼樣的容器或系統呼叫過濾把腳本關起來,誰能存取的檔案系統與網路要收多緊,都是論文的評測環境裡不需要處理、但實際部署一定要處理的成本。這塊工程投入不會出現在任何一張 benchmark 表格裡,卻是把 PTC 從論文搬進生產環境時,真正要多花的那一份工。
論文自己也劃了一條線,說清楚這套比較框架量不到什麼。它列出的第一項限制是:「BFCL v4 使用 echo-return stub,每個函式把參數原封不動地回傳,而不是真的去執行 API 呼叫」。這代表整篇論文量到的準確率,測的是「模型有沒有正確組出這次呼叫」;這次呼叫在真實世界裡會不會因為 API 逾時、限流,或回傳格式跟預期不同而失敗,這套框架測不到。合理的推測是:PTC 因為多了一層 shell subprocess 執行,一旦換成真的會打外部 API 的版本,subprocess 要處理的錯誤模式——網路逾時、API 端回傳非預期格式、部分呼叫成功部分失敗——只會比 echo-return stub 這種乾淨環境更複雜。論文的數字量的是這套機制本身的準確率上限,換到生產環境會直接拿到的數字是另一回事,這條界線在決定要不要換 PTC 的時候值得留在心裡。
論文列出的第三項限制也不能忽略:「近期一次稽核發現,BFCL v4 的 LLM-judge 評分模式存在 20% 的評分者與人類判斷不一致率」。BFCL v4 本身的計分機制——不分 PTC 或 JSON——就有五分之一的機率跟人類的判斷對不上。這條限制跟前面「echo-return stub 測不到真實 API 失敗」疊在一起看,結論是:這篇論文的數字是在一把本身就有雜訊的量尺上量出來的,世代切分、扇出優勢這類方向性的結論,比精確到小數點的百分比更值得信任。
| 限制 | 論文怎麼說 | 對決定的意義 |
|---|---|---|
| echo-return stub | 函式把參數原封不動回傳,不會真的執行 API 呼叫 | 準確率量的是「呼叫組得對不對」,不是真實 API 逾時、限流下的失敗率 |
| 消融樣本數小 | n=31 到 52,信賴區間很寬 | 個別模型的具體百分比要當方向性訊號讀,不是定論 |
| 評分機制本身有雜訊 | 近期稽核發現 BFCL v4 的 LLM-judge 評分模式有 20% 評分者與人類判斷不一致 | PTC 與 JSON 兩邊的數字都建立在同一把有雜訊的量尺上 |
| 固定 token overhead | system prompt 把指令樣板以散文嵌入,JSON 透過 API 的 tools 參數傳遞 | 這是 N≈26 這個成本交叉點存在的機制原因 |
怎麼選
把六個維度收攏起來,論文自己的定調是「PTC 是可行且穩健的替代方案,但表現跟著模型世代的能力走」——不是「PTC 全面優於 JSON」。對照今天要做選擇的工程師,這句話可以翻成幾條具體的條件:如果用的是能穩定寫出多行 Python、不會把逸出字元寫錯的前沿模型(論文給的分界線大約落在 GPT-5 這個世代,GPT-5-nano 已經沒有這個毛病,GPT-5.4-mini 還有),而且工作量本身就是長串接或高扇出(扇出數超過 N≈26 才會比較便宜),PTC 值得換——準確率上限更高,情境腐化下也更穩。如果模型還在舊世代,或者工作量就是零星的單次呼叫,JSON tool calling 依然是更保守的預設:token 成本可預期,錯誤是局部的欄位錯誤,不需要多開一個能執行任意程式碼的 shell subprocess。至於沙盒與程式碼隔離的工程成本,不管選哪一邊都得自己算,論文沒有替你算過。
把這幾條軸線套進自己的系統,具體要量的東西其實不多。先看模型世代:用的是 GPT-5 世代以後的 OpenAI 模型,或是 Anthropic 全系列裡的任何一支,論文的世代分界線對你有利;還卡在 GPT-4o、GPT-4.1 或 GPT-5.4-mini,先別急著換——那三支模型的失敗率是機械性的、可重現的 \n 逸出序列問題,合理的推測是換了很可能重演同一種語法錯誤。再量工作量形狀:算一下自己系統裡一次 agent turn 平均要平行呼叫幾個工具、平均鏈長是多少——平行呼叫數字長期低於論文給的 N≈26 這個交叉點,PTC 的 token 帳單會比 JSON 貴,除非你願意為了準確率上限多付這筆固定成本。最後看情境長度:如果 agent 的上下文常態性地被灌進大量不相關的工具定義,也就是論文說的 flood 情境,PTC 在這個條件下的表現方向比 JSON 穩,這點值得列入權衡,但不該當成唯一理由——這組消融的樣本數只有 31 到 52,論文自己都說是方向性訊號,不是定論。
怎麼選:模型夠新(GPT-5 世代以上或 Anthropic 全系列)、工作量是長串接或高扇出(N≈26 以上),選 programmatic tool calling;模型偏舊、工作量是零星單次呼叫,JSON tool calling 依然是更保守的預設——但不管選哪一邊,執行模型寫出來的程式碼所需要的沙盒設計,都得自己補,論文沒有替你補。