---
id: results-driven-video-split
title: 结果驱动的视频级 train/val/test 切分（替代自造题模块）
type: design
created: 2026-07-15
status: draft
---

# 结果驱动的视频级切分设计

## 1. 目标与范围

**目标**：用一条**离线、结果驱动的视频级切分管线**，替代"自造 Video-MME 同质量训练题"这一在 AAAI 截止前风险过高的模块。基于已有的 `infer_adhoc` baseline（900 题 / 300 视频 / 660 对 240 错 / 73.3%），把 300 视频按**视频原子**切成 train/val/test，产出一个冻结的 `pools.json` 供 harness run 消费。

**范围边界**：

| 在范围内 | 不在范围内 |
|---------|-----------|
| 诊断 240 错题 → signal 分层 | 改诊断模块内部（只**调用** `core/evolution/diagnose`） |
| signal_scorer + 视频级聚合 + 贪心联合选择器（新增） | 改进化循环内部 |
| `pools.py` 切分原子 unit→**video** + 信号感知 | AR 出题 v3 管线（另行废弃，本设计外） |
| `loader` 指向 Video-MME 900（已如此） | 重跑推理（复用现有 baseline） |
| — | **不改 `gate_ladder.py` / 信息阶梯冷启动 2:1**（Codex M-1；算法保真 §4.7 第 5 项不受本切分波及，切分只改 pools 归属不改冷启动比例） |

**故事重定位**：论文核心贡献是"视频树上可自进化的搜索 Agent（Harness Engineering）"，出题从来不是卖点。PyTorch 类比中 DataLoader **本就只加载已有数据集 + 切分、从不合成数据**——所以砍掉自造题、改为对 Video-MME 做结果驱动切分，类比反而更忠实。头号数字诚实、可比。

## 2. 背景：核心洞察（耦合）

全数据集错题总数固定 = 240。**守恒式为 `train错题 + val错题 + test错题 = 240`**（选择器在 **train+val 并集**上优化覆盖，再内部切 train/val）：越把信号往 train+val 塞，test 越简单、headline 越虚高。因此"把最难视频塞进 train+val"是有害的（偏置方向恰好注水 test）。**val 会分走部分 defect 视频——这是可接受的**：gate/e-process 本就需要错题才有功效（§8），但 val 的抽取受 §8 的 correctness 分层约束，不破坏 train 的多样性目标。

**第一原则**：**test 神圣（代表性 + 冻结）**；train 的信号富集只能靠三条合法途径——① 每类型 floor（硬约束）；② 调度层信息阶梯加权（运行时，本设计不涉及）；③ 同 profile 视频交换自由度。ε 代表性约束是耦合的算法化身，天然挡住"偷 test 难题"。

信号地图（baseline 逐类型错题）：Counting 24/48(0.50)、Action Recognition 22/63(0.65)、OCR 5/14、Object Reasoning 68/240、Action Reasoning 48/180、Temporal Reasoning 22/91 为高信号；Spatial Reasoning 0/11(全对，零信号)；Temporal/Spatial Perception 量微。视频级：125 视频全对(零信号)、115 一错、55 两错、5 全错。

## 3. 架构与数据流

```mermaid
flowchart TD
  P[infer_adhoc predictions<br/>900题/300视频] --> S{对错拆分}
  S -->|660 对| CG[correct 视频画像<br/>供 ε 代表性/难度约束]
  S -->|240 错| DG[诊断管线<br/>调用 core/evolution/diagnose]
  DG --> QA[每题: error_type + cause_category<br/>+ QuestionMetrics]
  QA --> SC[signal_scorer<br/>→ tier T0/T1/T2 + 多样性格子]
  SC --> VA[视频级聚合<br/>每视频: 覆盖格子集 + tier 构成]
  CG --> GS
  VA --> GS[贪心联合约束选择器<br/>max 多样性覆盖 s.t. test ε + floor]
  GS --> TV[选中 ~100 视频 train+val]
  GS --> TE[补集 ~200 视频 test]
  TV --> SP[correctness 视频原子切 train/val<br/>val 按 McNemar 功效定尺寸]
  TE --> FZ
  SP --> FZ[save_pools → 冻结 pools.json（原子写）]
  FZ --> RUN[harness run 消费]
```

**关键不变量**：① 视频为原子，三池视频集互斥、绝不共享视频 → 零内容泄漏；② test = train+val 的补集，代表性由 ε 约束保证（非事后随机）；③ 全程离线、可复现（固定 seed + baseline run_id 溯源），不触碰在线进化。

## 4. 组件落位（Clean Architecture 四层）

