Files
Video-Tree-TRM5/app/question_gen/strategy.py
T

259 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""题型出题策略 — Clean Architecture 的 Strategy 层。
将出题管线从 5 个粗粒度 QuestionFamilySpec 替换为 12 个题型级别的
TaskTypeStrategy。BaseTaskTypeStrategy 封装现有 family 行为,
特化策略(如 ActionRecognitionStrategy)自包含。
典型用法::
strategy = get_strategy("Action Reasoning")
level = strategy.sampling_level
constraint = strategy.sampling_constraint
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
from app.question_gen.families import (
ENUMERATION_FAMILY,
REASONING_FAMILY,
RETRIEVAL_FAMILY,
SPATIAL_FAMILY,
VISUAL_FAMILY,
QuestionFamilySpec,
SamplingConstraint,
)
from app.question_gen.sampler_v2 import _TASK_TYPE_TO_LEVEL
if TYPE_CHECKING:
import random
# ---------------------------------------------------------------------------
# SubPattern
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class SubPattern:
"""出题子模式 — 靶向特定失败机制。
属性:
name: 子模式标识名。
weight: 选择权重(按比例分配)。
sampling_level_override: 覆盖策略默认采样层级(None = 不覆盖)。
constraint_override: 覆盖策略默认采样约束(None = 不覆盖)。
instruction: 核心出题指令(1-3 句话)。
positive_examples: VME 原题 few-shot 示范。
negative_examples: 反面示例。
distractor_rules: 干扰项构造规则。
"""
name: str
weight: float
sampling_level_override: int | None
constraint_override: SamplingConstraint | None
instruction: str
positive_examples: list[dict] = field(default_factory=list)
negative_examples: list[dict] = field(default_factory=list)
distractor_rules: str = ""
# ---------------------------------------------------------------------------
# Protocol
# ---------------------------------------------------------------------------
@runtime_checkable
class TaskTypeStrategy(Protocol):
"""题型出题策略 — pipeline 的唯一接口。"""
@property
def task_type(self) -> str: ...
@property
def sampling_level(self) -> int: ...
@property
def sampling_constraint(self) -> SamplingConstraint: ...
@property
def prompt_template(self) -> str: ...
@property
def strategy_name(self) -> str: ...
@property
def skill_target(self) -> str: ...
@property
def leak_probe_template(self) -> str: ...
@property
def uses_grounded_selector(self) -> bool: ...
def select_sub_pattern(self, rng: random.Random) -> SubPattern | None: ...
def build_prompt_context(self, material: Any, sub_pattern: SubPattern | None) -> dict: ...
def extra_gates(self, candidate: Any) -> list: ...
# ---------------------------------------------------------------------------
# BaseTaskTypeStrategy
# ---------------------------------------------------------------------------
# 消歧绑定表:多归属题型确定性绑定到一个 family
_TASK_TYPE_TO_FAMILY: dict[str, QuestionFamilySpec] = {
"Action Recognition": VISUAL_FAMILY, # fallback — 注册表中已被 ActionRecognitionStrategy 替换
"Object Recognition": RETRIEVAL_FAMILY,
"Object Reasoning": REASONING_FAMILY,
"Action Reasoning": REASONING_FAMILY,
"Attribute Perception": VISUAL_FAMILY,
"OCR Problems": VISUAL_FAMILY,
"Counting Problem": ENUMERATION_FAMILY,
"Information Synopsis": REASONING_FAMILY,
"Temporal Reasoning": ENUMERATION_FAMILY,
"Temporal Perception": ENUMERATION_FAMILY,
"Spatial Reasoning": SPATIAL_FAMILY,
"Spatial Perception": SPATIAL_FAMILY,
}
class BaseTaskTypeStrategy:
"""封装现有 QuestionFamilySpec 行为的默认策略。
所有属性委托给绑定的 family,确保未特化题型的行为不变。
参数:
task_type: 题型名。
family: 绑定的 QuestionFamilySpec。
"""
def __init__(self, task_type: str, family: QuestionFamilySpec) -> None:
self._task_type = task_type
self._family = family
@property
def task_type(self) -> str:
"""返回题型名。"""
return self._task_type
@property
def sampling_level(self) -> int:
"""返回采样层级(从 _TASK_TYPE_TO_LEVEL 查询)。"""
return _TASK_TYPE_TO_LEVEL[self._task_type]
@property
def sampling_constraint(self) -> SamplingConstraint:
"""返回采样约束(委托给绑定的 family)。"""
return self._family.sampling
@property
def prompt_template(self) -> str:
"""返回 prompt 模板文件名(委托给绑定的 family)。"""
return self._family.prompt_template
@property
def strategy_name(self) -> str:
"""返回策略名(即 family.name)。"""
return self._family.name
@property
def skill_target(self) -> str:
"""返回目标失败机制编号(委托给绑定的 family)。"""
return self._family.skill_target
@property
def leak_probe_template(self) -> str:
"""返回泄漏探测模板文件名(委托给绑定的 family.leak_profile)。"""
return self._family.leak_profile.probe_template
@property
def uses_grounded_selector(self) -> bool:
"""默认不启用 grounded selector11 类题型走原路径)。"""
return False
def select_sub_pattern(self, rng: random.Random) -> SubPattern | None:
"""BaseTaskTypeStrategy 无子模式。"""
return None
def build_prompt_context(self, material: Any, sub_pattern: SubPattern | None) -> dict:
"""返回基础 prompt 上下文(family_name + prompt_template)。"""
return {
"family_name": self._family.name,
"prompt_template": self._family.prompt_template,
}
def extra_gates(self, candidate: Any) -> list:
"""BaseTaskTypeStrategy 无额外 gate。"""
return []
# ---------------------------------------------------------------------------
# 注册表
# ---------------------------------------------------------------------------
_STRATEGY_REGISTRY: dict[str, TaskTypeStrategy] = {}
def register_strategy(strategy: TaskTypeStrategy) -> None:
"""注册一个题型策略。同一 task_type 重复注册会覆盖。
参数:
strategy: 实现 TaskTypeStrategy 接口的策略实例。
"""
_STRATEGY_REGISTRY[strategy.task_type] = strategy
_BUILTIN_REGISTERED = False
def get_strategy(task_type: str) -> TaskTypeStrategy:
"""获取题型策略。未注册的自动创建 BaseTaskTypeStrategy。
首次调用时延迟注册内建特化策略(避免循环导入)。
参数:
task_type: 题型名。
返回:
TaskTypeStrategy 实例。
异常:
KeyError: task_type 不在消歧绑定表和注册表中。
"""
global _BUILTIN_REGISTERED # noqa: PLW0603
if not _BUILTIN_REGISTERED:
_BUILTIN_REGISTERED = True
_register_builtin_strategies()
if task_type in _STRATEGY_REGISTRY:
return _STRATEGY_REGISTRY[task_type]
return _build_default_strategy(task_type)
def _build_default_strategy(task_type: str) -> BaseTaskTypeStrategy:
"""根据消歧绑定表自动构造 BaseTaskTypeStrategy。
参数:
task_type: 题型名。
返回:
BaseTaskTypeStrategy 实例。
异常:
KeyError: task_type 不在消歧绑定表中。
"""
family = _TASK_TYPE_TO_FAMILY[task_type]
return BaseTaskTypeStrategy(task_type=task_type, family=family)
def _register_builtin_strategies() -> None:
"""注册内建的特化策略。由 get_strategy 首次调用时延迟执行。"""
from app.question_gen.strategy_action_recognition import ActionRecognitionStrategy
register_strategy(ActionRecognitionStrategy())