推理 effort 档位分两种实现方式:枚举档位(低/中/高)不透明且随模型更新漂移,显式 token 预算更可审计,建议优先选用预算模式。
providers 已经收敛到两种形式来表达同一个概念,但它们失败的方式各不相同。
如果可以选择,对于任何需要做成本模型的事情,首选 budget 风格,因为单位和你账单上的单位是一致的。effort enum 更方便但更难审计:两条完全相同的请求在"high"档位下可能相差数千个 token,而且一次模型更新就可能移动整个档位的分布,而你的代码没有任何变化。
三个常见的误读,按它们烧钱的速度排序。
它不是上限。 Anthropic 的文档明确说明思考预算是一个目标,模型可能不会完整使用它——而且另一方面,整个响应的硬上限仍然是 max_tokens。为分布做预算,而不是为均值做预算。
它对准确率不是单调的。 更高的 effort 并不一定更好;参见 overthinking,那里已经有公开发表的案例说明它反而更差。把这个档位当做一个需要调优的参数,而不是一个可以拧大的质量旋钮。
它在不同模型之间不可比较。 一个厂商的"high"和另一个厂商的"high"只是共享一个词,其他什么都不一样。任何跨模型比较都必须在匹配的消费量下进行,而不是匹配标签——这是你在非正式比较中最常见的一个缺陷。
在这件事值得做之前,你需要三样东西:五十到两百个你真实任务的例子、一个能对每个例子返回布尔值的自动评分器,以及接受一个与你的直觉不符的数字的意愿。步骤是机械化的。
const LEVELS = ["low", "medium", "high"];
for (const effort of LEVELS) {
let correct = 0, reasoning = 0, output = 0, input = 0;
const latencies = [];
for (const ex of evalSet) {
const t0 = Date.now();
const r = await call({ ...ex.request, reasoning: { effort } });
latencies.push(Date.now() - t0);
const d = r.usage.completion_tokens_details ?? {};
reasoning += d.reasoning_tokens ?? 0;
output += r.usage.completion_tokens; // includes reasoning
input += r.usage.prompt_tokens;
if (grade(ex, r)) correct++;
}
const cost = input * IN_RATE + output * OUT_RATE;
latencies.sort((a, b) => a - b);
report({
effort,
accuracy: correct / evalSet.length,
costPerCorrect: cost / Math.max(correct, 1),
reasoningShare: reasoning / output,
p95ms: latencies[Math.floor(latencies.length * 0.95)],
});
}
这里面有两个细节是承重的。第一,在 OpenAI 形状的 API 上,completion_tokens 已经包含了 reasoning tokens,所以再相加一次就会重复计算你的账单——这个错误会让 reasoning 看起来贵了一倍。第二,核心指标是每个正确答案的成本,而不是每次调用的成本。一个贵了百分之三十但把准确率从 0.62 提升到 0.81 的档位,在真正重要的指标上反而更便宜,而每次调用成本永远揭示不了这一点。
足够多到你能读出的差异不是噪声。eval 集上的准确率是一个二项比例,所以它的标准误是 sqrt(p(1-p)/n)。在 n=100 且 p=0.75 时大约是 4.3 个百分点,而两个档位之间差异的标准误更大——大约六个百分点。所以用一百个例子,五档和六档之间五个点的差距跟零没什么区别,把它当真的来对待是一支团队最终无限期为那个从未真正起作用的 effort 档位付费的方式。
两条出路,都很便宜。用配对比较而不是两个独立的准确率:在两个档位上跑同样的题目,只看那些改变了答案的题目,这就移除了两个档位都从未做对的那部分题目带来的方差。或者扩大样本集——在 n=400 时标准误会减半。如果两者都做不到,要明确你只能检测到大效应,并且只根据大效应来做决定。
在所有三个档位上准确率平坦。 你的任务不需要思考。用最低档位跑,或者完全用一个非 reasoning 模型——那些 thinking 什么都改变不了的任务的分类通常能告诉你你的任务属于哪一种。
从低到中有提升,之后再没有。 常见情况。选中档;高档的额外消费是纯粹的损失。
准确率随每正确答案成本上升而单调上升。 准确率是可以买到的,你现在有两边都有真实数字的商业决策,而不是一个偏好。
高档 effort 时准确率下降。 真的,有文档记录,也不是你的测试框架的 bug——在认定它是 bug 之前先检查那些失败案例。
也记录下 reasoningShare 这个数字。如果你的输出 token 中有百分之九十是不可见的思考,那么降低 effort 是你手里最大的单一成本杠杆,比你能做的任何提示缩短都大。
保留原始的每个题目的结果,而不只是摘要。摘要告诉你选哪个档位;每个题目的记录告诉你哪些题目变了、变向了哪里,而且只有通过这种视角,一个在总体提升的同时打破你之前做对的题目的档位才会变得可见。把它们存在某个地方,以便在下次模型更新后重新读取,因为那时你想做的比较是针对本次运行的,而不是针对某个文档里的一个数字。
在你跑完上面的流程之前:对于任何面向用户和交互式的场景,从最低的非零档位开始,因为延迟会随着 effort 复合,而读者在准确率提升来得及帮助他们之前就会离开。对于有评分器的批处理工作从中间档位开始。只在你能用货币命名错误答案成本的场景保留最高档位——比如报价错误、一次糟糕的迁移、一次升级——因为那是推理成本模型中的算术最终会偏向最高档位的唯一情况。
上面的测试框架需要每个模型的 IN_RATE 和 OUT_RATE,而且 reasoning tokens 是按输出费率计费的,即使 trace 是隐藏的——要用公开的每个模型的费率来代入,而不是一个悄悄排除了你看不见的那部分 completion 的 headline 价格。
What Is a Reasoning Model? A Definition You Can Check
The Cost Curve of Reasoning: Working Out Accuracy per Dollar
Overthinking: When More Reasoning Makes Accuracy Worse