日期:2026-07-06 主题:更强模型,更差工具——Anthropic 新模型工具调用正在退化 原文:Better Models: Worse Tools 来源:lucumr.pocoo.org (Armin Ronacher) 领域:🧠 AI/ML
背景
ChatGPT、Claude、Gemini——每一代新模型都在刷新纪录。然而 Armin Ronacher(Flask、Jinja2、Sentry 创始人)发现了一个反常现象:Anthropic 最新的旗舰模型——Opus 4.8 和 Sonnet 5——在调用某些工具时,反而比旧版更差。
这个发现源于他维护的 AI 代码编辑工具 Pi。用户报告说,Opus 4.8 在调用编辑工具时,会在参数中塞入各种不存在的字段,比如 requireUnique、oldText2、matchCase,甚至 event.0.additionalProperties。而旧模型如 Opus 4.5 从未出现过。
Armin 花了两天追踪这个问题,最终揭示了一个关乎整个 AI 工具生态的深层矛盾。
工具调用不是魔法
要理解这个 bug,先得明白 LLM 工具调用的底层机制。很多人以为工具调用是模型内部的一个"函数调用"机制——模型"决定"调用某个函数,然后引擎帮你把参数填好。但现实远没有那么优雅。
工具调用本质上是文本生成。
当模型收到包含工具列表的请求时,服务端把所有工具定义拼接成一大段提示词。模型经过训练,学会了在生成文本的某个时刻,输出一段特定格式的文本来表示"我要调用这个工具"。
在 Anthropic 的模型里,这段文本看起来像 XML 但不是真正的 XML——Anthropic 内部称之为 ANTML 格式。关键问题在于,当参数是嵌套对象数组时(比如 Pi 的 edits[]),模型需要在 ANTML 标签内部嵌入 JSON。这就意味着模型在生成了一个长达数百个 token 的转义字符串之后,必须在正确的位置决定是写 } 还是 , "..."。这个位置恰恰是熵最高的决策点。
凭空出现的假字段
Armin 在 Pi 中定义了一个支持多次替换的编辑工具,它的参数包含一个 edits 数组,每个元素有 oldText 和 newText 两个字段。在正常情况下,模型应该严格按照 schema 输出。
但 Opus 4.8 和 Sonnet 5 却开始往对象里添加 schema 中根本不存在的键。Armin 记录到的"假字段"包括:
requireUnique、unique、matchCase——看起来像 Claude Code 里replace_all的变体oldText2、newText2、oldText_2、newText_2——可能是试图表达"多次替换"type、id、kind、in_file、forceMatchCount——各种随机名称children、notes、cost——完全不相关的字段- 甚至还有
event.0.additionalProperties
最让人头疼的是,实际上正确的 oldText 和 newText 内容模型已经准确生成了,只是在对象的末尾画蛇添足地加了一个多余字段,导致整个工具调用被校验器拒绝,只能重试。
一个干净的单轮对话很难复现,但在模型已经读取文件、诊断问题、准备执行多行编辑的复杂 agent 会话中,大约有 20% 的概率会触发。从历史记录中移除"思考"(thinking)块可以将失败率降低一半。
为什么越新的模型越差?
Armin 提出了一个强有力的假设:这不是随机退化,而是训练的产物。 旧模型训练时还不存在像 Claude Code 这样大规模部署的 harness,而现代模型的后训练阶段几乎肯定包含了 Claude Code(或其高度相似的变体)作为训练环境。
Claude Code 的编辑工具 schema 是扁平的:file_path、old_string、new_string,再加一个可选的 replace_all。Pi 的工具则是嵌套结构——edits 数组中每个元素有自己的 oldText/newText。对模型来说,后者的形状偏离了它在 RL 阶段见过的模式。
更糟糕的是,RL 训练越强,模型的 prior(先验)也越强。它在被优化得越来越擅长 Claude Code 工具格式的同时,对其他格式的适应能力反而在下降。 Opus 4.5 能很好适配不同工具形状,但 Opus 4.8 已经"固化"了。
Claude Code 的"宽容客户端"
Armin 通过反编译 Claude Code 的 minified 代码,发现这个客户端对模型输出异常宽容:检查泄露标记并重试、修复损坏的 Unicode 转义、为参数维护大量别名映射、静默过滤未知键。而且它不使用 strict 模式——因为 Anthropic 对启用 strict 模式的工具定义有复杂度限制。
换句话说,Claude Code 本质上是一个"slop harness"(容错性极高的客户端),吸收了模型的大量不规范输出。这在产品层面有意义——用户希望操作成功而不是卡住。但在训练层面造成了一个副作用:模型学会了"差不多就行了"。
对比 OpenAI 的做法
有趣的是,Armin 测试了 OpenAI 的 Codex 模型系列,并没有发现类似的退化。他认为这可能与 OpenAI 开源的 Harmony 格式有关——Harmony 在 prompt 中包含了明确的 <|constrain|>json 标记,让推理栈能够在工具调用体的边界切换到 JSON 约束采样。Anthropic 的 strict 模式可能也有类似机制,但默认不开启且有复杂度限制。
对工具开发者的启示
这个故事给所有构建 AI agent 工具的人上了一课:工具 schema 不是中立的。
你可能认为,只要给出清晰的 JSON schema 文档,模型就会像通用推理器一样忠实遵循。但对于 Anthropic 最新模型来说,情况已经不再是这样了。工具的形状要么靠近模型在 RL 中见过的分布,要么远离它。
如果这种趋势继续,意味着:
- 后训练正在塑造模型的"工具方言",而 Anthropic 的默认方言就是 Claude Code 的那一套。
- 其他工具框架需要做更多适配来弥合差距,或者放弃使用 Anthropic 的最新模型。
- strict/grammar-constrained 采样可能是必要的——虽然可能有质量权衡,但比接受模型发明的假字段要好得多。
更深层的担忧在于:当越来越多后训练发生在某一个闭源 harness 内部时,其他所有 harness 都不得不继承它的 quirks。如果一个生态系统中只有一个支配性的客户端,模型的工具调用能力就会被这个客户端私有化。 这对追求开放和互操作的 AI 工具生态来说,不是一个令人安心的信号。
总结
"更强模型,更差工具"——这个看似矛盾的现象,是当前 AI 发展路径的一个缩影。模型在通用能力上越来越强,却在特定工具格式上越来越僵化。AI 能力的进步并不总是线性的,尤其是在与具体工具交互的接口层面。
对于正在构建 AI agent 的开发者来说,了解模型后训练阶段的"隐性偏见"越来越重要。你选择模型提供商时,不仅在选择智力水平,也在选择一套隐藏的工具调用约定——它们可能让你的工具要么如鱼得水,要么水土不服。