更好的模型:更差的工具
摘要
较新的Claude模型(Opus 4.8和Sonnet 5)在工具调用行为上表现更差,它们会在工具调用参数中发明额外的字段,导致验证失败,与旧模型相比是一种倒退。
暂无内容
查看缓存全文
缓存时间:
2026/07/04 21:42
# 更好的模型:更糟的工具
来源:https://lucumr.pocoo.org/2026/7/4/better-models-worse-tools/
发布于 2026 年 7 月 4 日
过去两天,一个非常奇怪的 Pi 问题(https://github.com/earendil-works/pi/issues/6278)让我一头扎进了一个兔子洞。简单来说,较新的 Claude 模型有时会在调用 Pi 的编辑工具时,在嵌套的 `edits[]` 数组中添加额外的、虚构的字段。而且这不是 Haiku 或某个小模型——是 Opus 4.8。编辑本身通常是对的,但参数不符合 schema,因为模型编造出了不存在的键,导致 Pi 拒绝该工具调用并要求重试。光这样并不奇怪,因为模型偶尔会发出格式错误的工具调用,尤其是小模型。但让我惊讶的是,这种情况在新版 Anthropic 模型中变得更糟——Opus 4.8 和 Sonnet 5 都表现出这个问题,而老模型却没有。换句话说,该系列最先进的模型在处理这个特定工具 schema 时,反而比它们的“哥哥姐姐”更差。
关于 Fable:我故意没有测试它,因为我不确定它们运行的分类器是否会把我悄悄降级到 Opus。
## 工具调用就是文本
如果你没花太多时间研究 LLM 工具调用的内部机制,那么需要理解的关键点是:工具调用不是魔法,它使用的是相当粗糙的带内信令。模型会收到一份对话记录、一条系统提示和一个可用工具列表。服务器将其塞进一个带有特殊标记 token 的大型提示中。由于模型是在这种格式的样本上训练和强化过的,因此在生成过程中的某个时刻,它会输出一些东西,被 API 或客户端解释为“用这些参数调用这个工具”。
对于一个文件编辑工具,预期的调用负载可能会是这样:
```json
{
"path": "some/file.py",
"edits": [
{
"oldText": "要替换的文本",
"newText": "替换文本"
}
]
}
```
然后,一个测试框架会验证参数、执行编辑,并将结果反馈给模型。如果验证失败,模型会看到错误信息,通常还会重试。
Anthropic 模型具体如何实现这种格式化尚不清楚,但有些人曾看到过“ANTML”标记,这些标记有时也会泄露到公开通信中。据我所知,上述调用从模型中序列化出来时大概会是这个样子:
```
<antml:tool_call>
<antml:parameter name="path">some/file.py</antml:parameter>
<antml:parameter name="edits">[ { "oldText": "要替换的文本", "newText": "替换文本" } ]</antml:parameter>
</antml:tool_call>
```
需要注意的一点是,这个东西虽然看起来像 XML,但并不是真正的 XML。它只是他们发现方便进行 token 化和训练的一种形式。另一点需要注意的是,一个基本的顶层字符串参数是内联出现的,而对象数组则是通过 JSON 序列化实现的。虽然我不*完全确定*这就是它的工作原理,但有一些迹象表明这离真相并不远。这将在后面变得重要。
有两种截然不同的方法可以让模型产生这样的结构:
1. 你可以*要求*模型产生符合某个 schema 的有效 JSON,然后在之后进行验证。
2. 你可以约束采样器,使其从一开始就无法采样出无效的 JSON,甚至无效的 schema 形状。
第二种方法通常被称为语法感知或约束解码。采样器会屏蔽那些会违反语法的 token。如果模型当前处于一个 JSON 对象内部,而 schema 只允许 `oldText` 和 `newText`,那么采样器可以阻止它输出 `"in_file"` 或 `"type"`。语法感知解码既可以用于约束输出为语法有效的 JSON,也可以用于强制特定的枚举值或键。
没有任何约束的话,模型只是遵循一个习得的惯例。
## 故障
Pi 的编辑工具支持在一次调用中执行多个精确的字符串替换。这就是为什么参数中包含一个 `edits` 数组。在失败的案例中,模型会生成这样的条目:
```json
{
"oldText": "...",
"newText": "...",
"requireUnique": true
}
```
或者这样:
```json
{
"oldText": "...",
"newText": "...",
"oldText2": "",
"newText2": ""
}
```
在反复试验中,我看到了各种各样编造出来的尾部键:`type`、`id`、`kind`、`unique`、`requireUnique`、`matchCase`、`in_file`、`forceMatchCount`、`children`、`notes`、`cost`、`oldText2`、`newText2`、`oldText_2`、`newText_2`,甚至还有一个 `event.0.additionalProperties` 键出现在编辑对象内部。
最烦人的是,在我检查的无效调用中,实际的 `oldText` 和 `newText` 负载都是字节正确的。模型实际上产生了正确的调用,但随后在对象的末尾添加了一些无意义的内容。
这个故障还严重依赖于上下文。一个全新的单轮提示(比如“编辑这个文件”)对我来说根本无法复现。一个包含读取文件、诊断问题、然后编写多行编辑操作的智能体历史记录则可以复现。更烦人的是,并非所有对话记录都会表现出这种行为。事实上,我需要 Petr Baudis(https://github.com/pasky)的对话记录才能复现这个问题!在那个用户的会话中,继续会话会导致 Opus 4.8 大约 20% 的时间失败。从历史记录中移除 thinking 块使得失败率降低了一半。在我的测试中,开启严格工具调用则完全消除了这个问题。
## 为什么越来越糟
我最大的假设是,这不是随机的退化,而是一个训练产物。当较老的 Anthropic 模型被训练时,它们是被训练在一些工具上(其中一些工具有文档记录)。但那时还没有像 Claude Code 这样已经交付给用户的测试框架作为明显的目标。现代的 Anthropic 模型很可能不同,因为它们的后训练包括 Claude Code 或一个非常相似的测试框架。模型学会了在那个环境下成功的工具调用是什么样的。它也学会了那个环境能容忍什么样的错误。
Claude Code 自己的工具相对扁平。普通的编辑工具不是 Pi 的那种嵌套 `edits[]` 形状,而更接近于 `file_path`、`old_string`、`new_string` 和一个可选标志(`replace_all`)。查看 Claude Code 的客户端非常有启发性:它包含针对格式错误工具使用、参数别名、类型强制转换、Unicode 修复以及未知键过滤的重试路径。换句话说,Anthropic 自己的客户端似乎预期并接受相当多的马虎行为,并且大多默默地修复它们。
如果在这样的测试框架(或它的模拟)中进行强化学习,那么稍微格式错误的工具调用仍然可以完成任务并获得奖励。测试框架完全吸收了错误,对于发明别名、添加多余字段或使用相近的参数名称,几乎没有梯度惩罚。更糟糕的是,模型可能会变得非常强烈地适应规范的 Claude Code 编辑工具形状。一个不同的测试框架可以提供一个具有相同语义意图但不同 schema 的工具。这样的工具会越来越偏离分布。训练得更好的模型可能反而会与你对抗得更厉害,因为它的先验更强。
这并不太令人惊讶,但与几个月前的情况相比,这确实是一个变化。当 Opus 4.5 发布时,它对其他编辑工具的适配异常出色。事实上,我相当确信我们正走在一条正确的道路上:只要指令足够好,模型更有可能适应任何出现的工具形状。现在我对我们目前所处的轨道有些担忧。替代的工具 schema 可能不仅仅是陌生。它们很可能被针对某个特定、宽容的工具生态进行优化的后训练所隐式惩罚。而且那个生态并没有文档记录。
虽然有一个记录在案的文本编辑器工具(https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool),但你会看到 Claude Code 实际上并不遵循这种格式。Claude Code 内部做的事情(是一个闭源的测试框架)对你来说是隐藏的。
## 马虎的测试框架
Claude Code 显然是闭源的,但我们可以查看其混淆后的代码,大致了解它做了什么。老实说,它对输入数据非常宽容。首先,Claude Code 会检查模型可见文本中是否泄露了标记,比如 `assistant<|channel|>commentary to=functions.get_weather <|constrain|>json<|message|>{"location":"San Francisco"}<|call|>`。关键部分是 `<|constrain|>json`。模型可以通过带内信号表示这个消息体是 JSON,推理堆栈可以利用这个边界,对工具调用的主体切换到 JSON 约束采样。
大概 Anthropic 的模型也有类似的操作,至少我猜测在 `strict` 模式下会是这样。Harmony 中的标记帮助采样器检测何时需要使用特定语法进行采样,并且由于它是对话记录的一部分,这相当容易做到。对于托管的 GPT 模型,还有一个选项可以为需要遵守此类规则的自定义工具提供 LARK 语法(https://lark-parser.readthedocs.io/en/latest/grammar.html)。
Anthropic 似乎与此不同,尽管可能不完全。如果对象数组以 JSON 形式表示(看起来确实如此),那么模型必须在工具参数内部编写 JSON。可能有基本的语法约束采样在进行,而这可能部分解释了额外的键。对于一个嵌套数组参数,该 JSON 在一个标签内包含字符串字面量中的转义多行文件内容。意外的虚构键恰好出现在任务中熵最高的点:在关闭一个长达数百 token 的转义 `newText` 字符串之后,模型必须决定是 `}` 还是 `, "..."`。Opus 4.8 和 Sonnet 5 似乎对编辑工具调用应该是什么样子有更强的先验,而那个先验似乎是 Claude Code 的编辑 schema:一个扁平的旧/新字符串对,加上可选的 `replace_all` 标志。
我的猜测是,Opus 已经学会了一个编辑操作可能有一个额外的可选字段,但在 Pi 的嵌套 `oldText`/`newText` 形状下,它没有该字段的已训练名称。所以它每次都在采样出一个合理的名称,这就是为什么失败会产生几十个随机键而不是一个稳定的别名。由于 Anthropic 的 `strict` 模式似乎能解决这个问题,我推测在服务器端他们拒绝采样 JSON schema 结构中不允许的键。这也能解释为什么在启用严格模式时他们对工具定义的复杂性有限制。
到目前为止,我测试的 Codex 模型没有表现出这种退化。我测试了所有可用的模型,除了 5.6(我还没有访问权限)。
## 这对测试框架意味着什么
令人不安的教训是,工具 schema 并非中立,至少在 Anthropic 模型上如此。我们喜欢假装 schema 是一个抽象契约,模型是一个会遵循它的通用推理引擎,但某些工具可能不再是这样了。工具 schema 处于某种分布中,有些形状接近模型在后训练中看到的,有些则相去甚远。有些对提供商的隐藏编码(例如 ANTML 中的顶层属性)来说很容易处理,而有些则要求模型在长多行字符串之后编写嵌套在数组内的大型转义 JSON 对象。模型可能足够聪明,能理解 schema,但在压力下仍难以采样出精确的形状。
如果这种模型行为持续下去,我不知道对测试框架意味着什么。显然,可以在 Anthropic 中打开 `strict` 采样,问题就应该消失。另一方面,模型表现出这种行为表明了强化学习对它们的影响。如果你想获得最佳的模型性能,对抗那个先验可能徒劳无功。目前现实是,Claude Code 不是开源的,我们也不能真正知道他们在其 RL 环境中做了什么。我们不能假设 Claude Code 训练出的行为会干净地迁移到你的工具上,除非它们很匹配。
在一个主导测试框架内部进行的后训练越多,其他所有测试框架就越需要继承它的怪癖。我之前对严格的语法约束工具调用持怀疑态度,因为约束解码可能会有质量权衡。我仍然认为这总体上可能是真的,但这个 bug 显著地改变了我之前的看法。如果最新的模型在解决问题上变得更好,同时在忠实地输出替代工具 schema 上变得更差,那么测试框架需要在某处提供更强的保证。
如果你想了解更多,或者想讨论这个问题,可以考虑阅读 Pi 跟踪器上的问题(https://github.com/earendil-works/pi/issues/6278)。
此条目标记了 ai(https://lucumr.pocoo.org/tags/ai/)和 pi(https://lucumr.pocoo.org/tags/pi/)
复制为(https://lucumr.pocoo.org/2026/7/4/better-models-worse-tools.md)/查看(https://lucumr.pocoo.org/2026/7/4/better-models-worse-tools.md)markdown
相似文章
Simon Willison's Blog
较新的Anthropic模型(如Opus 4.8和Sonnet 5)在使用第三方编辑工具(例如Pi的工具)方面比旧模型更差,这可能是因为它们通过强化学习训练使用Claude Code的内置编辑工具,导致它们在工具调用中发明了额外的字段。
X AI KOLs Timeline
Flask 作者 Armin Ronacher 发现新版 Claude 模型(Opus 4.8、Sonnet 5)的工具调用能力退化,根因是 RL 后训练过度适配 Claude Code 的工具 schema,导致替代工具 schema 越来越难以正确生成。文章揭示了模型在特定工具调用场景下性能不升反降的现象,对 agent 开发有重要警示。
Simon Willison's Blog
Anthropic 发布了 Claude Sonnet 5,该模型性能接近 Opus 4.8,价格更低,但采用了新的分词器,使得英文和代码的 token 数量增加约 30%,从而实际上提高了成本。
Anthropic Engineering
Anthropic 发布了一份事后分析报告,回应近期关于 Claude Code 的质量反馈,识别并修复了三个问题,涉及推理努力程度默认值、会话状态管理和系统提示词,这些问题影响了 Sonnet 和 Opus 模型。
X AI KOLs Following
一项针对四款大语言模型(Qwen、MiniMax、GLM)的评估显示,当使用 Claude 作为 Opencode 智能体工具的提示器时,一个较小的本地模型(运行在 3090 显卡上的 Qwen 27B)在代码质量与可靠性方面表现优于更大的剪枝模型。