feat(observability): 执行可视化 — 节点级实时轨迹(运行·观测)
把任务执行做成可观测:Dispatcher 在每个节点/阶段发结构化 ExecEvent, 经独立 NATS 通道回流,前端逐节点点亮(状态/耗时/工具入参产出)。 - shared: contract.ExecEvent + ExecSubject(sundynix.exec.<id>,与 Token 流分流); bus.PublishExec/CompleteExec/SubscribeExec(core NATS,复用结束头) - dispatcher: execTracer(自增 Seq 保序 + span 自动计耗时); Orchestrator 加 ExecSink;通用图(init 召回 / 各 tool 入参→产出 / prompt / model 首token+token数)与报告编排(规划大纲 / 各章并行 start-end / 渲染)全程埋点 - gateway: SubscribeExec + GET /tasks/:id/exec SSE(与 token 流并行) - desktop: streamExec + deriveNodes(按 node 归并 start/end/error/info); 复用组件 ExecTrace(竖向轨道,按 kind 着色,运行中脉冲灯); 新 RunsView(运行·观测:轨迹+输出双栏);BottomDrawer 轨迹/工具调用 tab 接真实数据; ReportView 加执行轨迹栏;左导航「运行」置就绪 实测: - 报告任务 /exec:规划(2680ms,4章) → 4 章并行(seq 交错,各~7-8s 重叠=真并行, 每章带 docs 知识库检索预览+成稿字数) → 渲染(docx 落盘) - 通用图 /exec:tool:kb_search(678ms,入参→Milvus 产出) → prompt(2消息) → model(首token 860ms / 4 tokens) - 浏览器(Preview):报告页执行轨迹逐节点点亮、章节带耗时/字数/检索片段,完成后下载 Word Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import type { RunState } from "../lib/run";
|
||||
import { deriveNodes, type RunState } from "../lib/run";
|
||||
import { ExecTrace } from "../components/ExecTrace";
|
||||
|
||||
type Tab = "output" | "trace" | "tools" | "cite" | "eval";
|
||||
const TABS: Array<{ key: Tab; label: string }> = [
|
||||
@@ -43,24 +44,14 @@ export function BottomDrawer({ run }: { run: RunState }) {
|
||||
</button>
|
||||
</div>
|
||||
{open && (
|
||||
<div className="h-40 overflow-auto p-3 text-xs">
|
||||
<div className="h-44 overflow-auto p-3 text-xs">
|
||||
{tab === "output" && (
|
||||
<pre className="whitespace-pre-wrap font-mono leading-relaxed text-emerald-300">
|
||||
{run.output || "在编排页搭图 → 运行,模型注入画像与历史后流式作答,token 在此呈现。"}
|
||||
</pre>
|
||||
)}
|
||||
{tab === "trace" && (
|
||||
<ul className="space-y-1 text-slate-400">
|
||||
{run.events.length === 0 && <li className="text-slate-600">尚无运行。</li>}
|
||||
{run.events.map((e, i) => (
|
||||
<li key={i}>
|
||||
<span className="text-slate-600">+{e.t}ms</span> · {e.label}
|
||||
</li>
|
||||
))}
|
||||
{run.events.length > 0 && <li className="mt-2 text-[11px] text-slate-600">(节点级轨迹待后端回流节点事件后逐节点点亮)</li>}
|
||||
</ul>
|
||||
)}
|
||||
{tab === "tools" && <p className="text-slate-600">工具调用日志:每次 sundynix.tools.* 的请求/响应(需后端回流工具事件)。</p>}
|
||||
{tab === "trace" && <ExecTrace events={run.exec} phase={run.phase} />}
|
||||
{tab === "tools" && <ToolCalls run={run} />}
|
||||
{tab === "cite" && <p className="text-slate-600">引用列表:RAG 答案的来源块(源文档 + 分数 + 来源徽标)。</p>}
|
||||
{tab === "eval" && <p className="text-slate-600">评测:忠实度 / 完整度质量门结果(需 harness eval)。</p>}
|
||||
</div>
|
||||
@@ -68,3 +59,32 @@ export function BottomDrawer({ run }: { run: RunState }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ToolCalls:从执行事件里筛出工具调用节点,逐条展示入参 → 产出 + 耗时/状态。
|
||||
function ToolCalls({ run }: { run: RunState }) {
|
||||
const tools = deriveNodes(run.exec).filter((n) => n.kind === "tool");
|
||||
if (tools.length === 0) {
|
||||
return <p className="text-slate-600">本次运行暂无工具调用。图里挂了检索/工具节点,或报告挂了知识库时,每次 sundynix.tools.* 调用会在此列出入参与产出。</p>;
|
||||
}
|
||||
return (
|
||||
<ul className="space-y-1.5">
|
||||
{tools.map((t) => (
|
||||
<li key={t.node} className="rounded-lg border border-line bg-ink-950/60 px-3 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-amber-300">⚙</span>
|
||||
<span className="font-mono text-[11px] text-slate-200">{t.node.replace(/^tool:/, "")}</span>
|
||||
<span
|
||||
className={`rounded px-1.5 py-0.5 text-[9px] ${
|
||||
t.status === "error" ? "bg-rose-500/15 text-rose-300" : t.status === "running" ? "bg-cyan-500/15 text-cyan-300" : "bg-emerald-500/15 text-emerald-300"
|
||||
}`}
|
||||
>
|
||||
{t.status === "error" ? "失败" : t.status === "running" ? "调用中" : "成功"}
|
||||
</span>
|
||||
{t.ms != null && t.ms > 0 && <span className="ml-auto font-mono text-[10px] text-slate-500">{t.ms} ms</span>}
|
||||
</div>
|
||||
{t.detail && <p className="mt-1 break-words text-[11px] leading-relaxed text-slate-400">{t.detail}</p>}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const ITEMS: Item[] = [
|
||||
{ key: "studio", label: "编排", icon: "◆", group: "BUILD", ready: true },
|
||||
{ key: "kb", label: "知识库", icon: "▣", group: "BUILD", ready: true },
|
||||
{ key: "report", label: "报告", icon: "▦", group: "BUILD", ready: true },
|
||||
{ key: "runs", label: "运行", icon: "▸", group: "RUN" },
|
||||
{ key: "runs", label: "运行", icon: "▸", group: "RUN", ready: true },
|
||||
{ key: "memory", label: "记忆", icon: "◇", group: "MANAGE", ready: true },
|
||||
{ key: "market", label: "市场", icon: "⌧", group: "MANAGE" },
|
||||
{ key: "admin", label: "管理", icon: "⚙", group: "MANAGE" },
|
||||
|
||||
Reference in New Issue
Block a user