From ec6b1b9c236c2bdf81f7ff0362af9dfa19e15e39 Mon Sep 17 00:00:00 2001 From: zizi Date: Sun, 23 Aug 2026 16:24:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B6=E6=AE=B5G=E7=AC=AC=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86:=20=E9=93=BE=E8=B7=AF=E9=80=8F=E8=A7=86=E6=8E=A5?= =?UTF-8?q?=E7=BA=BF=E2=80=94=E2=80=94=E8=BF=90=E8=A1=8C=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E6=8E=A5=E4=BA=8B=E4=BB=B6=E8=B4=A6=E6=9C=AC(?= =?UTF-8?q?=E6=8E=A2=E7=B4=A2=E8=BD=A8=E8=BF=B9/=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=9B=9E=E5=90=88/=E6=88=90=E6=9C=AC)=E4=B8=8E=E6=B4=BE?= =?UTF-8?q?=E5=8F=91=E9=93=BE=E7=88=B6=E5=AD=90=E5=AF=BC=E8=88=AA=EF=BC=88?= =?UTF-8?q?=E7=9C=8B=E6=9D=BF=E6=B5=8B=E8=AF=95=E7=BB=BF+=E7=9C=9F?= =?UTF-8?q?=E5=AE=9E=E8=BF=90=E8=A1=8C=E9=A1=B5=E8=B5=B0=E6=9F=A5=E9=80=9A?= =?UTF-8?q?=E8=BF=87=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/server.py | 90 ++++++++++++++++++- ...µG-第一部分-链路透视接线-事件账本与派发链.md | 37 ++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 docs/plans/2026-08-22-阶段G-第一部分-链路透视接线-事件账本与派发链.md diff --git a/dashboard/server.py b/dashboard/server.py index 351222c..66f1582 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -2548,9 +2548,94 @@ def _run_turn_panels_html(run_id: str, candidates: list, terminal_state) -> str: ) +def _agent_events_html(run_id): + """链路透视 · 事件账本:探索轨迹(读了什么)与模型回合(example_agent_event)。""" + + if not table_exists("example_agent_event"): + return empty_gap("事件账本 · 待建表", "事件账本表 example_agent_event 尚未建立。") + cols, rows = q( + "SELECT seq,event_type,tool_name,status,actual_model_id,input_tokens,output_tokens,cost_usd,details " + "FROM example_agent_event WHERE run_id=%s ORDER BY seq", + (run_id,), + ) + events = _row_dicts(cols, rows) + if not events: + return empty_gap("事件账本 · 该运行暂无记录", "框架派发运行后,探索轨迹与模型回合在此逐条可见。") + + def _details_text(value): + if value is None: + return "—" + text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False) + return esc(text[:300] + ("…" if len(text) > 300 else "")) + + tool_events = [e for e in events if str(e["event_type"]).startswith("tool.")] + model_events = [e for e in events if str(e["event_type"]).startswith("model.")] + tool_rows = "".join( + f"{esc(e['seq'])}{esc(e['event_type'])}" + f"{esc(e['tool_name'])}{chip(e['status'])}" + f"{_details_text(e['details'])}" + for e in tool_events + ) + model_rows = "".join( + f"{esc(e['seq'])}{esc(e['event_type'])}" + f"{esc(e['actual_model_id'])}{chip(e['status'])}" + f"{fmt_num(e['input_tokens'])}{fmt_num(e['output_tokens'])}" + f"{fmt_num(e['cost_usd'])}" + for e in model_events + ) + tool_table = ( + "
探索轨迹(工具事件)
" + "" + f"{tool_rows}
序事件类型工具状态参数/明细
" if tool_events + else "
探索轨迹(工具事件)
本运行未发生工具探索。
" + ) + model_table = ( + "
模型回合
" + "" + "" + f"{model_rows}
序事件类型实际模型编码状态输入令牌输出令牌成本美元
" if model_events + else "
模型回合
本运行无模型回合事件。
" + ) + return f"
{tool_table}{model_table}
" + + +def _dispatch_chain_html(run_id, trigger_detail): + """链路透视 · 派发链:经 trigger_detail 关联的生产/评测父运行与派发子运行。""" + + parent_ref = None + if isinstance(trigger_detail, dict): + parent_ref = trigger_detail.get("productionRunId") or trigger_detail.get("evaluationRunId") + _, child_rows = q( + "SELECT run_id, terminal_state, trigger_detail->>'stage' AS stage " + "FROM example_run WHERE deleted=false AND " + "(trigger_detail->>'productionRunId'=%s OR trigger_detail->>'evaluationRunId'=%s) " + "ORDER BY started_at", + (run_id, run_id), + ) + if not parent_ref and not child_rows: + return "" + parts = ["
派发链(父子运行)
"] + if parent_ref: + parts.append( + f"

父运行:{esc(str(parent_ref))}

" + ) + if child_rows: + parts.append( + "" + + "".join( + f"" + f"" for c in child_rows + ) + + "
派发子运行终态阶段
{esc(c[0])}{chip(c[1])}{esc(c[2] or '—')}
" + ) + parts.append("
") + return "".join(parts) + + def view_run_detail(run_id): _, rr = q( """SELECT r.run_id,r.work_id,r.target_chapter,r.trigger_source,r.started_at,r.finished_at,r.terminal_state, + r.trigger_detail, w.title AS work_title,c.id AS chapter_id,c.order_no AS chapter_order,c.title AS chapter_title FROM example_run r LEFT JOIN muse_content_work w ON w.id=r.work_id @@ -2560,7 +2645,7 @@ def view_run_detail(run_id): ) if not rr: return layout("运行", "

运行不存在

", "/runs") - rid, wid, chap, trig, started, finished, state, work_title, chapter_id, chapter_order, chapter_title = rr[0] + rid, wid, chap, trig, started, finished, state, trigger_detail, work_title, chapter_id, chapter_order, chapter_title = rr[0] rc_cols, rc_rows = q( """SELECT id,sample_id,revision,stage_kind,adapter_role,attempt,loop_seq,candidate_version,is_error, stop_reason,terminal_reason,safe_summary,raw_content_id,requested_model_id,actual_model_id, @@ -2758,9 +2843,12 @@ def view_run_detail(run_id): error_items.append("
运行终态为失败,但回执和质量表没有提供更细错误记录。
") error_html = ("
错误与诊断
" + "".join(error_items) + "
") if error_items else "" turn_html = _run_turn_panels_html(rid, candidates, state) + events_html = _agent_events_html(rid) + chain_html = _dispatch_chain_html(rid, trigger_detail) body = (f"
运行记录 / {esc(rid)}
" f"

运行详情

{info}{error_html}{turn_html}" f"

回执链

{rc_html}

质量评判

{q_html}

模型调用

{call_html}" + f"

链路透视

{chain_html}{events_html}" f"

候选与决策

{cand}{dec}

证据与上下文

{raw_html}{raw_detail_html}{freeze_html}") return layout("运行详情", body, "/runs") diff --git a/docs/plans/2026-08-22-阶段G-第一部分-链路透视接线-事件账本与派发链.md b/docs/plans/2026-08-22-阶段G-第一部分-链路透视接线-事件账本与派发链.md new file mode 100644 index 0000000..6b01138 --- /dev/null +++ b/docs/plans/2026-08-22-阶段G-第一部分-链路透视接线-事件账本与派发链.md @@ -0,0 +1,37 @@ +# 阶段 G 第一部分:链路透视接线——事件账本与派发链上看板 + +日期:2026-08-23 +状态:已完成(看板测试绿 + 真实运行页走查通过) +上游事实:两阶段写手、抽取、盲评派发运行已在 `example_agent_event` 与 `example_run` 留下真实数据;§11 矩阵中"探索轨迹与事件链""依赖清单"此前为接线缺口。 + +## 1. 意图 + +把框架派发链的运行时证据接上看板:运行详情页新增"链路透视"区块,回放一次运行的探索轨迹(读了什么)、模型回合(成本)与派发链父子关系。只读库,不拼命令,不写库。 + +## 2. 改动台账 + +| 文件 | 动作 | 原因 | +|---|---|---| +| `dashboard/server.py` | 修改 | `view_run_detail` 主查询补 `trigger_detail`;新增 `_agent_events_html`(探索轨迹 + 模型回合,读 `example_agent_event`)与 `_dispatch_chain_html`(经 `trigger_detail.productionRunId/evaluationRunId` 关联父子运行);页面加"链路透视"区块 | + +## 3. 验证 + +- `dashboard/test_server_display.py` 全绿(展示层中文语义与编码合同)。 +- 真实页走查(只读看板): + - `…-writer-v9`:探索轨迹含 `read_chapter_text`×8、`read_fine_outline`×4 等,模型回合带令牌/成本,"派发链"回链父运行。 + - 生产父运行 `…-42beb650`:列出子运行 explore-v9 / writer-v9。 + - 盲评运行 `…-30f33c50c7`:列出 judge-v1 / judge-v2。 + +## 4. §11 对照(本部分消解的缺口) + +| 内容 | 之前 | 现在 | +|---|---|---| +| 探索轨迹与事件链(example_agent_event) | 看板未接(缺口) | 运行详情页"链路透视"可见 | +| 依赖清单(本次运行实际读了什么) | 看板未接(缺口) | 以工具事件(工具名+参数明细)呈现 | +| 派发链父子关系 | 无视图 | "派发链"区块双向可导航 | + +## 5. 未决(阶段 G 后续) + +- 任务包(spec)与生产 artifacts 读侧迁到运行目录/库投影的看板呈现(§11"生产 artifacts"行的读侧迁移)。 +- 待审视图增强:候选详情页直达其生产运行与检查报告(现经 /runs 手工跳转)。 +- 五视图整体走查与 §11 全矩阵验收。