让LLM学会何时放弃:诊断与训练徒劳推理中止机制

发布时间:2026/8/30 4:41:39
让LLM学会何时放弃:诊断与训练徒劳推理中止机制 在很长一段时间里评估一个 LLM 会不会做数学题标准通常是最终答案对不对。但真实业务里把这个标准放到生产环境后会暴露一个更隐蔽的问题模型为了得到一个答案可能写出明显绕圈的推理甚至在第一步就走错方向却还是坚持生成满max_tokens。这种现象就是典型的 “futile reasoning”也就是徒劳推理。围绕它学界和工程界提出了一个更完整的命题Knowing When to Quit: Diagnosing and Training LLMs to Abort Futile Reasoning。核心工作可以拆成两部分一是诊断Diagnosing二是训练Training模型在推理走向无用的时候主动中止Abort。这篇文章不会停留在概念层面而是会从问题表现、诊断信号、训练方案、最小实验和常见故障几个层面展开。我会用示例代码说明如何构造“中止样本”如何微调一个小模型以及上线后怎么排查“模型还是不肯停”的问题。如果你正在做 LLM 推理优化、数学推理增强、Agent 或 RAG 的耗时优化这篇文章讨论的路径都值得关注。1. 先理解“徒劳推理”为什么是推理质量问题1.1 什么是 futile reasoning为什么 LLM 会陷入其中徒劳推理指的是模型在一条推理链上持续生成 token但继续生成对最终答案没有帮助甚至会把结果带偏。它和“推理过程复杂”不同复杂推理是有价值的中间探索而徒劳推理是已经可以判断“继续下去大概率没有收益”的情况下仍然往下写。LLM 之所以会陷入徒劳推理根源在于训练目标。自回归语言模型的核心目标是“最大化下一个 token 的预测概率”这个目标没有显式地奖励“完成任务并提前停止”。在长链推理中模型会先产生一个局部状态并围绕这个状态继续生成文本。如果一开始的方向就是错的后续每一步都可能保持内部自洽但距离正确答案越来越远。一个典型的例子是解应用题时把未知数设反。模型写到第三步时已经能看出方程结果不合理但它没有“回头评估”的机制也没有“这里应该放弃重来”的训练目标于是大概率继续沿着错误方向生成直到输出一个被证伪的答案。1.2 徒劳推理带来的实际代价徒劳推理不只是“多生成几个 token”的审美问题它会在生产系统中放大成本。影响维度具体表现业务后果计算成本每个请求多生成数百到数千个 token推理费用上升GPU 吞吐下降响应延迟长链生成时间明显增加在线服务超时用户体验下降答案质量后续推理可能引入新错误最终答案反而比早期版本更差可解释性推理链冗长、重复且包含矛盾业务方难以审计和信任Agent 稳定性模型反复调用工具、重复处理同一个状态任务卡死状态机长时间不退出在纯文本问答里徒劳推理可能只是浪费算力在 Agent 场景里模型可能因为不知道“何时放弃”而持续重试同一个失败的工具调用导致整个任务超时。这也是为什么 “Knowing When to Quit” 会成为模型能力的一部分而不是一个后处理优化。1.3 为什么“继续推理”是模型的默认行为模型默认倾向继续生成来自三个层面训练目标没有“完成度”概念。只要还有上下文窗口模型就会为下一个 token 预测一个合理分布。“写完整”远比“写正确”更容易被语言建模目标放大。生成策略缺少终止信号。除非触发 EOS token、停止字符串或max_new_tokens否则生成不会主动停下。模型内部并没有一个专门的“推理价值评估器”。训练数据分布里很少出现“这里应该放弃”的标注。标准指令微调数据通常只给正确答案不给“哪些轨迹继续下去是徒劳”的负例。因此要让模型学会在中途退出必须额外提供诊断信号并通过训练把“退出”变成模型可执行的决策。2. 诊断无效推理从现象到可量化信号诊断不是为了直接删除推理过程而是要找到“哪一段轨迹对最终结果没有贡献”。有了诊断结果才能构造训练标签、评估训练效果并在推理阶段决定是否触发中止。2.1 结果级诊断对比“继续推”和“现在停”的答案结果级诊断的思路很直接如果模型在第k步停下来就能给出正确答案而继续推完整个推理链得到的答案相同那么第k步之后就是冗余的。如果继续推完反而改成了错误答案那么后续推理就是有害的。实际操作流程是对每个问题让模型完整生成一条推理链。在多个候选位置截断推理链。对截断后的轨迹追加“请根据已有信息直接给出最终答案”的提示得到早停答案。比较早停答案、完整推理答案和标准答案。# 伪代码用于离线诊断的流程不是在线推理逻辑 def diagnose_stop_points(question, full_trajectory, correct_answer): steps split_into_steps(full_trajectory) useful_stop_points [] for k in range(1, len(steps)): # 在第 k 步之后截断并请模型直接给出结论 early_answer answer_with_prefix(question \n.join(steps[:k])) if early_answer correct_answer: useful_stop_points.append(k) return useful_stop_points这里的“答案一致”不能只看字符串还要看语义等价。对数学题可以直接比较数值对开放问题需要人工抽样或使用另一个评估模型。注意结果级诊断需要先有完整轨迹因此更适用于离线数据构造而不是在线实时判断。在线场景需要改为“边生成边评估”的过程级方法。2.2 过程级诊断重复、回退和“绕圈”过程级诊断不需要等待最终答案而是观察推理过程中已经出现的文本特征。最常用的信号是“绕圈”表现为某一类内容反复出现比如重复解释同一个条件、反复修正同一个数值、连续多句表达相近意思。可以设计一个简单的重复率指标在候选停止点之后计算一段文本中的 n-gram 是否大量出现在该点之前的上下文中。from collections import Counter def repetition_ratio(full_text, cut_idx, window200, n3): tail full_text[cut_idx:cut_idx window] head full_text[:cut_idx] tail_grams [tuple(tail[i:in]) for i in range(len(tail) - n 1)] head_grams Counter([tuple(head[i:in]) for i in range(len(head) - n 1)]) if len(tail_grams) 0: return 0.0 repeated sum(1 for g in tail_grams if head_grams[g] 0) return repeated / len(tail_grams)除了重复率还可以统计修正句数量例如包含“等等”“不对”“重新思考”的句子比例。一段轨迹中数值或变量名被重新赋值的次数。相邻步骤之间主题相似度是否过高。这些信号适合用于监控和告警但单独使用会有误报。模型可能在推理中刻意重复关键条件或者在正式证明前列举多种解法这些并不一定就是徒劳推理。因此过程级指标通常需要和结果级信号一起使用。2.3 利用奖励模型输出“继续价值”奖励模型是更系统的诊断手段。常用的有两类OVMOutcome Value Model输入“问题 当前推理轨迹”输出该轨迹最终答对的期望概率。PRMProcess Reward Model对推理链中的每个 step 打分反映当前步骤对最终答案的贡献。诊断时可以沿着完整轨迹计算每个位置的 OVM 分数。如果模型在k时刻的分数已经接近 1而继续生成到kn时分数没有明显上升那么k到kn这一段就是低价值轨迹。对于 PRM可以设置一个阈值。如果最近 3 到 5 步的分数低于某个阈值说明模型很可能在错误方向上继续探索。此时在训练数据中把该位置标记为“应中止”的候选点。信号类型计算方式主要用途局限结果对比截断后答案与完整答案比较发现后段无贡献需要完整答案只能离线用文本重复率n-gram 重合比例发现绕圈行为对长句和抽象描述不敏感OVM 分数对完整轨迹前缀打分预测继续生成的价值需要训练奖励模型PRM 分数对每个推理步骤打分定位低价值步骤人工标注成本高奖励模型不仅用于诊断后面训练模型主动中止时它还可以作为强化学习的奖励信号。3. 训练模型主动中止三种可落地的技术路线诊断出“哪些位置应该停”之后下一步是把这种能力压进模型。下面三条路线从简单到复杂可以根据项目阶段选择。3.1 路线一在思维链里加入显式“中止”标记最直接的做法是微调模型让它在推理过程中输出一个特殊标记比如|abort|。这个标记表示“当前推理已经到达收益边界继续写下去没有价值下面直接给出最终答案”。训练样本示例{ prompt: 一个直角三角形的两条直角边分别是3和4求斜边长度。\n解题步骤设斜边为c则c^23^24^225所以c5。, completion: 已经计算出斜边为5继续展开验证不会改变结果。|abort| 最终答案5 }实现上|abort|需要注册为特殊 token避免被 tokenizer 拆成多个 token。否则后处理阶段用字符串匹配会失败。tokenizer.add_special_tokens({ additional_special_tokens: [|abort|] }) model.resize_token_embeddings(len(tokenizer))这条路线实现成本最低适合快速验证。但它有个明显弱点模型可能只是学会在文本里插入标记而不是真正判断“何时应该停止”。如果训练数据中|abort|出现的位置都集中在固定句式后面模型会把它当成格式要求而不是推理决策。3.2 路线二用“截断正确轨迹”进行 SFT如果已经通过诊断确定了最佳停止位置可以把完整轨迹截断到该位置然后作为标准 SFT 数据训练。截断后的样本不再包含后续的无效推理模型学到的是“一条合理推理链应该有多长”。# 示例构造截断样本 def build_truncated_sample(item): # item 包含 question、完整推理轨迹、标准答案、最佳停止位置 stop_index truncated_reasoning item[trajectory][: item[stop_index]] text ( item[question] \n truncated_reasoning \n我已经获得了足够信息不需要继续推理。\n最终答案 item[correct_answer] ) return {text: text}“截断正样本”适合数据清理但它有一个潜在风险如果截断点标注不准确模型会被教坏。比如把一段有必要的验证步骤截掉模型以后遇到同类题目可能过早跳步甚至在条件不足时直接给答案。因此截断式 SFT 最好和显式中止标记一起使用。截断位置负责减少无效长度中止标记负责让模型学会“在某个节点切换行为”。3.3 路线三把“中止”作为强化学习动作如果想要模型真正学会“在什么时候退出”最好把推理过程建模成马尔可夫决策过程MDP。状态是当前问题加已生成轨迹动作可以是“继续生成一步”或“中止”。奖励由 OVM 或规则提供。训练流程可以这样设计用当前策略采样多条推理轨迹。对每条轨迹的每个前缀位置计算 OVM 分数。如果某个前缀位置的分数已经高于阈值但继续生成后最终分数没有提升则“继续”这个动作获得负奖励。反过来如果前缀位置分数很低继续生成后最终答案成功则“继续”获得正奖励。使用 PPO 或 GRPO 更新策略。这个流程比 SFT 更贴近目标模型不是模仿“什么时候该停”的文本格式而是在优化一个包含成本或收益的决策目标。当然强化学习路线的工程复杂度更高。你需要先有一个可用的 OVM需要管理大量采样轨迹还需要处理奖励噪声。但它的收益也更稳定尤其适合对 token 成本和延迟敏感的生产系统。3.4 三条路线怎么选路线实现复杂度数据要求可控性适用阶段显式中止标记 SFT低需要标注停止位置中快速验证截断正样本 SFT低需要求解器或人工标注切点中清理训练数据RL 决策高需要 OVM/PRM 和采样改写高生产级稳定控制实际项目可以先做路线一跑通评估指标再逐步升级到路线三。不要一开始就上强化学习因为诊断信号本身不准确时RL 会把噪声放大。4. 最小实验构造数据集并微调一个带“中止”能力的小模型下面给出一个可复现思路用于在数学推理场景验证“中止能力”。示例代码用于说明流程实际项目要根据自己的数据、路径和依赖版本调整。4.1 实验目标与环境准备实验目标在数学题数据上微调一个小模型让它学会在推理价值不足时输出|abort|同时保留答案正确率。推荐环境Linux 或 macOSPython 3.101 到 2 张 GPU显存建议 16G 以上PyTorch 2.x TransformersDatasets、Accelerate、TRL安装依赖python -m venv .venv source .venv/bin/activate pip install torch transformers datasets trl accelerate # 可选需要更快的推理评估时安装 pip install vllm注意依赖版本会持续变化落地前先锁定版本并重新验证。模型选择上可以用 Qwen2.5-1.5B-Instruct 或 Llama-3.2-1B-Instruct 这类小模型跑通流程。4.2 构造训练数据训练数据需要同时包含“正常推理”和“推理后中止”两类样本。如果只有中止样本模型会学到“所有问题都快速给出答案”这不符合预期。一个可行的数据构造来源是数学推理数据集。先用奖励模型或规则诊断出每条轨迹的最佳停止位置再在该位置插入|abort|。# 示例构造 SFT 训练样本 samples [] for item in reasoning_dataset: # item 包含 question、trajectory、correct_answer 和 stop_index question item[question] trajectory item[trajectory] stop_index item[stop_index] # 保留 stop_index 之前的推理后面直接进入最终答案 truncated trajectory[:stop_index] sample { messages: [ {role: user, content: question}, { role: assistant, content: truncated \n\n我已经获得足够信息继续推理不会提高正确率。|abort| 最终答案 item[correct_answer], }, ] } samples.append(sample)关键点|abort|必须作为特殊 token 加入 tokenizer。样本里不能只有“很快给出答案”的例子还要保留正常的复杂推理样本否则模型会过度压缩推理。字符串final answer之类的分隔符也可以按需加入但不要和基础模型的格式冲突。4.3 微调脚本使用 TRL 的SFTTrainer可以简化指令微调流程。from transformers import ( AutoModelForCausalLM, AutoTokenizer, TrainingArguments, ) from trl import SFTTrainer model_name Qwen/Qwen2.5-1.5B-Instruct model AutoModelForCausalLM.from_pretrained(model_name) tokenizer AutoTokenizer.from_pretrained(model_name) # 注册中止标记 tokenizer.add_special_tokens({additional_special_tokens: [|abort|]}) model.resize_token_embeddings(len(tokenizer)) trainer SFTTrainer( modelmodel, tokenizertokenizer, train_datasetdataset, max_seq_length2048, argsTrainingArguments( output_dir./abort-llm, per_device_train_batch_size2, gradient_accumulation_steps8, learning_rate2e-5, num_train_epochs3, logging_steps50, save_strategyepoch, fp16True, ), ) trainer.train()这里有几个容易踩的细节per_device_train_batch_size要按显存调整代码里写 2 只是一个示例。max_seq_length如果设置太小长推理样本会被截断|abort|可能出现在截断位置之外。fp16True只适合部分 GPU如果没有对应的硬件能力要改成bf16True或关闭。训练早期要先验证|abort|的 token id 是否生效比如tokenizer.convert_tokens_to_ids(|abort|)不应返回unknown_token_id。4.4 推理时如何让中止标记生效生成时不要把|abort|当作完全停止符。更合理的做法是把它作为“推理切换答案”的分隔符标记之前是推理过程标记之后是最终答案。def generate_with_abort(prompt, model, tokenizer, max_new_tokens1024): inputs tokenizer.apply_chat_template( [{role: user, content: prompt}], return_tensorspt, add_generation_promptTrue, ).to(model.device) outputs model.generate( inputs, max_new_tokensmax_new_tokens, do_sampleFalse, ) text tokenizer.decode(outputs[0], skip_special_tokensFalse) if |abort| in text: reasoning_part, final_part text.split(|abort|, 1) else: reasoning_part text final_part extract_answer_heuristic(text) return reasoning_part, final_part如果模型在|abort|之后仍然生成了大量内容不要急着截断整段文本。应该先检查final_part是否符合预期的答案格式再决定是否只取第一句或通过正则提取数值。注意生产环境必须设置max_new_tokens兜底。即使模型没有学会计时中止也不能让请求无限生成。4.5 验证指标训练完成后不能只看损失下降。需要用三个指标衡量中止能力指标计算方法说明答案正确率生成结果中正确答案占比中止能力不能以牺牲正确率为代价推理 token 节省率1 - 使用中止机制后的平均推理 token 数 / 普通生成的 token 数衡量成本收益过早中止率中止后最终答案错误的样本占所有中止样本比例衡量中止决策是否激进评估时最好同时跑两组实验一组是普通微调模型一组是带中止标记的模型。如果带中止标记的模型准确率下降超过 1 到 2 个百分点说明训练数据里“中止点”标注得太激进需要调整诊断阈值。5. 常见问题排查为什么模型还是不肯停下来实际训练中“模型不输出中止标记”和“模型过早中止”是最常见的两类问题。5.1 问题一模型从不输出中止标记现象微调完成后生成结果里仍然没有|abort|。可能原因训练数据里|abort|出现频率过低模型把它当成了噪声。特殊 token 没注册成功tokenizer 把|abort|拆成了多个 token。数据构造时标记放在过长样本的尾部训练时被max_seq_length截断。检查方式统计训练数据中|abort|的出现次数占比建议不低于 10%。执行tokenizer.convert_tokens_to_ids(|abort|)确认不是 unknown id。检查训练样本是否在abort标记处被截断。处理建议调整数据采样比例把|abort|样本保持在 20% 到 50% 之间并在数据清洗阶段单独校验标记是否完整。5.2 问题二模型过于激进频繁中止现象模型在中途没有任何推理成果时就直接输出|abort|最终答案大量错误。可能原因训练数据里大量样本都在很靠前的位置被截断模型学到了“快速退出”的捷径。或者诊断阶段把“冗余推理”误判为“整条推理无用”。处理建议在训练数据中加入更多“需要完整推理后才能正确回答”的样本。对中止位置做下界约束比如要求至少完成三步推理后才能触发中止。如果使用强化学习可以在奖励函数中加入“过早中止惩罚”只有最终答案正确时才给正向奖励。5.3 问题三模型在中止标记之前已经输出最终答案现象final_part为空但推理部分已经出现答案导致后处理取不到最终结果。可能原因基础模型在指令数据上已经学会了把答案写在推理链里新增的|abort|标记被当成了可选项。处理建议训练数据严格规定结论只能出现在|abort|之后。后处理时增加兜底逻辑如果|abort|不在输出中使用启发式规则从文本尾部提取答案。对固定业务场景可以关闭模型自带的特殊输出格式统一使用自定义模板。5.4 问题四训练后效果不稳定现象同一个 checkpoint 在多次推理测试中有时正常中止有时完全不输出标记。可能原因生成阶段开启了随机采样或者训练时没有固定随机种子导致 checkpoint 差异。处理建议在评估阶段使用do_sampleFalse保证对比公平。训练时固定seed。如果业务需要多样输出可以在采样时对是否输出|abort|做额外约束比如用 beam search 或对特殊 token 的 logit 做微调。问题现象可能原因检查方式解决建议不输出中止标记样本占比低、token 被拆开、被截断统计样本占比、检查 token id增加样本量、注册特殊 token过早中止截断点太靠前、缺少完整推理样本检查训练数据长度分布设置最小推理步数标记前已经有答案格式约束不严抽查生成结果统一数据格式并加兜底提取结果不稳定随机采样、训练种子未固定复现实验固定种子评估关闭采样6. 最佳实践和扩展方向6.1 在工程项目中落地“中止能力”的检查清单如果要把“让模型学会退出”这件事落地到生产系统建议按下面清单逐项检查是否已经定义“徒劳推理”的可操作指标。不要只说“推理太啰嗦”要具体到 token 数量、重复率、OVM 分数阈值或答案一致性。是否用离线数据画出了停止位置分布。先统计“第几步之后继续推理不再改变答案”再决定训练数据如何截断。训练数据是否同时包含“继续”和“中止”两类样本。只包含中止样本会让模型对所有问题都倾向提前退出。是否使用特殊 token而不是普通字符串。使用普通字符串容易被 tokenizer 拆分也会在生成时不稳定。是否同时评估准确率和 token 节省。只评估其中一个都无法判断中止能力是否真正可用。是否有人工抽样检查。模型可能在训练集上表现良好但在新题型上错误地过早中止。是否设置了生产兜底参数。即使模型没有输出|abort|也要有max_new_tokens、超时控制和重试机制。6.2 和自一致性、Agent 工具调用结合时的注意事项如果模型已经在用 self-consistency 或 best-of-n 做采样中止机制不能简单叠加。自一致性依赖多条完整采样路径如果每路都在很靠前的位置中止多样性会下降投票结果可能反而不准。推荐做法是先完整采样多条轨迹再做离线裁剪使用裁剪后的路径计算一致性。或者在不同路径上使用不同的中止阈值保留一部分长路径覆盖复杂情况。在 Agent 场景中可以把“中止”设计成一个显式的 action。ReAct 这一类方法允许模型在“思考-行动-观察”之间切换当某个工具连续返回无效结果而且模型在思考中出现重复语句时应该允许它选择“stop”或“abort”动作把控制权交还给调度层。这和“LLM 输出中止标记”本质上是同一个能力只是动作空间从文本扩展到了 Agent 状态机。6.3 下一步研究与实践方向这个方向接下来有四个值得关注的点从“最终答案价值”转向“继续价值”。“继续生成是否还会带来收益”和“当前答案对不对”不是一回事。用 OVM 预测全轨迹价值再和当前前缀价值做差可以更精确地定义徒劳。带推理预算的训练目标。让模型根据题目复杂度动态调整推理长度难题多写几步简单题直接出答案。把置信度校准和中止结合。模型在输出最终答案时同时输出置信度如果置信度较低不触发“强行中止并给出答案”而是触发“重新推理”或“调用工具”。跨任务泛化。数学题里的中止能力能否迁移到代码调试、文档问答、SQL 生成等场景需要在更多任务上验证。回到核心判断让 LLM 学会在什么时候退出和学会如何解题同等重要。真实系统里最好能观察到这样的状态模型在推理还没有走进死胡同之前就发现方向不对并直接回到结论。这个能力不会天然出现它需要显式的诊断信号和训练目标。无论你选择 SFT 还是强化学习都应该先回答一个问题在你的数据里“继续推理”和“答案正确”之间的关系到底是什么。把这个关系量化之后中止训练才有评判标准。