模型越好,工具越烂:AI 编程工具的一个悖论

模型越好,工具越烂:AI 编程工具的一个悖论

AI编程工具开发者体验CursorClaude Code开源工具链

数据源:HN + Lobsters

Armin Ronacher 博客文章「Better Models: Worse Tools」的社交分享图

2026 年 7 月 4 日,Flask 和 Jinja2 的作者、现就职于 Anthropic 的 Armin Ronacher 发了一篇博客,标题直白得像一记耳光:「Better Models: Worse Tools」。他在开源编程 harness Pi 的开发过程中发现了一个令人不安的趋势:Anthropic 的新一代模型在调用非 Claude Code 的编辑工具时,反而比旧模型更糟糕。

这个发现迅速在 Hacker News 上冲到了 150+ 分,Simon Willison 也发了评论。GenAI Secret Sauce 将它定性为「没人预料到的供应商锁定」。但问题比锁定的说法要微妙得多——它触及一个根本性的张力:AI 公司的商业模式和开发者对开放工具的需求,正在朝着相反的方向狂奔。

那个奇怪的 bug

Ronacher 在 Pi 项目中接到了一个 issue:新版 Claude 模型(Opus 4.8、Sonnet 5)在调用 Pi 的编辑工具时,会在 edits[] 数组里凭空发明字段。核心数据 oldTextnewText 通常是正确的,但模型会在 JSON 对象末尾附赠一堆不存在的键——requireUniquetypekindmatchCasein_fileoldText2newText2,甚至 event.0.additionalProperties

Pi 的工具校验当然拒绝这些调用。模型收到错误后重试,重试还是错——反复循环。

真正扎心的是这个 bug 的方向。旧版 Anthropic 模型(Opus 4.5 及更早)没有表现出这个行为。Opus 4.8——当前旗舰——反而有大约 20% 的失败率。也就是说,新一代 SOTA 模型在特定工具 schema 上的表现是倒退的。

一个独立思考的程序员看到这种模式,不会把它当作随机 bug。Ronacher 也没有。

工具调用不过是文本

先来回顾一下 LLM 工具调用的底层机制。它并不神秘:工具调用本质上是带内信号——模型生成的文本中嵌入了特殊标记,客户端解析这些标记来触发工具执行。

模型接收一个对话记录、一个系统提示词、以及可用工具列表。服务端把这些信息塞进一个巨大的 prompt 里,用特殊标记符分隔。模型在训练和强化学习中被引导到在生成过程中输出特定模式——「以这些参数调用这个工具」。然后 API 或客户端解析这些标记,提取出工具名和参数。

对于 Anthropic 模型,这种内部格式被社区称为「ANTML」,大致长这样:

<antml:function_calls>
  <antml:invoke name="edit">
    <antml:parameter name="path">some/file.py</antml:parameter>
    <antml:parameter name="edits">
[
  {
    "oldText": "text to replace",
    "newText": "replacement text"
  }
]
    </antml:parameter>
  </antml:invoke>
</antml:function_calls>

注意几个关键细节:

  • 这看起来像 XML 但不是 XML——它只是一种方便分词和训练的自定义标记格式;
  • 顶层简单参数(如 path)以标签内容形式呈现,嵌套数组则通过 JSON 序列化嵌入;
  • 模型写 JSON 时,如果没有语法约束采样(grammar-constrained decoding),那它只是在遵循训练中学到的统计模式——不是「理解」了 schema。

这也是为什么这个 bug 特别微妙。模型在写完一个几百 token 的 newText 字符串后,在 JSON 对象末尾面临一个决策:是写 } 闭合对象,还是写 , "..." 追加字段。Opus 4.8 在这个最高熵的决策点多做了一个选择——它「觉得」编辑工具应该有某个额外字段,但又不知道在 Pi 的 schema 下应该叫什么,于是每次都现场发明一个名字。

Slop Harness 如何训练出 Sloppy Model

Ronacher 给出了一个有力的假说:这一退化来自训练产物

旧版 Anthropic 模型训练时,还没有一个统一的目标 harness(工具运行环境)。但现代 Anthropic 模型的后训练几乎肯定包含了 Claude Code,或者一个和它高度相似的模拟环境。模型在那里学会了「什么样的工具调用是成功的」——同时也学会了「什么样的错误被那个环境容忍」。

