Skip to content
dsh.fish
Bundle

graph-monitor

Graph Monitor plugin for DeepSeek Harness: define and customize GRAPH workflows, then visualize each node live as it executes — Waku-agent style topology chart with animated node/edge status, clickable node details, and a desktop trajectory-like monitor view.

Source
asakumizy
stars
1 stars
License
MIT
Updated
Updated 13 days ago

Readme

# dsh-graph-monitor

一个 [DSH (DeepSeek Harness)](https://github.com/deepseek-ai) 插件:使用 LangGraph 风格的 **GRAPH 工作流**(节点 + 边 + 条件路由 + 并行分支 + 结构化 State),执行时每个环节实时点亮。拓扑动画风格参考 [Waku-agent](https://github.com/ShenSeanChen/Waku-agent) 的 dashboard(`http://localhost:7777/#graph`)。

- **GRAPH 可视化**:分层拓扑图(`START` → 节点 → `END`,每个深度一列),SVG 渲染,与引擎实际执行的拓扑完全一致(防漂移:图 *就是* workflow)。
- **实时动画**(Waku 风格 `hot`/`live` 闪烁):当前节点紫色脉动、完成节点绿色、失败节点红色、行进边琥珀色高亮。
- **自定义 workflows**:设置 → Graph Monitor,JSON 编辑器(节点 / 边 / 条件路由 / fn 处理器),校验后保存并持久化到 `~/.dsh/graph-monitor/workflows/*.json`,保存/删除后列表实时刷新。
- **点击节点看详情**:状态 / 耗时 / 输入 / 输出 / 错误。
- **对话集成**:
  - `/workflow`(不带参数)列出现有工作流;
  - `/workflow <workflow-id> <输入…>` 直接在对话中运行并实时可视化 node/edge;
  - 模型工具 `graph_monitor_run` —— 用自然语言(如「跑一下售后工作流」)触发运行。
- **运行历史**:每次运行可回看,含最终输出。
- **健壮轮询**:单调游标 + 截断安全事件日志(超过 2000 条后增量推送依然正确)。

> 说明:节点执行支持两种后端 —— `nodeExecutor: simulated`(默认,固定延迟 + 模板输出,不真正调用模型/工具)与 `nodeExecutor: real`(`fn` / `tool` / `agent` / `llm` 节点绑定 DSH 真实能力,能力缺失自动回退模拟)。详见下方「执行后端」。

---

## 目录结构

```
dsh-graph-monitor/
├── lib/index.js          # 宿主半面:GRAPH 引擎 + HTTP API(/graph-monitor/*)
├── lib/chat.js           # 对话集成:/workflow 命令 + graph_monitor_run 工具 + 系统提示
├── lib/client.js         # 客户端半面:监视器页签 + 聊天内嵌卡片 + 设置编辑器
├── test/engine.test.mjs  # 引擎单测(可独立运行,stub Cordis ctx)
├── package.json
├── cordis.patch.yml      # bundle 补丁(nodeDelayMs / llmDelayMs / keepRuns)
├── LICENSE               # MIT
└── README.md
```

---

## 依赖

- **DeepSeek Harness 运行时**:`@deepseek-ai/*` 服务(`sessions` / `webServer` / `tools` / `commands` / `systemPrompt` 等),以 `peerDependencies` 声明。
- **Node.js ≥ 18**(ESM)。

---

## 安装

插件运行在 DSH 的 loopback web server 上,客户端通过普通 `fetch()` 访问同源 API。二选一:

### 方式一:desktop profile 手动布放

```powershell
$dst = "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\graph-monitor"
New-Item -ItemType Directory -Path "$dst\lib" -Force | Out-Null
Copy-Item "package.json" "$dst\"
Copy-Item "README.md" "$dst\"
Copy-Item "cordis.patch.yml" "$dst\"
Copy-Item "lib\index.js" "$dst\lib\"
Copy-Item "lib\chat.js" "$dst\lib\"
Copy-Item "lib\client.js" "$dst\lib\"
```

然后在 `~\.dsh\profiles\desktop\cordis.patch.yml` 的 `insert:` 列表追加:

```yaml
    - id: graph-monitor
      name: 'graph-monitor'
      config:
        nodeDelayMs: 750
        llmDelayMs: 950
        keepRuns: 20
        nodeExecutor: real   # "simulated"(默认固定延迟)| "real"(绑定 DSH 真实能力,缺失回退模拟)
```

### 方式二:bundle 引用

在 profile 的 `package.json` 里把 `graph-monitor` 加入 `dsh.profile.bundles`(依赖其已存在于共享层 `~\.dsh\profiles\node_modules`):

```json
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "graph-monitor"] } }
```

**安装完成后必须重启 DSH Desktop**,旧会话的工具面在会话创建时固化,需**新建会话**才生效。

---

## 使用手册

### 1. 对话命令 `/workflow`

在聊天输入框直接输入:

| 输入 | 效果 |
|---|---|
| `/workflow` | 列出所有可用工作流(id + 名称 + 描述 + 节点/边数) |
| `/workflow <id> <输入…>` | 运行指定工作流,结果以实时卡片形式显示在聊天流中 |
| 自然语言 | 模型根据系统提示自动调用 `graph_monitor_run` 工具 |

示例:

```
/workflow
/workflow text-pipeline hello world
```

### 2. Graph Monitor 页签

任意会话右侧切到 **Graph Monitor** 页签:

1. 下拉框选择 workflow;
2. 输入框填写运行输入,点「运行」;
3. 节点逐个点亮(运行中紫、完成绿、失败红,行进边琥珀高亮);
4. 点击任意节点查看执行详情;
5. 拖动平移画布,`Ctrl + 滚轮` 缩放;
6. 「运行历史」回看任意一次运行。

### 3. 设置编辑器

设置 → Graph Monitor:

1. 「新建 workflow」或「编辑」;
2. 编辑 JSON(节点 / 边 / 条件路由 / fn 处理器);
3. 「校验」确认合法;
4. 「保存」后立即生效并持久化到 `~/.dsh/graph-monitor/workflows/`。

---

## GRAPH 词汇表

### 节点类型(kind)

| kind | 说明 |
|---|---|
| `llm` | LLM 推理步骤(模拟延迟) |
| `agent` | Agent 循环(规划 + 执行) |
| `tool` | 工具调用(`real` 模式下 `tool` 名 + `arguments` 调用真实工具) |
| `fn` | 命名处理器函数(目录白名单,绝不 `eval`);`real` 模式可改用内联 JS `code` |
| `echo` | 透传 |
| `sleep` | 暂停(用于动画节奏) |
| `parallel` | 并行扇出到多个下游分支 |
| `merge` | 汇合并行分支后继续 |
| `gate` | 条件放行/拦截:谓词为真透传到 `pass`,否则走 `fail`(无 `fail` 则 run 以 `blocked` 结束) |
| `switch` | 多选一分支:按 `cases` 顺序第一个命中走对应 `to`,`default` 兜底(恰好一条) |
| `subgraph` | 组合子工作流:把已固化套路封装成可复用新原语(`graph` 嵌套执行) |
| `loop` | 迭代子图:`while` 谓词为真时反复执行,`maxIterations` 硬上限 |
| `retry` | 失败重试子图:内嵌输出以 `ERROR` 开头即重试,至多 `maxAttempts` 次 |
| `timeout` | 时间预算子图:`ms` 预算内完成透传,超时输出 `ERROR: timeout...` |

### fn 处理器目录

**基础文本**:`uppercase` · `lowercase` · `reverse` · `wordCount` · `charCount` · `trim` · `slugify` · `titleCase`

**客服话术**(售前/售后 canned answers):`productAnswer` · `returnAnswer` · `warrantyAnswer` · `repairAnswer` · `orderAnswer` · `logisticsAnswer` · `handoff`

**天气**(高德 Web 服务 API):`weatherCity`(解析城市)· `amapWeather`(实时 + 预报;需配置 `AMAP_KEY` 环境变量或 `~/.dsh/graph-monitor/amap-key.txt`)

在 `real` 模式下,`fn` 节点还可改用内联 JS `code` 字段(优先于 `handler` 目录)。

### 条件路由(router)

| router | 判定 |
|---|---|
| `always` / `otherwise` | 恒真 / 默认兜底 |
| `nonEmpty` | 输出非空 |
| `containsYes` / `containsNo` | 输出含 yes/是/true · no/否/false |
| `isShort` / `isLong` | 输出 ≤ 40 字符 · > 40 字符 |
| `mentionsCode` | 输出提及 code/代码 |
| `human` / `presales` / `aftersales` | 客服意图:人工 / 售前咨询 / 售后问题 |
| `returnRequest` / `warranty` / `repair` / `order` / `logistics` | 客服细分:退货退款 / 质保保修 / 维修故障 / 下单支付 / 物流 |
| `isError` | 输出以 `ERROR` 开头(用于失败分支) |

### 内联谓词(`source: "code"`)

除命名目录 `router` 外,`gate` / `switch` / `loop` 的谓词(`condition` / `when` / `while`)还支持**内联 JavaScript**。内联代码经 DSH `codeRuntime` 执行(**绝不 `eval`**),运行时可用 `graph.input()` 取上游输入、`graph.stateGet(key)` 读结构化 State,返回值转为布尔;异常 / 空 / 无 codeRuntime 一律按「拦截 / 不命中」处理,不升级为运行失败。

```json
{ "kind": "gate", "condition": { "source": "code", "code": "return (await graph.input()).includes('approved')" } }
```

### workflow JSON 形状

```json
{
  "id": "my-workflow",
  "name": "My workflow",
  "description": "描述",
  "entry": "start-node",
  "nodes": [{ "id": "start-node", "kind": "llm", "label": "开始", "description": "第一步" }],
  "edges": [
    { "src": "START", "dst": "start-node", "label": "start" },
    { "src": "start-node", "dst": "END", "label": "done" }
  ]
}
```

条件边示例:`{ "src": "a", "dst": "b", "conditional": true, "router": "containsYes", "label": "简单" }`

---

## 内置 workflows

| workflow id | 说明 |
|---|---|
| `research-triage` | 分类请求:简单走小模型快路径,复杂走完整 agent 循环 + 工具 |
| `text-pipeline` | 确定性数据管道:normalize → transform → measure → 长短分流 |
| `customer-agent` | 客服:理解 →(已知→直接回答 \| 未知→搜索工具→升级)→ 关闭 |
| `parallel-demo` | 并行分支:fanout → sentiment/entities 并行 → merge → summarize(演示 `parallel`/`merge` 与 `reads`/`writesTo` 结构化 State) |

(可通过设置编辑器自行扩展现有或新建 workflow。)

---

## 控制流原语(可进化工作流引擎)

插件已从「固定 DAG 可视化」演进为**可进化工作流引擎**:除线性 `llm` / `agent` / `tool` / `fn` 节点外,新增六类**控制流原语**,用 `graph` 字段嵌套子工作流(子图节点会各自点亮、各自产生事件):

| 原语 | 关键字段 | 语义 |
|---|---|---|
| `gate` | `condition` / `pass` / `fail` | 谓词真 → 透传到 `pass`;假 → 走 `fail`,无 `fail` 则 run 以 `status:"blocked"` 结束(非失败、非继续) |
| `switch` | `cases[].when/to` / `default` | 按 `cases` 顺序取第一个命中的 `to`,`default` 必填保证「恰好一条」 |
| `subgraph` | `graph` | 组合子图,把已固化套路封装成可复用新原语 |
| `loop` | `while` / `maxIterations` / `graph` | 每轮以子图最终 state 为输入迭代,`while` 为假退出 |
| `retry` | `maxAttempts` / `graph` | 子图输出以 `ERROR` 开头即重跑,成功即透传 |
| `timeout` | `ms` / `graph` | 预算内完成透传,超时输出 `ERROR: timeout after {ms}ms` |

```json
{
  "id": "lp1",
  "kind": "loop",
  "label": "直到收敛",
  "while": { "source": "code", "code": "return (await graph.input()) !== 'done'" },
  "maxIterations": 10,
  "graph": {
    "entry": "step",
    "nodes": [{ "id": "step", "kind": "fn", "code": "return 'done';" }],
    "edges": [{ "src": "START", "dst": "step" }, { "src": "step", "dst": "END" }]
  }
}
```

### 模型驱动的编排(mutation ops)

`llm` / `agent` 节点可设 `emitOps: true`,让模型**直接决定编排**——通过结构化输出产出图编辑指令 `ops` 数组,引擎先整体校验、后原子应用,并以 `graph/mutate` 事件反馈结果(含失败原因):

```json
{ "ops": [
  { "op": "addNode",  "node": { "id": "x", "kind": "fn", "code": "return 'hi';" } },
  { "op": "addEdge",  "edge": { "src": "a", "dst": "x" } },
  { "op": "rewire",   "edge": { "src": "a", "dst": "x" } },
  { "op": "setEntry", "nodeId": "x" },
  { "op": "skip",     "nodeId": "y" }
] }
```

> 完整契约(每个原语的输入 / 输出 / 事件 / 边界情形,以及 ops 载荷 schema 与校验不变量)见 [docs/primitive-contract.md](docs/primitive-contract.md)。

---

## 执行后端(nodeExecutor)

`nodeExecutor` 配置键控制节点执行后端:

| 值 | 行为 |
|---|---|
| `simulated`(默认) | 固定延迟 + 模板输出,纯可视化演示,不调用任何真实能力 |
| `real` | `fn` / `tool` / `agent` / `llm` 节点绑定 DSH 真实能力;能力缺失或执行出错时自动回退到模拟,不会中断运行 |

`real` 模式下的节点映射:

- **`fn`** —— 可写内联 JS `code` 字段,通过 `codeRuntime.run` 执行;运行时可用 `input()` 取上游输入、`stateGet(key)` / `stateSet(key, value)` 读写结构化 State。无 `code` 时回退到目录 `handler`。
- **`tool`** —— 写 `tool`(工具名)+ `arguments`(对象),调用 `tools.execute()`。
- **`agent`** —— 运行 execContext 携带父 Agent 时经 `subagents.start()` 委派给子代理。
- **`llm`** —— 经 `llm.stream()` 调用模型流式推理。

> 提示:`fn`/`tool` 的真实路径无需运行 execContext,从设置页「运行」即可触发;`llm`/`agent` 的真实路径依赖 execContext(通常经对话 `/workflow` 指令或 `graph_monitor_run` 工具触发)。

---

## 配置项

| 配置键 | 默认值 | 说明 |
|---|---|---|
| `nodeDelayMs` | `750` | 普通节点模拟执行延迟(动画节奏) |
| `llmDelayMs` | `950` | llm / agent / tool 节点模拟延迟 |
| `keepRuns` | `20` | 保留的已完成运行记录数(LRU 淘汰) |
| `nodeExecutor` | `simulated` | 执行后端:`simulated`(模拟)\| `real`(绑定 DSH 真实能力,缺失回退模拟) |

---

## HTTP API

插件在 DSH 的 loopback web server 上注册以下接口(与 web app / 桌面壳同源):

| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/graph-monitor/workflows` | workflow 列表 + 编辑器目录 |
| PUT | `/graph-monitor/workflows` | 校验并持久化 workflow |
| POST | `/graph-monitor/validate` | 只校验不保存 |
| POST | `/graph-monitor/workflows/:id/run` | 启动运行(`{input, sessionId}`) |
| GET | `/graph-monitor/state?cursor=N` | 增量实时事件日志(游标轮询) |
| GET | `/graph-monitor/runs/:runId` | 完整运行记录(含每个节点详情) |
| DELETE | `/graph-monitor/workflows/:id` | 删除自定义 workflow |

---

## 测试

引擎单测用 stub Cordis ctx,不依赖真实 DSH 服务,但需要 `@deepseek-ai/*` 依赖可解析(通常在 DSH 的共享层 `~/.dsh/profiles/node_modules` 内运行):

```powershell
# 在 DSH 共享层 node_modules 内运行(以便解析 @deepseek-ai 依赖)
node "$env:USERPROFILE\.dsh\profiles\node_modules\graph-monitor\test\engine.test.mjs"
```

覆盖:内置 workflow 种子、HTTP 列表 / 运行 / 状态游标、自定义 workflow 保存/校验/删除、fn 处理器、持久化、会话事件。

---

## 已知限制

- `nodeExecutor: real` 依赖 DSH 真实能力(codeRuntime / tools / subagents / llm),对应能力缺失或执行出错时该节点回退到模拟。
- `llm` / `agent` 的真实路径依赖运行 execContext(经对话 `/workflow` 指令或 `graph_monitor_run` 工具触发);从设置页「运行」的无 execContext 运行只会走 `fn`(code)/`tool` 的真实路径,`llm`/`agent` 回退模拟。
- `fn` 的内联 `code` 仅在 `real` 模式执行;`simulated` 模式走 `handler` 目录(`code` 被忽略)。
- 插件改动需重启 DSH Desktop;旧会话的工具面在会话创建时固化。
- 运行记录与事件日志在内存中(LRU 上限 keepRuns / 2000 条事件),重启即清空。

---

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:asakumizy/dsh-graph-monitor

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source