Files
Video-Tree-TRM5/research-wiki/designs/2026-07-07-question-gen-design.md
T
iomgaa dbc9d38cd7 plan(tree/repair): 三项改造实现计划(遥测加固+断点续跑+并发)
6 个 Task: telemetry 防御加固 → call_id 根因修复 → detector L2/L1 扩展
→ progress 管理 → 并发编排+CLI → lint+全量测试
2026-07-09 00:08:23 -04:00

7.2 KiB
Raw Blame History

id, title, type, created, status
id title type created status
question-gen 出题模块迁移设计(question_gen design 2026-07-07 approved

出题模块迁移设计

1. 目标

从 TRM4 core/harness/question_gen.py 迁移出题数据结构与采样逻辑到 TRM5 Clean Architecture,同时预留 LLM 驱动出题的 Protocol 接口。

维度 说明
迁移范围 benchmark 加载 + 分层采样(纯函数,180 行)
预留接口 QuestionGenerator Protocol(不实现,后续参考 TRM4 research-wiki/designs/2026-07-06-question-gen-synth-design.md
不做 LLM 出题实现、校准脚本、去重机制

2. Clean Architecture 分层决策

2.1 类型放置

GeneratedQuestioncore/evolution/diagnose、validate)和 app/harness/runner、batching、pools、inference)跨层使用。按依赖方向(core 不可依赖 app),必须放 core/types.py,与 LLMResponse 同级。

flowchart LR
    CT["core/types.py\nGeneratedQuestion"] --> CE["core/evolution/\ndiagnose · validate"]
    CT --> AH["app/harness/\nrunner · batching · pools"]
    CT --> AQ["app/question_gen/\nloader"]

2.2 模块结构

core/types.py                ← 追加 GeneratedQuestion
app/ports.py                 ← 追加 QuestionGenerator Protocol
app/question_gen/
├── __init__.py              ← 公开 API re-export
└── loader.py                ← load_benchmark() + stratified_sample()

否决方案

方案 否决理由
GeneratedQuestionapp/question_gen/types.py core/evolution/ 无法 import app/ 层,违反依赖方向
loader / sampler 拆两文件 sampler 仅 ~100 行,不值得独立文件
Protocol 放 app/question_gen/protocols.py EmbeddingProviderapp/ports.py 的既有模式不一致

3. 类型定义

3.1 GeneratedQuestioncore/types.py 追加)

@dataclass(frozen=True)
class GeneratedQuestion:
    """单条生成/加载的题目。跨层共享类型。"""
    question_id: str
    video_id: str
    task_type: str
    question: str
    options: tuple[str, ...]
    answer: str
    source_nodes: tuple[str, ...]
    difficulty: str

与 TRM4 的有意变更

变更 理由
options: list → tuple 配合 frozen=True 不可变语义
source_nodes: list → tuple 同上
difficulty 移除默认值 "medium" 显式传入(§4.1 P4: 显式优于隐式)

移除 QuestionGenResultTRM5 无消费者,YAGNI。

3.2 QuestionGenerator Protocolapp/ports.py 追加)

@runtime_checkable
class QuestionGenerator(Protocol):
    """LLM 驱动的题目生成端口(预留接口)。"""
    async def generate(
        self,
        video_id: str,
        task_type: str,
        tree: TreeIndex,
        *,
        exemplars: list[GeneratedQuestion],
    ) -> GeneratedQuestion: ...

接口设计参考 TRM4 仓库 research-wiki/designs/2026-07-06-question-gen-synth-design.md(位于 /home/iomgaa/Projects/Video-Tree-TRM4/,不复制到 TRM5)中的"题型-层级映射 + few-shot exemplar"模式。tree 参数提供锚节点上下文,exemplars 提供风格示例。具体实现在后续 tools/generate_questions.py(一次性脚本)中完成,通过 adapters/ 层的 Protocol 实现注入。

4. 函数接口

4.1 load_benchmark

load_benchmark(questions_dir: Path) -> list[GeneratedQuestion]

从指定目录 glob *.json,每个文件以 stemvideo_id,解析为 GeneratedQuestion 列表。JSON 格式与 store/questions/benchmarks/Video-MME/*.json 完全一致。

与 TRM4 对比:算法 100% 保真。optionssource_nodes 转为 tuple

difficulty 字段处理规则:现有 benchmark JSONstore/questions/benchmarks/Video-MME/)不含 difficulty 字段,这是 legacy schema 特征。加载时按如下规则显式转换(非默认值掩盖):

JSON 情况 处理
difficulty 字段 取 JSON 值
difficulty 字段 _LEGACY_DEFAULT_DIFFICULTY = "medium" 常量

常量集中定义在 loader.py 顶部,测试用例覆盖两种情况。

4.2 stratified_sample

stratified_sample(
    questions: list[GeneratedQuestion],
    correctness: dict[str, bool],
    size: int,
    correct_ratio: float | None,
    task_types: list[str] | None,
    seed: int,
    min_per_class: int | None,
) -> list[GeneratedQuestion]

所有参数显式传入,无默认值(§4.1 P4)。

算法保真清单(逐一比对 TRM4):

逻辑点 TRM4 行为 TRM5 保持
task_types 过滤 task_types 非 None 时,先过滤 pool 只保留指定题型 保持
correct_ratio=None 自然分布分支,随机抽样 size 保持
correct_ratio 有值 按对错比例分层,对题 round(size * ratio) 保持
correctness.get(id, False) 未知 correctness 的题统一当错题处理 保持
分层返回顺序 对题在前、错题在后 保持
池不足 ValueError 报错,不静默降级 保持
min_per_class 补足 遍历 pool 全部题型(非仅 sampled 命中的),按首次出现顺序确定性枚举 保持
补足不足时 全取,不报错 保持
随机种子 random.Random(seed) 局部实例 保持

内部辅助函数 _ratio_stratified_sample_backfill_per_class 完整保留。

5. 职责边界

组件 职责 位置 谁 import 谁
GeneratedQuestion 题目数据结构 core/types.py 被所有层 import
load_benchmark / stratified_sample 加载 + 采样 app/question_gen/loader.py app/harness/ import
QuestionGenerator Protocol LLM 出题接口定义 app/ports.py 被未来 adapters/ 实现
tools/generate_questions.py(未来) LLM 出题一次性脚本 tools/ 独立工具,不被其他模块 import

tools/generate_questions.py 未来可实例化 QuestionGenerator 的 adapter 实现,但 tools/ 本身不被 app/ import(§5 硬性规则)。

6. 文档同步

以下章节需要更新:

文档 章节 变更
ARCHITECTURE.md §1 表格 DataLoader 行 app/question_gen/generator.py app/question_gen/loader.py
ARCHITECTURE.md §2.2 Mermaid QGEN 节点 generator.py loader.py
CLAUDE.md §1.5 表格 DataLoader 行 app/question_gen/generator.py app/question_gen/loader.py

不变更ARCHITECTURE.md §6 核心算法保真清单 — stratified_sample 是采样工具函数,不属于 13 项核心算法(那些是建树 + 训练的关键算法)。

7. 测试策略

测试 路径 覆盖点
GeneratedQuestion 冻结性 tests/unit/test_core_types.py(追加) frozen 不可变、字段完整性
load_benchmark tests/unit/test_question_loader.py 正常加载、空目录、JSON 格式异常
stratified_sample tests/unit/test_question_loader.py 自然分布、分层采样、题型过滤、未知 correctness 当错题、对题在前返回顺序、题型保底、池不足报错、种子可复现