Files
AgentLab/backend/tests/test_challenge_rationale_rules.py
T
ThearchyHelios 735dc32052 核查 agent 的依据照写作者的规矩挂在句末;收尾超时照实记时长上限;占位词不当理由
- 交结果的提示(VERIFY_FINAL_PROMPT,双盲复算、反方审稿、审批质询共用)以前写「提到某次查询时写 [[see:Q1]]」,模型就
  把它写进括号、句中;反方审稿的系统提示自己也把它写在括号里(「([[v:Qn.rX.列名]] 或 [[see:Qn]])」)。改成和写作者
  一致:依据挂在句末(「……结果一致。[[see:Q3]]」),说明它渲染后不显示,不要放进括号、句中或「依据」「见」「取自」
  后面。已经写进括号的由上一个提交的渲染收拾干净
- 模型自己停了查询、收尾那次调用超过时长上限时,limit 以前是 None:质询的报错写着「回答超过时长上限(180 秒)」,
  answer.limit 却是 null,界面少了「已达时长上限」;双盲复算、反方审稿把它当成别的原因,审批卡上照出内部原话。
  现在记 timeout_s;循环里先撞上的上限照旧优先(同金额被拒的收尾),结构化输出、退回普通调用两条路都一样
- 交回的理由是空的、只有标点,或者只是一个占位词(验收里盲算交了字面的「placeholder」,审批卡照着显示):当没给
  理由,不组装理由文档。反方审稿的记录里理由为空,审批卡照旧写「反方没有给出理由」;质询照「模型没有给出回答内容」
  报错;双盲复算的对照工件里说明为空

测试:交给模型的提示(含三处调用方的系统提示、任务)里不再示范括号里的依据;写进括号的依据在理由文档里渲染干净、
仍挂在句子上;审批质询的回答、反方审稿的理由、双盲复算的说明端到端都没有空括号;收尾超时 limit 为 timeout_s(核查
agent 和质询接口、事件里都是);占位词的理由三处都不当理由。
2026-10-07 00:05:17 +08:00

65 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""反方审稿的理由:依据写进括号不再断句,占位词不当理由(期 5 真实模型验收 M1)。
真实模型验收里,反方的理由「先按订单状态做全量概览核对口径([[see:Q1]]):」「复算写作者的原查询(同 SQL 同过滤条件,
[[see:Q2]])」渲染成「():」「(同 SQL 同过滤条件,)」:审稿记录(rationale 和 rationale_doc)随正式运行封存,审批卡上
照片段原文显示。
模型一律是 MockChatModel 剧本,区域、金额都是虚构的。
"""
from __future__ import annotations
from app.core import artifact_store
from tests.test_challenge_node import ( # noqa: F401 - 夹具按名字引入
NET,
REFUNDS,
Models,
Play,
engine_up,
finish,
graph,
output_of,
shop,
)
BROKEN = ("()", "(,", ",)", "()", "(,", ",)")
async def _record(monkeypatch, shop, plays) -> tuple[dict, dict]:
Models(monkeypatch, shop, plays=plays)
row = await finish(graph(shop))
assert row.status == "succeeded", row.error
out = await output_of(row.id, "chal")
return artifact_store.load(out["challenge_artifact"]), row.output["_issuance"]
async def test_see_in_brackets_does_not_break_the_rationale(monkeypatch, shop):
plays = {
"华南销售额超过华东": Play([NET], "refuted", "扣除退款后华南为 [[v:Q1.r1.净销售额]]([[see:Q1]]),"
"仍低于华东的 [[v:Q1.r0.净销售额]](同口径,[[see:Q1]])。"),
"居各区域之首": Play([REFUNDS], "weakened", "先核对退款口径([[see:Q1]]):华东含 [[v:Q1.r0.退款笔数]] 笔退款"
"([[see:Q1]],扣除后仍居首)。"),
}
record, issuance = await _record(monkeypatch, shop, plays)
first, second = record["claims"]
assert first["rationale"] == "先核对退款口径:华东含 1 笔退款(扣除后仍居首)。"
assert second["rationale"] == "扣除退款后华南为 900,仍低于华东的 1,200(同口径)。"
for claim in (first, second):
doc = claim["rationale_doc"]
assert doc["markdown"] == claim["rationale"] and doc["violations"] == []
assert not any(mark in claim["rationale"] for mark in BROKEN)
# 括号里的依据仍挂在句子上:判「被推翻」的引用到了自己的查询,照常采信、出具降档
assert second["verdict"] == "refuted" and issuance["tier"] == "degraded"
async def test_a_placeholder_rationale_is_recorded_as_no_rationale(monkeypatch, shop):
"""反方交回的理由只是占位词:记录里理由为空(审批卡据此写「反方没有给出理由」),判「被推翻」的照常不予采信。"""
plays = {"华南销售额超过华东": Play([NET], "refuted", "placeholder"),
"居各区域之首": Play([REFUNDS], "stands", "placeholder")}
record, issuance = await _record(monkeypatch, shop, plays)
first, second = record["claims"]
assert (first["verdict"], first["rationale"], first["rationale_doc"]) == ("stands", "", None)
assert second["verdict"] == "inconclusive"
assert second["rationale"] == "理由没有引用到任何查询结果,反方给出的「被推翻」不予采信,记为无法判断。反方没有给出理由"
assert "placeholder" not in str(record)
assert issuance["tier"] == "formal"