Reed's News
← 返回精选

更好的模型,更差的工具:后训练对齐如何扭曲工具调用

AI 61 leemoore 2026/7/4 2107 字 原文 ↗

2026年7月4日

最近两天,一个离奇的【Pi工具调用问题】https://github.com/earendil-works/pi/issues/6278让我深陷其中。简单来说,新版Claude模型(绝非Haiku这类轻量模型,而是Opus 4.8)在调用Pi的编辑工具时,会在嵌套的edits[]数组中额外生成一些虚构字段。编辑内容本身通常是正确的,但由于模型凭空造出了不符合规范的键名,Pi会拒绝该工具调用并要求重试。

模型偶尔生成格式有误的工具调用本不足为奇,尤其是轻量模型。但令我意外的是,Anthropic的新版模型在这方面表现反而更差——Opus 4.8和Sonnet 5均出现此问题,而旧版模型则完全正常。也就是说,该系列的最先进模型,在遵循这一特定工具规范上,竟不如老版本。

顺带提一句Fable模型:我刻意未对其测试,因为不确定它的分类机制是否会暗中将我的请求降级到Opus处理。

若你不熟悉大语言模型(LLM)工具调用的内部逻辑,需先明确一点:工具调用并非魔法,而是采用了一种相当原始的带内信号机制。模型会接收对话记录、系统提示词及可用工具列表,服务器将这些内容整合成带有特殊标记符的长提示词。由于模型在训练和强化阶段接触过大量此类格式示例,生成内容时会输出一段被API或客户端识别为"调用某工具并传入对应参数"的文本。

以文件编辑工具为例,标准调用负载大致如下:

{
"path": "some/file.py",
"edits": [
{
"oldText": "text to replace",
"newText": "replacement text"
}
]
}

随后会有验证程序检查参数合法性,执行编辑操作,并将结果反馈给模型。若验证失败,模型会收到错误信息,通常会尝试重新调用。

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,只是Anthropic团队为方便分词和训练设计的标记;其二,顶层字符串参数直接嵌入标记,而对象数组则通过JSON序列化实现。虽不能完全确认这就是实际运作方式,但已有不少迹象表明此推测与事实相去不远——这一点后续会至关重要。

让模型生成此类结构主要有两种方式:

第二种是人们常说的语法感知解码或约束解码。采样器会屏蔽违反语法规则的标记符。例如,若模型正处于JSON对象内部,且规范仅允许oldTextnewText两个键,采样器就会阻止它生成"in_file""type"这类无效键。语法感知解码既能确保输出符合JSON语法规范,也能强制使用特定枚举值或键名。

若没有任何约束,模型仅会遵循训练中学到的惯例生成内容。

Pi的编辑工具支持单次调用完成多段精准字符串替换,因此参数中包含edits数组。在调用失败的案例中,模型生成的数组元素会变成这样:

{
"oldText": "...",
"newText": "...",
"requireUnique": true
}

或是这样:

{
"oldText": "...",
"newText": "...",
"oldText2": "",
"newText2": ""
}

多次测试后,我发现了五花八门的虚构后缀键:typeidkinduniquerequireUniquematchCasein_fileforceMatchCountchildrennotescostoldText2newText2oldText_2newText_2,甚至还有在编辑对象内部出现的event.0.additionalProperties键。

最令人头疼的是,我检查的所有无效调用中,oldTextnewText的内容完全正确。模型其实生成了正确的调用核心,只是在对象末尾多添了些无意义的字段。

该问题还严重依赖上下文。我用"编辑这个文件"这类全新单轮提示词测试时,完全未出现问题;但在模型先读取文件、诊断问题,再编写多行编辑指令的智能体对话历史中,就会复现故障。更棘手的是,并非所有对话记录都会触发该问题——我甚至需要借助【Petr Baudis】https://github.com/pasky的对话记录才能复现!在该用户的会话中,继续对话会导致Opus 4.8约20%的调用失败;若移除历史中的思考块,失败率会降低一半;而开启严格工具调用模式后,在我的测试中问题完全消失。

我最倾向的假设是:这并非随机退化,而是训练导致的副作用。

旧版Anthropic模型训练时,接触过一些工具(部分有文档说明),但当时尚未以Claude Code这类面向用户的工具框架为明确训练目标。而新版Anthropic模型大概率不同,它们的后期训练包含Claude Code或类似框架。模型会学习在该环境中成功调用工具的范式,也会了解该环境能容忍哪些错误。

Claude Code自身的工具结构相对扁平。其常规编辑工具并非Pi那样的嵌套edits[]结构,而是更接近file_pathold_stringnew_string加一个可选标记replace_all的形式。研究Claude Code的客户端代码能得到不少启发:它包含针对格式错误工具调用的重试逻辑、参数别名机制、类型转换、Unicode修复,以及未知键过滤功能。换句话说,Anthropic自己的客户端预期并允许一定程度的格式混乱,且会在后台默默修复。

若强化学习是在这类框架或其模拟环境中进行,那么略有格式问题的工具调用仍能完成任务并获得奖励。框架会完全消化错误,因此模型几乎不会因生成别名、添加多余字段或使用相近参数名而受到负向反馈。

更糟的是,模型可能会强烈适配Claude Code编辑工具的标准结构。若其他工具框架语义相同但规范不同,就会逐渐偏离模型的训练分布。训练越充分的模型反而可能越难适配,因为它的先验认知更强。