| 模块 | 职责 | 新增/改动 | 层 |
|------|------|----------|----|
| `core/evolution/diagnose.py` | 诊断 240 错题 | 不动（只调用） | core |
| `app/harness/baseline_diagnosis.py` | 编排离线诊断：读 baseline 错题预测+轨迹 → 调 diagnose → 落库 | 新增 | app |
| `app/harness/split_selection.py` | 纯函数：`score_signal`(→tier+格子) / `aggregate_video_signal` / `select_split`(贪心) | 新增 | app |
| `app/harness/pools.py` | 切分原子 unit→video；`build_pools` 接选择器给的视频三分，做 video 原子 correctness train/val 切分 + 原子冻结 | 改动 | app |
| `app/question_gen/loader.py` | 指向 Video-MME 900 | 已如此 | app |
| `baseline_diagnosis` 落库端口 + SQLite 适配 | 每题 error_type/cause_category/tier/(派生)evolution_target/degraded/diag_fingerprint | 新端口(app/ports 或 core/protocols)+ adapters 实现 | ports+adapters |

`split_selection.py` 全为纯函数（相同输入→相同输出），诊断 I/O 隔离在 `baseline_diagnosis.py`（P6 可测试性）。**落库走端口/适配层，不在 app 拼裸 SQL**（Codex I-5，对齐 §4.8 遥测规范与依赖方向）。

**视频原子改造要点（Codex I-3，防实现复用 unit 逻辑漏泄漏）**：`build_units` 保留（pair 聚合仍需），但**新增 video 分组层**——`build_pools` 接选择器给定的 video→池 归属，`_split_one_category` 的采样原子从 `QuestionUnit` 提升为 **video 组**（同 video 全部 unit 同进同出）。改造后须由防御断言①验证三池视频不相交。

## 5. signal_score 与多样性定义

### 5.1 signal_score = DiagnosisResult 的确定性投影（不发明新分类）

`DiagnosisResult` 由现有两阶段管线产出：阶段1 规则指标(纯函数) + 5 个 LLM judge → `QuestionMetrics`；阶段2 瀑布归因出 **4 类 error_type**（`extraction_failure`→`search_failure`→`reasoning_failure`→`mixed`，代码 928–935），再由 `classify_defect_vs_lapse`(LLM judge) 判 `cause_category`；INFRA(`stop_reason∈{error,parse_error}`) 阶段1前排除。

signal_score 主要是**分层标签**（非脆弱连续加权分，避 P5 魔法数）：

| Tier | 判据（全来自诊断） | 训练处置 |
|------|------------------|---------|
| **T0 排除** | INFRA(`stop_reason∈{error,parse_error}`) | signal=0，永不进 train |
| **T1 低信号** | `cause_category='lapse'`（**接受此桶混着"真·无解/标注错"**，不做三分类） | 低权重，对照/appendix，不作主训练 |
| **T2 高信号** | `cause_category='defect'` 且映射到可执行进化目标 | 核心训练集，内部按多样性排序 |
| (uncertain) | `degraded`(judge 解析失败) 或诊断硬失败 | 排除出 T2 + 计数上报，不静默丢 |

> lapse 为低信号是诊断自身的判断（lapse 路由到受保护 appendix、不重写 skill），非本设计新增。

### 5.2 多样性 = 对诊断逐题一等字段的覆盖（非错题数量）

一个 train 集"多样"当且仅当它张成"可修复失败模式"的空间，使进化对每块参数面都拿到梯度。

**主格子 = `(task_type × error_type)` = 12 × 4 = 48 格**——两者都是**逐题一等字段**（task_type 来自题、error_type 来自 `attribute_error`），无歧义、可确定性计算：

| 轴 | 取值空间 | 来源 |
|----|---------|------|
| task_type | 12 类 | 题目字段 |
| error_type | extraction/search/reasoning/mixed（4 值） | `ErrorAttribution.error_type`（逐题一等字段） |

**`evolution_target` 是由 error_type 确定性派生的标注，不是独立第三轴**（Codex C-1：`ErrorAttribution` 无 evolution_target 字段，逐题 CasePack 路由非一等产物）。设计声明一张**固定映射表**（科研 config，可测试），用于报告"哪层参数组拿到梯度"，不增加维度：

| error_type | evolution_target | 理由 |
|-----------|-----------------|------|
| extraction_failure | tool | 抽取发生在 view_node/observe_frame 工具 prompt → ToolCasePack |
| search_failure | skill | 搜索策略是 skill → SkillCasePack |
| reasoning_failure | skill | 推理是 skill → SkillCasePack |
| mixed | system | 跨切面 → SystemCasePack |

**`behavioral mode` 降为 tie-breaker，不进主格子**（Codex I-2：system pack 实际只有 `{early_submit, high_conf_wrong, confirmation_bias}` 三类）。平局时用这三类的真实取值打破，不臆造名字。

