"""匿名分配、独立评委与原字引文的消费合同;不冒充真实模型执行。""" import hashlib import json from copy import deepcopy from dataclasses import replace from pathlib import Path from uuid import UUID import pytest import yaml from muse.任务运行.接口 import ( 校验模型输出, 模型协议错误, 模型结果, 模型请求, 角色策略目录, ) from muse.审校修订.接口 import 审校错误, 核对语义检测, 组装语义检测材料 from muse.效果评测.接口 import ( 仲裁文学评分, 核对匿名分配, 核对实验顺序, 核对成对判断, 核对文学判断, 比较未执行, 汇总独立选择, 生成匿名分配, 生成实验顺序, 组装成对材料, 组装文学材料, 评测错误, ) from muse.正式变更.接口 import 固定哈希 实验ID = "b0473aae-56d1-4485-9f6e-49042b02b130" 仓库根 = Path(__file__).resolve().parents[2] def _语义材料(): return 组装语义检测材料( "旅者等在城门。", { "sources": [{"source_id": "outline", "kind": "fine_outline", "text": "旅者守门。"}], "assertions": [{"statement_id": "guard", "text": "旅者是守卫。"}], "constraints": [{"statement_id": "stay", "text": "不得离开城门。"}], }, ) def _语义输出(material): quote = material["candidate"] return { "claims": [ { "claim_id": "claim-1", "text": "候选中人物采取行动。", "candidate_quote": quote, "state": "supported", "evidence_refs": ["source:outline"], "reason": "参照细纲核对。", } ], "findings": [], "assertion_verdicts": [ { "statement_id": "guard", "verdict": "pass", "candidate_quote": quote, "evidence_refs": ["assertion:guard"], "reason": "与给定命题相符。", } ], "constraint_verdicts": [ { "statement_id": "stay", "verdict": "pass", "candidate_quote": quote, "evidence_refs": ["constraint:stay"], "reason": "符合给定约束。", } ], "new_setting_candidates": [], } 文学甲 = "甲候选正文:茧撕开舱门。" 文学乙 = "乙候选正文:林深盯着深渊。" def _文学材料(*, reverse=False, scenario="turning_point"): return 组装文学材料( 文学乙 if reverse else 文学甲, 文学甲 if reverse else 文学乙, { "scenario": scenario, "sources": [ { "source_id": "outline", "kind": "fine_outline", "text": "硬约束甲;硬约束乙;拍一。", }, { "source_id": "history", "kind": "historical_prose", "text": "林深先前一直守着城门。", }, ], "assertions": [{"statement_id": "fact-one", "text": "新事实"}], "constraints": [{"statement_id": "must-stay", "text": "硬约束甲"}], }, ) def _文学引文(material, side, source_type="candidate", identity="q", source_id=None): if source_type == "candidate": source_id, text = side, material[side]["text"] elif source_type in {"fine_outline", "historical_prose"}: source = next(s for s in material["basis"]["sources"] if s["kind"] == source_type) source_id, text = source["source_id"], source["text"] else: field = "assertions" if source_type == "oracle_assertion" else "constraints" source = next( s for s in material["basis"][field] if source_id is None or s["statement_id"] == source_id ) source_id, text = source["statement_id"], source["text"] return { "evidence_id": identity, "source_type": source_type, "source_id": source_id, "quote": text, } def _文学输出(material, score=4.0): cards = [] for side in ("left", "right"): scores = [] for dimension in material["dimensions"]: evidence = [_文学引文(material, side, identity="candidate")] if dimension != "prose_readability": evidence.append( _文学引文( material, side, "historical_prose" if dimension == "style_consistency" else "fine_outline", identity="context", ) ) scores.append( { "dimension": dimension, "score": score, "rationale": "按候选及共同依据核对。", "evidence": evidence, "inferences": [], } ) cards.append({"side": side, "scores": scores}) output = { "choice": "tie", "rationale": "两侧按本次材料均可成立。", "candidate_scores": cards, "preferences": [ {"dimension": d, "choice": "tie", "rationale": "两侧表现相近。"} for d in material["dimensions"] ], } for field, source_type, definitions in ( ("assertion_verdicts", "oracle_assertion", "assertions"), ("constraint_verdicts", "preregistered_constraint", "constraints"), ): output[field] = [ { "side": side, "statement_id": statement["statement_id"], "verdict": "pass", "rationale": "根据两侧正文与共同命题核对。", "evidence": [ _文学引文(material, side, identity="candidate"), _文学引文( material, side, source_type, identity="statement", source_id=statement["statement_id"], ), ], "inferences": [], } for side in ("left", "right") for statement in material["basis"][definitions] ] return output def _核对文学(material, output): return 核对文学判断(material, output, writer_model="writer-model", judge_model="judge-model") def _文学评审(index, *, score=4.0, change=None, verdict=None): material = _文学材料(reverse=index == 2) raw = _文学输出(material, score) side = "right" if index == 2 else "left" if change is not None: dimension, value = change card = next(c for c in raw["candidate_scores"] if c["side"] == side) next(s for s in card["scores"] if s["dimension"] == dimension)["score"] = value if verdict is not None: next(v for v in raw["assertion_verdicts"] if v["side"] == side)["verdict"] = verdict return { "judge_ref": f"judge-run-judge-test-judge-v{index}", "model": f"judge-model-{index}", "scope_hash": 固定哈希([文学甲, 文学乙, material["basis"], material["rubric"]]), "sides": { "left": "blind-2" if index == 2 else "blind-1", "right": "blind-1" if index == 2 else "blind-2", }, "judgment": _核对文学(material, raw), } def _角色目录(): return 角色策略目录(yaml.safe_load((仓库根 / "配置/角色策略.yaml").read_text())) @pytest.mark.case_id( "TC-8925d954a09e", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=[ "五样本按数据集版本加样本ID的哈希排序,运行表循环ABC/BCA/CAB,两张表每个位置的臂数量差不超过一。" ], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_五样本运行和盲化表分别哈希排序且循环平衡__8925d9(): from collections import Counter ids = tuple(f"sample-{i}" for i in range(5)) registration, blind = 生成实验顺序(实验ID, "evaluation-set-v1", ids, ("A", "B", "C")) execution = registration["execution_order"] assert [r["sample_id"] for r in execution] == sorted( ids, key=lambda sid: hashlib.sha256(("evaluation-set-v1" + sid).encode()).hexdigest() ) rotations = [ ["A", "B", "C"], ["B", "C", "A"], ["C", "A", "B"], ["A", "B", "C"], ["B", "C", "A"], ] assert [r["arm_order"] for r in execution] == rotations for table in ([r["arm_order"] for r in execution], [list(r.order) for r in blind]): for position in range(3): counts = Counter(row[position] for row in table) assert ( max(counts[a] for a in ("A", "B", "C")) - min(counts[a] for a in ("A", "B", "C")) <= 1 ) @pytest.mark.case_id( "TC-91eae14dfc8a", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["输入样本顺序变化不影响预注册;盲化使用独立命名空间和排序,不复用运行顺序表。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_盲化顺序可重放且不复用运行排序命名空间__91eae1(): ids = tuple(f"sample-{i}" for i in range(5)) first = 生成实验顺序(实验ID, "evaluation-set-v1", ids, ("A", "B", "C")) assert first == 生成实验顺序(实验ID, "evaluation-set-v1", tuple(reversed(ids)), ("A", "B", "C")) registration, blind = first assert [r["sample_id"] for r in registration["execution_order"]] != [r.sample_id for r in blind] assert registration["blind_namespace"] == "writer-blind-assignment-v1" assert "blind_assignment" not in registration @pytest.mark.case_id( "TC-7a0ffd64f665", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["重复样本、运行表或盲化表失衡均拒绝,不允许对局部表重新签名后放行。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize("changed", ["duplicate", "unbalanced_execution", "unbalanced_blind"]) def test_预注册拒绝重复样本和任何一张失衡表__7a0ffd(changed): ids = ("one", "two", "three") arms = ("A", "B", "C") with pytest.raises(评测错误): if changed == "duplicate": 生成实验顺序(实验ID, "evaluation-set-v1", ("same", "same"), arms) else: registration, blind = 生成实验顺序(实验ID, "evaluation-set-v1", ids, arms) if changed == "unbalanced_execution": for row in registration["execution_order"]: row["arm_order"] = list(arms) else: blind = tuple(replace(r, order=arms) for r in blind) 核对实验顺序( registration, blind, experiment_id=实验ID, dataset_version_id="evaluation-set-v1", sample_ids=ids, arms=arms, ) def _成对输入(): return 组装成对材料("甲推开门。", "甲走了进去。", ("叙事", "声音")) def _成对报告(material, choice="left"): return { "choice": choice, "rationale": "按下列维度比较两个匿名文本。", "dimensions": [ { "dimension": d, "choice": choice, "rationale": "根据两侧原文核对叙述方式。", "evidence": [ { "side": side, "quote": material[side]["text"], } for side in ("left", "right") ], } for d in material["dimensions"] ], } @pytest.mark.case_id( "TC-a77c4d10ee77", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["同一实际模型身份不得作为独立选择模型;合成身份只证明拒绝条件。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_同一模型的选择不能当独立判断__a77c4d(): m = _成对输入() with pytest.raises(比较未执行): 核对成对判断(m, _成对报告(m), writer_model="same-model", judge_model="same-model") @pytest.mark.case_id( "TC-f131fa327bf0", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["成对判断总体理由为空白时拒绝,不把结构不完整的输出当执行成功。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_无选择理由不能执行成对判断__f131fa(): m = _成对输入() report = _成对报告(m) report["rationale"] = " " with pytest.raises(比较未执行): 核对成对判断(m, report, writer_model="writer-model", judge_model="judge-model") @pytest.mark.case_id( "NC-w25-250001", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize("count", [1, 2, 5, 11]) def test_匿名分配可复现且三臂各位置均衡__250001(count): from collections import Counter ids = tuple(f"sample-{i}" for i in range(count)) arms = ("control", "reference", "method") a = 生成匿名分配(实验ID, ids, arms) assert a == 生成匿名分配(实验ID, tuple(reversed(ids)), arms) assert a == 生成匿名分配(实验ID.upper(), ids, arms) assert tuple(r.sample_id for r in a) == tuple( sorted(ids, key=lambda s: hashlib.sha256(s.encode()).hexdigest()) ) for p in range(3): values = Counter(r.order[p] for r in a) assert max(values[x] for x in arms) - min(values[x] for x in arms) <= 1 other = 生成匿名分配("f4d59b1f-21cb-45ee-a294-97a4f8d0469a", ids, arms) assert not {r.assignment_id for r in a} & {r.assignment_id for r in other} assert all(UUID(r.assignment_id) for r in a) @pytest.mark.case_id( "NC-w25-250002", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( "bad", ["duplicate_samples", "duplicate_arms", "unbalanced", "foreign_namespace", "missing_sample"], ) def test_匿名分配拒绝重复失衡和跨实验复用__250002(bad): ids = ("one", "two", "three", "four", "five") arms = ("control", "reference", "method") with pytest.raises(评测错误): if bad == "duplicate_samples": 生成匿名分配(实验ID, ids + ("one",), arms) elif bad == "duplicate_arms": 生成匿名分配(实验ID, ids, arms + ("control",)) else: rows = 生成匿名分配(实验ID, ids, arms) if bad == "unbalanced": rows = tuple(replace(r, order=arms) for r in rows) elif bad == "foreign_namespace": rows = 生成匿名分配("f4d59b1f-21cb-45ee-a294-97a4f8d0469a", ids, arms) else: rows = rows[:-1] 核对匿名分配(rows, ids, arms, experiment_id=实验ID) @pytest.mark.case_id( "NC-w25-250003", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( "bad", [ "quote", "side", "dimension", "duplicate_dimension", "arm_leak", "model_leak", "boolean_offset", "no_evidence", "empty_rationale", "unsupported_winner", ], ) def test_逐维引文与匿名合同拒绝伪造或泄露__250003(bad): m = _成对输入() r = _成对报告(m) if bad == "quote": r["dimensions"][0]["evidence"][0]["quote"] = "不在原文里。" elif bad == "side": r["dimensions"][0]["evidence"][0]["side"] = "original" elif bad == "dimension": r["dimensions"][0]["dimension"] = "编排信息" elif bad == "duplicate_dimension": r["dimensions"][1]["dimension"] = r["dimensions"][0]["dimension"] elif bad == "arm_leak": r["dimensions"][0]["arm"] = "method" elif bad == "model_leak": r["selection_model"] = "claimed-judge" elif bad == "boolean_offset": r["dimensions"][0]["evidence"][0]["start"] = False elif bad == "no_evidence": r["dimensions"][1]["evidence"] = [] elif bad == "empty_rationale": r["dimensions"][0]["rationale"] = " " else: r["choice"] = "right" with pytest.raises(比较未执行): 核对成对判断(m, r, writer_model="writer-model", judge_model="judge-model") @pytest.mark.case_id( "NC-w25-250004", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize("choice", ["left", "right", "tie", "both_bad", "unknown"]) def test_成对结构合同保留五种合法判断但不认证调用__250004(choice): m = _成对输入() r = _成对报告(m, choice) result = 核对成对判断(m, r, writer_model="writer-model", judge_model="judge-model") assert result["choice"] == r["choice"] assert all( e["start"] == 0 and e["end"] == len(e["quote"]) for d in result["dimensions"] for e in d["evidence"] ) result = 汇总独立选择( [{"judge_ref": "actual-receipt-placeholder", "choice": choice}], left_is_original=True ) assert result["outcome"] == {"left": "original", "right": "candidate"}.get(choice, choice) assert not result["execution_verified"] @pytest.mark.case_id( "NC-w25-250005", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_分歧都差和缺执行不被多数投票掩盖__250005(): rows = [ {"judge_ref": f"receipt-{i}", "choice": c} for i, c in enumerate(("left", "left", "right")) ] result = 汇总独立选择(rows, left_is_original=True) assert result["outcome"] == "disagreement" and len(result["decisions"]) == 3 assert 汇总独立选择([], left_is_original=True)["outcome"] == "not_executed" with pytest.raises(评测错误): 汇总独立选择([rows[0], rows[0]], left_is_original=False) @pytest.mark.case_id( "NC-w25-250006", environment="离线纯合同", given="固定合成样本、实验身份和两侧文本", when="匿名分配、逐维码点引文校验或选择汇总", then=["拒绝重复/失衡/伪引文/身份泄露;保留原文、平局、都差、未知和分歧,执行认证保持false"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_空文本侧与相同文本不能伪造胜负__250006(): m = 组装成对材料("", "保留的版本。", ("叙事",)) r = _成对报告(m, "right") assert 核对成对判断(m, r, writer_model="writer", judge_model="judge")["choice"] == "right" m = 组装成对材料("相同文本。", "相同文本。", ("叙事",)) with pytest.raises(比较未执行): 核对成对判断(m, _成对报告(m), writer_model="writer", judge_model="judge") assert ( 核对成对判断(m, _成对报告(m, "tie"), writer_model="writer", judge_model="judge")["choice"] == "tie" ) @pytest.mark.case_id( "TC-8dcdb623a36c", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=[ "模型合同任意层均不接受位置、哈希、身份或回执;逐维引文只收side/quote,伪造字段为PAIRWISE_NOT_EXECUTED。" ], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( "field", ["start", "end", "candidateSha256", "reviewerInvocationId", "modelReceiptSha256"] ) def test_模型合同排除位置哈希身份回执且未知字段拒绝__8dcdb6(field): from copy import deepcopy from muse.效果评测.接口 import 成对输出合同 schema = 成对输出合同() nodes = [schema, *schema.get("$defs", {}).values()] forbidden = { "start", "end", "startCodePoint", "endCodePoint", "candidateSha256", "reviewerInvocationId", "modelReceiptSha256", } assert all(n.get("additionalProperties") is False for n in nodes if n.get("type") == "object") assert all(forbidden.isdisjoint(n.get("properties", {})) for n in nodes) material = _成对输入() assert all(set(material[side]) == {"text"} for side in ("left", "right")) report = deepcopy(_成对报告(material)) report["dimensions"][0]["evidence"][0][field] = "伪造" with pytest.raises(比较未执行): 核对成对判断(material, report, writer_model="writer", judge_model="judge") @pytest.mark.case_id( "TC-fec1f69eba18", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=[ "模型原始引文不带位置;代码按实际原文派生Unicode码点,原始输出不修改;具体内部sourceRef表示替换为start/end。" ], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_模型只给原文代码派生正确码点并保留原始报告__fec1f6(): from copy import deepcopy material = 组装成对材料("😀\r\n林澈守住城门。e\u0301", "另一侧正文。", ("叙事",)) raw = _成对报告(material, "tie") raw["dimensions"][0]["evidence"][0] = {"side": "left", "quote": "林澈"} original = deepcopy(raw) result = 核对成对判断(material, raw, writer_model="writer", judge_model="judge") quote = result["dimensions"][0]["evidence"][0] assert quote == {"side": "left", "quote": "林澈", "start": 3, "end": 5} assert material["left"]["text"][quote["start"] : quote["end"]] == quote["quote"] assert raw == original and "start" not in raw["dimensions"][0]["evidence"][0] @pytest.mark.case_id( "TC-9d8097665fa7", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["唯一原字引文定位为确定码点区间,切片必须与引文逐字相同。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_唯一引文保留原字码点位置__9d8097(): material = 组装成对材料("林澈守住城门。", "其他侧正文", ("叙事",)) raw = _成对报告(material, "tie") raw["dimensions"][0]["evidence"][0] = {"side": "left", "quote": "城门"} quote = 核对成对判断(material, raw, writer_model="writer", judge_model="judge")["dimensions"][ 0 ]["evidence"][0] assert (quote["start"], quote["end"]) == (4, 6) assert material["left"]["text"][quote["start"] : quote["end"]] == "城门" @pytest.mark.case_id( "TC-23aa8e8b249f", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["重复原字引文绑定首次出现,不需要模型指认出现序号。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_重复引文按首次原样出现位置定位__23aa8e(): material = 组装成对材料("城门连着城门。", "其他侧正文", ("叙事",)) raw = _成对报告(material, "tie") raw["dimensions"][0]["evidence"][0] = {"side": "left", "quote": "城门"} quote = 核对成对判断(material, raw, writer_model="writer", judge_model="judge")["dimensions"][ 0 ]["evidence"][0] assert (quote["start"], quote["end"], quote["quote"]) == (0, 2, "城门") @pytest.mark.case_id( "TC-86ec39ac775c", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", given="独立固定样本、合成模型身份或原字引文;每个原用例仅使用自身参数", when="调用该原例对应的公开预注册或成对判断核验", then=["原文不存在的引文拒绝,原旧脚本错误码由B10公共错误PAIRWISE_QUOTE_NOT_FOUND承接。"], contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_原文不存在的引文精确拒绝__86ec39(): material = _成对输入() raw = _成对报告(material) raw["dimensions"][0]["evidence"][0] = {"side": "left", "quote": "不在原文"} with pytest.raises(比较未执行) as error: 核对成对判断(material, raw, writer_model="writer", judge_model="judge") assert error.value.错误码 == "PAIRWISE_QUOTE_NOT_FOUND" @pytest.mark.case_id( "TC-0163585ebf22", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_report_must_not_reveal_arm_or_judge_target_role_coverage__016358(): material = _语义材料() arm_leak = _语义输出(material) arm_leak["arm"] = "outline_only" with pytest.raises(审校错误): 核对语义检测(material, arm_leak) target_role_judgment = _语义输出(material) target_role_judgment["findings"] = [ { "finding_id": "role-coverage", "category": "missing_target_role_card", "severity": "low", "candidate_quote": material["candidate"], "evidence_refs": ["source:outline"], "message": "越权判断", } ] with pytest.raises(审校错误): 核对语义检测(material, target_role_judgment) @pytest.mark.case_id( "TC-ac50e47e39ba", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( "category", [ "target_new_character_card_missing", "new_role_without_card", "invented_detector_category", ["frozen_context_gap"], ], ) def test_semantic_alias_and_unknown_categories_fail_closed__ac50e4(category): material = _语义材料() output = _语义输出(material) output["findings"] = [ { "finding_id": "unknown-category", "category": category, "severity": "low", "candidate_quote": material["candidate"], "evidence_refs": ["source:outline"], "message": "测试", } ] with pytest.raises(审校错误): 核对语义检测(material, output) @pytest.mark.case_id( "TC-0f5099de0182", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_registered_categories_are_accepted_and_detect_skill_assigns_target_coverage_to_eval__0f5099(): # noqa: E501 material = _语义材料() output = _语义输出(material) output["findings"] = [ { "finding_id": "entity-state", "category": "fact", "severity": "medium", "candidate_quote": material["candidate"], "evidence_refs": ["source:outline"], "message": "与冻结状态不一致", } ] report = 核对语义检测(material, output) assert report["status"] == "passed" assert report["findings"][0]["category"] == "fact" assert "coverageFindings" not in report and "missing_target_role_card" not in str(report) @pytest.mark.case_id( "TC-087fe08ba218", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_nested_findings_reject_arm_and_card_manifest_leakage__087fe0(): material = _语义材料() base = _语义输出(material) base["findings"] = [ { "finding_id": "entity-state", "category": "fact", "severity": "low", "candidate_quote": material["candidate"], "evidence_refs": ["source:outline"], "message": "冻结状态核对", } ] leaking_finding = deepcopy(base) leaking_finding["findings"][0]["arm"] = "outline_plus_cards" leaking_claim = deepcopy(base) leaking_claim["claims"][0]["cardManifest"] = {"count": 1} for output in (leaking_finding, leaking_claim): with pytest.raises(审校错误): 核对语义检测(material, output) @pytest.mark.case_id( "TC-6a2ca9bfcd0a", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_experiment_has_no_tools__6a2ca9(): policy = _角色目录().冻结( "blind_judge", provider="configured", model="deepseek-v4.1-flash", 阶段="执行", ) assert policy.角色 == "judge" and policy.工具 == () @pytest.mark.case_id( "TC-566cefcd2cd9", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_production_preflight_read_tools__566cef(): tools = ("read_chapter_text", "search_entities") policy = _角色目录().冻结( "judge", provider="configured", model="deepseek-v4.1-flash", 工具=tools, 阶段="执行", ) assert policy.工具 == tools @pytest.mark.case_id( "TC-e12d849e05bf", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_task_spec_carries_no_identity__e12d84(): model_input = _文学材料() forbidden = { "arm", "realArm", "evidenceStrategy", "rawPath", "rawDirectory", "writerContextPath", "runId", "sampleId", "reviewerInvocationId", "candidateSha256", "blindInputSha256", "authorizationSnapshotId", } def keys(value): if isinstance(value, dict): return set(value) | {key for item in value.values() for key in keys(item)} if isinstance(value, list): return {key for item in value for key in keys(item)} return set() assert forbidden.isdisjoint(keys(model_input)) assert set(model_input["left"]) == {"text"} and set(model_input["right"]) == {"text"} @pytest.mark.case_id( "TC-2bd29465a077", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_mode_controls_tool_allowlist_in_spec__2bd294(): policy = _角色目录().冻结( "blind_judge", provider="configured", model="deepseek-v4.1-flash", 工具=(), 阶段="执行", ) request = 模型请求( "judge-call", policy.provider, policy.model, "只评审匿名候选。", "合成盲评输入", {"type": "object"}, 1024, 30, 工具=(), ) assert policy.工具 == () and request.工具 == () and request.历史 == () @pytest.mark.case_id( "TC-145f4b68af3d", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_dispatch_failure_fails_closed__145f4b(): request = 模型请求( "judge-call", "configured", "claude-opus-4-8[1M]", "只评审匿名候选。", "合成盲评输入", {"type": "object"}, 1024, 30, ) failed = 模型结果("failed", "", None, None, 失败码="synthetic-dispatch-failure") with pytest.raises(模型协议错误) as error: 校验模型输出(request, failed) assert error.value.错误码 == "MODEL_PROTOCOL_INVALID" @pytest.mark.case_id( "TC-86fa000f251e", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_assembled_input_passes_official_validator__86fa00(): material = 组装文学材料( "甲候选正文:茧撕开舱门。", "乙候选正文:林深盯着深渊。", { "scenario": "battle", "sources": [ { "source_id": "outline", "kind": "fine_outline", "text": "硬约束甲;硬约束乙;拍一。", }, {"source_id": "history", "kind": "historical_prose", "text": "林深盯着城门。"}, ], "assertions": [ {"statement_id": "fact-one", "text": "新事实"}, {"statement_id": "fact-two", "text": "林深仍在场"}, ], "constraints": [ {"statement_id": "hard-one", "text": "硬约束甲"}, {"statement_id": "hard-two", "text": "硬约束乙"}, ], }, ) checked = _核对文学(material, _文学输出(material)) assert sorted((material["left"]["text"], material["right"]["text"])) == sorted( ("甲候选正文:茧撕开舱门。", "乙候选正文:林深盯着深渊。") ) assert len(checked["assertion_verdicts"]) == 4 assert len(checked["constraint_verdicts"]) == 4 @pytest.mark.case_id( "TC-968a24cafe30", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_every_dimension_requires_evidence__968a24(): material = _文学材料() assert len(_核对文学(material, _文学输出(material))["candidate_scores"]) == 2 missing_evidence = _文学输出(material) missing_evidence["candidate_scores"][0]["scores"][0]["evidence"] = [] with pytest.raises(比较未执行): _核对文学(material, missing_evidence) @pytest.mark.case_id( "TC-73f1f4e5ae77", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_prose_dimensions_are_rejected__73f1f4(): material = _文学材料() scores = _文学输出(material) scores["candidate_scores"][0]["scores"].append( { "dimension": "style_fit", "score": 5, "rationale": "不应出现", "evidence": [_文学引文(material, "left")], "inferences": [], } ) with pytest.raises(比较未执行): _核对文学(material, scores) @pytest.mark.case_id( "TC-bf000c7c899a", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_profile_and_score_range_are_checked__bf000c(): material = _文学材料() report = _文学输出(material, 6) assert all( score["score"] == 6 for candidate in _核对文学(material, report)["candidate_scores"] for score in candidate["scores"] ) bad_profile = deepcopy(material) bad_profile["rubric"]["version"] = "quality_gate" with pytest.raises(比较未执行): _核对文学(bad_profile, report) with pytest.raises(比较未执行): _核对文学(material, _文学输出(material, 10.5)) @pytest.mark.case_id( "TC-e75ca4811e14", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_large_reviewer_gap_warns__e75ca4(): first = _文学评审(1, score=4) dimension = "style_consistency" second = _文学评审(2, score=4, change=(dimension, 5)) result = 仲裁文学评分([first, second]) assert result["status"] == "needs_third_reviewer" assert result["score_gaps"] == [["blind-1", dimension]] @pytest.mark.case_id( "TC-a204c4f941c4", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_missing_dimension_fails_stability_closed__a204c4(): first, second = _文学评审(1), _文学评审(2) second["judgment"]["candidate_scores"][0]["scores"].pop() result = 仲裁文学评分([first, second]) assert result == {"status": "invalid_report", "execution_verified": False} @pytest.mark.case_id( "TC-98cb753cc286", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_batch_report_requires_exact_candidates_and_distinct_judge_identity__98cb75(): first, second = _文学评审(1), _文学评审(2) assert 仲裁文学评分([first, second])["status"] == "stable_report" duplicate = deepcopy(second) duplicate["judge_ref"] = first["judge_ref"] assert 仲裁文学评分([first, duplicate])["status"] == "invalid_report" wrong_candidate = deepcopy(second) wrong_candidate["sides"]["left"] = "blind-3" assert 仲裁文学评分([first, wrong_candidate])["status"] == "invalid_report" @pytest.mark.case_id( "TC-8381e1e91e63", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_nested_evaluation_and_score_reject_arm_card_manifest_leakage__8381e1(): material = _文学材料() arm_leak = _文学输出(material) arm_leak["candidate_scores"][0]["arm"] = "outline_only" card_leak = _文学输出(material) card_leak["candidate_scores"][0]["scores"][0]["cardManifest"] = {"count": 1} for output in (arm_leak, card_leak): with pytest.raises(比较未执行): _核对文学(material, output) @pytest.mark.case_id( "TC-8ada90c788e8", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_adapter_sends_anonymous_common_input_and_binds_report__8ada90(): material = 组装文学材料( "林澈守住城门。", "林澈掩住门缝。", _文学材料()["basis"], ) report = _核对文学(material, _文学输出(material)) assert set(material) == {"left", "right", "dimensions", "rubric", "basis"} assert all(set(material[side]) == {"text"} for side in ("left", "right")) assert tuple(material["dimensions"]) == ( "setting_entity_fidelity", "fine_outline_fidelity", "style_consistency", "narrative_tension", "prose_readability", ) assert report["basis_hash"] == 固定哈希(material["basis"]) assert report["rubric_hash"] == 固定哈希(material["rubric"]) serialized = json.dumps(material, ensure_ascii=False) for forbidden in ( "runId", "sampleId", "reviewerInvocationId", "candidateSha256", "blindInputSha256", "authorizationSnapshotId", ): assert forbidden not in serialized @pytest.mark.case_id( "TC-c800224586bd", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( ("field", "value"), [("scenario", "romance"), ("rubricPolicyVersion", "writer-replay-rubric-v1")], ) def test_unregistered_scenario_and_policy_drift_fail_before_model_call__c80022(field, value): calls = [] if field == "scenario": with pytest.raises(评测错误): _文学材料(scenario=value) else: material = _文学材料() material["rubric"]["version"] = value with pytest.raises(比较未执行): _核对文学(material, _文学输出(_文学材料())) assert calls == [] @pytest.mark.case_id( "TC-43c633c527fb", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_judge_inference_refs_bind_to_current_dimension_or_stable_id__43c633(): material = _文学材料() draft = _文学输出(material) for candidate in draft["candidate_scores"]: for score in candidate["scores"]: score["inferences"] = [ { "claim": "推断只使用当前维度已登记证据。", "refs": [score["evidence"][0]["evidence_id"]], } ] assert _核对文学(material, draft)["candidate_scores"] invalid = deepcopy(draft) invalid["assertion_verdicts"][0]["inferences"] = [ {"claim": "字段名不是稳定证据身份。", "refs": ["assertionId"]} ] with pytest.raises(比较未执行): _核对文学(material, invalid) @pytest.mark.case_id( "TC-5db0211546d6", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize( "version", ["blind-judge-report-v3", "blind-judge-report-v2", "blind-judge-report-v1"] ) def test_old_v3_v2_and_v1_reports_fail_closed__5db021(version): material = _文学材料() report = _文学输出(material) report["schemaVersion"] = version with pytest.raises(比较未执行): _核对文学(material, report) @pytest.mark.case_id( "TC-8537f235f675", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_input_rejects_real_arm_and_stale_candidate_hash__8537f2(): material = _文学材料() leaked = deepcopy(material) leaked["arm"] = "A" stale = deepcopy(material) stale["left"]["candidateSha256"] = "sha256:" + "f" * 64 for invalid in (leaked, stale): with pytest.raises(比较未执行): _核对文学(invalid, _文学输出(material)) @pytest.mark.case_id( "TC-d94345b3c760", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) @pytest.mark.parametrize("changed", ["raw_source", "future", "nested_extra"]) def test_fine_outline_and_oracle_freeze_validation_remain_fail_closed__d94345(changed): basis = deepcopy(_文学材料()["basis"]) if changed == "raw_source": basis["sources"][0]["source_id"] = "/private/tmp/raw/outline.json" elif changed == "future": basis["sources"][1]["chapterEnd"] = 489 else: basis["constraints"][0]["debug"] = True with pytest.raises(评测错误): 组装文学材料(文学甲, 文学乙, basis) @pytest.mark.case_id( "TC-b2a1b2e61fa7", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_model_must_cover_full_verdict_cartesian_product__b2a1b2(): material = _文学材料() draft = _文学输出(material) draft["assertion_verdicts"].pop() with pytest.raises(比较未执行) as error: _核对文学(material, draft) assert error.value.错误码 == "PAIRWISE_NOT_EXECUTED" @pytest.mark.case_id( "TC-9a7e59fe1698", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_reviewer_identity_reuse_fails_before_call__9a7e59(): calls = [] first, reused = _文学评审(1), _文学评审(2) reused["judge_ref"] = first["judge_ref"] result = 仲裁文学评分([first, reused]) assert result["status"] == "invalid_report" and calls == [] @pytest.mark.case_id( "TC-176c97a3b2d9", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_reviewers_with_different_scenarios_fail_before_call__176c97(): calls = [] first, second = _文学评审(1), _文学评审(2) second["scope_hash"] = 固定哈希("battle") result = 仲裁文学评分([first, second]) assert result["status"] == "invalid_report" and calls == [] @pytest.mark.case_id( "TC-3ee55ac52985", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_score_instability_triggers_exactly_one_third_reviewer__3ee55a(): dimension = "setting_entity_fidelity" first = _文学评审(1, score=8.0) second = _文学评审(2, score=8.0, change=(dimension, 6.5)) pending = 仲裁文学评分([first, second]) assert pending["status"] == "needs_third_reviewer" third = _文学评审(3, score=8.0) result = 仲裁文学评分([first, second, third]) assert result["status"] == "adjudicated_report" assert len({r["judge_ref"] for r in (first, second, third)}) == 3 @pytest.mark.case_id( "TC-27a56a41221e", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_ellipsis_in_free_text_not_killed_but_real_traversal_blocked__27a56a(): material = 组装成对材料("林澈说:等等...别走。", "林澈说:慢着...且听我说完。", ("叙事",)) report = _成对报告(material, "tie") report["dimensions"][0]["evidence"] = [ {"side": "left", "quote": "等等..."}, {"side": "right", "quote": "慢着..."}, ] checked = 核对成对判断(material, report, writer_model="writer-model", judge_model="judge-model") assert checked["dimensions"][0]["evidence"][0]["quote"] == "等等..." basis = deepcopy(_文学材料()["basis"]) basis["sources"][0]["source_id"] = "a/../b" with pytest.raises(评测错误): 组装文学材料(文学甲, 文学乙, basis) @pytest.mark.case_id( "TC-bb1f26f796ff", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_three_reviewers_pairwise_unstable_fail_closed_without_winner__bb1f26(): reviews = [ _文学评审(1, score=7.0), _文学评审(2, score=8.0), _文学评审(3, score=9.0), ] result = 仲裁文学评分(reviews) assert result["status"] == "invalid_unstable" assert not result["execution_verified"] and len(reviews) == 3 assert "scores" not in result and "winner" not in result and "candidate_scores" not in result @pytest.mark.case_id( "TC-d22b57090ab1", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_pair_must_be_independent_blind_and_reverse_ordered__d22b57(): first, second = _文学评审(1), _文学评审(2) assert 仲裁文学评分([first, second])["status"] == "stable_report" same_order = deepcopy(second) same_order["sides"] = first["sides"] assert 仲裁文学评分([first, same_order])["status"] == "invalid_report" different_candidate = deepcopy(second) different_candidate["sides"]["left"] = "blind-3" assert 仲裁文学评分([first, different_candidate])["status"] == "invalid_report" leaked = deepcopy(second) leaked["arm"] = "C" assert 仲裁文学评分([first, leaked])["status"] == "invalid_report" @pytest.mark.case_id( "TC-125ee8fdcfd6", environment="离线新版匿名、引文、语义与文学仲裁公开合同;不认证真实派发或外部模型", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_deblind_uses_preregistered_mapping_not_position__125ee8(): first, second = _文学评审(1), _文学评审(2) result = 仲裁文学评分([first, second]) assert result["status"] == "stable_report" assert {score["candidate_id"] for score in result["scores"]} == {"blind-1", "blind-2"} assert len(result["scores"]) == 10 def _盲评请求载荷(**覆盖): from decimal import Decimal 载荷 = dict( dataset_version_id="b0473aae-56d1-4485-9f6e-49042b02b130", dataset_hash="0" * 64, split="holdout", target={ "kind": "method", "target_ref": "B04.method:x", "version": "1", "content_hash": "0" * 64, }, generator_role="writer", generator={"config_id": "aa000000-0000-4000-8000-000000000001", "version": "1"}, judges=({"config_id": "aa000000-0000-4000-8000-000000000002", "version": "1"},), arms=("original", "candidate"), dimensions=("清晰度",), max_cost_usd=Decimal("1.0"), max_calls_per_sample=3, max_judge_corrections=1, ) 载荷.update(覆盖) return 载荷 @pytest.mark.case_id( "TC-9ccd856d98b6", environment="离线,匿名样本、独立评委替身", when="匿名分配样本、校验逐维引文并处理评委分歧。", contract="docs/系统架构/新版设计/模块设计/B10-效果评测.md", ) def test_盲评合同拒绝未知模式或外泄身份字段__9ccd85(): """未知比较模式与未登记 mode 字段在创建实验前即被合同拒绝(fail closed)。""" from pydantic import TypeAdapter, ValidationError from muse.效果评测.实验条件 import 实验请求 as 盲评实验请求 with pytest.raises(ValidationError): TypeAdapter(盲评实验请求).validate_python(_盲评请求载荷(comparison_profile="unknown_mode")) with pytest.raises(ValidationError): TypeAdapter(盲评实验请求).validate_python(_盲评请求载荷(mode="round_robin"))