--- name: bad-case-to-dpo description: 把生产 bad case / 失败轨迹转成训练数据时使用——从失败归因、轨迹前缀截取、DPO 偏好对构造、LoRA 训练到边界集与保留集双集验证的完整链路;也用于判断某类问题该走 SFT、DPO、RL 还是先修 Harness/工具/tokenizer,以及如何从运行轨迹中提取带证据的结构化学习信号。触发词:bad case、失败归因、DPO、偏好对、轨迹前缀、拒绝采样、过度矫正、边界集、保留集、过早结束、reward hacking、LoRA 微调、训练评估隔离。 --- # 从问题案例到训练数据 ## 何时使用 - 生产出现 bad case:用户纠正、点踩、事后审计发现失败轨迹,想把它变成后训练输入 - 判断某类失败该修 Harness/工具/tokenizer,还是值得投入训练 - 构造 DPO 偏好对、轨迹前缀回归任务、决策边界示范 - 决定一个失败案例该进 SFT 数据、DPO 偏好对、PRM 负标签还是 RLVP 惩罚规则 - 训练后验证修复效果,担心"修好一个、弄坏一片" - 从运行轨迹中提取可学习信号,而不是只存一个总分 ## 核心原则 - **归因到首个错误步骤,不是整条轨迹。** 以 Coding Agent "过早结束"为例:读代码、改代码可能都没错,错的是"在缺乏证据时下结论"那一个决策边界。归因记录必须落到这一步,训练数据才能对着它构造。 - **失败轨迹不应直接当作正确的 SFT 示范。** 它的正确用法是:构造偏好对、发现任务覆盖缺口,或补上诊断与修复后再加入训练。 - **判定必须靠模型写不了的机制。** "是否完成"要由隐藏测试、状态断言或外部终止钩子给出;测试必须读取真实文件或环境状态,不能只检查模型是否说了"已完成",否则模型会学会口头承诺验证而不真正验证。 - **边界集与保留集缺一不可。** 边界集(任务未完成)看模型是否选择继续验证而非宣称完成;保留集(任务已完成)看模型是否仍能正常收尾。只盯边界集会把模型训练成永远不敢结束的**过度矫正**状态:每个任务都无限验证下去,延迟与成本崩溃。这是"改动不能破坏既有行为"在参数层面的版本。 - **训练与评估严格隔离。** 训练集与评估集必须是不同的任务、不同的参数、不同的模板组合;参考解法、隐藏测试和验证器反馈不能泄露给模型。隔离规则应由单元测试强制检查,而不是靠人记得。 - **先排除非模型原因。** 沿"文件原始字节 → 工具返回 → Harness 序列化 → 模型上下文 → 模型 token 输出 → 解码字符串 → JSON/tool-call 解析 → 工具匹配"逐层对比。只有模型收到的上下文与原始字符串完全一致、而**模型输出是链路上首个出现差异的位置**,才能标为模型能力问题。否则修工具/Harness/tokenizer,修完再看还剩多少。 - **小数据 DPO 必须混入通用任务数据。** 案例数量少时,按小配比混入通用任务做 LoRA 微调,避免把"逢收尾必验证"学成新的过拟合,也降低灾难性遗忘风险。 - **学习信号必须带证据。** 单一总分无法指出该改哪里。可供学习的评价至少包含:任务成功/部分成功/失败、每个维度各自的结论、每条结论对应的证据位置(哪一轮对话、哪一次工具调用)、失败的类型标签。验证器还应被允许在证据不足时**拒绝评分**——低置信度案例应排除在学习集之外,而不是当作事实固化。 ## 实践模式 ### 完整链路:失败归因 → 双集验证 **① 采集与归因。** 从用户纠正、点踩、事后审计三类生产信号收集失败轨迹,按错误分类定位**首个错误步骤**并记录错误类别。轨迹前缀回归任务就是从这个决策边界截取的。 **② 分流:该不该训练。** 先问 prompt、工具、代码约束、上下文管理能否解决;再沿字节链路排除工具/Harness/tokenizer 问题。都不要动权重时就不训练。 **③ 选训练形态。** 按第七章评估数据集的类型映射到第八章用法: | 你手上的材料 | 训练用法 | |---|---| | 端到端回归任务(含验证器) | RL rollout 任务与可验证奖励(RLVR);拒绝采样(RFT)的采样池 | | 轨迹前缀回归任务 | DPO 偏好对、决策边界的 SFT 示范、On-Policy Distillation 的教师状态 | | 失败归因记录(首个错误步骤与错误类别) | 过程监督的负标签(PRM)、RLVP 路径惩罚的规则来源 | | Rubric 多维评分与人工金标集 | 向量奖励的各维度、生成式奖励模型(GRM)的训练与校准数据 | **④ 截取轨迹前缀,构造偏好对。** 在决策边界上截出"准备宣称完成"的轨迹前缀:rejected 是过早结束的错误行为,chosen 是"先运行测试、逐条核对验收条件,再下结论"的期望行为。chosen 由教师模型生成,再经规则验证器过滤(拒绝采样)。案例太少时用数据扩充(换任务类型、换缺失的验证项、换完成措辞)形成数百条偏好对。失败原因、适用条件和验证器应随样本保存,方便追溯和复查。 **⑤ LoRA DPO 训练。** 小配比混入通用任务数据防过拟合。参考配置:Qwen2.5-7B-Instruct + bf16 LoRA、4 epochs、学习率 3e-5、单卡约 24GB 显存。训练回执记录配置、数据哈希与时间戳。 **⑥ 双集验证。** 对未完成任务集和已完成任务保留集分别让模型给出"下一步动作",判定属于"宣称完成"还是"继续验证": - **未完成任务集的过早结束率** —— 训练后应下降 - **已完成任务保留集的正常收尾率** —— 训练后应保持 - **过度矫正率 = 1 − 正常收尾率** —— 应维持在低位 优先采用**格式固定、与训练提示一致的候选比较**(固定两个候选动作,比较模型更偏好哪一个,直接测量决策边界),而不是开放式自由生成。同时抽查通用能力,确认 LoRA 补丁没有破坏其他能力。 **⑦ 记录与追溯。** 训练数据与评估集分开保存;数据构造有确定性模板和教师模型两条路径时,两条都留证据回执。 ### 案例参照:Coding Agent 过早结束(实验 8-17) 配套数据:24 条轨迹前缀 bad case,覆盖四类过早结束——未跑测试就宣称完成、多目标只完成一部分、声称完成但验收条件未满足、遇错放弃宣称不可能(含删除失败测试/skip 等 reward hacking 变体);与训练数据严格隔离的留出评估集为 boundary 12 条 + retention 8 条。 实测结果:基座在固定候选比较的未完成任务集上选对 **3/12(25.0%)**,已完成任务保留集 **8/8(100%)**;LoRA DPO 后分别为 **11/12(91.7%)** 和 **8/8(100%)**。自由生成是补充诊断:过早结束 1/12→0/12,但正常收尾 6/8→0/8——**说明小数据 DPO 会让模型在开放式回答里过于谨慎**,因此主要结论只采用候选比较口径,不能外推成线上总体成功率提升。 同一个问题还有 RL 分支:把"宣称完成前必须跑通验收测试"写成可验证奖励,测试对模型不可见,模型宣称完成时才运行,通过 +1、不通过 −1。 ### 另外两类常见案例的处理差异 - **规则类反馈(如"中文直引号应统一为弯引号")**:期望描述不是可直接训练的规则,同一个引号在中文自然语言、英文原文、Markdown 行内代码、代码块、注释、JSON、路径中角色完全不同。正确做法是**作用域敏感的最小编辑**:先把规则写成可审计的 Skill(正例覆盖需转换的中文段落与中文注释,反例覆盖英文原文、字符串字面量、JSON、路径、行内代码),它同时作为合成数据的标签依据、训练后的回归规范和规则变更时的重训输入。 - **byte-exact 复制类(如 `edit_file` 的 `old_string` 匹配失败)**:把复制任务抽象成三个可验证任务——直接逐字复述、在相似且等长的多个字符串中选择完全相同的目标、把指定字符串完整抄写到 `old_string` 工具参数。样本特意包含真实编辑最容易损坏的空格、换行、反斜杠、Unicode 组合字符。训练后要用独立 tokenizer 审计排除词元化造成的假象:若 round-trip 上限本身就是 80.1%,模型测到 80.1% 就不能算作纯能力提升。 ### 从运行轨迹中提取学习信号(三层验证) 评价一条轨迹实质是依次回答三个问题:**事情是否办成了、是否以允许的方式办成、是否让用户舒服。** - **底层结果验证器**——读测试结果、数据库状态、工具返回。这类信号来自环境真实状态,比模型对自己行为的描述可靠,是三层中最应优先建立的一层。 - **中间过程验证器**——检查业务规则、权限和动作序列,区分"结果已达成"与"结果以允许的路径达成"。删除失败测试用例也能让测试通过;口头承诺也能得到暂时满意。政策库、权限表与动作轨迹均可精确表达,因此这层同样可以由代码判定。 - **上层质量验证器**——是否耐心、是否提供合规变通、文本是否自然简洁。用 LLM-as-a-Judge 加预定义 Rubric,要求逐项给分并引用轨迹证据。 输出形态决定它能否成为学习信号:单一总分只能反映一次运行的优劣。四项内容(成败判定、每维度结论、证据位置、失败类型标签)齐备,后续才能判断该更新知识、提示词、程序还是模型参数。 ## 常见陷阱 - **把失败轨迹直接当正确示范训练**,把错误行为固化进参数。 - **只优化边界集,不看保留集**,把模型训成永远不敢收尾的过度矫正状态。 - **训练集与评估集共享任务或参数**,评估失去独立性;隔离规则要由测试强制,不靠人记得。 - **用模型自述当完成判定**,模型很快学会"口头声称去验证"而不真的验证。 - **把系统层损坏误报为后训练收益**:直接复述探针正确但工具调用仍失败时,该修的是 Harness 或工具协议。 - **把 tokenizer/序列化损坏误判为模型复制能力不足**,训练前先做独立 tokenizer round-trip 审计。 - **案例太少又不做扩充**,几条偏好对撑不起一个决策边界;或扩充时只换措辞不换任务类型与缺失验证项。 - **小数据 DPO 不混通用数据**,学出"逢 X 必 Y"的新过拟合并带来灾难性遗忘。 - **低置信度结论进入学习集**,错误经验和提示注入被固化并在后续任务持续放大。 - **把开放式自由生成当主要验收口径**——小数据 DPO 下自由生成会过度保守,格式固定的候选比较才与训练提示一致。 ## 配套代码 - `chapter8/premature-completion-dpo/` — 全书唯一从生产 bad case 出发的训练实验:24 条 bad case、boundary 12 + retention 8 留出评估集、隐藏测试;`python demo.py` 离线端到端演示,`python -m pytest -q test_pipeline.py` 机制单元测试,`python build_preference_data.py` 生成偏好对(`--teacher` 走教师模型拒绝采样路径),`python train_dpo.py --smoke` 数据/tokenizer 前向检查,`python train_dpo.py` 单卡 LoRA DPO,`python evaluate.py --decision-score` 双集候选比较评估,`python train_grpo_optional.py` 同一问题的可选 RL 分支。 - `chapter8/curly-quote-sft/` — 规则类反馈的另一种监督目标:先写 `SKILL.md` 规范,再结构化合成数据做作用域敏感 SFT。 - `chapter8/exact-copy-sft/` — byte-exact 复制 SFT,含 `tokenizer_audit.py` 排除词元化假象。 - `chapter8/cot-distillation/` — "生成候选—验证过滤—只留正确轨迹"的拒绝采样流水线范例。 ## 深度阅读 - `book/chapter8.md`「从问题案例到后训练」 - `book/chapter8.md`「SFT 数据合成:从示范到可训练轨迹」 - `book/chapter8.md`「奖励设计:如何把任务目标变成学习信号」 - `book/chapter9.md`「从运行轨迹中获得学习信号」