From e62d6d2af4c7f2c9c4b25f4d2440baa7bbd462dd Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Mon, 5 Oct 2026 18:45:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=BA=BF=E5=A4=8D=E6=A0=B8?= =?UTF-8?q?=E8=AE=A4=E5=BE=97=E6=A0=B8=E6=9F=A5=E7=B1=BB=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=9A=E5=8F=8C=E7=9B=B2=E5=A4=8D=E7=AE=97?= =?UTF-8?q?=E3=80=81=E5=8F=8D=E6=96=B9=E5=AE=A1=E7=A8=BF=E7=9A=84=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=9B=B8=E5=90=8C=E5=90=8C=E8=A3=81=E5=88=A4=E4=B8=80?= =?UTF-8?q?=E6=A0=B7=E5=8F=AA=E8=AE=B0=E8=BD=A8=E8=BF=B9=E3=80=81=E4=B8=8D?= =?UTF-8?q?=E5=8F=AB=E5=A4=8D=E6=A0=B8=E6=A8=A1=E5=9E=8B=EF=BC=9B=E8=A7=A6?= =?UTF-8?q?=E9=A1=B6=E3=80=81=E4=B8=A4=E8=B7=AF=E4=B8=8D=E4=B8=80=E8=87=B4?= =?UTF-8?q?=E3=80=81=E6=97=A0=E6=B3=95=E5=A4=8D=E7=AE=97=E3=80=81=E8=A2=AB?= =?UTF-8?q?=E6=8E=A8=E7=BF=BB=E3=80=81=E6=B2=A1=E8=83=BD=E5=8F=8D=E6=9F=A5?= =?UTF-8?q?=E3=80=81=E5=8F=AA=E6=9F=A5=E4=BA=86=E9=83=A8=E5=88=86=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=BA=90=E6=8C=89=E5=90=84=E8=87=AA=E7=9A=84=20kind?= =?UTF-8?q?=20=E8=AE=B0=E6=88=90=E9=99=8D=E7=BA=A7=E7=BC=BA=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=8D=E8=90=BD=E6=88=90=E7=AC=BC=E7=BB=9F?= =?UTF-8?q?=E7=9A=84=20warn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/engine/review.py | 12 +++++++ backend/tests/test_review_verify_codes.py | 39 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 backend/tests/test_review_verify_codes.py 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])