mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:11:59 +00:00
证据接口重新渲染分析结论的引用改用组装、出口复核同一个函数:_analysis_rendered 按封存的结论走 evidence.resolve_ref(resolve_ref 加 display,按文档记下的单元格显示规则),结果表的格照结果表记的列类型、显示规则、标识列渲染,和组装时的 _resolve_cell 一致。以前不看列类型和显示规则,文本码值「1203」重渲染成「1,203」、ISO 日期时间带着 T,render_ok 为假,证据面板和导出页报假的「与按结论渲染的结果不一致」。片段上没记引用的(口径卡指标上的可比性、分析范围的编号)照旧按判定语渲染。测试:门店码、时段两种格在片段接口和导出页数据块里都 render_ok
This commit is contained in:
+20
-12
@@ -104,6 +104,7 @@ from app.db.base import SessionLocal
|
||||
from app.db.models import DataSource, Run, RunEvent, Workflow
|
||||
from app.engine import analysis_claims, direct_select, recompute
|
||||
from app.engine.evidence import (
|
||||
CELL_DISPLAY,
|
||||
ENTITY_KINDS,
|
||||
GUESS_NOTE,
|
||||
GUESS_SCHEMA,
|
||||
@@ -116,6 +117,7 @@ from app.engine.evidence import (
|
||||
analysis_recheck,
|
||||
build_catalog,
|
||||
closest_entities,
|
||||
doc_cell_display,
|
||||
find_segment,
|
||||
guess_sources,
|
||||
input_eid,
|
||||
@@ -126,6 +128,7 @@ from app.engine.evidence import (
|
||||
query_entry_fields,
|
||||
render_cell,
|
||||
render_metric,
|
||||
resolve_ref,
|
||||
schema_column,
|
||||
schema_table,
|
||||
sql_tables,
|
||||
@@ -1041,17 +1044,22 @@ def _analysis_cited(cite: dict[str, Any], result: dict[str, Any] | None) -> dict
|
||||
return {"what": "phrase"}
|
||||
|
||||
|
||||
def _analysis_rendered(cite: dict[str, Any], result: dict[str, Any]) -> str | None:
|
||||
"""按封存的结论重新渲染这一处引用(判定语按文本格、具名标量和结果表的格按单元格的规则)。渲染不出来是 None。"""
|
||||
try:
|
||||
cited = _analysis_cited(cite, result)
|
||||
if cited["what"] == "phrase":
|
||||
return render_cell(result.get("phrase"), kind="text") if isinstance(result.get("phrase"), str) else None
|
||||
if cited["what"] == "value":
|
||||
return render_cell(cited["raw"], cite.get("conv"))
|
||||
return render_cell(cited["raw"], cite.get("conv"), plain=cite.get("plain") is True)
|
||||
except RenderError:
|
||||
return None
|
||||
def _analysis_rendered(seg: dict[str, Any], cite: dict[str, Any], entry: dict[str, Any], result: dict[str, Any],
|
||||
doc: dict[str, Any]) -> str | None:
|
||||
"""按封存的结论把这一处引用重新解析一遍,交回渲染出来的字(渲染不出来是 None)。和组装、出口复核同一个函数
|
||||
(evidence.resolve_ref,按文档记下的那一版单元格显示规则):判定语按文本格,具名标量按单元格的规则,结果表的格照结果表
|
||||
记的列类型(文本码值 "1203" 不加千分位)、显示规则(ISO 日期时间的 T 换成空格)、标识列不分千位。片段上没记引用的
|
||||
(口径卡指标上的可比性、分析范围里的编号)按判定语渲染。"""
|
||||
ref = seg.get("ref")
|
||||
if not isinstance(ref, str) or not ref.strip():
|
||||
phrase = result.get("phrase")
|
||||
try:
|
||||
return render_cell(phrase, kind="text") if isinstance(phrase, str) else None
|
||||
except RenderError:
|
||||
return None
|
||||
alias = str(cite.get("alias") or entry.get("alias") or "")
|
||||
again = resolve_ref(ref, {alias: {**entry, "result": result}}, display=doc_cell_display(doc) or CELL_DISPLAY)
|
||||
return again.get("rendered") if again.get("status") == "resolved" else None
|
||||
|
||||
|
||||
async def _analysis_chain(seg: dict[str, Any], cite: dict[str, Any], entry: dict[str, Any], doc: dict[str, Any],
|
||||
@@ -1082,7 +1090,7 @@ async def _analysis_chain(seg: dict[str, Any], cite: dict[str, Any], entry: dict
|
||||
in_ledger = sealed.trusted and (str(entry.get("node_id") or ""), artifact) in sealed.ledger \
|
||||
if chained is None else sealed.trusted and chained
|
||||
eid = make_eid(LEDGER_KIND, artifact or None, {})
|
||||
rendered = _analysis_rendered(cite, result) if result is not None else None
|
||||
rendered = _analysis_rendered(seg, cite, entry, result, doc) if result is not None else None
|
||||
judged: dict[str, Any] = {
|
||||
"step": "analysis", "alias": alias, "op": entry.get("op"), "mode": entry.get("mode"),
|
||||
"method_label": method_label(entry.get("op"), entry.get("mode")),
|
||||
|
||||
@@ -2106,8 +2106,11 @@ def resolve_support(ref: str, catalog: dict[str, Any], *, loader: Loader | _Snap
|
||||
|
||||
|
||||
def resolve_ref(ref: str, catalog: dict[str, Any], *, cells_allowed: bool = True,
|
||||
loader: Loader | _Snapshots | None = None) -> dict[str, Any]:
|
||||
"""片段上记的 ref(m:gmv|万、i:week、v:Q1.r0.amount)→ Citation。证据接口点开片段时用它重新解析。"""
|
||||
loader: Loader | _Snapshots | None = None, display: int | None = None) -> dict[str, Any]:
|
||||
"""片段上记的 ref(m:gmv|万、i:week、v:Q1.r0.amount)→ Citation。证据接口点开片段时用它重新解析。display 是按哪一版
|
||||
单元格显示规则渲染(文档记下的那一版,doc_cell_display;复核按它),不给按 loader 的(缺省当前版)。"""
|
||||
if display is not None:
|
||||
loader = _snapshots(loader, display=display)
|
||||
return resolve_marker(_parse_ref(ref), catalog, cells_allowed=cells_allowed, loader=loader)
|
||||
|
||||
|
||||
|
||||
@@ -178,6 +178,51 @@ async def test_named_values_and_result_cells_point_at_what_they_cite(client):
|
||||
assert cell["note"] == "该值由系统从分析结论的结果表中取出、按固定规则显示,未经模型转写"
|
||||
|
||||
|
||||
#: 按门店拆的快照:门店码是文本("1203"),渲染时不加千分位
|
||||
STORES: dict[str, Any] = {
|
||||
"columns": ["store", "week", "gmv"], "column_types": {"store": "text", "week": "text", "gmv": "number"},
|
||||
"rows": [["1203", "W41", 820], ["1203", "W42", 700], ["1305", "W41", 600], ["1305", "W42", 560],
|
||||
["2201", "W41", 700], ["2201", "W42", 666]],
|
||||
"sql": "SELECT store, week, SUM(gmv) AS gmv FROM orders GROUP BY store, week ORDER BY store, week",
|
||||
"source": "shop", "truncated": False, "row_count": 6,
|
||||
}
|
||||
#: 按时段拆的快照:时段是 ISO 日期时间(带 T),文档按 2 版显示规则写成空格
|
||||
SLOTS: dict[str, Any] = {
|
||||
"columns": ["slot", "week", "gmv"], "column_types": {"slot": "text", "week": "text", "gmv": "number"},
|
||||
"rows": [["2026-10-12T08:00:00", "W41", 820], ["2026-10-12T08:00:00", "W42", 700],
|
||||
["2026-10-12T20:00:00", "W41", 600], ["2026-10-12T20:00:00", "W42", 560]],
|
||||
"sql": "SELECT slot, week, SUM(gmv) AS gmv FROM orders GROUP BY slot, week ORDER BY slot, week",
|
||||
"source": "shop", "truncated": False, "row_count": 4,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("snapshot, by, shown", [
|
||||
(STORES, "store", "1203"),
|
||||
(SLOTS, "slot", "2026-10-12 08:00:00"),
|
||||
])
|
||||
async def test_a_result_cell_rerenders_the_way_it_was_composed(client, monkeypatch, snapshot, by, shown):
|
||||
"""证据接口按组装时同一套规则重新渲染结果表的格(evidence.resolve_ref,按文档记下的单元格显示规则):文本码值照结果表
|
||||
记的列类型不加千分位,ISO 日期时间的 T 换成空格。以前不看列类型和显示规则,渲染成「1,203」「2026-10-12T08:00:00」,
|
||||
render_ok 为假,证据面板和导出页报假的「与按结论渲染的结果不一致」。"""
|
||||
import tests.test_analysis_evidence_api as here
|
||||
|
||||
monkeypatch.setattr(here, "SNAPSHOT", snapshot)
|
||||
q = await artifact_store.put_json(snapshot, kind="query_snapshot")
|
||||
result = await analyzed({"Q2": (snapshot, q)}, by=[by])
|
||||
ref = f"v:A1.r0.{by}"
|
||||
made = await sealed_run(f"{by} [[{ref}]] 变化 [[v:A1.r0.delta]]。[[see:A1]]", result=result)
|
||||
segment = next(s for s in iter_segments(made["doc"]) if s.get("ref") == ref)
|
||||
assert segment["text"] == shown
|
||||
body = (await client.get(f"/api/runs/{made['run']}/evidence/segments/{segment['id']}")).json()
|
||||
judged = body["chain"][0]
|
||||
assert judged["rendered"] == shown and judged["render_ok"] is True
|
||||
assert judged["cited"] == {"what": "cell", "row": 0, "column": by, "raw": snapshot["rows"][0][0]}
|
||||
# 导出页的数据块是同一条链
|
||||
data = data_block((await client.get(f"/api/runs/{made['run']}/report.html")).text)
|
||||
chain = data["chains"][data["refs"][segment["id"]]]
|
||||
assert chain[0]["step"] == "analysis" and chain[0]["render_ok"] is True
|
||||
|
||||
|
||||
async def test_the_recompute_gets_the_sealed_inputs_by_hash(client, monkeypatch):
|
||||
"""复算的 load 交工件原内容(不加出处、不改列的类型),只认封存范围内交回过的查询;别的一律当取不回。"""
|
||||
seen: dict[str, Any] = {}
|
||||
|
||||
Reference in New Issue
Block a user