**度量 = 对 48 格 `(task_type × error_type)` 的覆盖数（set-cover）**，submodular。**视频级计数规则（Codex I-1）**：一个视频贡献其全部 T2 错题所覆盖格子的**并集（去重集合）**，每格全局只计一次 → 多错题视频不会虚高覆盖增益。奖励"开新格子"，惩罚"同格子第 10 道重复错题"。对比"最大化错题数"——既有害(偷 test 难题)又冗余。

## 6. 贪心联合约束选择器（`select_split`）

**floor = 硬约束（先满足，取值克制）；多样性 = 目标（floor 后最大化）。** floor 违反 = 某高信号类型零梯度(灾难)，少覆盖一格子仅边际损失。

```
输入: videos(每个带 其题对错 + 错题 tier/格子), 全局目标(类型比例,难度画像),
      config(N_trainval, floor_K[type], ε, reportable_types, seed)
1. trainval=∅; test=all
2. # Floor 阶段(硬约束, 同样受 ε 守护 —— Codex C-2):
   while 存在未达 floor 的高信号类型:
       cand = 能填未满 floor 槽 且 "移走后 test 仍满足 ε" 的视频
       若 cand 非空: 选填槽最多者 → 移入 trainval
       否则: fail loud(floor 与 ε 死锁, 报是哪个类型的 floor 无法在不破 ε 下满足)
3. # 多样性阶段(submodular 贪心):
   while |trainval| < N_trainval:
       cand = 各候选视频移入 trainval 的"新开格子数"(并集去重边际增益)
       按增益降序试: 取增益最大且"移走后 test 仍满足 ε"的视频 → 移入
       若无任一视频可加而不破 ε → 停(报欠额, 不静默)
4. 返回 (trainval, test=补集)
```

- **submodular → 1−1/e 保证**；**ε 可行性检查在两个阶段都生效 = 耦合的算法化身**（拉太多难视频破坏 test 难度画像→被拒）。floor 阶段先前漏了 ε 检查（Codex C-2），已补：floor 视频移动同样必须保 test ε。
- **可行性冲突 fail loud**：floor 与 ε 死锁（某类型 floor 只能靠"会破 ε 的视频"填）/欠额时明确报哪条约束差多少，人放松旋钮，**绝不静默退随机**。
- 平局与遍历顺序由固定 seed 决定 → 可复现。

## 7. 长尾类型处理（report_floor 拟定 = 总题数 ≥ 27）

| 类型（总题） | per-type 报告 | train floor | 归属 |
|------|:---:|:---:|------|
| 8 类 ≥27（Object/Action Reasoning, Info Synopsis, Temporal Reasoning, Action/Object Recognition, Counting, Attribute Perception） | ✅ | ✅ | 参与 ε + floor 约束 |
| OCR(14) | ❌ 折进 overall | 🟡 无硬 floor，defect 机会性进多样性池 | 随视频落 |
| Spatial Reasoning(11) | ❌ | ⛔ 全对零 defect | 随视频落 test |
| Temporal Perception(6)/Spatial Perception(3) | ❌ | ⛔ | 随视频落，披露计数 |

非报告类型不进 ε 代表性约束（太少无法分层），但其视频仍参与选择（每视频跨~3 类，长尾题搭车跟随其视频）。报告折进 overall + 一行"rare types (aggregate)"，披露计数。

## 8. val 尺寸（McNemar 功效）

选中 100 视频内部用**现有 `_split_one_category`（改视频原子）** 做 correctness 分层切 train/val：val 取较小片但带 `eval_min_per_class` 保底，且**含足够错题使 McNemar 有功效**（val 期望错题数 ≥ 功效阈值，具体值诊断后标定）；train 取大头(defect 更密,供 rollout)。

## 9. 非功能性需求（强制四维）

离线一次性管线，但诊断阶段有 240 次 LLM judge 调用，必须可续跑。

| 维度 | 设计 |
|------|------|
| **持久化** | 诊断结果**逐题** upsert 入 `baseline_diagnosis` 表（崩溃最多丢在飞那题）；signal/选择纯内存从表重算；最终 `pools.json` 冻结快照 |
| **幂等性** | 诊断表主键 = `(question_id, baseline_run_id, diag_fingerprint)`，其中 `diag_fingerprint = hash(诊断 prompt 版本 + model + 诊断代码版本)`（Codex C-4：仅 question_id upsert 不足以约束 judge 非确定/prompt/model 漂移）；`--force` 在**新 fingerprint** 下写、**不覆盖**旧记录；冻结的 `pools.json` 记录其构建所依据的 `diag_fingerprint`，重建时 fingerprint 不匹配 → **fail loud**（防脏续跑污染已冻结切分）。选择=纯函数(表切片,全局统计,config,seed)→同输入必同切分 |
| **断点续跑** | 诊断重启查已完成集 → 跳过（镜像建树逐项续跑）；崩在第 150 题从 150 续；选择/冻结秒级重跑即可 |
| **原子性** | 逐题写=SQLite 单行事务原子；**pools.json 补原子写**——现有 `save_pools`(341) 与 per_category 增量路径(566) **都用 `path.write_text`(非原子, Codex I-4)**，抽出共用冻结助手统一改 tmp + `os.replace`，两条路径同受益 |

