Files
Video-Tree-TRM5/research-wiki/designs/2026-07-16-preflight-fixes-design.md

17 KiB
Raw Permalink Blame History

训练前缺陷修复设计(video-split → Video-MME 900 自进化训练)

2026-07-16。两轮审查(第一轮接线预检 + 第二轮 8 维度多代理审查:19 条确认 / 4 条存疑 / 12 条误报)。本设计覆盖:19 条确认缺陷 + 4 条存疑中采纳 2 条 + 接线缺口 2 项 + 切分层优化 3 项,逐条映射见 §2.5 覆盖矩阵。 目标:让 video-split 冻结切分驱动的 Video-MME 900 题自进化训练一次跑出有效结果(不崩、不静默失效、信号不被污染)。

1. 背景与问题总览

自进化训练对标 PyTorch 训练循环(出题=DataLoader / 推理=forward / 诊断=backward / 进化=optimizer.step)。审查发现三类根问题:

类别 代表 后果
进化引擎哑火 5 个 evolve/momentum 模板 TRM4→TRM5 迁移遗漏,else "" 静默兜底 3 epochs 跑完但 skills 零进化,best 恒 v1
诊断链路坍缩 traces 表从不写入(TracePlugin 未迁移) 诊断瀑布拿空轨迹,算法保真 #7 失效
崩溃与信号污染 微型题型断言崩、prediction 非标量击穿、缓存跨 epoch 重放、SSE 截断毒化等 训练中途崩溃或统计信号失真

叠加接线缺口:冻结 pools.json 无路径进入训练 workspace--fresh 从 seed 重建不带切分),直接训练会静默丢弃标定成果、按 unit 重切。

2. 设计目标与非目标

目标:修复 §2.5 矩阵所列全部缺陷(19 确认 + 2 存疑采纳)+ 接线;切分层做三处优化(可训练性门槛、val_ratio、tier 感知分配)提升信号质量;产出零参可复现的训练入口。

