【Bug已解决】Proposal: Change default `max_completion_length` from 256 to 512 解决方案
【Bug已解决】Proposal: Change defaultmax_completion_lengthfrom 256 to 512 解决方案原始报错Proposal: Change defaultmax_completion_lengthfrom 256 to 512 场景在 GRPO / RL 训练里生成completion阶段有个超参数max_completion_length默认 256。但很多任务的回答本就需要更长比如带推理链、带工具调用结果256 不够生成被悄悄截断。截断后的 completion 拿去算奖励/对齐信号失真训练效果差。提议把默认调到 512。 关键词超参数默认值、生成截断、max_completion_length、奖励信号、向后兼容、可配置覆盖。一、现象长什么样用默认max_completion_length256训练模型生成回答超过 256 token 的部分被直接截断截断是静默的——日志不总提示样本看起来正常完成被截断的 completion 往往正是带完整推理/工具结果的长回答价值最高奖励模型基于残缺文本打分分数偏低且不稳定把默认改 512 后长回答不再被截训练曲线改善。用户提这个 proposal本质是指出默认值不能覆盖典型任务的真实长度导致系统性截断。二、背景max_completion_length 决定什么在 RLGRPO/PPO训练的生成阶段模型对每个 prompt 采样一条 completion。max_completion_length限制这条 completion 的最大 token 数达到上限还没生成 EOS结束符→ 强制截断截断文本进入后续奖励计算、价值估计截断发生在句子/推理中间时语义破碎奖励模型也难给准。默认值 256 是早期设定当时任务短。如今任务推理链、工具调用、长格式普遍更长256 成了瓶颈。问题是默认值太小且静默截断而不是报错。三、根因默认过小 截断不可见根因拆解默认过小256 覆盖不了典型长任务截断常态化。截断静默达到上限不告警用户不知道有多少样本被截。无统计训练中不统计被截断样本比例问题潜伏。无覆盖入口旧代码把 256 写死用户想调大得改源码。默认值没随任务演进任务变长默认值没跟。下面用最小模型复现默认 256 悄悄截断长 completion再给修复。四、最小可运行复现def generate(prompt_len, true_length, max_completion_length): # 模拟生成真实需要 true_length但被 max 截断 gen min(true_length, max_completion_length) truncated gen true_length return gen, truncated if __name__ __main__: # 默认 256真实需要 400 gen, trunc generate(10, 400, 256) print(f生成 {gen} token被截断? {trunc}) # 生成256截断True # 截断的 completion 进入奖励计算 - 信号失真运行显示 completion 被截断且没有任何提示——就是 proposal 描述的隐患。五、方案调大默认 暴露为可配置第一层把默认值调到能覆盖典型任务的 512并务必暴露为配置项不写死DEFAULT_MAX_COMPLETION_LENGTH 512 # 从 256 调到 512 class TrainerConfig: def __init__(self, max_completion_lengthNone): # 用户不传则用新默认传了用用户的向后兼容 self.max_completion_length ( max_completion_length or DEFAULT_MAX_COMPLETION_LENGTH) def generate_cfg(prompt_len, true_length, cfg: TrainerConfig): gen min(true_length, cfg.max_completion_length) return gen, gen true_length if __name__ __main__: cfg TrainerConfig() # 用新默认 512 print(默认最大长度:, cfg.max_completion_length) # 512 gen, trunc generate_cfg(10, 400, cfg) print(f生成 {gen}截断? {trunc}) # 400不再截断新默认 可配置老用户传旧值仍生效向后兼容新用户自动享受 512。六、方案截断检测 统计告警第二层生成阶段统计被截断样本比例超过阈值就告警让静默截断变可见class TruncationMonitor: def __init__(self, warn_ratio0.05): self.total 0 self.truncated 0 self.warn_ratio warn_ratio def observe(self, gen_len, max_len, true_len): self.total 1 if gen_len max_len and true_len max_len: self.truncated 1 ratio self.truncated / self.total if self.total else 0 if ratio self.warn_ratio: print(f[warn] 截断比例 {ratio:.1%} 过高建议调大 max_completion_length) if __name__ __main__: mon TruncationMonitor() cfg TrainerConfig() for _ in range(20): gen, _ generate_cfg(10, 400, cfg) # 400512? 否不截断 mon.observe(gen, cfg.max_completion_length, 400) # 若默认仍是 256这里会触发告警监控把有多少样本被截变成可观测指标默认值是否够大一目了然。七、方案向后兼容与迁移第三层改默认值不能破坏老用户。策略是新默认 显式旧值仍可传 文档说明def resolve_max_completion(user_value, legacy_config_fileNone): if user_value is not None: return user_value # 用户显式优先 if legacy_config_file and max_completion_length in legacy_config_file: return legacy_config_file[max_completion_length] # 旧配置文件 return DEFAULT_MAX_COMPLETION_LENGTH # 否则新默认 512 if __name__ __main__: # 老用户配置文件写了 256 - 尊重老配置 old_cfg {max_completion_length: 256} print(老配置:, resolve_max_completion(None, old_cfg)) # 256 # 新用户无配置 - 新默认 print(新默认:, resolve_max_completion(None, None)) # 512优先级用户 CLI 旧配置文件 新默认既改进默认又不破坏既有行为。八、验证把默认值 截断可见锁进测试def test_default_raised_to_512(): assert TrainerConfig().max_completion_length 512 def test_user_value_respected(): assert TrainerConfig(256).max_completion_length 256 def test_truncation_detected(): mon TruncationMonitor(warn_ratio0.0) cfg TrainerConfig(256) # 故意用小值触发 for _ in range(3): gen, _ generate_cfg(10, 400, cfg) mon.observe(gen, cfg.max_completion_length, 400) assert mon.truncated 3 # 全部截断被记录 if __name__ __main__: test_default_raised_to_512() test_user_value_respected() test_truncation_detected() print(max_completion_length 默认与截断测试通过。)九、排查清单生成被静默截断按顺序查默认够大吗max_completion_length 是否覆盖任务典型长度256 对长任务常不够。截断静默达到上限是否有告警/统计静默截断最危险。可配置默认值是否暴露为配置项还是写死在代码里截断比例训练中被截断样本占比多少超过 5% 就该调大。向后兼容改默认后老用户的显式配置/旧文件是否仍被尊重奖励影响被截的 completion 是否正好是高价值长回答截断是否污染奖励监控是否有截断比例指标可观测没有则问题潜伏。十、小结把默认 max_completion_length 从 256 改 512是默认值过小导致生成静默截断、污染训练信号的超参数问题。修复三层调大默认 可配置默认升到 512且暴露为配置项不写死截断可见统计被截断样本比例超阈值告警杜绝静默截断向后兼容优先级用户 CLI 旧配置 新默认改进不破坏老行为。核心原则超参数默认值必须能覆盖典型场景且任何截断都不该静默发生。把默认调到合理值、把截断变成可观测指标、把默认值做成可覆盖的配置训练才不会因为悄悄砍掉长回答而默默变差。