---
id: grounded-question-gen-phaseA
title: 出题质量提升 Phase A — Grounded 单题质量（候选池 + VLM 视觉打分 selector + 单维反事实）
type: design
created: 2026-07-14
status: draft
---

# Phase A：Grounded 单题质量

## 1. 目标与范围

把"干扰项 grounded 约束"从 prompt 文案（求 VLM 主观写得像）下沉到**机制层**：VLM 生成候选池 → VLM 视觉打分 → 按视觉可信度区间选出真实 near-miss 干扰项，从机制上消灭 Easy-Options Bias（负空间干扰项 → 排除法秒杀）。Phase A **完全不依赖 inference agent**，纯出题质量 + 观测。

**问题来源**：[2026-07-14-ar30-too-easy-analysis.md]（负空间干扰项 96.7% 正确率、SubPattern 归属跑偏）、[2026-07-14-hard-distractor-literature.md]（GroundAttack/TempCompass/VITATECS 范式）。

### 路径归属（严格区分，避免改错路径）

| 改动 | 代码位置 | 生效范围 |
|------|---------|---------|
| L0 tree 错配 bug 修复 | `pipeline_v2.py` | 公共（纯 bug，全题型正向） |
| 候选池 + VLM 视觉打分 selector | 新建 `distractor_selector.py` | 公共层，**strategy 声明开关，默认只 AR 启用** |
| 单维反事实约束 | `strategy_action_recognition.py` | **仅 AR** |
| multi_true 门松绑 | `gate_multi_true.md` | 公共，12 类统一（grounded 干扰项过门的前提） |
| sub_pattern 持久化 + selector 观测 | `tools/generate_questions.py`、`run_store.py` | 公共（纯数据） |

**非 AR 的 11 个 BaseTaskTypeStrategy**：`uses_grounded_selector=False`，走原路径（VLM 直接出 3 个干扰项），行为不变。

## 2. 架构与数据流

```mermaid
flowchart TD
    A[sample_material_v2] --> B[VLM 生成正解 + N 个候选干扰项]
    B --> C{strategy.uses_grounded_selector?}
    C -->|False 11类| D[取前 3 个候选 = 原行为]
    C -->|True AR| E[VLM 视觉打分: 每候选 + 正解 打 0-1]
    E --> F[区间选择: 选 3 个落在 正解分-δ 区间的]
    F -->|凑不齐 3 个| G[退回重出 prev_reason 引导]
    F -->|成功| H[组题 candidate]
    D --> H
    H --> I[四门 gate 用 current_tree 修复]
    I -->|通过| J[on_accept 落盘 含 sub_pattern]
```

## 3. 组件设计

### 3.1 L0：tree 错配 bug 修复

`pipeline_v2.py` `_process_one_slot`：`run_gates(tree=tree, ...)` → `run_gates(tree=current_tree, ...)`。retry 换视频后门控用当前树验证，消除"无 source material"假拒绝。1 行改动。

### 3.2 策略开关与候选池生成（`distractor_selector.py`）

**策略开关（路径隔离的核心）**：`TaskTypeStrategy` protocol 新增 `uses_grounded_selector` property；`BaseTaskTypeStrategy` 默认返回 `False`（11 类走原路径），`ActionRecognitionStrategy` 覆盖为 `True`。`generator_v2._build_v2_prompt` 的公共输出 schema **不变**；候选池 + selector 逻辑放新模块 `distractor_selector.py`，仅当 `strategy.uses_grounded_selector=True` 时 `_process_one_slot` 调用它，其余题型仍走 `generate_one_v2` 直接出 4 选项。

出题拆为两步（**仅 uses_grounded_selector=True**）：
1. **正解生成**（现有 generate_one_v2）：VLM 生成 question + 正解 + difficulty。
2. **候选池生成**（新 distractor_selector）：给定 question + 正解 + 素材，VLM 一次生成 `N` 个候选干扰项（默认 N=24），每个都要求 grounded near-miss（真实发生、只在一维不同）。

### 3.3 VLM 视觉打分 Selector（核心，消灭 EOB）

```
输入: N 个候选干扰项 + 正解 + 采样帧
VLM(看真帧 + question + 每个选项) → 每个选项打"视觉可信度" score ∈ [0,1]
  正解得分 s_correct（应偏高）
选 3 个 distractor，使 score ∈ [s_correct - δ_high, s_correct - δ_low]:
  - 上界 s_correct - δ_low: 不能太接近正解（否则真歧义/也对）
  - 下界 s_correct - δ_high: 不能太低（否则负空间，排除法可解）
凑不齐 3 个 → 退回重出（reject_reason="grounded 干扰项不足"）
```

`δ_low`、`δ_high` 为配置参数（默认 δ_low=0.05、δ_high=0.35）。核心洞察：干扰项与正解视觉可信度接近 → 排除法（"搜不到就排除"）失效。

**退化处理（避免 AR 系统性欠产）**：候选池凑不齐 3 个落区间时按序退火：
1. 扩大候选池再生成一轮（N → 2N）；
2. 仍不足则逐步放宽 δ_high（+0.1，最多 2 次），保证下界不越过 δ_low；
3. 仍不足才走正常重出（reject_reason="grounded 干扰项不足"，retry_limit 兜底）。
每次 hard-fail（走到步骤 3）记录到 run_store 的 selector 观测，供调参。

### 3.4 单维反事实约束（仅 AR，`strategy_action_recognition.py`）

