docs: update graph visualization RFC — Phase 0 done (#198)

This commit is contained in:
2026-05-12 10:13:32 +08:00
parent f723daa014
commit 2c26be6ec6
@@ -1,68 +1,53 @@
# Dashboard Workflow Graph Visualization # Dashboard Workflow Graph Visualization
**Issue**: #198 **Issue**: #198
**Status**: Draft **Status**: In Progress
**Author**: xingyue **Author**: xingyue
## Overview ## Overview
在 Dashboard 的 ThreadDetail 页面中嵌入一个交互式流程图,将 workflow 的 `ModeratorTable` 可视化为有向图。用户可以一眼看到角色流转结构和当前执行进度。 在 Dashboard 的 ThreadDetail 页面中嵌入一个交互式流程图,将 workflow 的 `ModeratorTable` 可视化为有向图。用户可以一眼看到角色流转结构和当前执行进度。
## 数据 ## 数据层(✅ 已完成 — PR #201)
### 问题:ModeratorTable 在 bundle 端,Dashboard 在前端 ### WorkflowGraph 类型
`ModeratorTable` 是运行时数据结构(包含 JS 函数引用如 `check`),无法直接序列化给前端。需要一个**静态图描述格式**作为中间层。 `WorkflowDefinition.moderator`(函数)已替换为 `WorkflowDefinition.table`(声明式 `ModeratorTable`),`buildDescriptor` 自动从 table 提取 graph:
### 方案:扩展 WorkflowDescriptor
当前 `WorkflowDescriptor` 只有 roles + description,不包含转换图信息:
```ts ```ts
type WorkflowDescriptor = { type WorkflowGraphEdge = {
description: string; from: string; // source role 或 "__start__"
roles: Record<string, WorkflowRoleDescriptor>; to: string; // target role 或 "__end__"
}; condition: string; // condition.name 或 "FALLBACK"
``` conditionDescription: string | null;
**扩展为**
```ts
type TransitionEdge = {
condition: string; // condition.name,或 "FALLBACK"
description: string | null; // condition.description,FALLBACK 为 null
target: string; // role name 或 "__end__"
}; };
type WorkflowGraph = Record<string, TransitionEdge[]>; type WorkflowGraph = {
// key = source role name 或 "__start__" edges: readonly WorkflowGraphEdge[];
};
type WorkflowDescriptor = { type WorkflowDescriptor = {
description: string; description: string;
roles: Record<string, WorkflowRoleDescriptor>; roles: Record<string, WorkflowRoleDescriptor>;
graph: WorkflowGraph | null; // null = legacy bundles without graph graph: WorkflowGraph; // 必填,新 bundle 自动生成
}; };
``` ```
`buildDescriptor``workflow-register`)中,从 `ModeratorTable` 提取静态图结构。`condition.check` 函数不序列化,只保留 `name``description` ### 数据流
### 数据暴露路径
``` ```
ModeratorTable (runtime) ModeratorTable (WorkflowDefinition.table)
→ buildDescriptor() 提取 graph → buildDescriptor() 自动提取 graph
→ descriptor.yaml 持久化 → descriptor.yaml 持久化(hash.yaml)
→ CLI serve /workflows API 返回 → CLI serve /workflows/:name API 返回 descriptor
→ Dashboard 前端拿到 graph → Dashboard 前端拿到 graph
``` ```
同时需要新增或扩展一个 API,让 Dashboard 能拿到指定 workflow 的 descriptor(含 graph): ### 剩余数据层工作
``` **serve API 需要返回 descriptor**:当前 `GET /workflows/:name` 只返回 registry entry(hash + timestamp),不含 descriptor。需要从 `bundles/{hash}.yaml` 读取 descriptor 并返回给前端。
GET /workflows/:name → { descriptor: WorkflowDescriptor }
```
或者在现有 `listWorkflows` 返回中附带。 方案:在 `routes-workflow.ts``GET /workflows/:name` 响应中附带 `descriptor` 字段。或者:thread-detail 发现 workflow name 后,请求 `GET /workflows/:name/descriptor` 拿到 graph
## 前端渲染 ## 前端渲染
@@ -87,17 +72,17 @@ GET /workflows/:name → { descriptor: WorkflowDescriptor }
### 图结构映射 ### 图结构映射
``` ```
WorkflowGraph → React Flow nodes + edges WorkflowGraph.edges → React Flow nodes + edges
节点: 节点(自动从 edges 推导):
- __start__ → 圆形小节点(入口) - __start__ → 圆形小节点(入口)
- role → 圆角矩形,显示 role name + description - role → 圆角矩形,显示 role name + description
- __end__ → 圆形小节点(终止) - __end__ → 圆形小节点(终止)
边: 边:
- FALLBACK → 虚线(dashed),无 label - FALLBACK → 虚线(dashed),无 label
- condition → 实线,label = condition.name - condition → 实线,label = condition
hover tooltip = condition.description hover tooltip = conditionDescription
``` ```
### 布局 ### 布局
@@ -172,7 +157,7 @@ function getNodeStates(records: ThreadRecord[]): Map<string, "completed" | "acti
workflow-dashboard/src/ workflow-dashboard/src/
components/ components/
workflow-graph/ workflow-graph/
types.ts — TransitionEdge, NodeState 等前端类型 types.ts — NodeState 等前端类型
index.ts — export { WorkflowGraph } index.ts — export { WorkflowGraph }
workflow-graph.tsx — 主组件,React Flow canvas workflow-graph.tsx — 主组件,React Flow canvas
role-node.tsx — 自定义 role 节点 role-node.tsx — 自定义 role 节点
@@ -196,15 +181,22 @@ workflow-dashboard/src/
图高度固定 300px,React Flow 支持 pan + zoom,不影响下方 records 滚动。 图高度固定 300px,React Flow 支持 pan + zoom,不影响下方 records 滚动。
## 分阶段实施 ## 实施计划
### Phase 1: 数据层 + 静态图 ### ~~Phase 0: 数据层~~ ✅ Done (PR #201)
1. `workflow-protocol` 中新增 `TransitionEdge` / `WorkflowGraph` 类型 - [x] `WorkflowDefinition.moderator` `table` (ModeratorTable)
2. `workflow-register``buildDescriptor` 中从 `ModeratorTable` 提取 graph - [x] `WorkflowDescriptor` 新增 `graph: WorkflowGraph`
3. `stringifyWorkflowDescriptor` / `validateWorkflowDescriptor` 支持 graph 字段 - [x] `buildDescriptor` 自动提取 graph
4. CLI serve 的 `/workflows` API 返回 descriptor(含 graph - [x] `validateWorkflowDescriptor` 校验 graph
5. Dashboard 新增 `WorkflowGraph` 组件,静态渲染图
### Phase 1: API + 静态图渲染
1. serve API:`GET /workflows/:name` 返回 descriptor(含 graph),或新增 `GET /workflows/:name/descriptor`
2. Dashboard `api.ts` 新增 `getWorkflowDescriptor(agent, name)` 函数
3. 安装 `@xyflow/react` + `@dagrejs/dagre`
4. 实现 `workflow-graph/` 组件集
5. ThreadDetail 中集成:从 thread-start record 拿 workflow name → 请求 descriptor → 渲染图
**产出**:打开 ThreadDetail 看到 workflow 流程图,无高亮。 **产出**:打开 ThreadDetail 看到 workflow 流程图,无高亮。
@@ -219,21 +211,14 @@ workflow-dashboard/src/
### Phase 3: 交互增强 ### Phase 3: 交互增强
1. 点击节点滚动到对应 role 的 RecordCard 1. 点击节点滚动到对应 role 的 RecordCard
2. 边 hover 显示 condition description tooltip 2. 边 hover 显示 conditionDescription tooltip
3. 节点 hover 显示 role description + schema summary 3. 节点 hover 显示 role description + schema summary
**产出**:图和记录列表联动。 **产出**:图和记录列表联动。
## 注意事项 ## 注意事项
- **向后兼容**:`graph` 字段为 `null` 时(旧 bundle),不渲染图,只显示 records
- **自循环边**:如 `coder → coder (FALLBACK)`,React Flow 支持自循环,dagre 需要特殊处理(self-edge 用 loop 路径) - **自循环边**:如 `coder → coder (FALLBACK)`,React Flow 支持自循环,dagre 需要特殊处理(self-edge 用 loop 路径)
- **大图性能**:dagre 在 <50 节点时性能无忧,workflow 通常 <10 个 role - **大图性能**:dagre 在 <50 节点时性能无忧,workflow 通常 <10 个 role
- **暗色主题**:Dashboard 已使用 CSS variables,节点/边样式复用现有色板 - **暗色主题**:Dashboard 已使用 CSS variables,节点/边样式复用现有色板
- **不提交 pnpm-lock.yaml** - **不提交 pnpm-lock.yaml**
## 开放问题
1. **graph 放 descriptor 还是独立字段?** — 建议放 descriptor,因为它描述的就是 workflow 结构
2. **是否需要 WorkflowList 页也展示图?** — Phase 1 先只在 ThreadDetail,后续按需扩展
3. **`buildDescriptor` 需要 `ModeratorTable` 参数** — 当前 `buildDescriptor` 只接收 roles,需要扩展签名或在 bundle 注册时额外传入 table