Claude Code 的客户端代码(虽然闭源,但可以从混淆后的 JS 中窥见)极其宽松:

  • 有重试路径处理格式错误的工具调用;
  • 有参数别名(old_str 也是 old_stringpath 也是 file_path);
  • 会自动过滤掉不认识的字段;
  • 甚至修复 Unicode 转义错误和孤立的 surrogate 字符;
  • 不使用 strict 模式(因为 strict 对工具定义的复杂度有限制)。

换句话说,Claude Code 自身就预期并接受模型产生大量的「slop」,然后悄悄修复它们,大多时候甚至不让模型知道出错了。

如果在这样的 harness 中进行强化学习——「完成编辑任务就获得奖励」——那么稍微不精确的工具调用仍然可以成功获得奖励。模型学到的行为模式变成了「随便写,harness 会兜底」。更糟糕的是,模型变得高度适配 Claude Code 的编辑工具形态——扁平的 old_string / new_string 加可选的 replace_all 标志——而对 Pi 那种嵌套的 edits[] 数组感到陌生。

这绝非理论猜想。HN 上有开发者报告了类似的经验:Owl-alpha 模型偶尔会把 Codex 的 V4A patch 格式推进任何 diff 工具里——很大可能是因为训练语料中混杂了 Codex 的转录记录。

锁定的新形态:不是 API,是 Harness

这个技术细节折射出一个更大的结构性问题。我们习惯于讨论「供应商锁定」时关注 API 格式——OpenAI 的 function_calling vs Anthropic 的 tool_use vs Google 的格式。但 Ronacher 的文章揭示了一种更深层的锁定:

模型的行为被后训练固定在了提供商的私有 harness 上。

LangChain 的 Neil Dahlke 在 2026 年 6 月的一篇文章中把这个逻辑推得更远。他指出,AI 时代的锁定模式完全复刻了 AWS/Azure/GCP 当年的策略:

  • 卖的是商品(云时代是计算/存储/网络,AI 时代是 token);
  • 锁定的手段是专有工具层(云时代是 CloudFormation/ARM 模板,AI 时代是 Claude Agent SDK/OpenAI Agents API);
  • 「如果他们在编排层拥有你的业务逻辑,那即使有更好、更便宜、更合适的模型出现,你仍然会继续消费他们的 token。」

这个判断和 Ronacher 的发现遥相呼应。Claude Code 是闭源的。它的 RL 环境是闭源的。模型正在围绕一个你看不到内部细节的 harness 优化自己。如果你用的是 Pi、Aider、Cline 或任何其他第三方 harness——甚至是自己搭建的工具系统——你就在模型的训练分布之外。模型可能「更聪明」了(benchmark 更高了),但在你的工具上反而更不稳定。

Simon Willison 在评论中提出了一个实用主义的问题:「这是否意味着第三方编程 harness 应该为不同的底层模型实现多套编辑工具,只为用那个对当前模型训练最优的?」

这个问题问得很好,也足够荒谬。如果开发者必须在自己的工具系统中为 Claude 实现一套「Claude Code 兼容模式」、为 GPT 实现一套「Codex 兼容模式」,那工具的中立性就彻底破产了。

从 IDE 到 Text Box:我们在失去什么

Ronacher 的文章没有直接讨论 IDE 退化的问题,但他点出的趋势能自然地延伸到更大的画面。

AI 编程模型出现之前,开发者工具的核心价值是增强用户的控制力。一个好 IDE 让你更精确地重构,一个好 linter 让你更早地发现错误,一个好 VCS 让你更细粒度地追溯变更。工具越强,你对代码的理解和控制越深。

AI 编程的演进方向——至少在当前的商业模式驱动下——看上去正好相反。CursorClaude CodeCopilot 这些工具的核心交互是:一个对话框,一个「Accept All」按钮。模型的推理过程藏在黑盒里,工具的内部行为藏在黑盒里,你的代码是如何被修改的也藏在一键接受之后。