6 个 SubPattern 的 `instruction` + `distractor_rules` 改为内嵌"只改一维"模板 + few-shot 正例，硬约束"干扰项必须是视频中真实发生的动作/实体，仅在时点/主体/方式/对象**单一维度**上与正解不同，严禁缺席事件"：

| SubPattern | 反事实维度 | few-shot 要求 |
|-----------|-----------|--------------|
| temporal_reasoning_failure | 事件顺序（同组真实事件错序） | 正解正确排序，干扰项是真实事件的错误排列 |
| fine_grained_visual_action | 执行方式（同动词的另一种真实做法） | 4 选项为同一动词的真实执行方式变体 |
| cross_segment_entity_tracking | 主体（另一实体的真实动作） | 干扰项=另一实体在该段真实做的事 |
| semantic_rigidity | 表述（同义 vs 字面陷阱） | 正解同义改写，1 干扰项复用素材原文但语义错 |
| premature_evidence_anchoring | 时点（前段真实诱饵） | 前段必须有与某干扰项字面吻合的真实局部匹配 |
| evidence_gap_confabulation | 因果完整性（诚实 vs 编造） | 正解仅陈述可观测，干扰项编造未展示因果 |

### 3.5 multi_true 门松绑（`gate_multi_true.md`，公共）

rubric 从"≥2 选项 plausibly correct → fail"改为"**仅当 ≥2 选项完全满足题干限定（同主体/时点/方式/对象下都为真）→ fail**"。放行"错误选项有局部真实证据、但在题干限定下为假"的近似干扰项。12 题型统一（用户确认全局松绑）。

### 3.6 sub_pattern 持久化 + selector 观测（公共，纯数据）

- `accepted_questions.json` 每题增 `sub_pattern` 字段：改 `tools/generate_questions.py` 的 `_on_accept`（当前**未写**该字段），从 pipeline 逐题传下的 sub_pattern 写入 JSON。**这是 Phase B 配对翻转门的硬前提**（Phase B 按 sub_pattern 查 supports_flip），务必落实。
- selector 打分观测：`question_gen_items` 表新增列 `selector_scores TEXT`（JSON：正解分、选中 3 干扰项分、候选池大小、退火轮次、是否 hard-fail），幂等 ALTER TABLE 迁移（与 sub_pattern 列同法）。供 EOB 观测与调参。

## 4. Prior-Version Audit（现有 v2 管线行为清单）

| 现有行为 | Phase A 处置 |
|---------|-------------|
| 每 slot 独立重出循环（retry_limit，prev_reason 传回） | **保留** — selector 凑不齐干扰项走同一重出机制 |
| on_accept 逐题追加 JSON（崩溃最多丢一题） | **保留** — 仅新增 sub_pattern 字段 |
| progress 断点续跑（slot_id → status） | **保留** — 不动 |
| 四门 gate 并发 + verbatim 短路 | **保留** — 仅改 multi_true rubric（prompt）+ 修 tree 传参 |
| embedding 去重 | **保留** |
| heavy_check 抽检 | **保留** |
| 11 个 BaseTaskTypeStrategy 出题 | **保留** — uses_grounded_selector=False 走原路径 |

未发现隐式删除。唯一行为变更：AR 出题多一个 selector 步骤；multi_true rubric 放宽（有意，全局）。

## 5. 非功能性需求

| 维度 | 设计 |
|------|------|
| **持久化** | 不变 — on_accept 逐题追加，崩溃最多丢一题。selector 打分随题落 run_store |
| **幂等性** | 不变 — 同 seed → 同 slot；候选池生成与 selector 打分由 rng/VLM 决定，同输入同输出（VLM 非确定性已有，不新增） |
| **断点续跑** | 不变 — progress 机制在 pipeline 层；selector 无状态，重出即重跑 |
| **原子性** | 不变 — 逐题落库；JSON 用 tmp + os.replace 原子写（现有） |

## 6. 行为保真检查清单

| # | 行为 | 状态 |
|---|------|------|
| 1 | 12 题型 slot 分配 | 保留 |
| 2 | 重出循环 + 换视频 | 保留 |
| 3 | 四门 gate | 保留（multi_true rubric 有意松绑 + tree 传参修复） |
| 4 | 去重/heavy_check/on_accept/断点续跑 | 保留 |
| 5 | 11 个 BaseTaskTypeStrategy 行为 | 保留（selector 默认关闭） |
| 6 | AR 出题 | **有意变更** — 候选池 + selector + 单维反事实 |

## 7. 配置参数（YAML）

| 参数 | 默认 | 说明 |
|------|------|------|
| `candidate_pool_size` | 24 | VLM 生成的候选干扰项数 N |
| `selector_delta_low` | 0.05 | 干扰项视觉分与正解的最小差 |
| `selector_delta_high` | 0.35 | 干扰项视觉分与正解的最大差 |

## 8. 测试策略

- 单元：selector 区间选择（构造候选打分，验证选出落区间的 3 个 / 凑不齐时退回）；uses_grounded_selector 分流（AR 走 selector、其余走原路径）。
- 集成：AR 出题端到端（mock VLM 返回候选池 + 打分）→ 验证干扰项来自候选池且落区间；sub_pattern 写入 JSON。
- 回归：11 个非 AR 题型出题行为不变（现有测试全绿）。

## 9. 与 Phase B 的衔接

Phase A 产出 grounded 单题；Phase B（[2026-07-14-adversarial-question-gen-phaseB-design.md]）在其上加"完整 agent 对抗过滤门 + 配对翻转门"揪残余 shortcut。Phase A 的 selector 观测数据为 Phase B 的难度报告提供基线。