## 10. 错误处理（P5：不静默、不兜底）

| 情形 | 处置 |
|------|------|
| 诊断 LLM 基础设施失败 | GovernedLLMClient 重试栈后仍失败 → 传播报错，不掩盖 |
| 单题诊断硬失败 | slot 级隔离：记 `error`/`degraded` 入表(带错误)+计入报告，不静默跳；该题→uncertain→不进 T2 |
| judge 解析失败(degraded) | 现有 `degraded` → uncertain → 排除 T2 + 计数上报 |
| 选择器不可行(floor 与 ε 冲突/欠额) | **fail loud**：报哪条约束差多少，人放松旋钮，绝不静默退随机 |
| correctness 缺失 | 现有 `_assert_correctness_complete` fail-fast 保留 |
| judge 判不准默认 lapse | 该 fallback **仅用于语义歧义**（judge 回复无法解析），基础设施失败照常传播（`classify_defect_vs_lapse` 994-996）；默认 lapse 的题**计数上报**，不静默（Codex M-2） |

**防御性断言清单（P5，Codex I-6，实现须逐条落地）**：① 三池视频集两两不相交断言；② baseline predictions 覆盖全 900 题（缺题 fail-fast）；③ 每 video 恰 3 题完整性；④ 诊断表 `diag_fingerprint` 与冻结 pools 记录一致；⑤ 冻结产物写 manifest（含 baseline_run_id/diag_fingerprint/config/seed/文件 hash）供复现校验；⑥ question_id/video_id 唯一性。

## 11. 测试策略

- **单元**：`score_signal`(INFRA/lapse/defect→正确 tier+格子)；`aggregate_video_signal`(混 tier 视频→正确覆盖格子集)；`select_split`(floor 满足 / **视频不跨池** / test ε 代表性 / 贪心覆盖下界 / **构造冲突→抛错** / 同 seed→同切分)；video 原子 `_split_one_category`。
- **集成**：真实 infer_adhoc 240 错题 + 缓存真实诊断端到端 → 产出合法冻结 pools.json（视频互斥、floor 达标、test 代表）。**续跑**：诊断中途 kill→重启跳过已完成→同一最终结果。
- **LLM 类测试产出 MD**（§4.6）：诊断跑涉及 LLM → 结构化 MD 报告。
- **回归**：现有 pools/loader 测试在视频原子改造后仍过；非 AR/pair 行为保留。

## 12. 前序版本继承（step 1.5 审计逐条落实）

| 前序行为 | 处置 |
|---------|------|
| 三池 / 逐步排除互斥 / pair 原子 / baseline_val_accuracy / correctness dict / 复现 | 保留 |
| test 自然分布 | 升级为 ε 代表性约束（更强） |
| val correctness 分层 + min_per_class | 保留 |
| pools.json 持久化 | 保留 + 补原子写 |
| **per_category 增量模式** | **保留但闲置（选项 A）**——AR 废弃在本设计外，新流程只走 global 视频感知；per_category 删除留给将来 AR 清理 |

## 13. 待标定旋钮（诊断跑完后用真实分布定，写入科研 YAML + run 快照）

`N_trainval`(~100)、`floor_K[type]`、代表性容差 `ε`、`report_floor`(≈27)、val 尺寸(McNemar 功效)、`seed`。设计固定**算法与约束结构**，数值不臆造。

## 14. 风险与回退

| 风险 | 回退 |
|------|------|
| 100 训练视频信号不足、进化曲线平 | 切方案 B：外部 benchmark 建树当训练集，全 900 Video-MME 留 held-out（A 打底 B 升级） |
| floor 与 ε 联合不可行 | 选择器 fail loud 报冲突 → 放松 floor_K 或 ε 或 N_trainval |
| 诊断 LLM 成本/耗时（240 题） | Redis 缓存 + 逐题续跑；一次性离线 |
| test 长尾类型不可报 | 折进 overall + rare-aggregate 行，披露计数 |

## 相关
- baseline 结果：`workspaces/default/harness.db` run_id=`infer_adhoc`
- 诊断模块：`core/evolution/diagnose.py`、`core/evolution/types.py`
- 被替代：自造题 v3（`2026-07-15-question-gen-v3-construction-paradigm-design.md`）
