mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:11:59 +00:00
双盲复算:盲算触顶、按无法复算记时(_limit_text 那一路),把盲算交回的理由(渲染后的正文,已经过遮罩)截一段带进原因(「盲算已达每项最多查询次数(1 次);盲算交回的理由:…」),审批卡、缺口说明上看得到它查到了什么,没有理由的照旧。原值所在的格子落在「查询区间里这张表没有数据」的列上时不派盲算、按无法复算记;盲算交回的格子落在这样的列上时不判一致、按无法复算记;原因都写「查询区间里这张表没有数据(…)」
This commit is contained in:
@@ -69,6 +69,7 @@ from app.core.events import EventType
|
||||
from app.db.base import SessionLocal
|
||||
from app.db.models import DataSource
|
||||
from app.data import catalog_view
|
||||
from app.data.coverage import NO_DATA_SHORT, no_data_of
|
||||
from app.engine import deadline, recompute
|
||||
from app.engine.approval import read_decision
|
||||
from app.engine.context import NodeContext, NodeError, NodeRejected
|
||||
@@ -635,6 +636,10 @@ async def _query_input(item: dict[str, Any], sources: _Sources, aliases: dict[st
|
||||
hidden = await sources.hidden(artifact)
|
||||
if column.lower() in hidden:
|
||||
return {}, f"输入 {item['path']} 取自设置了遮罩的列,不能用于复算"
|
||||
if (gap := no_data_of(snap, column)) is not None:
|
||||
# 原值所在的那一格落在「查询区间里这张表没有数据」的列上(data/coverage.py):那个 0 不代表真的是 0,盲算查到 0
|
||||
# 也说明不了一致,不白派一次盲算,按无法复算记
|
||||
return {}, f"{NO_DATA_SHORT}(输入 {item['path']}:{gap.get('message') or ''})"
|
||||
value = cell_value(item.get("value"))
|
||||
if not _number(value):
|
||||
return {}, f"输入 {item['path']} 不是数值"
|
||||
@@ -1003,6 +1008,30 @@ def _settled(found: Mapping[str, Any]) -> bool:
|
||||
return found.get("limit") == "max_queries" and found.get("value") is not None
|
||||
|
||||
|
||||
#: 无法复算的原因里带上盲算交回的理由时最多截多少字(理由是渲染后的正文,已经过遮罩)
|
||||
RATIONALE_CHARS = 160
|
||||
|
||||
|
||||
def _rationale_tail(found: Mapping[str, Any]) -> str:
|
||||
"""盲算触顶、按无法复算记时(_limit_text 那一路),它交回的理由截一段接在原因后面:审批卡、缺口说明上看得到它查到了
|
||||
什么。没有理由的交回空串(原因照旧)。"""
|
||||
text = " ".join(str(found.get("rationale") or "").split())
|
||||
if not text:
|
||||
return ""
|
||||
return f";盲算交回的理由:{text[:RATIONALE_CHARS]}{'…' if len(text) > RATIONALE_CHARS else ''}"
|
||||
|
||||
|
||||
def _no_data_cell(artifact: Any, column: Any, load: Callable[[str], Any]) -> dict[str, Any] | None:
|
||||
"""这一格所在的列「查询区间里这张表没有数据」(查询快照的 no_data,data/coverage.py):那一条,不是为 None。"""
|
||||
if not isinstance(artifact, str) or not artifact or not isinstance(column, str):
|
||||
return None
|
||||
try:
|
||||
snapshot = load(artifact)
|
||||
except (ValueError, OSError):
|
||||
return None
|
||||
return no_data_of(snapshot, column)
|
||||
|
||||
|
||||
def _limit_text(limit: str | None, options: _Options) -> str:
|
||||
"""盲算为什么停下,接在「盲算」后面读:「盲算已达每项最多查询次数(6 次)」。"""
|
||||
if limit == "max_queries" and options.max_queries is not None:
|
||||
@@ -1027,12 +1056,17 @@ def _compare(row: dict[str, Any], metric: dict[str, Any], results: dict[str, Any
|
||||
row.update(status=UNCHECKED, reason="盲算没有执行")
|
||||
return
|
||||
if found.get("limit") and not _settled(found):
|
||||
row.update(status=UNCHECKED, reason="盲算" + _limit_text(found["limit"], options))
|
||||
row.update(status=UNCHECKED, reason="盲算" + _limit_text(found["limit"], options) + _rationale_tail(found))
|
||||
return
|
||||
if found.get("value") is None:
|
||||
row.update(status=UNCHECKED, reason=found.get("reason") or "盲算没有交回可用的数值",
|
||||
**{k: found[k] for k in ("cause", "run_seconds") if k in found}) # 运行时长上限将至(_blind)
|
||||
return
|
||||
if gap := _no_data_cell(found.get("artifact"), found.get("column"), load):
|
||||
# 盲算交回的那一格落在「查询区间里这张表没有数据」的列上(data/coverage.py):那个 0 不代表真的是 0,拿它和原值
|
||||
# 比一致不一致都说明不了什么,按无法复算记
|
||||
row.update(status=UNCHECKED, reason=f"{NO_DATA_SHORT}(盲算查到的那一格:{gap.get('message') or ''})")
|
||||
return
|
||||
values[inp["path"]] = found["value"]
|
||||
outcome = recompute.recompute(metric, recompute.Changes(inputs=values), recompute.Sources(
|
||||
load=load, outputs=outputs, masked=masks, queries=queries))
|
||||
|
||||
@@ -271,9 +271,11 @@ async def test_two_metrics_using_one_cell_are_checked_once(monkeypatch, shop):
|
||||
|
||||
|
||||
async def test_hitting_a_limit_counts_as_unchecked(monkeypatch, shop):
|
||||
"""查询次数用完了还没查到要交的那一格:按无法复算计入缺口;一个指标都没核成,产出是 unchecked,不是 agreed。"""
|
||||
"""查询次数用完了还没查到要交的那一格:按无法复算计入缺口;一个指标都没核成,产出是 unchecked,不是 agreed。
|
||||
盲算交回的理由截一段带进原因:审批卡、缺口说明上看得到它查到了什么。"""
|
||||
name, _ = shop
|
||||
Blind(monkeypatch, name, {"销售额": ([BLIND_TOTAL, RIGHT], "gmv")})
|
||||
Blind(monkeypatch, name, {"销售额": ([BLIND_TOTAL, RIGHT], "gmv")},
|
||||
rationale="只查到了不含退款的合计,还没来得及按支付日核对。")
|
||||
run = await start(graph(name, RIGHT), options={"verify": {"dual_check": {"max_queries": 1}}})
|
||||
assert run.status == "succeeded", run.error
|
||||
evs = await events(run.id)
|
||||
@@ -281,15 +283,16 @@ async def test_hitting_a_limit_counts_as_unchecked(monkeypatch, shop):
|
||||
out = dual_output(run, evs)
|
||||
assert out["status"] == "unchecked" and out["metrics"][0]["status"] == "unchecked"
|
||||
assert out["counts"] == {"agreed": 0, "disagreed": 0, "unchecked": 1}
|
||||
assert out["issuance_gaps"] == ["指标「销售额」无法复算:盲算已达每项最多查询次数(1 次)"]
|
||||
reason = "盲算已达每项最多查询次数(1 次);盲算交回的理由:只查到了不含退款的合计,还没来得及按支付日核对。"
|
||||
assert out["issuance_gaps"] == [f"指标「销售额」无法复算:{reason}"]
|
||||
[said] = logs(evs, "dual_check_unchecked")
|
||||
assert said["metrics"] == [{"id": "gmv", "name": "销售额", "reason": "盲算已达每项最多查询次数(1 次)"}]
|
||||
assert said["metrics"] == [{"id": "gmv", "name": "销售额", "reason": reason}]
|
||||
assert not logs(evs, "dual_check_agreed")
|
||||
assert not [e for e in evs if e.type == "human.requested"]
|
||||
[finished] = [e for e in evs if e.type == "node.finished" and e.node_id == "dual"]
|
||||
assert finished.data["evidence"][0]["status"] == "unchecked"
|
||||
assert run.output["_issuance"]["tier"] == "degraded"
|
||||
assert run.output["_issuance"]["gaps"] == ["双盲复算「复核销售额」:指标「销售额」无法复算:盲算已达每项最多查询次数(1 次)"]
|
||||
assert run.output["_issuance"]["gaps"] == [f"双盲复算「复核销售额」:指标「销售额」无法复算:{reason}"]
|
||||
|
||||
|
||||
async def test_an_answer_on_the_last_allowed_query_still_counts(monkeypatch, shop):
|
||||
@@ -1575,3 +1578,41 @@ async def test_the_report_catalog_leaves_out_the_blind_queries(monkeypatch, shop
|
||||
if e.type == "tool.end" and e.node_id == "dual" and e.data.get("query_artifact")]
|
||||
ledger = [entry for e in evs if e.type == "node.finished" for entry in e.data.get("evidence") or []]
|
||||
assert blind_queries and not any(entry.get("artifact") in blind_queries for entry in ledger)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 查询区间里这张表没有数据(data/coverage.py):不判一致,按无法复算记
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
#: 订单表只有 2026 年的数:2030 年那一周表里没有数据,COUNT 是 0
|
||||
EMPTY_WEEK = "BETWEEN '2030-01-07' AND '2030-01-13'"
|
||||
NO_ORDERS = f"SELECT COUNT(*) AS orders FROM orders WHERE status <> 'refunded' AND paid_date {EMPTY_WEEK}"
|
||||
ORDERS = {"id": "orders", "name": "订单数", "unit": "单", "expression": "cell(nodes.fetch, 0, 'orders')"}
|
||||
|
||||
|
||||
async def test_an_original_cell_without_data_is_not_checked(monkeypatch, shop):
|
||||
"""原值所在的格子落在 no_data 的列上:那个 0 不代表真的是 0,盲算查到 0 也说明不了一致。不派盲算,按无法复算记。"""
|
||||
name, _ = shop
|
||||
blind = Blind(monkeypatch, name, {"订单数": (NO_ORDERS, "orders")})
|
||||
run = await start(graph(name, NO_ORDERS, metrics=[ORDERS], dual={"definitions": {"orders": "已支付订单数"}}))
|
||||
assert run.status == "succeeded", run.error
|
||||
evs = await events(run.id)
|
||||
out = dual_output(run, evs)
|
||||
[row] = out["metrics"]
|
||||
assert row["status"] == "unchecked" and row["reason"].startswith("查询区间里这张表没有数据(输入 cell(nodes.fetch")
|
||||
assert "2030-01-07 到 2030-01-13" in row["reason"]
|
||||
assert "step" not in blind.calls, "原值就没有数据:不白派一次盲算"
|
||||
assert out["counts"] == {"agreed": 0, "disagreed": 0, "unchecked": 1}
|
||||
|
||||
|
||||
async def test_a_blind_cell_without_data_is_not_agreed(monkeypatch, shop):
|
||||
"""盲算交回的格子落在 no_data 的列上(它查了一段表里没有数据的区间):不判一致,按无法复算记。"""
|
||||
name, _ = shop
|
||||
buyers = {"id": "orders", "name": "订单数", "unit": "单", "expression": "cell(nodes.fetch, 0, 'orders')"}
|
||||
original = f"SELECT COUNT(*) AS orders FROM orders WHERE status <> 'refunded' AND paid_date {WEEK}"
|
||||
Blind(monkeypatch, name, {"订单数": (NO_ORDERS.replace("AS orders", "AS n"), "n")})
|
||||
run = await start(graph(name, original, metrics=[buyers], dual={"definitions": {"orders": "已支付订单数"}}))
|
||||
assert run.status == "succeeded", run.error
|
||||
[row] = dual_output(run, await events(run.id))["metrics"]
|
||||
assert row["status"] == "unchecked", row
|
||||
assert row["reason"].startswith("查询区间里这张表没有数据(盲算查到的那一格:「n」查的是 2030-01-07 到 2030-01-13")
|
||||
|
||||
Reference in New Issue
Block a user