refactor: remove block-sequential gate path and gate_block knob (algo #6)
config/train_videomme.yaml 同时收录待入库的实验配置变更(run_id v2 / concurrency 32 / batch_size 40)。tests/integration/test_v3_contract_e2e.py 的 run_id 断言按 Task 5 显式契约同步修正(原断言依赖旧隐式实例注入)。
This commit is contained in:
+15
-420
@@ -1,15 +1,15 @@
|
||||
"""async 块序贯验证编排 — CE-Gate 局部验证的唯一独立子编排器。
|
||||
"""async 连续并发 gate 验证编排 — CE-Gate 局部验证的唯一独立子编排器。
|
||||
|
||||
从 TRM4 core/harness/validate.py (626 行) 迁移,重大重构:
|
||||
- 同步 → async(run_inference 注入为 async callable)
|
||||
- _classify_quadrants → core.evolution.classify_quadrants 纯函数
|
||||
- 配对逻辑 → 复用 core.evolution.pair_block + 本地证据行组装
|
||||
- _load_run_rows / _candidate_correctness_from_db → 共享 log.query()
|
||||
- materialize_candidate_skill 保持同步(纯文件操作)
|
||||
多题型全部 (单元, 臂) 任务共享题槽并发(validate_skills_concurrent),
|
||||
统计推进不按到达序,而按预声明的阶梯序前缀消费(_advance_prefix):
|
||||
base 臂缓存命中瞬间返回、cand 臂必新鲜跑,两臂延迟不对称,按到达序判定
|
||||
会系统性偏向早到翻转;前缀消费把判定顺序钉回阶梯序,anytime-valid 无条件
|
||||
成立(核心算法保真 #6,语义修订:块序贯 → 阶梯序前缀逐对序贯)。
|
||||
|
||||
基线与候选在同一阶梯前缀上逐块配对,只数翻转(基线错→候选对 = W,
|
||||
基线对→候选错 = L),每块结束调 gate_decision 做四出口判定。
|
||||
基线侧逐题对错走 BaselineCache 内容寻址缓存,miss 才新鲜跑。
|
||||
基线与候选在同一阶梯前缀上逐单元配对,只数翻转(基线错→候选对 = W,
|
||||
基线对→候选错 = L),每消费一个单元调一次 gate_decision 做四出口判定,
|
||||
过线即冻结、τ 之后的 in-flight 结果整体丢弃。基线侧单元级对错走
|
||||
BaselineCache 内容寻址缓存,miss 才新鲜跑;INFRA 单元不写缓存、从配对剔除。
|
||||
判定逻辑全部在 core/evolution/gate,本模块只负责推理编排与证据收集。
|
||||
"""
|
||||
|
||||
@@ -26,7 +26,7 @@ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
||||
from loguru import logger
|
||||
|
||||
from app.harness.gate_ladder import BaselineCache, skill_hash
|
||||
from app.harness.question_units import build_units, flatten_units, unit_correctness_view
|
||||
from app.harness.question_units import build_units, unit_correctness_view
|
||||
from core.evolution import (
|
||||
INFRA_STOP_REASONS,
|
||||
GateParams,
|
||||
@@ -68,7 +68,7 @@ class RunInferenceFn(Protocol):
|
||||
|
||||
调用方(runner)负责绑定 llm、tool_dispatch_fn、prompt_builder、
|
||||
log、concurrency、max_steps、skill_mode 等共享依赖。
|
||||
validate 侧只传 questions、run_id、skills_dir 三个逐块变化的参数。
|
||||
validate 侧只传 questions、run_id、skills_dir 三个逐任务变化的参数。
|
||||
"""
|
||||
|
||||
async def __call__(
|
||||
@@ -85,21 +85,6 @@ class RunInferenceFn(Protocol):
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InferenceRunConfig:
|
||||
"""一次推理运行的配置三元组,把"如何跑推理"内聚成一组。
|
||||
|
||||
字段:
|
||||
concurrency: 推理并发度。
|
||||
max_steps: 单题最大推理步数。
|
||||
skill_mode: 推理 skill 模式("auto" / "manual" / "none")。
|
||||
"""
|
||||
|
||||
concurrency: int
|
||||
max_steps: int
|
||||
skill_mode: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class ValidationOutcome:
|
||||
"""CE-Gate 局部验证结果:三态动作 + e-process 证据(单元口径)+ 逐题溯源对错。
|
||||
@@ -260,23 +245,6 @@ def _infra_question_ids_from_db(
|
||||
}
|
||||
|
||||
|
||||
def _count_infra_units(units: list[QuestionUnit], infra_qids: set[str]) -> int:
|
||||
"""统计含 INFRA record 的 unit 数(一个 unit 任一题 INFRA 即计 1)。
|
||||
|
||||
使护栏分子与分母(r.total,unit 粒度)同口径:AR pair 一 unit 含两 record,
|
||||
逐 record 计数会放大分子致 gate_guard_err 误触发,破坏 unit 粒度一致性
|
||||
(核心算法保真 #5/#6)。
|
||||
|
||||
参数:
|
||||
units: 当前块的单元列表(single 或 AR pair)。
|
||||
infra_qids: 本 run 中 stop_reason 属 INFRA 故障族的 question_id 集合。
|
||||
|
||||
返回:
|
||||
含至少一题 INFRA 的 unit 数。
|
||||
"""
|
||||
return sum(1 for u in units if any(q.question_id in infra_qids for q in u.questions))
|
||||
|
||||
|
||||
def _candidate_correctness_from_db(
|
||||
log: HarnessLog,
|
||||
run_id: str,
|
||||
@@ -296,164 +264,13 @@ def _candidate_correctness_from_db(
|
||||
return {q.question_id: rows.get(q.question_id, {}).get("_correct", False) for q in chunk}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 块级 async 函数
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
async def _resolve_baseline_block(
|
||||
units: list[QuestionUnit],
|
||||
task_type: str,
|
||||
s_hash: str,
|
||||
prompts_version: str,
|
||||
baseline_cache: BaselineCache,
|
||||
base_skills_dir: Path,
|
||||
run_inference: RunInferenceFn,
|
||||
log: HarnessLog,
|
||||
run_id: str,
|
||||
) -> tuple[dict[str, bool], list[QuestionUnit], int, int]:
|
||||
"""基线侧处理一个块:缓存优先(unit 键),miss 的单元新鲜跑基线版本并回写缓存。
|
||||
|
||||
缓存以 unit_id 为键、存单元级对错(AR pair 双向 AND 折叠后一个布尔)。
|
||||
miss 的单元展开为逐题送推理,读回逐题预测后经 unit_correctness_view 折叠成
|
||||
单元级对错再写缓存(核心算法保真 #5)。逐题 predictions 仍逐题落库溯源。
|
||||
|
||||
INFRA 隔离(算法 #6):miss 单元内**任一题** stop_reason ∈ {error, parse_error}
|
||||
即判定该单元为 INFRA 故障——**不写 BaselineCache**(否则瞬时故障永久污染基线
|
||||
快照)、**不入 b_units**、并从返回的有效单元集中剔除,避免污染 W/L 翻转与配对。
|
||||
命中缓存的单元恒为有效(此前已成功验证过)。
|
||||
|
||||
参数:
|
||||
units: 当前块的单元列表(single 或 AR pair)。
|
||||
task_type: 当前验证题型(缓存键成分)。
|
||||
s_hash: 基线侧生效 skill 的内容哈希(缓存键成分)。
|
||||
prompts_version: 当前 prompts 版本(缓存键成分)。
|
||||
baseline_cache: 基线侧单元级对错缓存(键含 unit_id)。
|
||||
base_skills_dir: 基线 skills 版本目录。
|
||||
run_inference: 注入的 async 推理函数。
|
||||
log: HarnessLog 共享实例(推理后读预测)。
|
||||
run_id: 本块基线 run_id。
|
||||
|
||||
返回:
|
||||
(b_units, valid_units, errors_inc, denom_inc):块内有效 unit_id -> 基线单元
|
||||
对错、剔除 INFRA 后的有效单元列表、本块新增的 INFRA error 计数与推理题次
|
||||
分母增量(全命中时为 0, 0)。
|
||||
"""
|
||||
miss_units = [
|
||||
u
|
||||
for u in units
|
||||
if baseline_cache.get(task_type, s_hash, prompts_version, u.unit_id) is None
|
||||
]
|
||||
errors_inc = 0
|
||||
denom_inc = 0
|
||||
infra_qids: set[str] = set()
|
||||
if miss_units:
|
||||
miss_questions = flatten_units(miss_units)
|
||||
r_b = await run_inference(miss_questions, run_id=run_id, skills_dir=base_skills_dir)
|
||||
infra_qids = _infra_question_ids_from_db(log, r_b.run_id, miss_questions)
|
||||
# 护栏分子与分母(r.total,unit 粒度)同口径:含 INFRA record 的 unit 计 1,
|
||||
# 避免 AR pair(一 unit 两 record)逐 record 计数放大分子致误触发;仍涵盖
|
||||
# error + parse_error(_infra_question_ids_from_db 口径),parse_error 风暴不被绕过。
|
||||
errors_inc = _count_infra_units(miss_units, infra_qids)
|
||||
denom_inc = r_b.total
|
||||
fresh_per_q = _candidate_correctness_from_db(log, r_b.run_id, miss_questions)
|
||||
fresh_units = unit_correctness_view(miss_units, fresh_per_q)
|
||||
# 只回写非 INFRA 单元;INFRA 单元不入缓存(不永久污染基线快照)
|
||||
for u in miss_units:
|
||||
if any(q.question_id in infra_qids for q in u.questions):
|
||||
continue
|
||||
baseline_cache.put(task_type, s_hash, prompts_version, u.unit_id, fresh_units[u.unit_id])
|
||||
|
||||
valid_units = [
|
||||
u for u in units if not any(q.question_id in infra_qids for q in u.questions)
|
||||
]
|
||||
|
||||
b_units: dict[str, bool] = {}
|
||||
for u in valid_units:
|
||||
val = baseline_cache.get(task_type, s_hash, prompts_version, u.unit_id)
|
||||
assert val is not None, f"基线缓存补齐后仍有 miss: unit={u.unit_id} run_id={run_id}"
|
||||
b_units[u.unit_id] = val
|
||||
return b_units, valid_units, errors_inc, denom_inc
|
||||
|
||||
|
||||
async def _run_candidate_block(
|
||||
units: list[QuestionUnit],
|
||||
cand_dir: Path,
|
||||
run_inference: RunInferenceFn,
|
||||
log: HarnessLog,
|
||||
run_id: str,
|
||||
) -> tuple[dict[str, bool], int, int]:
|
||||
"""候选侧处理一个块:单元展开为逐题全块新鲜跑候选版本并从 db 读逐题对错。
|
||||
|
||||
返回逐题对错映射(question_id -> bool),折叠为单元视图交由调用方完成,
|
||||
逐题结果同时用于 candidate_correctness 溯源与二轨 correctness 合并。
|
||||
|
||||
参数:
|
||||
units: 当前块的单元列表。
|
||||
cand_dir: 已物化的候选 skills 目录。
|
||||
run_inference: 注入的 async 推理函数。
|
||||
log: HarnessLog 共享实例(推理后读预测)。
|
||||
run_id: 本块候选 run_id。
|
||||
|
||||
返回:
|
||||
(c_per_q, errors_inc, denom_inc):块内 question_id -> 候选对错。
|
||||
"""
|
||||
questions = flatten_units(units)
|
||||
r_c = await run_inference(questions, run_id=run_id, skills_dir=cand_dir)
|
||||
c_per_q = _candidate_correctness_from_db(log, r_c.run_id, questions)
|
||||
infra_qids = _infra_question_ids_from_db(log, r_c.run_id, questions)
|
||||
# 护栏分子与分母(r.total,unit 粒度)同口径:含 INFRA record 的 unit 计 1
|
||||
# (见 _count_infra_units),涵盖 error + parse_error。
|
||||
errors_inc = _count_infra_units(units, infra_qids)
|
||||
return c_per_q, errors_inc, r_c.total
|
||||
|
||||
|
||||
def _build_evidence_rows(
|
||||
units: list[QuestionUnit],
|
||||
b_units: dict[str, bool],
|
||||
c_units: dict[str, bool],
|
||||
task_type: str,
|
||||
block_idx: int,
|
||||
) -> list[dict]:
|
||||
"""组装一个块的 gate_evidence 单元级证据行。
|
||||
|
||||
证据行按 unit 口径(question_id 字段存 unit_id、correct 存单元级对错),
|
||||
与 e-process 判定同粒度;逐题预测明细仍在 predictions 表逐题溯源。
|
||||
e_value 留 None 待块判定后回填,stop_reason 留空串待终态回填。
|
||||
|
||||
参数:
|
||||
units: 当前块的单元列表。
|
||||
b_units: 块内 unit_id -> 基线单元对错。
|
||||
c_units: 块内 unit_id -> 候选单元对错。
|
||||
task_type: 当前验证题型。
|
||||
block_idx: 当前块序号。
|
||||
|
||||
返回:
|
||||
单元级证据行列表。
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"question_id": u.unit_id,
|
||||
"task_type": task_type,
|
||||
# 落库列已更名 ladder_rank(阶梯序号);旧块路径此处值仍为块号,
|
||||
# 仅键名对齐 gate_evidence 表结构以保持落库兼容。
|
||||
"ladder_rank": block_idx,
|
||||
"baseline_correct": b_units[u.unit_id],
|
||||
"candidate_correct": c_units[u.unit_id],
|
||||
"e_value": None,
|
||||
"stop_reason": "",
|
||||
}
|
||||
for u in units
|
||||
]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# INFRA 护栏
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _check_infra_guard(errors: int, infra_denom: int, gate_guard_err: float) -> None:
|
||||
"""跨块累计 INFRA 错误率护栏:分母 >=10 且超阈值时 raise。
|
||||
"""累计 INFRA 错误率护栏:分母 >=10 且超阈值时 raise。
|
||||
|
||||
参数:
|
||||
errors: 两侧累计 error 计数。
|
||||
@@ -484,13 +301,13 @@ def _finalize_outcome(
|
||||
evidence_rows: list[dict],
|
||||
task_type: str,
|
||||
) -> ValidationOutcome:
|
||||
"""将块循环终态判定组装为 ValidationOutcome。
|
||||
"""将终态判定组装为 ValidationOutcome。
|
||||
|
||||
四象限/准确率/W/L 均按单元口径(base_obs/cand_obs 为 unit_id -> bool),
|
||||
candidate_correctness 独立保留逐题溯源(供 runner 二轨合并进 state.correctness)。
|
||||
|
||||
参数:
|
||||
verdict: 最后一块的 gate 判定结果。
|
||||
verdict: 终态 gate 判定结果。
|
||||
w: 累计 W(基线错→候选对单元翻转)。
|
||||
l: 累计 L(基线对→候选错单元翻转)。
|
||||
n_used: 已消费的阶梯单元数。
|
||||
@@ -576,228 +393,6 @@ def _ladder_units(ladder_items: list[GeneratedQuestion]) -> list[QuestionUnit]:
|
||||
return units
|
||||
|
||||
|
||||
async def _run_local_validation(
|
||||
workspace_dir: Path,
|
||||
cand_dir: Path,
|
||||
base_skills_version: str,
|
||||
task_type: str,
|
||||
base_skill_content: str,
|
||||
units: list[QuestionUnit],
|
||||
gate_params: GateParams,
|
||||
gate_block: int,
|
||||
gate_guard_err: float,
|
||||
baseline_cache: BaselineCache,
|
||||
prompts_version: str,
|
||||
run_inference: RunInferenceFn,
|
||||
log: HarnessLog,
|
||||
gate_run_prefix: str,
|
||||
) -> ValidationOutcome:
|
||||
"""块序贯循环主体:逐块基线(缓存优先)/候选按单元配对推理,块间 e-process 判定。
|
||||
|
||||
按 gate_block 切**单元**前缀(AR pair 整锁在同一块,不跨块拆分),每块先补齐
|
||||
基线侧缓存 miss(新鲜跑基线版本并按 unit_id 写 BaselineCache),再全块跑候选,
|
||||
折叠成单元视图后配对累计 W/L 调 gate_decision;非 continue 即早停。单元尽时
|
||||
最后一块的判定即终态(n_remaining=0 走 provisional/inertia 分支),无循环外补判。
|
||||
|
||||
参数:
|
||||
workspace_dir: Workspace 根目录。
|
||||
cand_dir: 已物化的候选 skills 目录。
|
||||
base_skills_version: 基线 skills 版本名。
|
||||
task_type: 当前验证题型。
|
||||
base_skill_content: 基线侧生效 skill 全文(skill_hash 作缓存键成分)。
|
||||
units: 已截断到 gate_n_max 的阶梯单元序(single 或 AR pair)。
|
||||
gate_params: e-process 判据阈值组。
|
||||
gate_block: 块大小(单位为**单元数**)。
|
||||
gate_guard_err: 跨块累计 INFRA 错误率护栏(分母 >=10 才触发)。
|
||||
baseline_cache: 基线侧单元级对错缓存(键含 unit_id)。
|
||||
prompts_version: 当前 prompts 版本(缓存键成分)。
|
||||
run_inference: 注入的 async 推理函数。
|
||||
log: HarnessLog 共享实例。
|
||||
gate_run_prefix: 块 run_id 前缀(含 "_gate_" 标记)。
|
||||
|
||||
返回:
|
||||
ValidationOutcome。
|
||||
|
||||
关键实现:
|
||||
INFRA 护栏跨块累计基线+候选两侧的 error 计数,分母(总推理题次,仍逐题计)
|
||||
>=10 且错误率超 gate_guard_err 时直接 raise,避免坏批次污染判定。
|
||||
"""
|
||||
w = 0
|
||||
l = 0 # noqa: E741
|
||||
n_used = 0
|
||||
n_excluded = 0 # 累计被 INFRA 隔离剔除的单元数(从阶梯分母扣除)
|
||||
errors = 0
|
||||
infra_denom = 0
|
||||
evidence_rows: list[dict] = []
|
||||
base_obs: dict[str, bool] = {}
|
||||
cand_obs: dict[str, bool] = {}
|
||||
candidate_per_q: dict[str, bool] = {}
|
||||
s_hash = skill_hash(base_skill_content)
|
||||
base_skills_dir = workspace_dir / "skills" / base_skills_version
|
||||
unit_chunks = [units[i : i + gate_block] for i in range(0, len(units), gate_block)]
|
||||
n_plan = len(units)
|
||||
verdict: GateVerdict | None = None
|
||||
|
||||
for block_idx, unit_chunk in enumerate(unit_chunks):
|
||||
# Phase 1: 基线侧(缓存优先,miss 新鲜跑,INFRA 单元剔除)
|
||||
b_units, valid_chunk, err_b, den_b = await _resolve_baseline_block(
|
||||
units=unit_chunk,
|
||||
task_type=task_type,
|
||||
s_hash=s_hash,
|
||||
prompts_version=prompts_version,
|
||||
baseline_cache=baseline_cache,
|
||||
base_skills_dir=base_skills_dir,
|
||||
run_inference=run_inference,
|
||||
log=log,
|
||||
run_id=f"{gate_run_prefix}_b{block_idx}_base",
|
||||
)
|
||||
# 本块全 INFRA:无有效单元可配对——候选无需空跑,仅把基线侧错误计入护栏后
|
||||
# 累计剔除数进入下一块(护栏仍能在整轮 INFRA 错误率超阈值时熔断)。
|
||||
n_excluded += len(unit_chunk) - len(valid_chunk)
|
||||
if not valid_chunk:
|
||||
errors += err_b
|
||||
infra_denom += den_b
|
||||
_check_infra_guard(errors, infra_denom, gate_guard_err)
|
||||
continue
|
||||
|
||||
# 候选侧只跑基线侧判定有效(非 INFRA)的单元,保证配对 unit_ids 两侧一致
|
||||
c_per_q, err_c, den_c = await _run_candidate_block(
|
||||
units=valid_chunk,
|
||||
cand_dir=cand_dir,
|
||||
run_inference=run_inference,
|
||||
log=log,
|
||||
run_id=f"{gate_run_prefix}_b{block_idx}_cand",
|
||||
)
|
||||
|
||||
# Phase 2: INFRA 护栏(跨块累计,分母 >=10 才触发)——写缓存前置于此已由
|
||||
# _resolve_baseline_block 保证 INFRA 单元不落缓存,此处仅做整轮错误率熔断。
|
||||
errors += err_b + err_c
|
||||
infra_denom += den_b + den_c
|
||||
_check_infra_guard(errors, infra_denom, gate_guard_err)
|
||||
|
||||
# Phase 3: 折叠成单元视图 + 配对 + 证据行 + 块间判定(均用有效单元)
|
||||
c_units = unit_correctness_view(valid_chunk, c_per_q)
|
||||
candidate_per_q.update(c_per_q)
|
||||
unit_ids = [u.unit_id for u in valid_chunk]
|
||||
pair_result = pair_block(b_units, c_units, unit_ids)
|
||||
for uid, (b, c) in pair_result.observed.items():
|
||||
base_obs[uid] = b
|
||||
cand_obs[uid] = c
|
||||
|
||||
block_rows = _build_evidence_rows(valid_chunk, b_units, c_units, task_type, block_idx)
|
||||
|
||||
w += pair_result.w
|
||||
l += pair_result.l # noqa: E741
|
||||
n_used += len(valid_chunk)
|
||||
# 阶梯剩余按扣除 INFRA 后的有效分母计:n_remaining = (n_plan - n_excluded) - n_used
|
||||
verdict = gate_decision(w, l, n_used, (n_plan - n_excluded) - n_used, params=gate_params)
|
||||
|
||||
for row in block_rows:
|
||||
row["e_value"] = verdict.e_value
|
||||
evidence_rows.extend(block_rows)
|
||||
|
||||
if verdict.decision != "continue":
|
||||
break
|
||||
|
||||
# verdict 仍为 None ⟺ 全部单元被 INFRA 排除(空 ladder 已在入口拒绝)。
|
||||
# 明确失败,避免落到误导性的"空阶梯"断言而无法定位为 INFRA 原因。
|
||||
if verdict is None:
|
||||
raise RuntimeError("gate 阶梯所有 unit 被判为 INFRA 排除,无法验证(检查推理基础设施)")
|
||||
# 最后一块判定即终态(n_remaining=0 → provisional/inertia)
|
||||
return _finalize_outcome(
|
||||
verdict=verdict,
|
||||
w=w,
|
||||
l=l,
|
||||
n_used=n_used,
|
||||
n_plan=n_plan,
|
||||
base_obs=base_obs,
|
||||
cand_obs=cand_obs,
|
||||
candidate_per_q=candidate_per_q,
|
||||
evidence_rows=evidence_rows,
|
||||
task_type=task_type,
|
||||
)
|
||||
|
||||
|
||||
async def validate_skill_local(
|
||||
workspace_dir: Path,
|
||||
base_skills_version: str,
|
||||
task_type: str,
|
||||
target_file: str,
|
||||
candidate_content: str,
|
||||
base_skill_content: str,
|
||||
ladder_items: list[GeneratedQuestion],
|
||||
gate_params: GateParams,
|
||||
gate_block: int,
|
||||
gate_n_max: int,
|
||||
gate_guard_err: float,
|
||||
baseline_cache: BaselineCache,
|
||||
prompts_version: str,
|
||||
run_inference: RunInferenceFn,
|
||||
log: HarnessLog,
|
||||
gate_run_prefix: str,
|
||||
) -> ValidationOutcome:
|
||||
"""块序贯配对验证:阶梯出题,基线/候选逐块配对,e-process 四出口早停。
|
||||
|
||||
参数:
|
||||
workspace_dir: workspace 根目录。
|
||||
base_skills_version: 基线 skills 版本名(候选物化复制源)。
|
||||
task_type: 待验证题型。
|
||||
target_file: fallback 解析后该题型的真实生效 skill 文件名
|
||||
(record.target_file,可能是共享 default-strategy.md);
|
||||
候选物化写此文件,与 accept 路径同源。
|
||||
candidate_content: 候选 skill 全文。
|
||||
base_skill_content: 基线侧该题型解析后生效 skill 文件全文
|
||||
(skill_hash(base_skill_content) 作 BaselineCache 键成分)。
|
||||
ladder_items: 阶梯序题目列表(已排除本 step 案例包题)。
|
||||
gate_params: e-process 判据阈值组。
|
||||
gate_block: 块大小(单位为**单元数**,AR pair 整锁不跨块拆)。
|
||||
gate_n_max: 单 gate 单元数上限(阶梯截断到此数量个单元)。
|
||||
gate_guard_err: 跨块累计 INFRA 错误率护栏(分母 >=10 才触发)。
|
||||
baseline_cache: 基线侧单元级对错缓存(键含 unit_id)。
|
||||
prompts_version: 当前 prompts 版本(缓存键成分)。
|
||||
run_inference: 注入的 async 推理函数(RunInferenceFn 协议)。
|
||||
log: HarnessLog 共享实例(供 DB 回读逐题对错)。
|
||||
gate_run_prefix: gate 内推理 run_id 前缀,必须含 "_gate_"
|
||||
(防泄露过滤靠它识别)。块 run_id = f"{prefix}_b{block_idx}_{arm}"。
|
||||
|
||||
返回:
|
||||
ValidationOutcome。单元级证据记入 outcome.evidence_rows 随结果返回,
|
||||
gate_evidence 落库由调用方(runner)负责。
|
||||
"""
|
||||
if "_gate_" not in gate_run_prefix:
|
||||
raise ValueError(f"gate_run_prefix 必须含 '_gate_'(防泄露过滤依赖): {gate_run_prefix!r}")
|
||||
if not ladder_items:
|
||||
raise ValueError(f"task_type={task_type} 阶梯为空,无法验证")
|
||||
|
||||
# 阶梯题序聚合为单元并按信息阶梯序截断到 gate_n_max 个单元(AR pair 整锁不拆)
|
||||
units = _ladder_units(ladder_items)[:gate_n_max]
|
||||
cand_dir = materialize_candidate_skill(
|
||||
workspace_dir, base_skills_version, target_file, candidate_content
|
||||
)
|
||||
try:
|
||||
return await _run_local_validation(
|
||||
workspace_dir=workspace_dir,
|
||||
cand_dir=cand_dir,
|
||||
base_skills_version=base_skills_version,
|
||||
task_type=task_type,
|
||||
base_skill_content=base_skill_content,
|
||||
units=units,
|
||||
gate_params=gate_params,
|
||||
gate_block=gate_block,
|
||||
gate_guard_err=gate_guard_err,
|
||||
baseline_cache=baseline_cache,
|
||||
prompts_version=prompts_version,
|
||||
run_inference=run_inference,
|
||||
log=log,
|
||||
gate_run_prefix=gate_run_prefix,
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
shutil.rmtree(cand_dir)
|
||||
except OSError as e:
|
||||
logger.warning("候选临时目录清理失败 {}: {}", cand_dir, e)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 连续并发 gate:数据结构 + 前缀消费(algo #6 语义修订:块序贯 → 阶梯序前缀逐对序贯)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user