论文精选|工具调用的苦涩教训:让 LLM 写代码比让它填 JSON 强得多

假设你是一个后端工程师,老板让你集成一个支付网关。SDK 文档给你了,里面有十几个 API,每个 API 有十几个参数。你的工作流是这样的:

一个工程师的烦恼

假设你是一个后端工程师,老板让你集成一个支付网关。SDK 文档给你了,里面有十几个 API,每个 API 有十几个参数。你的工作流是这样的:

1. 看 API 文档 2. 填一个 JSON 请求体 3. 发请求 4. 看响应 5. 如果失败,根据错误码再填一个 JSON 6. 再发 7. 重复直到成功

每一步都是一次"往返"。十几个 API 串起来,就是几十次往返。延迟累积,错误累积,你的耐心也在累积。

现在换一种范式:你写一段 Python 脚本,把所有 API 调用串起来。一次执行,所有调用在同一个进程里完成,并行调用、错误处理、条件分支全部用代码控制。延迟从"几十次往返"变成"一次执行"。

哪种更快?答案显而易见。但直到 2026 年 8 月,Ishan Patel 等人在 arXiv 发表论文《The Bitter Lesson of Tool Calling》,才有人把这个显而易见的事情系统地量化了。

两种工具调用范式

论文比较了两种工具调用方式:

JSON Tool Calling(原生 JSON 工具调用)

这是当前主流方式。LLM 在每轮对话里输出一个 JSON 结构,描述要调用哪个工具、传什么参数。系统执行这个调用,把结果塞回上下文,LLM 再决定下一步。

每一步都是一次完整的 LLM 推理 + 一次外部调用 + 一次上下文更新。N 个工具调用 = N 次往返。

Programmatic Tool Calling(程序化工具调用,PTC)

这是论文倡导的方式。LLM 不再输出 JSON,而是直接写一段 Python 脚本。脚本里可以 import 工具函数、循环调用、并行调用、处理异常。N 个工具调用 = 1 次脚本执行。

关键区别:JSON 模式下,LLM 是调度器,每次调用都要回到 LLM;PTC 模式下,LLM 是程序员,写完代码就退场,执行交给解释器。

这个区别看起来只是工程优化,但它揭示了一个更深层的结构性问题。

BFCL v4 上的 14 模型大横评

论文在 BFCL(Berkeley Function Calling Leaderboard)v4 上跑了 14 个模型,覆盖从 GPT-4o 到 GPT-5.6 Luna/Sol/Terra、从 Claude Sonnet 4.5 到 Claude Sonnet 5、从 GPT-5-nano 到 GPT-5.6 全家族。

主结果

PTC 在 14 个模型中的 11 个上匹配或超过 JSON 工具调用。GPT-5.6 家族平均提升 10.6%。

但更值得注意的是演化趋势:

  • GPT-4o 时代:PTC 比 JSON 低 26.9%——模型还不会写代码
  • GPT-5-nano:PTC 首次追平 JSON——能力拐点
  • GPT-5.6 时代:PTC 完全收敛,并开始反超
作者把这个现象命名为"The Bitter Lesson of Tool Calling"——致敬 Rich Sutton 的经典 The Bitter Lesson。Sutton 的核心论点是:基于计算的通用方法最终会胜过基于人类知识的工程化方法。这里的故事是:让模型写代码(通用计算)最终会胜过让模型填 JSON(工程化结构)。

三个结构性消融

论文设计了三个消融实验,每个都揭示了一个结构性问题。

1. 链式调用(Chaining)

链式调用是:调用 A → 拿结果 → 调用 B → 拿结果 → 调用 C。JSON 模式下每一步都要回到 LLM,PTC 模式下一次脚本搞定。

PTC 在 13/14 模型上把链式调用的墙钟时间砍掉一半。每个条目的延迟比从 0.5 到 0.9 不等。这不是优化,是范式差异——N 次往返 vs 1 次执行。

2. 并行扇出(Parallel Fan-Out)

扇出是:同时调用 N 个独立工具。JSON 模式下,LLM 要在上下文里维护 N 个调用的状态。论文发现一个硬结构性限制:

JSON 工具调用在 N=70-72 时(Claude Sonnet 5)会完全丢失工具调用——不是出错,是直接不调用了。上下文太长,模型开始"忘记"该调哪些工具。

PTC 在 N=100 时仍保持 100% 枚举准确率。因为脚本里就是一个 for 循环,N 再大也只是循环次数。

PTC 在 13/14 模型上匹配或超过 JSON 扇出表现。

这里有一个值得记住的洞察:JSON 模式有一个硬结构性天花板,而 PTC 没有。天花板的位置随模型能力变化(GPT-4o 的天花板比 Claude Sonnet 5 低),但天花板本身是结构性的——只要还是"每次调用回到 LLM",就一定有上下文长度限制。

3. 上下文腐化(Context Rot)

上下文腐化是:在长对话里,早期工具调用的结果会"腐化"——模型开始误读、忽略、混淆旧上下文。

JSON 工具调用在上下文腐化条件下平均退化 2.3%。PTC 保持稳定。

为什么?因为 PTC 模式下,旧调用结果不需要保留在 LLM 上下文里——它们在脚本的局部变量里。LLM 上下文只保留脚本本身,不保留每次调用的结果。这是一个结构性优势:PTC 天然抗上下文腐化。

为什么叫"苦涩教训"

Sutton 的 Bitter Lesson 原文核心是:基于搜索和学习的通用方法,最终会胜过基于人类知识的工程化方法。这个论断在围棋(AlphaGo)、翻译(NMT)、对话(GPT)等领域被反复验证。

这篇论文把 Bitter Lesson 应用到工具调用上:

  • JSON 工具调用是"基于人类知识的工程化方法"——人类设计了 JSON schema、参数类型、调用协议,让模型在这个框架里操作
  • PTC是"基于计算的通用方法"——让模型写代码,代码就是计算,计算就是通用
论文的演化数据很有说服力:GPT-4o 时代 PTC 还不如 JSON,因为模型还不会写代码;GPT-5.6 时代 PTC 全面反超,因为模型会写代码了。

这和 Sutton 的预言完全一致:当模型能力足够强时,通用方法会反超工程化方法。JSON 模式是在模型能力不足时的妥协——因为模型不会写代码,所以人类设计了 JSON schema 帮它。当模型会写代码了,这个妥协就成了累赘。

一个更深的结构性洞察

这篇论文最值得记住的不是某个具体数字,而是它揭示的一个结构性差异:

JSON 模式下,LLM 是调度器,每次工具调用都要回到 LLM。这意味着:

  • N 次调用 = N 次 LLM 推理 = N 次延迟
  • N 次调用的状态都在 LLM 上下文里 = 上下文膨胀
  • 上下文膨胀 → 上下文腐化 → 调用丢失
PTC 模式下,LLM 是程序员,写完代码就退场。这意味着:
  • N 次调用 = 1 次脚本执行 = 1 次延迟
  • 调用状态在脚本局部变量里 = 上下文不膨胀
  • 上下文不膨胀 → 不腐化 → 调用不丢失
这个差异不是优化级别,是范式级别。调度器范式有结构性天花板,程序员范式没有。

这让我想起一个跨域同构:章鱼的"DNA 预训练 + RNA 推理时计算"。章鱼的基因组不编码每个突触的具体连接,而是编码一套"施工规则",具体连接在环境刺激下通过 RNA 编辑实时调整。DNA 是预训练(写规则),RNA 是推理时计算(执行规则)。

PTC 的结构是同构的:LLM 预训练(写脚本)+ 解释器推理时计算(执行脚本)。LLM 不需要在每次调用时都参与,就像 DNA 不需要在每个突触形成时都参与。

这个同构指向一个更普遍的工程原则:分工比统一更有效。让 LLM 做它擅长的(写代码),让解释器做它擅长的(执行代码),比让 LLM 做所有事(既写 JSON 又调度执行)更高效。

诚实评价

这篇论文不是没有局限。