非目标:不改 gate/EMA 核心数学(算法保真 #4/#5);不实现 epoch 层 per-skill best 混搭(见 §9 future work);不引入向后兼容(P4 显式优于隐式,直接改)。

2.5 缺陷覆盖矩阵(可追溯性)

原始编号取自第二轮工作流报告(research-wiki/reviews/2026-07-16-preflight-train-review.md)与第一轮接线预检。状态:C=确认 / P=存疑采纳 / W=接线(已知缺口)。

设计编号 原始位置 状态 修法节
P0-1 runner.py:2272 evolve 模板缺失 C §4
P0-6 momentum.py:151 slow_momentum.md 缺失 C §4
P0-2 inference.py:462 traces 未写 C §6
P0-3 validate.py:682 + gate.py:99 + runner.py:2044 微型题型三连雷 C §6
P0-4 inference.py:423 prediction 非标量击穿 C §6
P0-5 runner.py:316 early_stop step 单位 C §6
P1-1 main.py:93 缓存跨 epoch 重放 + TTL C §8
P1-2 llm.py:116 SSE 截断毒化 C §8
P1-3 llm.py:182 断连不重试 C §8
P1-4 validate.py:304 基线臂 INFRA 污染 C §8
P1-5 diagnose.py:2194 + runner.py:1019 降级误入 defect C §6
P1-6 patch.py:343 冻结区跨度 C §7
P1-7 workspace.py:282 manifest 非原子写 C §8
P2-1 video_split_cli.py:557 冻结产物无覆盖保护 C §5.2
P2-2 log.py:77 基线元数据被改写 C §8
P2-3 diagnose.py:2081 + inference.py:461 重复行双计 C+P §6step DELETE
P2-4 diagnose.py:2194(离线)uncertain 永久降级 C §8--retry-uncertain
P2-5 runner.py:1444 dual_metric 口径歧义 P §8
P2-6 breaker.py:36 熔断半开烧穿 P §8
慢更新非幂等 第一轮已知 #5 W §6gate_epoch_observed 立即落盘)
接线-1 冻结 pools 无路径进 workspace(已知 #1 W §5.3
接线-2 global 加载零一致性校验(已知 #2) W §5.3

有意合并/不单列的项(说明去向,避免"静默漏修"):

第一轮原始项 处置
gate_ladder.py:93 strict=False 缺题静默当错题 由 §6 可训练性预检消除主场景(缺题类被剔除);残余以 fail-loud 补强(预检后 gate 建立时断言 baseline 覆盖全部 ladder 单元)
batching.py:200 缺 correctness 静默丢弃 同上,预检保证 diag 池题全在 correctness 内;保留 fail-loud(缺 correctness 即报错而非丢弃)
resume 硬依赖 checkpoint(已知 #8 本轮为 fresh 训练,seed 携带 pools 后无需 resume 建池;不改 resume 语义
baseline_diagnosis 断点续跑粒度(存疑,非采纳) 仅修 docstring 表述(§8 末),行为不改(Redis 缓存已缓解重烧)

3. 架构:四工作包按依赖序

graph LR
    WP1[WP1 资产迁移<br/>模板+fail-loud+死字段] --> WP3[WP3 训练循环与进化引擎]
    WP2[WP2 切分与接线<br/>产出训练输入] --> RUN[训练启动]
    WP3 --> RUN
    WP4[WP4 韧性与持久化] --> RUN
    WP1 -.集成测试依赖.-> WP3

WP1 零风险纯搬运先解锁引擎;WP2/WP4 与 WP3 无代码交集可并行;WP3 主体修复依赖 WP1 模板做集成测试。

4. WP1 —— 资产迁移(修 P0-1 / P0-6

/home/iomgaa/Projects/Video-Tree-TRM4/prompts/ 拷 5 文件到 TRM5 根 prompts/evolve_skill/system/tool/rank.mdslow_momentum.md(进化引导 prompt = 引擎一部分,不参与版本化进化)。

  • 迁移验收(强制):逐个核对模板输出契约与 TRM5 解析代码期望——evolve_*{suggestions, edits} 结构 vs _parse_llm_json/_apply_oneslow_momentum.md vs run_slow_momentum。TRM4→TRM5 代码有漂移,裸拷贝不核对 = 新 bug。
  • 加载器 fail-loud_load_evolve_prompts/_load_diagnose_promptselse ""FileNotFoundError(列缺失文件名)。
  • 死字段清理:删 EvolvePrompts.consolidate_systemconsolidate_appendix 用内联 prompt)、DiagnosePrompts.span_eval_user(无消费点)及对应 runner 加载行。

5. WP2 —— 切分与接线

5.1 tier 感知切分(split_selection.py / build_split.py

trainval 内部视频组分配从"correctness 分层随机"升级为 tier 感知确定性贪心(纯函数、固定 seed):

输入: trainval 视频组集合 G(每组: t2_count/t1_count/wrong_count/question_count
      tier 来自 baseline_diagnosis 按 fingerprint 读);val_ratioval_wrong_min
目标: 最大化 diag 池 T2 捕获,同时满足 val 功效
约束: ①视频原子(组内题同进同出) ②val 题数 ≈ val_ratio×trainval题数 ③val 错题 ≥ val_wrong_min

阶段1 初分(单趟,O(n log n):
  按 (t2_count desc, wrong_count desc, video_id) 排序 G
  依次装入 diag,累计题数达 (1-val_ratio)×总题数 即停 → 余下全部进 val
阶段2 功效修复(单调收敛,每组至多移动一次):
  while val 错题 < val_wrong_min:
    从 diag 中挑「wrong_count>0 且 t2_count 最小」的组移入 val   # 严格减少 diag 错题冗余
    该组标记 moved,后续不再参与挑选                          # 单调变元:候选集严格缩小
    if 无 moved=false 且 wrong_count>0 的组可挑 → raise InfeasibleSplitError
  # 终止性:每轮候选集 |{未 moved 且 wrong>0}| 至少减 1,有限步内收敛或 fail loud

与既有 video-split 设计(2026-07-15-results-driven-video-split-design.md §8)的口径变更:保留 correctness 分层的本质(阶段2 只在 wrong_count>0 的组间挪动,保证两池都有对/错题)、eval_min_per_class(由 §6 预检独立强制)、McNemar 功效(val_wrong_min≈20 硬约束不变);替代"随机分层"为"tier 优先确定性贪心"(新增 T2 捕获目标 + 固定 seed 可复现)。

配置:val_ratio 作用域 = trainval 池(当前 300 题)内部的 val 题数占比,0.3 → 0.4。数字推算基于当前冻结切分统计(trainval 错题 93 = val 29 + diag 64):val 错题 ≈ ⌈93×0.4⌉ = 38、diag 错题 ≈ ⌊93×0.6⌋ = 55~56tier 感知使 T2 尽量留 diag,实际错题落点略有偏移,以重切后统计为准)。甜点判断:val 38 让整包终审更抗噪、diag 56 进化信号仍过剩;0.5 过冲(每类 3 epochs 仅 ~4 次进化机会,迭代受损)。val_ratioval_wrong_min 作为数值参数进科研 YAML + run 快照(会被扫动/对比);tier 感知策略固定不加 on/off 开关(无扫动需求,YAGNI)。重切零 LLM 成本(诊断已按指纹落库)。

5.2 冻结产物覆盖保护(修 P2-1)

build_split 冻结前检查产物存在性:存在且内容指纹不同 → 报错并提示 --forceCLI 补上此缺失参数);--force 时旧产物重命名 pools.json.bak.<旧指纹前8> 再写。

5.3 seed 携带切分(接线核心)

设计
init_seed 新增可选 pools_json/split_manifest,提供则拷入 seed 目录
init_workspace_from_seed seed 目录有 pools.json 则连 manifest 拷入 workspace(拷 baseline.db 之后)
build_or_load_poolsglobal 补校验,修已知 #2 加载冻结 pools 强校验 baseline_run_id 匹配 seed;有 manifest 时校验 sha256(pools.json)==manifest.pools_sha256
新 seed adhoc-baseline extract_run_dbdedupe_per_question(每 qid 按 rowid 取首行,902→900 对齐 canonical+ init_seed(baseline_run_id='infer_adhoc', pools_json=…)

seed 最终形态:seed.json / baseline.db(900) / pools.json / split_manifest.json / skills/v1 / prompts/v1

6. WP3 —— 训练循环与进化引擎

# 缺陷 修法
P0-3 微型题型三连雷 train()可训练性预检(纯函数):题型 n_val<eval_min_per_class(2)n_units<trainable_min_units(8) → 从 diag/val + gate task_types 剔除并打印清单;断言/空阶梯保留作纵深防御;test 池不过滤
P0-4 prediction 非标量击穿 落库前 _to_text_field 归一化;log.insert 移入单题 try 块,绑定异常记 error 不击穿 gather
P0-5 early_stop 单位错误 step→epoch 计数(epochs_since_best_improved),config 注释对齐,checkpoint 字段同步改名
P0-2 traces 空 复用离线管线 StepsJsonRunLog 适配器包装训练 RunLogget_traces 空时从 steps_json 转换,算法 #7 恢复
holdout 四向重复评估 baseline 从 seed 基线预测推导(0 推理)final 真评 600best_hard 版本备忘录(未评过且≠final 才评);best_mixed 引用赢家成绩标指针(0 推理)。去重做在 harness 逻辑层(配合 epoch 盐,同版本不重采样)。resume 语义:备忘录不单独持久化,而在 resume 时从 holdout_eval 表 hydrate——查已落库的 (skills_v,prompts_v)→test_acc 重建已评集合(该表本就是每次评估的落库处),checkpoint 无需新增字段、无旧 checkpoint 迁移问题;hydrate 后同版本不再重评
P1-5 诊断降级误入 defect cause_category=None(judge 基础设施异常)与 degraded=True 错题按 lapse 方向分流step 降级占比>50% 报错中止
P2-3/#5 step 重跑双计 + 慢更新非幂等 _run_step 开始先 DELETE ... WHERE run_id=<本step>_refresh_gate_ladder 保存 gate_pools 后立即原子落盘 checkpoint(gate_epoch_observed=True) 消除双计窗口

run_holdout_eval 保持开启(去重版),逐 epoch test 曲线四线齐全,成本从 2400 降至 600~1200 次/epoch。

7. WP3 —— 进化引擎 patch 加固(修 P1-6,算法 #8 防御)

改动
跨度判定 _in_ranges 从"只查起点"改为"整个 target 跨度 [pos,pos+len) 与保护区相交即拒"_do_insert_after
注入检查 edit payload/target 含 APPENDIX_START/ENDMOMENTUM_START/END 字面量 → 拒绝该 edit
最后防线 validate_skill 增 marker 完整性校验(成对、有序、各至多一对),违反整体 reject

不改"保护跨度"语义方向,只把已声明的保护做完整。

8. WP4 —— 韧性与持久化

# 缺陷 修法
P1-1 缓存跨 epoch 重放 chat()cache_salt;训练 rollout/val/test/gate 候选臂推理注入含 epoch 的盐(跨 epoch 真实重采样、同 epoch 续跑仍命中);judge/evolve/离线切分无盐。TTL 修:REDIS_CACHE_TTL≤0 启动即 ValueError(消灭"0=永不过期"隐式语义)。同步改 .env/.env.example 给正整数(当前值 0,不改则修复后启动即崩),训练场景建议 ≥ 单次训练时长
P1-1a epoch 盐 vs gate 配对保真(Codex Critical gate 双臂配对语义由架构保证、不受 LLM 缓存盐影响:基线臂走 app 层 BaselineCacheunit 键 task_type+s_hash+prompts_version+unit_id,固定快照,validate.py:_resolve_baseline_block),候选臂每块新采样(_run_candidate_block)。配对翻转比较的是「固定基线快照 vs 新候选采样」(算法保真 #6 现有语义),epoch 盐只作用于候选臂/rollout 的 LLM 层缓存键,不触碰基线臂、不改变配对语义。gate 序贯 e-process 消费阶梯上不同 block(不同 unit累积 e-value,非对同 unit 反复测;候选臂盐用 block 级 run_id(已含 epoch+block+candidate 区分)保证每次真实重测独立采样。实现约束:基线臂 miss 时的新鲜推理不注入 epoch 盐(保持基线快照跨 epoch 稳定,与 BaselineCache 语义一致)
P1-2 SSE 截断毒化 _consume_stream 校验 [DONE]/finish_reason 才算成功;流耗尽未完成抛 _SseAnomaly 进重试梯,绝不写缓存
P1-3 断连不重试 _is_transient_error 扩为 httpx.TimeoutException+httpx.TransportError 两族
P2-6 熔断半开烧穿 cooldown 到期只放行 1 探针(half-open 锁),成功才闭合
P1-4 基线臂 INFRA 污染 INFRA 单元不写 BaselineCache、不计 W/L;护栏检查移到写缓存之前
P1-7 manifest 非原子写 update_manifest/record_run/update_best/_scaffold 统一 tmp+os.replace(复用 checkpoint 模式)
P2-2 基线元数据被改写 只读查询基线的两处改走只读连接,不经 HarnessLog.__init__ upsert
P2-5 dual_metric 口径歧义 慢更新 R2 行 version_kindslow_candidatefinal 恢复唯一语义
P2-4 离线 uncertain 不可重试 video_split_cli--retry-uncertaindone 集排除 uncertain 行);docstring "逐行落库"表述修正为"run 末批量落库"

9. Rejected alternatives / Future work

选项 为何不做
epoch 层 per-skill best 混搭 per-class val 样本太小(中类 5-10 题)终审频繁选错;prompts 随慢更新漂移致跨 epoch 成绩不可拼接。合理形态是训练结束后一次性 final assembly(每类取 gate 末次接受版本 + 最终 prompts,全 val 评一次比 best_hard),成本一次推理、不动循环 → 记 future work
val_ratio 0.5 每类 3 epochs 仅 ~4 次进化机会,迭代受损;val 错题 38→46 边际收益趋零
确定性评估(temp=0) 统计效率最高但基线用默认温度采,换口径要重烧全部基线,本轮不可行
会计层去重替代 epoch 盐 需动 gate/EMA 保真区且造不出新信息(每题永远只见一次抽样)

10. 非功能四维(全设计汇总)

维度 保障
持久化 冻结产物/manifest/checkpoint 全 tmp+replace 原子写;holdout 备忘录入 checkpoint
幂等 step 重跑先 DELETE 后写;seed/冻结产物存在即拒覆盖(--force 显式);诊断按指纹 upsert 不变
断点续跑 epoch 盐保同 epoch 命中;gate_epoch_observed 在 gate_pools 保存后立即落盘,消除慢更新双计窗口
原子性 manifest 补齐原子写(对齐 checkpoint 先例);pools 冻结沿用现有原子写

11. 测试策略

覆盖
单元(新增) tier 选择器(真实 baseline_diagnosis 二次构造:T2 捕获/val 错题≥20/视频原子);可训练性预检(真实 pools 驱动,5 微型类被剔);patch 三层校验;SSE 未完成流拒收;瞬时错误清单;缓存盐入键;extract_run_db 去重;seed 携带拷贝;holdout 备忘录
单元(改语义) early_stop 按 epoch;加载器 fail-loudprediction 归一化
集成 fake-LLM 小池 train 冒烟(预检→模板→traces 适配→holdout 去重计数→step DELETE)5 模板契约测试
回归 全量 1473 保持绿;agent/LLM 测试产 MD 到 tests/outputs/

12. 训练启动 runbook(修复完成后)

1. 四工作包完成 + make test 绿
2. 备份 workspaces/video-split/ → 改 val_ratio:0.4 → 重跑 build_video_split.sh(诊断命中缓存秒级)
   → 核对: val 错题≥20 / T2 入 diag 数 / 预检模拟的可训类清单
3. 建 seed: extract_run_db(infer_adhoc,dedupe) + init_seed('adhoc-baseline', pools_json=…)
4. 新增 config/train_videomme.yaml + scripts/train_videomme.sh(零参可复现):
   epochs=3, early_stop_patience=2(epoch), run_holdout_eval=true(去重版),
   trainable_min_units=8, 其余 gate/batch 沿用 default.yaml
5. tmux: CUDA_VISIBLE_DEVICES=0 bash scripts/train_videomme.sh