Files
AgentLab/backend/tests/test_dual_check_rationale_rules.py
ThearchyHelios 60be16a0bb M1 复核修正(第 3 轮):依据两边什么都没有时也判粘连,两个数不再并成一个;只装着依据的括号认【】[]〔〕[];右边紧贴着下文的半角逗号后面补空格
渲染(evidence._tidy):
- 上一轮只沿着去掉的字往两边找粘连:依据前后没有空白、括号、标点时没有切口,「华东 [[v:Q1.r0.销售额]][[see:Q1]]
  [[v:Q1.r0.订单数]] 笔」渲染、组装都是「4,919.5212 笔」,「[[v:…]][[see:Q1]]2 月」是「4,919.52 月」,「**华东 [[v:…]]**
  [[see:Q1]]2 月」组装出「华东 4,919.52 月」,verify_doc 都不报。现在每个依据(连着写的一串、只装着依据的括号都算)
  两边都判(_glue 看所有不显示的字,不只看切口),会读成一串的补一个空格:这一片有去掉的字的照旧换掉头一个,没有的
  插在头一个依据前面(粗体收尾的星号外面)
- 只装着依据的括号认【】、[](全角半角混用也算)、〔〕、[],和()一样连括号去掉,外面悬着的停顿标点照原规则收拾;
  依据在这些括号开头、末尾紧挨着分隔标点的,同()一样收拾。核查 agent 拿到的查询结果以「【证据 Qn】」开头,模型照着
  写成「【[[see:Q1]]】」,以前渲染成「【】」
- 右边是写作者紧贴着下文写的半角逗号的(「1,200([[see:Q1]]),900」去掉括号读成「1,200,900」),空格补在逗号后面
  (同「212,[[see:Q1]],5」→「212, 5」)。上一轮把这一类当成可以不管的「),5」写法;和基线 58a6759 逐份比,结构化组合里
  有 2481 份是上一轮去掉括号之后才并成一个数的(「增长 12([[see:Q1]]),212 笔」→「增长 12,212 笔」),现在都隔开了
- 找挨上的字时也越过删除线的两个波浪线:前端照样画出删去的数,挨着后面的数读
- 流式渲染把这几种括号、半角方括号、波浪线也攒到后面的字来了再放;半角「]」攒着时,标记收尾的「]]」和标记里末尾的
  空白不算,标记整个放出去,从它后面攒起

不含依据的文档渲染结果、内容哈希逐字不变:随机 1.6 万份不含依据的文档和基线逐份比过,BEFORE 两份哈希照旧。

测试:依据两边什么都没有(数、标记、粗体、斜体、删除线、连着写的一串)、几种括号(只装着依据、全角半角混用、套着、
和别的括号连着写、开头末尾带标点、不含依据的不动)、半角逗号的用例;结构化组合加上两种括号,中间只有依据的也算;流式
渲染、随机语料加上几种括号和波浪线;审批质询、核查 agent、反方审稿、双盲复算各补一个端到端用例。15 处改动逐一去掉实现,
相关测试都变红(第 2 轮的 7 处也重跑过,照样变红)。另在测试之外:复核用的抽查脚本 6000 份 × 6 个种子只剩写作者自己
写成「212,,2」的一份;结构化组合 29.4 万份、随机含依据的 1.6 万份和基线比没有新并成的数,流式逐字、随机分块都等于整篇
渲染。
2026-10-12 12:24:14 +08:00

71 lines
3.8 KiB
Python
Raw Permalink 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)。
盲算 agent 和反方审稿、审批质询共用核查 agent 交结果的提示,同样会把 [[see:Q1]] 写进括号;真实模型验收里还见过盲算交回
字面的「placeholder」当说明,审批卡上照着显示。
模型一律是 MockChatModel 剧本,订单库是虚构的(tests/fixtures/dual_check_shop.py)。
"""
from __future__ import annotations
from app.core import artifact_store
from tests.fixtures.dual_check_shop import BY_ORDER_DATE, RIGHT, Blind
from tests.test_dual_check_node import ( # noqa: F401 - 夹具按名字引入
clean_settings,
engine_up,
graph,
pending,
shop,
start,
)
async def _blind_input(monkeypatch, shop, rationale: str) -> dict:
name, _ = shop
Blind(monkeypatch, name, {"销售额": (RIGHT, "gmv")}, rationale=rationale)
run = await start(graph(name, BY_ORDER_DATE))
assert run.status == "interrupted", run.error
card = artifact_store.load((await pending(run.id)).payload["card_artifact"])
[mine] = card["metrics"][0]["blind"]["inputs"]
return mine
async def test_see_in_brackets_does_not_break_the_blind_rationale(monkeypatch, shop):
mine = await _blind_input(monkeypatch, shop, "按口径说明统计([[see:Q1]],已排除退款),结果是 {ref}([[see:Q1]])。")
assert mine["rationale"] == "按口径说明统计(已排除退款),结果是 330。"
doc = mine["rationale_doc"]
assert doc["markdown"] == mine["rationale"] and doc["violations"] == []
async def test_see_brackets_written_together_do_not_glue_the_number(monkeypatch, shop):
"""两个只装着依据的括号连着写、数字包着粗体(复核第 2 轮):去掉之后数字不能和后面的字挨成一个数(「3302 月」),
句末前悬着的逗号也去掉。"""
mine = await _blind_input(monkeypatch, shop, "结果是 **{ref}**([[see:Q1]])([[see:Q1]])2 月起按支付日统计,"
"已排除退款,([[see:Q1]])([[see:Q1]])。")
assert mine["rationale"] == "结果是 330 2 月起按支付日统计,已排除退款。"
doc = mine["rationale_doc"]
assert doc["markdown"] == mine["rationale"] and doc["violations"] == []
async def test_a_bare_see_before_a_number_and_other_brackets_do_not_break_the_rationale(monkeypatch, shop):
"""依据直接夹在数字和后面的数之间(两边什么都没有)、写进六角括号(复核第 3 轮):数字不能和后面的字挨成一个数
(「3302 月」),括号不能剩下「〔〕」。"""
mine = await _blind_input(monkeypatch, shop, "结果是 {ref}[[see:Q1]]2 月起按支付日统计,已排除退款〔[[see:Q1]]〕。")
assert mine["rationale"] == "结果是 330 2 月起按支付日统计,已排除退款。"
doc = mine["rationale_doc"]
assert doc["markdown"] == mine["rationale"] and doc["violations"] == []
async def test_a_placeholder_rationale_is_not_shown_as_the_explanation(monkeypatch, shop):
mine = await _blind_input(monkeypatch, shop, "placeholder")
assert mine["value"] == 330.0, "数照样采用"
assert (mine["rationale"], mine["rationale_doc"]) == ("", None)
async def test_a_rationale_that_shows_nothing_is_not_kept(monkeypatch, shop):
"""只写了依据(显示出来是空的),或者占位词后面挂着依据:说明为空、不留文档。以前文档照样记下,审批卡按文档画出
「理由」两个字,后面什么都没有。"""
for said in ("([[see:Q1]])", "placeholder。[[see:Q1]]"):
mine = await _blind_input(monkeypatch, shop, said)
assert mine["value"] == 330.0, "数照样采用"
assert (mine["rationale"], mine["rationale_doc"]) == ("", None), said