diff --git a/backend/app/engine/review.py b/backend/app/engine/review.py index 65c0b752..c9242c0f 100644 --- a/backend/app/engine/review.py +++ b/backend/app/engine/review.py @@ -111,6 +111,18 @@ WARN_CODES: dict[str, tuple[str, str]] = { # 估不出裁判的金额、裁判和写作是同一个模型:配置上的提醒,答案本身没受影响 "judge_unpriced": ("judge_unpriced", BENIGN), "judge_same_model": ("judge_same_model", BENIGN), + # 双盲复算、反方审稿的核查模型和被核查的模型相同:同 judge_same_model,只关乎独立性,结论本身没受影响 + "dual_check_same_model": ("dual_check_same_model", BENIGN), + "challenge_same_model": ("challenge_same_model", BENIGN), + # 核查没做完、没通过:盲算触顶、两路不一致、指标无法复算;反方审稿触顶、推翻了结论、有句子没能反查、只查了 + # 部分数据源。答案照常交付(出具契约另按缺口降档),是缺口不是断裂,按各自的 kind 记,界面说得清是哪一种 + "dual_check_limit": ("dual_check_limit", DEGRADED), + "dual_check_disagreed": ("dual_check_disagreed", DEGRADED), + "dual_check_unchecked": ("dual_check_unchecked", DEGRADED), + "challenge_limit": ("challenge_limit", DEGRADED), + "challenge_refuted": ("challenge_refuted", DEGRADED), + "challenge_unreviewed": ("challenge_unreviewed", DEGRADED), + "challenge_sources_lost": ("challenge_sources_lost", DEGRADED), } # 这些 kind 重跑一次大概率能好:原因说得清,且不是「问题本身没问明白」。 diff --git a/backend/tests/test_review_verify_codes.py b/backend/tests/test_review_verify_codes.py new file mode 100644 index 00000000..b6f1efa8 --- /dev/null +++ b/backend/tests/test_review_verify_codes.py @@ -0,0 +1,39 @@ +"""跑完之后的复核层认得双盲复算、反方审稿的 warn 日志代码(engine/review.py 的 WARN_CODES)。 + +没登记的代码一律按 degraded、kind 记成笼统的 warn:时间线复核和问数据页说不清是哪一种缺口,模型相同这种只关乎独立性、 +不影响结论的提醒也会被当成缺口去叫一次复核模型。这里守住两类: +- 模型相同(dual_check_same_model、challenge_same_model)同 judge_same_model,是配置上的提醒,不进信号; +- 触顶、不一致、无法复算、被推翻、没能反查、只查了部分数据源是缺口,按各自的 kind 记成 degraded。 +""" +from __future__ import annotations + +import pytest + +from app.core.events import EventType +from app.engine import review as rv + +OK_OUTPUT = {"结论": "本周订单 120 单。"} + + +def warn(message: str, code: str) -> dict: + return {"type": str(EventType.LOG), "node_id": "gate", "data": {"level": "warn", "message": message, "code": code}} + + +@pytest.mark.parametrize("code", ["dual_check_same_model", "challenge_same_model"]) +def test_same_model_reminders_do_not_trigger_review(code): + """和 judge_same_model 一样:配置上的提醒,答案本身没受影响,不值得为它叫一次复核模型""" + assert rv.WARN_CODES[code] == (code, rv.BENIGN) + assert rv.scan([warn("核查模型与原流程相同(「mx-1」)", code)], OK_OUTPUT) == [] + + +@pytest.mark.parametrize("code", [ + "dual_check_limit", "dual_check_disagreed", "dual_check_unchecked", + "challenge_limit", "challenge_refuted", "challenge_unreviewed", "challenge_sources_lost", +]) +def test_verify_gaps_are_degraded_signals_of_their_own_kind(code): + """核查没做完、没通过:答案能用,但有缺口要说清是哪一种(kind 是代码本身,不是笼统的 warn)""" + assert rv.WARN_CODES[code] == (code, rv.DEGRADED) + [signal] = rv.scan([warn("核查的一条提醒", code)], OK_OUTPUT) + assert (signal.kind, signal.severity, signal.detail) == (code, rv.DEGRADED, "核查的一条提醒") + # 缺口不是断裂:不触发「重跑一次大概率能好」 + assert not rv.should_retry([signal])