Prhub

#43757 [Bugfix][Reasoning] Fix thinking_token_budget not enforced on re-entry after forced end

原始 PR 作者 ashwing 合并时间 2026-06-30 09:04 文件变更 2 提交数 8 评论 26 代码增减 +146 / -2

执行摘要

修复 thinking_token_budget 在强制结束后的重入问题

修复 Issue #43708:多轮对话中当 max_completion_tokens 远大于 thinking_token_budget 时,thinking_token_budget 未能强制生效,导致模型重新进入 模式不受检查,返回 content=null。生产环境中观察到 40% 的空内容率。

值得精读:该 PR 展示了状态机重入问题的典型修复方法,通过引入扫描偏移和状态重置精确控制重入检测。实现简洁但有效,设计决策清晰,代码注释详尽。建议关注 reasoning 相关功能的团队仔细审查并学习。

讨论亮点
  • rishitdholakia13 建议将测试移动到 test_correctness.py 统一管理,作者采纳。
  • rishitdholakia13 询问 force_index=[0] 的含义,作者解释这是在重入时强制下一位置输出结束 token,且仅在 scan_offset>0 时触发,不影响自然结束。
  • llsj14 提出自然结束后的 token 是否应计入总预算的问题,作者说明自然结束的重入不会触发强制结束,且存在已知的预存 bug,本 PR 仅修复强制结束的重入。

实现拆解

  1. _init_state_entry 中新增 scan_offset 字段,初始化为 0,用于记录强制结束后的扫描起始位置。
  2. _update_think_state 中,检测到强制结束完成后(in_end=Trueend_count 达到结束序列长度),重置 start_thinking, end_thinking, think_count, continue_thinking 为初始值,并将 scan_offset 设为当前输出长度,确保后续扫描只检查新生成的 token。
  3. _update_think_state 中查找 start_thinking 时,使用 max(scan_offset, start_search_pos - (seq_len - 1)) 作为搜索起始位置;当 scan_offset > 0 且检测到新的 <think> 时(即重入),立即设置 state["in_end"] = Trueforce_index = [0],强制结束思考,不给任何额外预算。
  4. tests/v1/logits_processors/test_correctness.py 中添加 TestThinkingBudgetReentry 类,覆盖单 token 结束、多 token 结束和单块不破坏等场景。
文件 模块 状态 重要度
vllm/v1/sample/thinking_budget_state.py 预算状态 modified 6.49
tests/v1/logits_processors/test_correctness.py 正确性测试 modified 6.92

关键符号

_update_think_state _init_state_entry TestThinkingBudgetReentry.test_single_token_end_reentry TestThinkingBudgetReentry.test_multi_token_end_reentry TestThinkingBudgetReentry.test_single_block_not_broken

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

测试文件位置 设计

rishitdholakia13 建议将新增的测试从 tests/v1/sample/ 移动到 tests/v1/logits_processors/test_correctness.py,以便将所有 thinking budget 正确性测试集中在一起。ashwing 最初认为放在 sample 更直接,但同意移动。

结论:ashwing 将测试移到了 test_correctness.py · 已解决

force_index=[0] 的意义 设计

rishitdholakia13 询问为什么在重入时 explicit set force_index 为 0。ashwing 解释 force_index=[0] 表示强制在下一位置输出结束 token,该路径只在 scan_offset>0(强制结束完成)时触发,不会影响自然结束后的重入。

结论:rishitdholakia13 接受了该解释。 · 已解决

自然结束重入的预算计数行为 question

llsj14 提问:自然结束后重入时,之前思考的 token 是否应计入同一预算?ashwing 回答:本 PR 仅修复强制结束的重入,自然结束后的重入 scan_offset 仍为 0,因此不会触发强制结束;但自然结束的预算计数存在预存的 bug,本 PR 不解决。

结论:llsj14 表示理解。 · unresolved

风险与影响

风险集中在 scan_offset 的维护:如果 scan_offset 设置错误或未正确重置,可能导致重入检测失败或误判。但通过测试覆盖和逻辑设计(仅在强制结束完成后设置 scan_offset),风险可控。对自然结束的重入行为没有影响,因为 scan_offset 保持为 0。但自然结束预算计数问题可能仍存在,这是一个遗留问题。

影响使用 vLLM reasoning parser 和 thinking_token_budget 功能的所有用户,尤其是在多轮对话或大 budget 差距场景下,能避免空内容响应。对系统性能影响极小,仅增加一次状态重置和简单的条件判断。

核心路径变更 状态重置 重入检测

关联 Issue

#43708 [Bug]: `thinking_token_budget` enforcement fails on multi-turn conversations when `max_completion_tokens` >> `thinking_token_budget` with ignore_eos:true

完整报告

参与讨论