这其实并不意外,但与几个月前的情况相比已是天差地别。Opus 4.5刚推出时,对其他编辑工具的适配能力极强。当时我坚信,只要指令清晰,模型就能适配各类工具结构,发展前景一片光明。

但现在我开始担忧当前的发展轨迹。替代工具规范可能不仅是模型不熟悉,甚至会因针对特定容错型工具生态的后期训练而受到隐性惩罚。而这个生态系统并无公开文档。尽管有一个【文本编辑工具】https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool有官方文档,但Claude Code实际并未遵循该格式。Claude Code作为闭源框架,其内部运作细节对外完全隐藏。

Claude Code虽闭源,但我们可通过分析混淆后的代码一窥究竟。说实话,它对输入数据的容错度极高:

首先,Claude Code会检查模型输出文本中是否泄露<invoke标记,若发现则发送遥测数据,并通过自有状态机推动模型重试错误调用。

它具备明确的Unicode转义修复功能,可修复字符串值中损坏的\uXXXX序列和孤立代理对。还为工具参数设置了别名,例如Edit工具既接受旧版训练时使用的old_str(对应官方文档中的文本编辑工具),也接受新版规范中的old_string,同时支持new_str/new_stringpath作为file_path的别名等。

它还会默默过滤意外出现的键,且未启用strict模式。推测原因在于,Anthropic对工具定义的复杂度设有限制,启用strict模式可能导致API请求失败,因此Claude Code未尝试使用该模式。

其他工具框架也会遇到这个问题吗?Anthropic的一大问题在于,模型和框架均为闭源。Codex模型虽也闭源,但至少其工具框架是开放的。此外还有【gpt-oss】https://github.com/openai/gpt-oss也颇具参考价值。这类模型明确接受过OpenAI【harmony】https://github.com/openai/harmony响应格式的训练,且有大量文档说明OpenAI团队的设计思路。

Harmony将信道和工具调用内容类型纳入提示词格式,函数调用示例如下:

关键在于<|constrain|>json标记。模型可通过带内信号表明消息主体为JSON,推理栈则可借此切换至JSON约束采样模式处理工具调用内容。推测Anthropic模型也有类似机制,至少在strict模式下应该如此。

Harmony的标记能帮助采样器判断何时需采用特定语法采样,且由于标记属于对话记录的一部分,实现起来相对容易。对于托管的GPT模型,还可为自定义工具提供【LARK】https://lark-parser.readthedocs.io/en/latest/grammar.html语法规则,以确保格式合规。

Anthropic的机制看似有所不同,但也并非完全割裂。如前文推测,若对象数组以JSON形式表示,模型就需在工具参数内编写JSON内容。这可能涉及基础的语法约束采样,而这或许能部分解释多余键的出现:对于嵌套数组参数,JSON需在标记内包含经过转义的多行文件内容字符串。那些意外生成的虚构键,恰好出现在任务熵值最高的节点——即在写完数百词的转义newText字符串后,模型需要决定是写}还是, "..."

Opus 4.8和Sonnet 5对编辑工具调用的格式有更强的先验认知,且这一认知显然基于Claude Code的编辑规范:扁平的新旧字符串对,加一个可选的replace_all标记。我猜测Opus已习得"编辑操作可包含一个额外可选字段"的规则,但在Pi的嵌套oldText/newText结构下,它没有经过训练的对应字段名,因此每次都会随机生成一个看似合理的名称,这也解释了为何失败案例中会出现数十种不同的随机键,而非固定别名。

既然Anthropic的strict模式能解决此问题,我推测服务器端会拒绝采样JSON规范不允许的键名。这也解释了为何启用strict模式时,工具定义的复杂度会受到限制。

截至目前,我测试过的Codex模型均未出现此类性能退化(除了我暂无权限访问的5.6版本)。

这一现象带来了一个令人不安的结论:至少在Anthropic模型上,工具规范并非中性的。我们总假设规范是抽象契约,模型是能遵循契约的通用推理器,但对于部分工具而言,这可能已不再成立。

工具规范存在于模型的训练分布中,有些结构与模型后期训练所见高度契合,有些则相去甚远;有些适配服务商的隐藏编码方式(如ANTML的顶层属性),有些则要求模型在长多行字符串后的嵌套数组内编写大段转义JSON对象。模型或许足够聪明,能理解规范,但在压力下可能仍难以精准生成所需格式。

若此类模型行为持续,工具框架将面临何种影响?显然,在Anthropic模型上开启strict采样模式可解决问题,但这种行为也凸显了强化学习对模型的影响——若想获得最佳模型性能,与其对抗先验认知可能徒劳无功。

当前现实是,Claude Code不开源,我们也无从知晓其强化学习环境的具体运作。除非你的工具与Claude Code高度相似,否则不能假设经Claude Code训练的模型行为能无缝迁移。模型在某一主流框架下接受的后期训练越多,其他框架就越可能被迫继承该主流框架的特性。

我曾对严格语法约束的工具调用持怀疑态度,因为约束解码可能会牺牲部分生成质量。总体而言我仍认为这一顾虑存在,但此次问题极大地改变了我的看法。如果最新模型在任务解决能力提升的同时,遵循替代工具规范的能力反而下降,那么工具框架就需要更强的机制来保障格式合规。

若想了解更多细节或参与讨论,可阅读【Pi追踪器上的相关问题】https://github.com/earendil-works/pi/issues/6278。