mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 19:22:00 +00:00
84 lines
5.1 KiB
Python
84 lines
5.1 KiB
Python
"""分析卡里随数据变化的调用错误(复核 R6):模板定死了调用,这期的数据里没有要用的那一项(上一层没做成、下钻的段这期没有……)
|
|
记成算不了的一项,不让卡失败;结构性的调用错误照旧让卡失败并说清哪一项。接真算子跑(tests/test_analysis_e2e 的虚构零售库
|
|
shop),每份结论按封存的工件复算、逐字段相同。"""
|
|
from __future__ import annotations
|
|
|
|
from app.core import artifact_store
|
|
from app.engine.runner import run_manager
|
|
from tests.test_analysis_e2e import ( # noqa: F401 - engine_up、shop 是自动用的夹具
|
|
BY_REGION, EAST, TOOL, TWO_WEEKS, WEEK, chain, engine_up, events, node, recomputes, shop, wait,
|
|
)
|
|
|
|
ONLY_EAST = (f"SELECT region, {WEEK} AS week, SUM(amount) AS gmv FROM orders "
|
|
"WHERE region = '华东' AND day >= '2026-05-11' GROUP BY region, week ORDER BY region, week")
|
|
WHERE = {"id": "where", "op": "decompose", "mode": "additive", "from": "by_region",
|
|
"params": {"by": ["region"], **TWO_WEEKS}}
|
|
DRILL = {"id": "east", "op": "decompose", "mode": "additive", "from": "east_q",
|
|
"params": {"by": ["channel"], **TWO_WEEKS, "parent": {"of": "where", "key": ["华东"]}}}
|
|
|
|
|
|
def card_graph(region_sql, items):
|
|
return chain(node("start", "input"), node("by_region", "tool", tool=TOOL, args={"sql": region_sql}),
|
|
node("east_q", "tool", tool=TOOL, args={"sql": EAST}),
|
|
node("card", "analysis", items=items),
|
|
node("out", "output", fields=[{"name": "r", "value": "{{ nodes.card.results.east.phrase }}"}]))
|
|
|
|
|
|
async def _card(region_sql, items):
|
|
run = await run_manager.start(graph=card_graph(region_sql, items), input_payload={})
|
|
row = await wait(run.id)
|
|
return row
|
|
|
|
|
|
async def test_a_drill_under_a_refused_parent_is_refused_and_the_card_goes_on():
|
|
row = await _card(ONLY_EAST, [WHERE, DRILL])
|
|
assert row.status == "succeeded", row.error
|
|
[finished] = await events(row.id, "node.finished", "card")
|
|
results = artifact_store.load(finished["artifact"])["results"]
|
|
assert results["where"]["status"] == "refused" and results["east"]["status"] == "refused"
|
|
east = artifact_store.load(results["east"]["artifact"])
|
|
assert east["reason_code"] == "parent_refused"
|
|
assert east["reason"] == "上一层「where」没有做成(按 region 只有 1 段,至少要 2 段才拆得开),不能在它下面下钻"
|
|
assert row.output["r"] == "变化拆解没有做成:" + east["reason"]
|
|
ends = {e["item"]: e for e in await events(row.id, "analysis.end", "card")}
|
|
assert ends["east"]["reason_code"] == "parent_refused" and ends["east"]["status"] == "refused"
|
|
warned = [e for e in await events(row.id, "log", "card") if e.get("code") == "analysis_refused"]
|
|
assert [e["item"] for e in warned] == ["where", "east"]
|
|
assert [(e["item"], e["status"]) for e in finished["evidence"]] == [("where", "refused"), ("east", "refused")]
|
|
recomputes(artifact_store.load(results["where"]["artifact"]))
|
|
recomputes(east)
|
|
|
|
|
|
async def test_a_segment_missing_this_time_is_refused_and_the_card_goes_on():
|
|
drill = {**DRILL, "params": {**DRILL["params"], "parent": {"of": "where", "key": ["东北"]}}}
|
|
row = await _card(BY_REGION, [WHERE, drill])
|
|
assert row.status == "succeeded", row.error
|
|
[finished] = await events(row.id, "node.finished", "card")
|
|
results = artifact_store.load(finished["artifact"])["results"]
|
|
assert results["where"]["status"] == "ok" and results["east"]["status"] == "refused"
|
|
east = artifact_store.load(results["east"]["artifact"])
|
|
assert east["reason_code"] == "input_unavailable" and east["reason"] == "上一层「where」里没有「东北」这一段"
|
|
assert [i["role"] for i in east["inputs"]] == ["parent", "data"]
|
|
recomputes(east)
|
|
|
|
|
|
async def test_a_structural_call_error_still_fails_the_card_and_names_the_item():
|
|
wrong = {**WHERE, "params": {**WHERE["params"], "by": ["channel"]}}
|
|
row = await _card(BY_REGION, [wrong])
|
|
assert row.status == "failed"
|
|
assert "分析项「where」(变化拆解)" in row.error and "没有「channel」这一列" in row.error
|
|
assert "请在分析卡中改正这一项" in row.error
|
|
|
|
|
|
async def test_overrides_that_conflict_with_the_run_settings_fail_the_card():
|
|
# 画图时对着缺省值校验能过(缺省「超出常规波动」是 2);这次运行的设置把它调到 3,卡上覆盖的「明显超出」2.5 就永远判不出
|
|
card = node("card", "analysis", items=[WHERE], overrides={"k_alert": 2.5})
|
|
graph = chain(node("start", "input"), node("by_region", "tool", tool=TOOL, args={"sql": BY_REGION}), card)
|
|
run = await run_manager.start(graph=graph, input_payload={}, options={"analysis": {"k_warn": 3.0}})
|
|
row = await wait(run.id)
|
|
assert row.status == "failed"
|
|
assert "阈值覆盖和这次运行的分析设置冲突:「明显超出」的倍数要大于「超出常规波动」的倍数" in row.error
|
|
assert await events(row.id, "analysis.end", "card") == []
|
|
ok = await run_manager.start(graph=graph, input_payload={}, options={"analysis": {"k_warn": 2.0}})
|
|
assert (await wait(ok.id)).status == "succeeded"
|