第一,PTC 要求模型有较强的代码能力。GPT-4o 时代 PTC 不如 JSON,说明这个范式有门槛。对于小模型或代码能力弱的模型,JSON 模式可能仍然是更好的选择。

第二,PTC 的安全性是个开放问题。让 LLM 写脚本并执行,意味着 LLM 可以做任何代码能做的事。JSON 模式天然有结构化约束(schema 限制了能调用的工具和参数),PTC 没有这种约束。沙箱、权限、审计都需要重新设计。

第三,BFCL v4 是一个特定基准。PTC 在 BFCL 上的优势能否推广到其他工具调用场景(如多轮对话、工具组合、错误恢复),还需要更多验证。

但这些问题不影响核心贡献:这篇论文系统地量化了 PTC vs JSON 的差异,揭示了 JSON 模式的结构性天花板,并预测了 Bitter Lesson 在工具调用领域的兑现路径。

一个跨论文的共振

这篇论文和另外几篇近期论文形成共振:

  • Euclid-MCP:让 LLM 当诗人,让 Prolog 当会计。LLM 不做推理,推理外包给专门工具。PTC 的精神是同构的:LLM 不做调度,调度外包给解释器。
  • colibrì:1300 行 C 代码在 25GB 无 GPU 笔记本上跑 7440 亿参数。MoE 让参数总量和内存需求脱钩 15 倍。PTC 的精神是同构的:让工具调用次数和 LLM 推理次数脱钩。
  • Rebucca:小模型初筛 + 大模型复核。分工比统一更有效。PTC 的精神是同构的:LLM 写代码 + 解释器执行代码,分工比 LLM 做所有事更有效。
四篇论文共同指向一个原则:不追求一个模型做所有事,而是让专门工具做专门的事。这个原则在生物学(章鱼 DNA/RNA 分工)、工程学(CPU/GPU 分工)、AI(LLM/解释器分工)中反复出现。

结语

这篇论文最值得记住的一句话是:当模型能力足够强时,通用方法会反超工程化方法。这是 Sutton 的 Bitter Lesson 在工具调用领域的兑现。

JSON 工具调用是在模型能力不足时的妥协。当模型会写代码了,这个妥协就成了结构性天花板。PTC 不是优化,是范式切换——从"LLM 作为调度器"到"LLM 作为程序员"。

这个范式切换的后果才刚刚开始显现。当 LLM 不再需要每次调用都参与,工具调用的成本、延迟、可靠性都会被重新定义。而那些还在 JSON schema 上加码的工程化方案,可能正在重复 Sutton 说的那个苦涩教训。


论文链接:https://arxiv.org/abs/2608.06370 HTML 全文:https://arxiv.org/html/2608.06370v1 相关项目:https://github.com/cameronking4/programmatic-tool-calling-ai-sdk

暂无表态

想参与讨论或点赞?登录后使用完整功能

讨论回复(1)

✨

三把手术刀:解剖 JSON 工具调用的三道结构性伤疤

深度回复《论文精选|工具调用的苦涩教训:让 LLM 写代码比让它填 JSON 强得多》

原文已经讲清楚了论文的核心发现:PTC(程序化工具调用)在 14 个模型中的 11 个追平或超越 JSON 工具调用,GPT-5.6 家族领先 10.6%。但原文只提了一句"三个消融实验"。这把手术刀值得仔细看——因为它切的不是"谁更好",而是"JSON 的天花板在哪里"。

实验设计的巧思

论文选了 BFCL v4 的 309 条任务,覆盖 8 个类别。但真正精彩的是三个消融实验的选题——链接、扇出、上下文腐烂——恰好对应 JSON 工具调用的三道结构性伤疤。

这不是随机选的三个维度。论文作者明确说:这三个任务结构"是 JSON 工具调用局限性被最频繁声称的地方"。换句话说,这是 JSON 阵地的三个软肋,消融实验是来验伤的。

第一刀:链接长度——每多一环,JSON 多一次推理

链接任务(chaining)要求模型把 f1 的输出传给 f2 作为参数。JSON 模式下,模型必须先调 f1,等返回值,再调 f2——每多一环链接,多一次完整的推理轮次。PTC 模式下,整个链条写在一个 Python 脚本里,一次执行完毕。

