一篇所有做本地评测的人都该读的论文
9 月 22 日提交到 arXiv 的这篇论文(编号 2609.26693)研究了一个看起来很小、实际上污染面极广的问题:当你用 Ollama 或 vLLM 在本地评测一个模型的工具调用能力时,测出来的数字里有多少是模型的能力,有多少是推理服务栈的副作用?
作者的回答很直接:混淆大到足以颠覆结论。同一个模型、同一个请求,在不同服务栈上的行为差异,会让你的排行榜排名完全重排。
三个层层递进的发现
第一层:服务栈会静默拦截请求。 在 Ollama 里,默认的 tools= 请求按模型逐个由静态模板标志门控。结果是三种命运:一部分模型被接受并返回文本形式的调用;一部分模型返回原生 tool_calls;而 Phi-3 和 Gemma-3 在推理开始之前就被拒绝——请求根本没有到达模型。如果你的评测框架把「请求失败」和「模型选择不调用」混在一起统计,Phi-3 的工具调用能力就被系统性低估了,而它实际上从未获得过证明自己的机会。
第二层:失败元数据丢失制造虚假的 0%。 作者自己的 harness 在这个问题上也不干净:服务栈的拒绝、重试耗尽没有被保存为结构化的失败元数据,下游分析只能把它们笼统归类为「模型未发出调用」,于是报告出 0% 的工具调用保真度。这个发现的价值在于它的自指性——连写这篇论文的团队自己都会踩这个坑,说明这是评测基础设施的系统性缺陷,不是个别人的粗心。
第三层:协议选择会惩罚能力强的模型。 修复手段也有讲究。在保留原生工具通道的同时附加一份文本形式的工具列表,能为被接受的模型挽回大部分测量损失;但如果「为了统一」改成纯文本协议,反而会降低 Llama-3.2 的保真度——因为它原生支持结构化工具调用,强行走文本等于自废武功。跨栈探针实验也证实,Ollama、vLLM、SGLang 对同一请求的处理路径各不相同。
两个容易被忽略的统计陷阱
论文还量化了两个方法学陷阱。其一是受约束解码:它确实消灭了解析失败,但代价是可能诱导不终止——模型在约束下永远生成不出合法的结束符号,评测循环卡死。省下的解析错误预算,被墙钟时间加倍奉还。换句话说,受约束解码不是免费的修复,它把一种失败形态转换成了另一种,转换后的失败更隐蔽,因为不终止的实例往往直接被丢弃而不是被计入失败,样本构成因此被悄悄改变。
其二是统计口径:池化估计(按轮次合并统计)与逐实例估计的差异最高可达约 55 个百分点。55 个点是什么概念?足以让一个「可用」和一个「不可用」的结论同时从同一份数据里诞生,取决于你选哪种口径。差异的根源在于长会话实例贡献了更多轮次,池化时它们的话语权被放大,逐实例时每个任务平权。哪个口径对,取决于你的产品里一次会话和一次任务的相对价值——但无论如何,跨团队对比必须先对齐口径再谈数字。
为什么这个问题现在才被看见
本地推理栈的普及速度远超评测方法学的跟进速度。一两年前,工具调用评测基本发生在 API 厂商自己的服务端,服务行为由厂商统一保证;现在 Ollama、vLLM、SGLang 进入千家万户,同一份开源权重在不同栈、甚至同一栈的不同版本上,暴露出的协议行为都不一样。评测者手里的「环境」从黑盒变成了千人千面的拼装货,但大多数人还在用测 API 的方法测本地栈。
这解释了为什么社区里同一个模型会测出天差地别的工具调用成绩:一部分差异是真实的(量化、模板),另一部分根本是测量伪影。这篇论文的贡献不是提出新方法,而是第一次把这个混淆系统地摆上台面量化——它证明了伪影大到不能忽略。
工程意义:把服务栈写进评测协议
论文结尾给出了一份核查清单,核心思想是:服务栈行为是评测协议的一部分,不是环境噪声。落到实操,至少五件事必做:
- 记录服务栈的名称、版本与工具门控状态,评测报告里一并披露;
- 把拒绝、重试耗尽、超时保存为结构化失败类别,而不是笼统归入「模型未发出调用」;
- 对每个模型选择与其原生能力匹配的调用协议:原生支持工具调用的模型走结构化通道,纯文本协议只留给确实没有原生能力的模型;
- 同时报告池化与逐实例两种统计口径,让读者看到口径敏感性;
- 用已知能力上限的模型做一次冒烟测试,确认失败计数里没有混入基础设施故障。
第五条尤其值得展开。它的逻辑等同于实验科学里的阳性对照:先用一个工具调用能力明确很强的模型跑通全链路,如果这个模型都测出异常低的保真度,问题一定在管道而不是模型。很多团队的评测框架只在加新模型时才被审视,而正确的做法是每次评测会话开始前都先校准管道本身。
对正在做本地模型选型的团队,这篇论文几乎是一份免费的避坑手册。下次你在内部报告里写「这个模型工具调用成功率只有 3%」之前,先问一句:被拒的那部分请求,记到谁头上了?