mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 15:41:59 +00:00
799c80b97e
一次真实运行里写作者只看到上百次查询里的前二十来次,却写出「某类数据缺失、本轮没有查到任何结果」这样的断言,可那个 Agent 实际查到了上百条记录。 - 写作规则:清单有省略、或有来源的结构化结论为空时,不得断言没有查到、不存在、缺失、没有数据,只能写「本报告未覆盖」并说明依据不足 - 组装文档:清单有省略时按一组明确的说法认出缺失断言(没有查到、未查询到、查不到、没有任何记录、数据为空、数据缺失、不存在相关记录、暂无数据等;「不存在明显差异」「不代表没有数据」这类不算,代码块不查),记新违规 absence_claim,带 span、所在片段和原句 - 和句中的 [[see:]] 一样归入 REWRITE_ONLY:交回重写一次,重写后仍有的只标注、不判失败;只剩这几类时不再按最多重写次数反复重写。仍有时报告节点发 report_absence_claim(带 count、texts),运行复核算作缺口 - 证据面板的违规清单标「缺失断言」并写明为什么依据不足,时间线给 report_absence_claim 专门的一行,文案在 terms.ts;check-decode、check-evidence 补断言 - 没展开的查询的摘要在没有表结构快照时从查询快照里的 SQL 认表名 - 回归测试:合成的门店、会员、卡券、活动库真跑「4 个 Agent 并行查库 → 报告」(查询次数虚构、分布悬殊),覆盖抽取截断重试、两次截断记空值、清单均衡和缺失断言的重写
83 lines
4.2 KiB
Python
83 lines
4.2 KiB
Python
"""缺失断言(absence_claim):写作目录有省略时,「没有查到」「不存在」「缺失」「没有数据」这类结论依据不足。
|
||
|
||
2026-10 一次运行,写作者只看到上百次查询里的前二十来次,写出「某类数据缺失:本轮没有查到任何结果」这样的断言,
|
||
可那个 Agent 实际查到了上百条记录。
|
||
|
||
- 写作规则:清单有省略、或有来源的结构化结论为空时,不得断言没有数据,只能写「本报告未覆盖」并说明依据不足
|
||
- 组装文档:清单有省略时按一组明确的说法认出缺失断言,记 absence_claim,交回重写一次;重写后仍有的只标注、不判失败
|
||
(report.REWRITE_ONLY,和句中的 [[see:]] 同一种处理)
|
||
- 只认拿得准的说法:「不存在明显差异」「本报告未覆盖」「不代表没有数据」都不算
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.engine.evidence import ABSENCE_RULE, build_catalog, compose_doc
|
||
from app.engine.nodes.report import REWRITE_ONLY
|
||
|
||
|
||
@pytest.fixture
|
||
def catalog():
|
||
card = {"kind": "metric_set", "caliber": "周报口径", "caliber_version": "v1",
|
||
"metrics": [{"id": "gmv", "name": "销售额", "value": 45678.5, "unit": "元", "format": "thousands"}]}
|
||
return build_catalog(nodes={"card": card}, ledger=[{"kind": "metric_set", "node_id": "card", "artifact": "c" * 64}])
|
||
|
||
|
||
def claims(doc):
|
||
return [v for v in doc["violations"] if v["code"] == "absence_claim"]
|
||
|
||
|
||
def test_the_rule_tells_the_writer_what_to_write_instead():
|
||
for phrase in ("没有查到", "不存在", "缺失", "没有数据", "本报告未覆盖", "依据不足"):
|
||
assert phrase in ABSENCE_RULE
|
||
assert "absence_claim" in REWRITE_ONLY and "inline_see" in REWRITE_ONLY
|
||
|
||
|
||
@pytest.mark.parametrize("text, found", [
|
||
("卡券数据缺失:本轮没有查到核销、兑换、退券的任何结果。", ["数据缺失", "没有查到"]),
|
||
("卡券核销没有任何记录。", ["没有任何记录"]),
|
||
("本周门店客流数据为空。", ["数据为空"]),
|
||
("活动内容无数据。", ["无数据"]),
|
||
("未查询到会员流失的情况。", ["未查询到"]),
|
||
("库里不存在相关记录。", ["不存在相关记录"]),
|
||
("兑换记录均不存在。", ["记录均不存在"]),
|
||
("暂无数据,查不到核销明细。", ["暂无数据", "查不到"]),
|
||
("| 方向 | 结论 |\n| --- | --- |\n| 卡券 | 没有查到 |", ["没有查到"]),
|
||
])
|
||
def test_absence_claims_are_recognised(catalog, text, found):
|
||
doc = compose_doc(text, catalog, absence=True)
|
||
got = claims(doc)
|
||
assert [v["text"] for v in got] == found, doc["violations"]
|
||
for v in got:
|
||
assert doc["markdown"][v["span"][0]:v["span"][1]] == v["text"], "span 指着正文里的那几个字"
|
||
assert v.get("unit") and v.get("context")
|
||
assert "依据不足" in v["message"] and "本报告未覆盖" in v["for_model"]
|
||
|
||
|
||
@pytest.mark.parametrize("text", [
|
||
"本报告未覆盖卡券核销的明细:证据目录有省略,相关查询未展开,依据不足。",
|
||
"两家门店的客单价不存在明显差异。",
|
||
"目录里没展开的查询不代表没有数据。",
|
||
"这并非没有查到,而是没有展开。",
|
||
"本周无数据库变更,无数据源切换。",
|
||
"本周销售额 [[m:gmv]],缺少口径说明的部分已注明。",
|
||
"```sql\nSELECT * FROM t -- 没有查到\n```",
|
||
])
|
||
def test_other_wording_is_left_alone(catalog, text):
|
||
assert not claims(compose_doc(text, catalog, absence=True))
|
||
|
||
|
||
def test_only_checked_when_the_catalog_was_trimmed(catalog):
|
||
text = "卡券核销数据缺失:没有查到任何记录。"
|
||
assert not claims(compose_doc(text, catalog))
|
||
assert claims(compose_doc(text, catalog, absence=True))
|
||
|
||
|
||
def test_the_claim_can_be_located_in_the_document(catalog):
|
||
doc = compose_doc("## 卡券\n\n本周销售额 [[m:gmv]]。核销数据缺失。", catalog, absence=True)
|
||
[v] = claims(doc)
|
||
segments = {s["id"]: s for b in doc["blocks"] for u in b["units"] for s in u["segments"]}
|
||
seg = segments[v["segment"]]
|
||
assert seg["kind"] == "text" and seg["span"][0] <= v["span"][0] < seg["span"][1]
|
||
assert v["context"] == "核销数据缺失。"
|