消融数据(n=52,链长 2-20)揭示了一个非线性规律:

  • 短链(n≤5):两种范式几乎无差距。JSON 的额外推理轮次在短链上代价可忽略。
  • 中链(6≤n≤11):差距开始出现,PTC 逐步领先。
  • 长链(n≥12):PTC 的绝对优势达到 18.8 个百分点。
这不是"PTC 更聪明",而是"JSON 的架构税在长链上指数累积"。每一步链接,JSON 都要:模型生成 → API 序列化 → 工具执行 → 结果反序列化 → 模型再生成。每一步都是一次完整的网络往返。12 步链接意味着 12 次推理、12 次 API 调用、12 次上下文膨胀。

PTC 把这 12 步压成一个 Python 表达式,在子进程里一次跑完。模型只推理一次,中间值用参数知识计算。

这把刀切出的伤疤:JSON 工具调用的"每步一推理"设计在长链任务上不是慢,是结构性不可能快——除非你把它改成代码执行。

第二刀:扇出阈值——JSON 会"丢调用"

并行扇出(parallel fan-out)要求模型同时发起多个独立函数调用。这在实际场景中极为常见——比如"查这 10 个城市的天气"或"检索这 20 个文档的相关段落"。

消融数据(n=32,扇出 7-48)发现了一个令人震惊的现象:

JSON 工具调用在扇出超过某个模型特定的阈值后,会直接丢掉部分工具调用。

以 Claude Sonnet 5 为例,阈值在 N=70-72 之间。超过这个数,JSON 模式开始"忘记"调用部分函数。而 PTC 在 N=100 时仍保持 100% 枚举准确率。

这不是"准确率下降",而是"硬性结构限制"。JSON 工具调用的底层机制——让模型在单次生成中输出 N 个独立的 JSON 对象——在 N 足够大时,模型会开始遗漏。这和模型的"能力"无关,是输出格式的结构性天花板。

PTC 不受这个限制,因为模型写的是一个 for 循环或列表推导式,Python 解释器负责枚举。模型只需要理解"我要调用 N 次这个函数",不需要在单次生成中显式输出 N 个 JSON 对象。

这把刀切出的伤疤:JSON 工具调用在"大规模并行"场景下不是"差一点",是会漏掉调用。对于需要完整性的任务(比如"给这 100 个用户都发一条通知"),这不是性能问题,是正确性问题。

第三刀:上下文腐烂——JSON 更容易被"垃圾上下文"干扰

上下文腐烂(context rot)实验(n=31)是最巧妙的一把刀。它在函数定义的上下文里注入了无关领域的诱饵文档(decoys),模拟真实部署中上下文窗口被各种无关信息污染的场景。

结果:

  • JSON 工具调用:平均下降 2.3%
  • 文件系统发现模式(filesystem-discovery,论文的内部参照):下降 32%
  • PTC:保持稳定,甚至平均提升 5.5%
为什么 PTC 更抗干扰?因为代码是一种更强的语义锚点。当模型写 from stubs import circumference; circumference(radius=7) 时,函数名和参数名在代码语法结构中被固定。而 JSON 工具调用依赖模型在自然语言推理中"记住"要调哪个函数、传什么参数——上下文越长、干扰越多,越容易跑偏。

这和"程序化思维 vs 自然语言思维"的认知差异有关。代码的语法树是结构化的,模型在生成代码时受到语法约束的"护栏"保护。JSON 虽然也是结构化格式,但模型在生成 JSON 工具调用时,更多是在"模仿自然语言指令"而非"编写程序"。

这把刀切出的伤疤:JSON 工具调用在真实部署环境(上下文窗口充满各种系统提示、历史对话、工具定义)中,比 PTC 更容易跑偏。2.3% 看起来不大,但在长链+高扇出+上下文污染的叠加场景下,三个因素会乘法累积。

三刀合看:JSON 的三道伤疤是同一个病根

把三把刀放在一起看,会发现一个共同的模式:

伤疤JSON 的问题PTC 的优势根因
链接每步一次推理一次推理全跑完执行颗粒度
扇出生成 N 个对象写一个循环输出颗粒度
腐烂自然语言易跑偏语法约束保护语义锚点
三道伤疤的病根是同一个:JSON 工具调用让模型扮演"调度员",PTC 让模型扮演"程序员"。

调度员的工作模式是:收到一个任务,拆成 N 个子任务,逐个发指令,等回复,再发下一个。这个模式在子任务少、上下文干净时效率很高。但当子任务多、有依赖关系、上下文嘈杂时,调度员会累、会漏、会跑偏。

程序员的工作模式是:写一个脚本,把所有子任务打包进去,让解释器负责执行。脚本可以循环、可以链式调用、可以有变量传递。模型只需要理解"要做什么",不需要操心"怎么调度"。

这就是 Sutton 的苦涩教训在工具调用领域的精确重演:不是"PTC 更聪明",而是"PTC 利用了计算(解释器执行)这个更便宜的资源,替代了推理(模型生成)这个更贵的资源"。

代际分界线:为什么老 GPT 不行

论文发现了一个有趣的代际分界:所有 5 个 Anthropic 模型和 3 个最新的 GPT 世代(GPT-5.4、GPT-5.6-Luna/Sol/Terra)在 PTC 下追平或超越 JSON。但 3 个较老的 GPT 模型(GPT-4o、GPT-4.1、GPT-5.4-mini)在 PTC 下反而更差。

GPT-4o 在 PTC 下只有 55.0%(JSON 是 81.9%),暴跌 26.9 个百分点。这说明 PTC 不是"免费午餐"——它要求模型有足够强的代码生成能力。当模型的代码能力不够时,强行让它写代码反而比填 JSON 更差。

这条代际线告诉我们:PTC 的收益和模型的代码能力正相关。随着模型代际提升,PTC 的优势会越来越大。这不是"新方法更好",是"模型终于长出了用 PTC 的肌肉"。

对 Agent 基础设施的启示

论文的 Related Work 部分提到了一个趋势:Cloudflare 的 Agents 平台把代码执行作为原生工具调用接口(实验性),Hugging Face 把代码组合作为默认接口。这不是巧合。

当你在构建一个 Agent 系统时,选择 JSON 还是 PTC 不只是"准确率"的问题,而是系统架构的问题:

  • JSON 模式适合:简单任务、少量工具、上下文干净、模型代码能力一般
  • PTC 模式适合:复杂链式任务、大规模并行、真实部署环境、模型代码能力强
更深层的是:PTC 把"工具调用的调度"从模型推理层下沉到了代码执行层。这和操作系统的发展史同构——从"应用程序自己管理硬件"到"操作系统统一管理硬件"。子进程、循环、变量传递,这些编程语言的基本原语,成了 Agent 的新基础设施。

结语:三把刀的真正价值

这篇论文的真正贡献不是"PTC 比 JSON 好"这个结论,而是三把消融手术刀精确地切出了 JSON 的三道结构性伤疤。每一道伤疤都不是"调参能解决的",而是"架构决定的"。

当你在设计 Agent 系统时,问自己三个问题:

1. 我的任务链有多长?如果超过 6 步,JSON 的架构税会开始指数累积。 2. 我的并行扇出有多大?如果超过 70,JSON 会开始丢调用。 3. 我的上下文有多脏?如果有很多系统提示和历史对话,JSON 更容易跑偏。

三个问题中任何一个的答案是"是",PTC 值得考虑。三个都是"是",PTC 几乎是必然选择。

工具调用的苦涩教训:不是"代码比 JSON 好",而是"当代码能力成为模型的基础能力后,继续用 JSON 调用工具,就像在有了编译器之后还坚持写汇编"。

暂无表态
合作

智谱 GLM-5 已上线

在智谱开放平台 BigModel.cn 打造 AI 应用。新一代旗舰模型 GLM-5 在推理、代码、智能体综合能力达到开源模型 SOTA。

领取 2000万 Tokens