修好工具调用层,小模型也能胜任更多编码任务
Ajna作者介绍如何改进模型与工具之间的适配层,处理小模型虚构工具名、参数名称错误及类型不匹配等问题。作者称这些改进提升了20B模型的任务完成能力,但摘录未给出可核对的基准成绩。
AI 深度解读
一个 20B 模型,能不能完成原来要靠大模型才能做对的编程任务?Ajna 作者的尝试是:先别换模型,把模型和工具之间的执行层修好。工具名叫错、参数类型不对、写完文件语法已经坏了,这些错误可以由程序接住,不必每次都交给模型补救。
结果有亮点,也有边界:一个修复测试的任务从 81 秒降到 15 秒,但这不是小模型全面追平前沿模型的证据。更值得程序员参考的,是作者如何用可执行检查约束 Agent,以及为什么一句「再检查一下」反而可能让它越改越坏。
小模型犯的错,哪些可以交给程序处理
作者开发的 Ajna 是一个纯 Python 终端编程 Agent,没有第三方依赖和构建步骤,冷启动约 180ms。它支持 Ollama、OpenAI、Anthropic、Groq、OpenRouter,以及其他兼容 OpenAI 的接口。
作者把精力放在 harness 上。这里可以把它理解成围绕模型搭建的执行环境:模型提出工具调用,它负责解释、执行、检查,再把结果反馈回去。
小模型的失败经常发生在这个边界上。它会调用不存在的 exec、bash、str_replace、read_file,把参数写成 file_path,把整数和布尔值传成字符串 "50"、"true"。它还会把带 +/- 的 diff 内容当作替换正文,甚至在文件出现 IndentationError 后,继续报告创建成功。
这些错误会浪费完整的一轮交互,而弱模型未必能自行恢复。Ajna 因此增加了几类修复和检查:
- 把约 60 种模型臆造的工具名映射到真实工具,包括
exec、bash、str_replace、search等。 - 将
file_path、old_string、target_file、query等参数别名转换为 schema 要求的名称,修正类型,并丢弃未知参数。 - 在写入前拦截疑似 diff 的替换内容;每次写入后解析 Python、JSON、XML、TOML,解析失败就返回错误。
- 编辑删除了已有函数或类时,明确列出名称;遇到
grep "total("这种无效正则时,退回字面量搜索。
模型负责提出操作,执行层负责检查操作是否符合约定。
作者给出的单任务结果是:修复层加入前,gpt-oss:120b 耗时 81 秒,还留下了损坏文件;加入后,gpt-oss:20b 用 15 秒正确完成。但这里同时改变了模型和执行层,不能把它当成严格的模型能力对比。
为什么「再检查一下」可能越查越坏
作者原本也用了常见做法:要求模型完成任务后验证自己的工作。这个指令对较大的模型有帮助,对较小的模型却产生了反效果。
在 v2 实验里,小模型把验证理解成继续修改。已经正确的文件被重新编辑,结果包括删掉 JSON 的一个键、生成无法解析的 JSON,以及删除明确要求保留的测试。每项任务的平均耗时也从 16s 增加到 33s。
问题不在于验证没有价值,而在于「验证」缺少具体边界。模型需要自己判断查什么、是否有错、要不要修改,每一步都可能产生新错误。
v3 把这部分工作移回执行层:检查修改后的文件是否还能解析,检查被删除的名称是否仍在其他位置被引用。只有发现明确问题才反馈;检查通过,就直接结束这一轮,不再让模型寻找下一件事做。
作者称,这让两个模型的分差从 30 个百分点缩小到 10 个百分点。不过文章没有完整展开这组分差的统计细节,更适合把它视为当前评测中的观察。
可借鉴的设计是:把泛泛的自检要求改成可执行条件,再用具体失败信息驱动下一轮修复。
跑出来的结果,比完成声明更可信
Ajna 随工具提供了一个包含 10 项任务的基准。每项任务都在新的临时目录运行,评分依靠执行代码,而不是阅读模型的完成声明。
任务包括修复测试能发现的 bug、端到端接通功能、跨文件重命名、精确编辑 JSON、消除重复但保持行为、修复无法解析的文件。它还专门测试「只回答问题,不修改文件」。对日常编程 Agent,这条边界和能否正确改代码同样重要。
作者也披露了不漂亮的数据:
同等采样下,小模型从 v2 的 65% 提升到 v3 的 80%;但 v1 与 v3 的差异仍落在样本噪声范围内,不能宣称 v3 胜过 v1。
更明显的问题是重复运行的稳定性。同一个项目构建提示,gpt-oss:20b 六次得分分别为 34、34、32、33、27、0,满分 34。零分那次留下了 IndentationError,语法检查其实已经发现问题,却把它塞进了「成功」消息里。
作者随后将无法解析的写入改为硬错误,之后四次得分为 34、32、33、33。灾难性失败暂时没有再出现,但四次运行仍不足以证明问题已经彻底消失。
他们还用差分测试,让不同生成实现处理相同的异常输入,再检查结果分歧,找到了四个真实缺陷,其中两个涉及 Windows 编码。相关处理后来以精确代码写入系统提示词,因为让小模型自行判断如何处理坏字节时,它可能只执行一半要求,把正常情况改成崩溃。
我的判断
我认同作者把可靠性投入放在工具边界上。工具名称、参数类型、文件语法,这些都有明确规则,适合用确定性程序处理。比起再加一段长提示词,这种处理更容易检查,也更容易定位失败原因。
但修复层需要区分「可纠正」和「应拒绝」。明确的参数别名可以转换,未知参数直接丢弃却可能掩盖模型意图;工具名映射也需要足够清晰的语义边界。文章没有交代歧义如何处理。
它的检查覆盖也有限。JSON 能解析,不代表必需字段还在;Python 没有语法错误,不代表功能正确。原文列出的写后解析范围不包含 JavaScript、TypeScript,不能据此认定前端项目已经获得同等保护。
这篇文章支持的是加强执行层的价值,还不足以证明 20B 模型普遍达到前沿模型水平。
作者没有给出与其他 Agent 的完整对照数据,提示词与修复层各自贡献多少也没有拆清。将结论限定在这些任务和采样结果内,才符合这套测量方法的初衷。
能拿来做什么
- 给现有编程 Agent 增加明确的失败反馈:文件解析失败时返回工具错误,把错误位置交给下一轮处理,避免在成功消息末尾附带警告。
- 为 JSON 配置编辑补充任务相关断言,例如必需字段仍然存在、指定键值正确、禁止删除的内容得到保留,不能只检查能否解析。
- 评估 AI 编程工具时,挑选日常任务重复运行,并加入「只问不改」用例,同时记录正确率、耗时和严重失败。
- 想复现作者的环境,可以按下面的命令安装、配置并执行任务。原文标注项目采用 MIT 许可证,运行时为零第三方依赖。
pip install ajna-cli
ajna --setup # pick a provider (Ollama's free tier works), a key, a model
ajna "fix the failing test"
重点是第二行:先选择 provider、密钥和模型,再比较相同任务的运行结果。