工具调用成本评测:空回复与重试的代价
一项涵盖300个函数调用任务的测试称,Mistral Small在最多两次重试后仍有58个任务返回空回复,单个成功任务成本高于GPT-6 Luna。测试仅重试无法解析的输出,可解析但错误的答案直接计为最终结果,比较成本时需同时审视这一口径。
AI 深度解读
Mistral Small (2603) 的输入价格低至每百万 tokens 0.15 美元,但作者在 300 个公开函数调用任务中发现,它有 58 个任务在两次重试后仍返回空内容。按成功任务计算成本,它反而比 GPT-6 Luna 贵。
这组测试值得看的,是作者如何计算重试成本,以及如何区分「读不出来」和「读得出来但答错了」。不过,空回复的原因尚未查清,结果也只对应这套提示词和数据,不能直接当成模型的通用排名。
为什么单价便宜,完成任务却更贵
作者采用了一条接近实际使用条件的规则:回复无法解析,可以重试;回复能够解析,即使答案错误,也结束任务。业务代码通常没有标准答案,无法像评测程序一样随时判断模型是否答对。
这里的函数调用任务,要求模型输出工具名称和参数。例如,用户需要一个 1 到 100 之间的随机数,模型应返回:
{"name":"generate_random_number","arguments":{"min":1,"max":100}}
作者的 parseJsonLoose(text) 会先检查空字符串,再尝试提取代码围栏和 JSON 对象。原文只展示了部分解析器,没有给出完整实现。
重试配置是 config.retries = 2、temperature = 0,因此每个任务最多发送三次相同请求。下面这段循环决定什么时候停止,以及最终如何累计成本:
for (let a = 0; a <= config.retries; a++) {
const res = await transport({ model, messages, max_tokens: maxTokens, temperature: config.temperature, extra: extra && extra.body });
const { cost, estimated } = res.ok ? attemptCost(res, model, prices) : { cost: 0, estimated: false };
const rec = { ms: res.ms, cost, cost_estimated: estimated, status: res.status };
// (error-status branch omitted)
const j = judge(config, res.text, row);
rec.valid = j.valid;
attempts.push(rec);
if (j.valid) {
last = { outcome: j.pass ? "pass" : "wrong", reason: j.reason, output: res.text };
break;
}
last = { outcome: "invalid", reason: j.reason, output: res.text };
}
...
cost: attempts.reduce((s, x) => s + x.cost, 0),
要看的是 if (j.valid):解析通过就退出,答案错误也不会继续尝试。最后的 reduce 则把各次尝试的记录成本相加。
每次成功的成本,要让成功任务承担失败任务和重试的开销。
按这个口径,Mistral Small 每个成功任务花费 0.000098 美元;GPT-6 Luna 是 0.000051 美元,后者通过率为 93.0%,且没有发生重试。只比较输入 token 单价,会漏掉这层差别。
两次重试,为什么没能救回大多数空回复
Mistral Small 完成 300 个任务,共发出 434 次请求,其中 134 次是重试。最终有 58 个任务仍为空,占全部任务的 19.3%。
这 58 个任务各消耗两次重试,共计 116 次。剩余只有 18 次重试用于最终恢复可读输出的任务,因此恢复任务最多也只有 18 个。按这个上限推算,触发重试的任务中,至少 76.3% 最终仍然不可读。
这里要分清「18 次重试」和「18 个任务」。一个恢复任务可能使用两次重试,所以作者给出的是恢复任务数量的上限。
重试占该模型总支出的 23.9%。作者用已存数据计算,扣除重试费用后,每个成功任务的成本约为:
$0.020673 × (1 - 0.239) / 212 = $0.0000742
它仍然高于 Luna 的 0.000051 美元。这个数字是保持 212 个成功任务作为分母的费用拆分,并非重新进行一次「禁用重试」实验。
空回复也不只出现在复杂任务里。tc-2 提供了 generate_random_number(min, max) 和 calculate_loan_payment(loan_amount, interest_rate, loan_term),用户只要求随机数,连续三次却都没有内容。tc-10、tc-19、tc-21、tc-37 也属于短对话、工具匹配明确的情况。
另外 30 次失败则比较常见:14 次是相同值的不同措辞,10 次是不同值,5 次是数字写法不同,1 次缺少参数。这也提醒我们,评测中的失败不全等于业务不可用,部分结果可能受判分规则影响。
同场另外八个模型中,六个没有重试;DeepSeek V4.1 Flash 重试 2 次,Qwen 3.8 Flash 重试 5 次,最终都没有不可读回复。因此,这组证据指向 Mistral Small 在该提示词下的表现,不能推导成「便宜模型都不擅长工具调用」。
我的判断
我认同作者用「每个成功任务的成本」比较模型。对于需要结构化输出的程序,空回复会直接阻断后续步骤,低 token 单价无法抵消这种损耗。
但这次测试还不足以解释故障。作者保存了回复文本,却没保存 finish_reason,也缺少空回复对应的 usage.completion_tokens 信息。究竟是模型没有生成内容,还是调用链路存在其他情况,原文没有说清。
同样,连续重发相同请求是否合适,也没有对照实验。作者没有比较调整提示词、改变输出约束或切换模型后的恢复率。因此,这份结果能证明当前重试循环效果有限,不能证明所有恢复策略都无效。temperature = 0 也不保证完全确定,作者明确承认重复运行可能得到不同结果。
作者提供了数据、提示词、脚本和价格,但原文没有完整展开请求配置,无法据此确认它与读者日常使用的原生工具调用方式是否一致。文章最后还引导读者使用 Clearbar 评测服务,其建议具有产品推广背景;是否适合自己的业务,仍要靠自己的输入样本验证。
能拿来做什么
- 给 Node 里的 AI 调用记录尝试次数、累计费用和最终结果,分别统计不可解析、答案错误与成功,避免只看 token 单价。
- 对空回复设置有限重试,并检查恢复率。如果多数重试始终失败,就评估调整请求或切换模型,别默认再发一次就能解决。
- 从自己的任务中抽取 10–100 组输入与期望结果,先确定业务允许的等价表达,再比较成功率和每个成功任务的成本。
- 复现时保留
finish_reason、usage.completion_tokens和回复文本。原文的整轮测试覆盖九个模型,总费用为 0.503184 美元,平均每行约 0.0017 美元;这些只是作者该次运行的费用。