Coverage for src\genesis\qa\guardrails.py: 100%
27 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 14:20 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-26 14:20 +0800
1from __future__ import annotations
3from dataclasses import dataclass, field
4from typing import Any
6# QA 校验反馈循环的最大轮次上限(OV6:防止「QA→Writer修正→重新QA」无限循环)
7DEFAULT_MAX_QA_ROUNDS = 3
10def resolve_qa_model(models: Any | None) -> str | None:
11 """解析 QA 校验应使用的模型名。
13 OV6 修复:QA 校验必须走 fallback 模型(独立模型族),避免「主模型自校验盲区」
14 (如 DeepSeek 生成、DeepSeek 校验 → 同族模型盲点,难以发现自身偏误)。
16 返回 fallback 模型名;若无 fallback 配置则返回 None,迫使调用方显式指定
17 一个独立于生成模型的校验模型(而非静默回退到 primary)。
18 """
19 if models is None:
20 return None
21 fallback = getattr(models, "fallback", None)
22 if fallback is not None:
23 name = getattr(fallback, "name", None)
24 if name:
25 return name
26 return None
29@dataclass
30class QALoopController:
31 """QA 反馈循环护栏:约束「QA校验→Writer修正→重新校验」的最大轮次。
33 防止 OV6 指出的无边界循环风险:超过 max_rounds 后必须停止循环,
34 转为上报人工或降级处理,不得无限重试。
35 """
37 max_rounds: int = DEFAULT_MAX_QA_ROUNDS
38 _round: int = field(default=0, init=False)
40 @property
41 def round(self) -> int:
42 """当前已执行的轮次(0 表示尚未开始)。"""
43 return self._round
45 def can_continue(self) -> bool:
46 """是否还能进入下一轮 QA 校验。"""
47 return self._round < self.max_rounds
49 def advance(self) -> int:
50 """推进一轮,返回推进后的当前轮次(从 1 开始计数)。"""
51 self._round += 1
52 return self._round
54 def is_exhausted(self) -> bool:
55 """已达上限:必须停止循环并上报,禁止继续重试。"""
56 return self._round >= self.max_rounds