当模型变得足够好——能写出正确的代码——这些黑盒看上去是令人愉快的。但 Ronacher 的发现提示了一个冷水时刻:当模型不够好时(比如在非标准工具 schema 上),那些黑盒就变成了无法调试的障碍。你看不到模型为什么凭空发明了一个 requireUnique 字段,你看不到 RL 环境里发生了什么,你甚至连 Claude Code 内部到底接受哪些别名都不清楚——因为它没有文档。

这让人联想到 HN 上一位评论者的感慨:「几十年前我在做 MOO 客户端时,试图把控制序列嵌入文本流中(in-band signaling),遇到了和今天 LLM 工具调用完全一样的问题——带内信号永远无法干净地分离控制逻辑和内容。读了这些 agentic harness 的实现后,我对二十岁自己写的代码感觉没那么羞愧了。」

工具调用本质上是把结构化的工具接口强行塞进非结构化的文本生成管道里,然后在两边各加一个解析器来弥补裂缝。当 AI 公司训练模型适配自家工具时,他们实际上是在强化「只有在我们家的解析器下,模型的行为才可靠」这个事实。

有没有出路

Ronacher 自己的态度在文章中有明显转变。他原本对严格语法约束采样(grammar-constrained decoding)持保留态度,认为强制约束可能会降低生成质量。但这个 bug 让他「显著调整了先验判断」:

如果新一代模型在完成任务上变得更好,但同时在忠实输出替代工具 schema 上变得更差,那 harness 必须在某处获得更强的保障。

在 Anthropic 的 API 下,开启 strict 模式似乎可以消除这个 bug。但这只是权宜之计——它没有解决「模型在 RL 中被迫适应单一工具形态」这个根本问题。

另一个可能的方向是 LangChain 的 Dahlke 提出的「模型中立 harness」。理想情况下,一个中性编排层应该:

  • 开源(无隐藏捕获机制);
  • 多模型开箱即用(同一个 agent 定义可以跑在任意后端上);
  • 对模型差异性有感知(不假装所有模型可以互换,而是暴露每个模型的特性 profile)。

arXiv:2512.04123 的调查数据表明,59.1% 的生产部署已经在协调多个模型(成本优化、模态需求、合规约束)。这不是未来畅想,是当下实践。但关键问题是这些多模型系统是否跑在中性 harness 上——还是跑在某个实验室的专有 SDK 上。

开源的替代品也在涌现。Qwen Code 声称支持多协议(OpenAI、Anthropic、Gemini、Qwen),标榜「无供应商锁定」。Freebuff 试图做一个免费开源的全替代。但这些项目面临一个根本不对称:模型的后训练发生在实验室的私有基础设施中,开源社区无法控制模型学到了什么样的工具行为。

最终,问题回到了激励机制。Anthropic 优化 Claude 在 Claude Code 里的表现——这从商业上看完全合理。OpenAI 训练 GPT 更好地使用 Codex 的 apply_patch 格式——同样合理。但这些优化对他们自家的封闭 harness 越有效,对其他工具就越不利。

这不是阴谋论。它就是一个竞争性市场中的自然结果:拥有最强模型的公司,也拥有最强的动机去让模型在自家工具上表现最好、在其他工具上表现未知。

Ronacher 写道:「我曾经比较乐观,认为模型足够聪明,只要指令写得好,就能适配任何工具形态。现在我有点担心我们正走上的这条路。」

这句话的分量在于说的人是谁——一个同时在开源工具领域(Flask、Jinja2)和闭源 AI 实验室(Anthropic)都有深入经验的人。

站在 2026 年年中看,AI 编程工具正在经历一个奇特的时刻。模型本身在变得越来越强大,benchmark 分数在持续走高,但工具的质量——从开放性、互操作性、可调试性、可理解性的角度——并没有同比例提升。某种程度上,更聪明的模型反而让工具提供商更少关注工具本身的质量:因为模型可以写代码了,工具的底线似乎就可以低一点。

这不是一个「AI 会取代程序员」还是「AI 会让程序员更牛」的二元选择。真正的问题是:在模型进步的过程中,程序员手里会剩下什么样的工具?是开放的、可组合的、可理解的工具——还是被锁在一个对话框后面的黑盒?


参考链接


本文的素材来自公开信息和社区讨论。如果你对这个话题有更深入的一手经验,欢迎指出文中的不足。