目录
一、同一道题,多想一会儿确实更对
先看一道小学题:
3 个人 3 天做 3 个零件,9 个人 9 天做几个零件?
标准模型的典型回答是 9:人数和天数都乘了 3,答案就乘 3。推理模型会先把速率拆出来:3 人 3 天做 3 个,说明 3 人 1 天做 1 个,也就是 1 人 1 天做 1/3 个;9 人 9 天就是 9 × 9 × 1/3 = 27。它多花了几百个 token,把一个会被直觉带偏的题做对了。
这个对比很容易让人得出一个结论:多想一会儿 = 更聪明。但把同一套配置换到另一类任务上,结论会立刻变形。
比如从一份采购合同里抽「付款账期」。合同正文写「验收后 30 日内付款」,补充条款写「账期以附件二为准」,附件二又写「月结 60 天」。标准模型可能直接返回 30;推理模型则会写一段很长的推理,论证「正文优先」「附件是补充」「以验收日为起点」,最后给出一个看起来很严谨的 30 天,或者 60 天——它没有停下来指出这三处互相冲突。
两件事放在一起,才是这篇文章的起点:test-time compute 不是一枚「质量旋钮」。它是一组把推理时间换成准确率的手段,有明确的适用条件,也有明确的失效方式。用对了,它能把一个做不对的任务抬过可用线;用错了,它只是让你为同一个错误多付几倍的钱。
二、先把「推理模型」拆成两件事
「推理模型」这个词经常把两个层面的东西混在一起:
| 层面 | 发生了什么 | 你能否在调用时改变 |
|---|---|---|
| 训练侧 | 用可验证奖励(RLVR)或长思维链数据,让模型学会先展开再回答 | 不能,取决于你选的模型 |
| 推理侧 | 在生成时投入更多算力:更长的思维链、多条样本、搜索、迭代 | 能,取决于你的调用参数与编排 |
一个模型可以在训练侧被专门训练成「愿意长时间思考」,也可以在推理侧被要求「多想几步」;两者可以叠加,但不是一回事。推理侧的手段并不要求模型经过推理训练——自洽性投票、best-of-n、树搜索,这些方法在普通指令模型上也能用,只是收益通常更小。
另一个容易踩的坑是计费单位。推理模型产生的 thinking token(不同厂商叫 reasoning token、thinking token 或内部推理 token)通常按输出 token 计费,而且会计入 max_tokens / max_completion_tokens 这类总预算。也就是说:
- 你看到的输出可能只有 200 个 token,账单上却按 2,200 个输出 token 算;
- 预算给少了,模型可能把预算全花在思考上,最后返回一个空答案或半截答案;
- 「思维链不可见」不等于「思维链不花钱」。
后面所有的成本计算,都建立在这一点上。
三、四个旋钮,四种成本形状
把推理侧的手段拆开,常用的其实是四个旋钮。它们买的不是同一种东西,成本曲线也不一样。
| 旋钮 | 做法 | 买到什么 | 成本随什么增长 | 典型失效条件 |
|---|---|---|---|---|
| 更长思维链 | 让模型在给出答案前展开更多步骤、回溯、验算 | 单条轨迹更深,减少「跳步」错误 | 输出 token 线性增长 | 前提理解错、知识缺失、无验证信号 |
| 并行采样 + 投票 | 独立采样 k 条,按多数票或加权投票选答案 | 降低随机错误,提升「至少一条对」的概率 | 输出 token × k | 错误高度相关,样本不独立 |
| 搜索 + 验证器 | 生成多个候选,用规则、测试或打分模型挑选 | 把「生成能力」换成「选择能力」 | 候选数 + 验证器调用 | 验证器并不比生成器可靠 |
| 迭代修订 | 生成 → 批评 → 修改,重复若干轮 | 修正表面错误、补全遗漏 | 轮数 × 单轮成本 | 没有外部反馈时震荡,甚至越改越差 |
它们有一个共同的上限:正确路径必须已经在模型的输出分布里,而且你得有办法把它认出来。 前者决定「采样有没有用」,后者决定「采样之后能不能选对」。这两个前提,值得单独拆开看。
四、前提一:路径覆盖——样本再多,也救不回共享的错误前提
自洽性(self-consistency)的常见说法是:采样 k 条思维链,取多数答案,准确率就会上升。这个说法隐含了一个很强的假设:样本之间是独立的。现实里,模型的多条样本往往共享同一个错误前提。
写一个小模拟就能看清这件事。假设:
- 有 15% 的概率,模型从一开始就理解错了题意(q = 0.15)。这时 k 条样本会一起错,而且错得一致;
- 在理解正确的前提下,每条样本有 60% 的概率得到正确答案(p = 0.60),否则随机落到 20 个错误答案之一。
import random, collections
def simulate(k, q=0.15, p=0.60, trials=50000, seed=7): rng = random.Random(seed) maj = any_ok = 0 for _ in range(trials): if rng.random() < q: votes = ["shared_wrong"] * k # 共享错误前提:k 条一起错 else: votes = ["right" if rng.random() < p else f"w{rng.randrange(20)}" for _ in range(k)] top = collections.Counter(votes).most_common(1)[0][0] maj += top == "right" any_ok += "right" in votes return maj / trials, any_ok / trials
for k in (1, 3, 5, 9, 15): m, a = simulate(k) print(f"k={k:<2} majority={m:.3f} pass@k={a:.3f}")输出:
k=1 majority=0.512 pass@k=0.512k=3 majority=0.629 pass@k=0.795k=5 majority=0.773 pass@k=0.843k=9 majority=0.836 pass@k=0.849k=15 majority=0.848 pass@k=0.849三个读数值得注意:
pass@k的上限是 0.85,不是 1。 因为 15% 的情况下所有样本共享同一个错误前提,采样再多也覆盖不到正确路径。这就是「路径覆盖」的上限。- 多数投票也会撞到同一个上限。 k 从 1 加到 15,多数投票从 0.512 升到 0.848,然后停住。它并没有把「至少一条对」变成「交付的那条对」的免费转换器。
- k=5 时,
pass@k是 0.843,多数投票只有 0.773。 中间那 7 个百分点,就是「选择器」的缺口。候选里有正确答案,但你没能把它选出来。
这个模拟的参数是人为设定的,但结构在真实任务里很常见:模型把「附件二」当成「附件一」,把「税前」读成「税后」,把「不含运费」漏掉——这些错误不会因为采样次数增加而分散,只会被重复采样放大成一种「共识」。
所以看到一个「多采样几条、取多数」的方案时,先问两个问题:
- 这个任务的错误,是分散的随机错误,还是共享的前提错误?前者投票有用,后者投票有害。
- 你有没有一个独立于采样过程的选择器?如果没有,多数投票只是成本较低的选择器,不是可靠的选择器。
五、前提二:可判定——没有验证器,搜索只是掷骰子
第二个前提是「可判定」:你得能在多个候选里判断哪个更对。
验证器的强弱,直接决定了 test-time compute 的回报。把验证器按可靠性排一下:
| 验证器 | 例子 | 可靠性 | 成本 |
|---|---|---|---|
| 精确规则 | 数学答案比对、单位检查、正则、JSON Schema | 高(前提是规则写对) | 低 |
| 可执行测试 | 单元测试、类型检查、SQL 约束、编译 | 高(覆盖到就算数) | 中 |
| 环境状态 | 工具返回、数据库查询、页面实际渲染结果 | 高(但只覆盖被观测到的部分) | 中到高 |
| 交叉约束 | 多个字段互相校验、金额合计、日期先后 | 中(能抓矛盾,未必能给正解) | 低 |
| 奖励模型 / PRM | 过程奖励模型、打分器 | 中(分布外会失准) | 中 |
| LLM-as-a-Judge | 另一个模型打分 | 低到中(与生成器同源时有系统性偏差) | 中 |
| 人工 | 编辑、标注、复核 | 高 | 高 |
一个反直觉的结论是:best-of-n 的效果上限由验证器决定,而不是由 n 决定。 如果验证器把一个错误答案排在第一,采样 100 条也只是给这个错误答案增加候选;如果验证器和生成器同源、共享同一套偏好,它会把生成器的错误也一起「验证」过去。
这也是为什么推理模型在数学和代码上看起来进步明显:这两类任务恰好有便宜、可靠、可自动化的验证器。数学题可以对答案,代码可以跑测试。换到开放写作、战略判断、需求优先级,验证器要么很贵(人工),要么很弱(LLM 打分),test-time compute 的回报就会明显下降。
六、买不到的四样东西
把「买不到」的部分单独列出来,比列「买到了什么」更有用。
6.1 买不到缺失的知识
模型不知道某个内部 API 的签名,多想十分钟也不会知道。它更可能做的是:根据命名习惯编一个看起来合理的签名,再用一段严谨的推理证明这个签名「应该」成立。推理能力会把「不知道」包装成「想清楚了」。
知识缺失的解法是检索、工具、上下文注入,不是提高推理预算。判断方法也很简单:如果正确答案依赖一个模型训练时不可能见过的事实(内部文档、今天的数据、你刚改的接口),先补上下文,再谈 reasoning budget。
6.2 买不到正确的题意
题目本身有歧义时,更长的思维链往往会加固模型选中的那一种解释。它会把「我假设这里指的是 X」写成一段不容置疑的推导,而不是停下来问「你说的 X 是哪一个」。
前文的合同例子就是这一类:三处条款冲突,正确动作是报告冲突、请求澄清;推理模型却可能在几种解释里选一个,然后把它论证得很像那么回事。对歧义输入,更好的做法是约束输出结构——要求它先列出「我识别到的歧义与假设」,再给答案;或者把「无法确定」作为一个合法输出。
6.3 买不到外部执行与状态
「这个用户上个月的订单有没有退款」「这段代码在 Node 22 上能不能跑」「今天的汇率是多少」——这些问题的答案不在参数里,也不在思维链里。推理可以帮你决定调用哪个工具、怎么组合结果,但不能替代调用本身。
Agent 场景里常见的误判是:把「规划得更细」当成「执行得更对」。规划再细,工具返回 500 就是 500。预算应该分给「规划 + 验证 + 重试」,而不是只堆在第一步的思考上。
6.4 买不到低延迟与低单价
这一条是算术,不是观点。假设生成速度是 50 token/s,多花 2,000 个推理 token,就多出约 40 秒;即使算力更快、达到 100 token/s,也要多 20 秒。对实时客服、语音助手、IDE 补全这类有硬延迟预算的场景,20 秒的「更聪明」通常不可用。
成本同理:推理 token 按输出计费,而输出 token 的单价通常高于输入 token。你买到的准确率,是用延迟和账单换来的。下一节把这笔账算清楚。
七、成本曲线:按「每个正确答案」算账
比较两个配置,不能只比「每次调用多少钱」,也不能只比「准确率多少」。把两者放在一起,才有可比的单位:每个正确答案的成本。
先看一个简化的算例。假设:
- 输入 1,500 token,答案 300 token;
- 示例单价:输入 1 元 / 百万 token,输出 4 元 / 百万 token(只为演示数量级,不代表任何厂商的真实报价);
- 标准模型每次调用 0.00270 元;
- 推理模型多花 2,000 个推理 token,每次调用 0.01070 元,准确率 0.90。
IN_TOK, OUT_TOK = 1500, 300PI, PO = 1.0, 4.0 # 示例单价:元 / 百万 tokenBASE = (IN_TOK * PI + OUT_TOK * PO) / 1e6
def per_correct(cost, acc): return cost / acc
def reasoning(think, acc): cost = (IN_TOK * PI + (OUT_TOK + think) * PO) / 1e6 return cost, per_correct(cost, acc)
for a in (0.25, 0.50, 0.75): c, pc = reasoning(2000, 0.90) print(f"std acc={a:.2f} std/correct={per_correct(BASE, a):.5f} " f"reason/correct={pc:.5f}")输出:
std acc=0.25 std/correct=0.01080 reason/correct=0.01189std acc=0.50 std/correct=0.00540 reason/correct=0.01189std acc=0.75 std/correct=0.00360 reason/correct=0.01189结果有点反直觉:即使标准模型的准确率只有 25%,推理模型的「每个正确答案成本」仍然略高(0.01189 对 0.01080)。原因很简单——推理模型每次调用贵了约 4 倍,而准确率最多只能到 1,单位正确成本不可能低于单次调用成本。
把盈亏平衡点算出来:
a0* = a1 × c0 / c1 = 0.90 × 0.00270 / 0.01070 ≈ 0.227也就是说,在这个示例单价下,只有当标准模型的准确率低于约 22.7% 时,推理模型才在「每个正确答案」上更便宜。标准模型越准,推理模型越难靠成本说服人。
| 标准模型准确率 | 标准:每次 / 每正确 | 推理:每次 / 每正确 | 每正确谁更便宜 |
|---|---|---|---|
| 0.25 | 0.00270 / 0.01080 | 0.01070 / 0.01189 | 标准(略低) |
| 0.50 | 0.00270 / 0.00540 | 0.01070 / 0.01189 | 标准 |
| 0.75 | 0.00270 / 0.00360 | 0.01070 / 0.01189 | 标准 |
这不是说推理模型没有价值,而是说它的价值不在「普遍更省钱」。它买到的是:在标准模型几乎做不对、而错误又足够昂贵的任务上,把正确率抬过可用线。 如果一次错误要人工复核 30 分钟,多付几厘钱把准确率从 25% 抬到 90%,账立刻就算得过来;如果错误只是让用户多点一次重试,推理模型的溢价就很难成立。
7.1 级联:先便宜后贵,但前提是有验证器
比「全量上推理模型」更常见的做法是级联:先用便宜模型,验证不过再升级。
在上面的例子里,标准模型准确率 0.60,验证器能抓出所有错误,升级后准确率 0.90:
级联成本 = 0.00270 + (1 - 0.60) × 0.01070 ≈ 0.00698级联准确率 = 0.60 + 0.40 × 0.90 = 0.96每个正确答案成本 ≈ 0.00698 / 0.96 ≈ 0.00727比全量推理(0.01189 / 正确)便宜,也比纯标准模型(0.00450 / 正确)更准。但这个结果依赖一个很强的假设:验证器能可靠地判断「这次答错了」。验证器有漏判,级联就会把错误直接放出去;验证器有误判,级联就会为本来正确的答案多付一次推理费。
所以级联的工程问题不是「怎么路由」,而是「验证器从哪来」。有单元测试、Schema、金额合计、外部 API 校验,就值得做;只有「再问一个 LLM 觉得对不对」,级联的收益会被验证器的偏差吃掉。
八、什么时候值得开推理:一张决策表
把前面的条件收成一张表。判断顺序是:先问「错了能不能被发现」,再问「这个错误值不值得多花钱」。
| 任务类型 | 可验证信号 | 建议 | 理由 |
|---|---|---|---|
| 竞赛数学、形式化证明 | 精确答案、证明检查器 | 开,预算可高 | 验证便宜且可靠,搜索和投票都有落点 |
| 代码修复、重构 | 单元测试、类型检查、编译 | 开,把测试反馈接进循环 | 测试既是验证器,也是下一轮的输入 |
| 结构化抽取 | Schema、跨字段约束、合计校验 | 开中等预算,验证失败再重试 | 能抓矛盾,但「哪个解释对」仍需人工规则 |
| 分类、意图识别 | 标签、抽样人工复核 | 通常不开 | 任务短、答案空间小,小模型加校准更划算 |
| 创意写作、文案 | 人工偏好、品牌规范 | 选择性开,用并行采样给编辑挑 | 验证器贵,投票不能替代品味 |
| 多轮 Agent | 工具返回、环境状态、任务完成条件 | 开,但预算分给规划与验证 | 只堆第一步的思考,不会让工具返回变对 |
| 实时客服、语音 | 延迟硬约束、业务规则 | 谨慎,级联 + 流式 | 20 秒的思考在对话里不可用 |
| 高风险决策 | 人工复核、审计记录 | 开,但输出必须带证据与不确定性 | 正确率之外,还要能解释「为什么」 |
一个实用的经验法则:如果这个任务错了以后,你能在 5 分钟内写出一条自动检查,推理预算就值得花;如果检查本身要靠人看半天,先把预算花在「让人更快发现错误」上。
九、工程落地:把预算花在刀刃上
9.1 先管住预算,再谈效果
推理 token 计入总输出预算,所以第一件事是把截断当成一等失败,而不是等它变成空答案。
type Result = { text: string; finishReason: string; reasoningTokens: number };
async function ask(prompt: string, budget: number): Promise<Result> { const r = await callModel({ prompt, maxOutputTokens: budget }); if (r.finishReason === "length") { // 预算可能被推理 token 吃光,答案被截断或为空。 // 不要静默返回空字符串:要么加预算重试,要么降级到非推理模型。 return ask(prompt, Math.ceil(budget * 1.5)); } return r;}不同厂商的字段名不一样(finish_reason、status、incomplete_details),但规则相同:看到长度截断,就把这次调用标记为失败并进入降级路径。在日志里单独记 reasoningTokens,否则你只会看到「输出 200 token,账单 2,200 token」这种对不上的账。
9.2 级联要有验证器,也要有回退上限
级联不是「便宜模型 + 贵模型」两行代码,它至少需要三个东西:
- 验证器:规则、测试、Schema、外部校验,任选其一;
- 升级条件:验证失败、置信度低于阈值、输出结构不合法;
- 回退上限:最多升级一次,避免在验证器和生成器之间来回打转。
没有第 1 项,级联只是随机分流;没有第 3 项,一次坏输入可能烧掉整条链路的预算。
9.3 流式输出遮不住全部延迟
流式可以把「首 token 时间」降下来,但推理模型的思考阶段通常不产生用户可见的 token。用户看到的是:转圈 15 秒,然后答案突然开始流。对交互式场景,这 15 秒仍然是实打实的等待。
能做的有三种:把非关键路径的推理移到后台、先返回一个可用的草稿再异步升级、或者对简单请求直接走非推理模型。不要用「我们在流式输出」来解释为什么用户要等 20 秒。
9.4 缓存能省输入,省不了思考
- 精确缓存:把「规范化后的输入 + 模型 + 预算」作为 key,缓存最终答案。适合 FAQ、固定报表、重复工单。
- Prompt caching:缓存长系统提示词、文档、few-shot 示例,降低输入侧成本。它不会缓存模型这次生成的推理过程。
- 语义缓存:相似问题复用答案,命中率更高,但错配的代价也更高。至少要用阈值 + 抽样复核,别把「差不多」当成「一样」。
9.5 蒸馏:把大模型的思考变成小模型的直觉
如果某类任务已经被验证为「推理模型稳定做对」,可以把成功的推理轨迹筛出来,蒸馏到更小的模型里。关键是筛:只用验证器通过的轨迹,别把失败轨迹也喂进去;否则小模型学到的是「自信地犯错」。
蒸馏之后仍然要重新评测:小模型在分布内的表现可能接近,分布外会退化得更快。它是把单位成本打下来的手段,不是一次性替代。
9.6 观测指标
| 指标 | 为什么看它 |
|---|---|
| 输入 / 输出 / 推理 token 分开记 | 定位账单涨在哪一段 |
| 每个正确答案的成本 | 把准确率和成本放进同一个单位 |
| 延迟 p50 / p95 | 推理 token 的方差通常比答案 token 大 |
| 验证器通过率、升级率 | 级联是否真的在省钱 |
| 截断率、重试率 | 预算是否给少了 |
| 分布外抽样准确率 | 防止蒸馏或缓存把退化藏起来 |
十、评测陷阱:别用「感觉更聪明」做结论
推理模型很容易在演示里显得更强,因为演示通常挑的是「标准模型答错、推理模型答对」的题。评测方法本身——公共基准、自建评测集、A/B 与人工评估——在《如何科学评测一个 LLM 应用》里已经展开,这里只列推理模型特有的几个陷阱。
1. 同成本比较,而不是同模型比较。 公平的对照是:标准模型在同样的 token 预算下采样多次(或加长提示词),对比推理模型的一次调用。只拿「标准模型一次」对「推理模型一次」,你比较的是预算,不是模型。
2. 同时报告准确率、token 和延迟。 只看准确率,会把「贵 4 倍、慢 20 秒」的代价藏起来;只看成本,又会把「便宜但不可用」的配置留下。
3. 公共基准有污染风险。 数学和代码 benchmark 的题目可能出现在训练数据里。至少留一套私有题、改写题或时间上更近的题,作为对照。
4. 多数投票的高一致不等于高正确。 前文的模拟已经说明:共享错误前提会让多条样本一起错、且错得一致。把「一致率」当成置信度,会在正需要报警的时候给出高分。
5. LLM-as-a-Judge 与生成器同源时有系统性偏差。 用同一个模型家族做裁判,它更容易认可自己风格的答案。能用规则就用规则;必须用模型时,换一个家族、交换位置、加人工抽样。
6. 思维链不是模型的真实计算过程。 它是模型生成的文本,不是可审计的内部状态。把 CoT 当成「它为什么这么答」的证据,容易过度信任;把 CoT 原样回放给用户,还可能泄露提示词、内部数据或本不该出现的推理细节。
十一、五个反复踩的坑
- 预算被思考吃光,返回空答案。
max_tokens是总预算,不是答案预算。看到长度截断就重试或降级,别把空字符串当成模型「不会」。 - 把推理 token 当成免费或不可见。 它按输出计费,通常还更贵。账单对不上时,先查 reasoning token,再查重试。
- 没有验证器就上自洽投票。 错误相关时,投票会把同一个错误放大成共识;先确认错误是分散的,再谈采样。
- 用同源 LLM 当验证器。 生成器和裁判共享偏好,裁判会把生成器的错误一起放过。优先用规则、测试、Schema,其次才是异源模型加人工抽样。
- 把思维链原样回放给用户或写进日志。 它可能包含敏感信息、错误假设和不适合展示的内部推理。对外给结论和证据,对内只留脱敏摘要。
十二、小结
- 推理模型是两层东西。 训练侧让模型「愿意想」,推理侧让你「让它多想」;后者不要求前者,但前者的收益通常更大。
- test-time compute 买的是采样、搜索和选择,不是智力。 它的上限由两件事决定:正确路径在不在输出分布里,以及你有没有可靠的验证器。
- 错误相关时,多采样会加固错误。 共享的错误前提不会因为 k 变大而分散;多数投票只能处理分散的随机错误。
- 验证器决定搜索的上限。 数学和代码好看,是因为它们有便宜可靠的验证器;开放任务的回报会明显下降。
- 推理买不到知识、题意、外部状态和低延迟。 缺知识补检索,有歧义先澄清,要状态调工具,延迟敏感就级联或降级。
- 按每个正确答案算账。 推理模型通常每次更贵;只有当标准模型准确率足够低、且错误足够昂贵时,溢价才划算。
- 级联的关键是验证器。 没有验证器的级联只是随机分流;有验证器,才能把预算花在真正需要的请求上。
- 把截断、token、延迟、升级率都记下来。 没有这些数,你无法判断下一次该加预算、换模型,还是改验证器。
参考来源
- Scaling LLM Test-Time Compute Optimally can be More Effective than Scaling Model Parameters(Snell et al., 2024)
- Chain-of-Thought Prompting Elicits Reasoning in Large Language Models(Wei et al., 2022)
- Self-Consistency Improves Chain of Thought Reasoning in Language Models(Wang et al., 2022)
- Let’s Verify Step by Step(Lightman et al., 2023)
- Large Language Monkeys: Scaling Inference Compute with Repeated Sampling(Brown et al., 2024)
- Large Language Models Cannot Self-Correct Reasoning Yet(Huang et al., 2023)
- Self-Refine: Iterative Refinement with Self-Feedback(Madaan et al., 2023)
- DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning(2025)
- s1: Simple test-time scaling(2025)
- OpenAI:Learning to reason with LLMs
- OpenAI Reasoning models 文档(推理 token 与计费说明)