dsh-flow-canvas
在 DeepSeek Harness(DSH)对话里给用户一张流程图卡片:点开是全屏画布,用户在画布上选分支、提交,选择结果回到模型,模型据此继续。
它只是一个表现工具,不含业务语义——流程图画什么、决策点是什么,由模型或技能决定。
演示

12 秒演示:多分支总览 → 图片节点 hover 全屏 → Markdown 展开 → 多选 → 自定义输入 → 提交 → 拖拽缩放。 高清版:flow-canvas-demo.mp4(1280×800 / 12fps)。
画布
| 默认态(分叉 + 汇合) | 选中路径高亮 | 多选 + 信息节点 |
|---|---|---|
![]() |
![]() |
![]() |
信息节点与自定义输入
| 图片节点 + Markdown 节点 | 全屏图片查看器 | 自定义输入 |
|---|---|---|
![]() |
![]() |
![]() |
| Markdown 查看器 | 提交后只读态 | 提交载荷(含 custom) |
|---|---|---|
![]() |
![]() |
![]() |
以上示例图与视频由
ui-demo/在干净环境生成:npm run dev+npm run screenshot/node scripts/record-demo.mjs(依赖本机 Chrome 与 ffmpeg)。
长什么样
- 模型调用工具
flow_canvas,对话正文里出现一张卡片(缩略内容,放不下用…):
┌──────────────────────────────────────────────────┐
│ [流程图] 游戏开发方向选择 点击展开 │
│ 沿流程图确认玩法类型、美术风格与发布渠道… │
│ 开始 →「玩法类型」→ Roguelike 原型 → … +4 │
│ 8 个节点 · 3 个决策点 │
└──────────────────────────────────────────────────┘
- 点击卡片 → 全屏画布(SVG 分层布局、决策节点可点、整条路径高亮、右侧选择汇总)。
- 选完点「提交选择」→ 卡片变成「已提交」,一条用户消息进入会话,模型下一轮继续。
两处入口各司其职:
- 对话正文卡片(
conversation.chat.node,自定义节点flow-canvas-card)——主入口,像普通消息一样出现在对话流里,不受「过程组折叠」影响;- 过程组内的工具卡片(
tool.call.toolview)——展开「已完成」或看轨迹时的调用记录。DSH 默认会在轮次结束后把工具调用折叠进「过程组」,所以正文卡片是通过注册 一个
groupTree: 'turn'的自定义对话节点实现的(详见docs/02与src/client/flowNodeDefinition.ts)。 早期版本还有一条输入框上方的横幅作为兜底,已移除(src/client/FlowDock.tsx保留,需要时可恢复注册)。
截图见 docs/screenshots/(e2e-10-chat-card.png 是正文卡片)。
安装
前置:DSH 0.2.1-rc.2,Node ≥ 20。
方式一:从 GitHub 仓库安装(推荐)
# GitHub 简写
dsh plugin --profile desktop add github:ahao0150/dsh-flow-canvas
# 或者用 tarball 链接(与 dsh-purge 同款写法)
dsh plugin --profile desktop add https://github.com/ahao0150/dsh-flow-canvas/archive/refs/heads/main.tar.gz
dsh plugin 会把参数原样转发给 profile 目录里的 pnpm,所以 github:用户/仓库、tarball URL、.tgz 文件、本地目录都能装。
装完后把包名加进 profile 的 bundle 列表,DSH 才会自动加载(~/.dsh/profiles/desktop/package.json):
{
"dsh": {
"profile": {
"bundles": ["…", "dsh-flow-canvas"]
}
}
}
然后重启 DSH。
desktop与web是两套独立 profile,插件不共享;给 Web 装用--profile web。- 本仓库自带
cordis.patch.yml(package.json的dsh.bundle.patch),装进 bundles 后不需要再手改 profile 的 patch 文件。- 仓库里已提交构建产物
lib/,从 GitHub 装完即用,无需本地构建。
方式二:本地目录(开发调试)
git clone https://github.com/ahao0150/dsh-flow-canvas.git
cd dsh-flow-canvas
npm install && node tools/build.mjs
dsh plugin --profile desktop add .
方式三:手工 patch 挂载(本仓库早期的调试方式)
cd D:\work\dsh-game-ui
npm install
node tools/build.mjs # 产出 lib/index.js(host)+ lib/client.js(client bundle)
node tools/mount.mjs # 备份并写入 desktop profile 的 cordis.patch.yml
mount 会备份 cordis.patch.yml 与 package.json(*.bak-flow-canvas-<时间戳>);node tools/unmount.mjs 精确还原并删除 staged 产物。
用「方式一 / 方式二」安装时先执行一次
node tools/unmount.mjs,避免手工 patch 与 bundle 双重加载。
验证
- 重启 DSH 后,输入框
+→「指令」组末尾应有「flow 流程图选择」; - 对话里说「用 flow_canvas 画一张流程图让我选」,正文出现流程图卡片;
- 隔离环境验收(不碰真实 profile):
node tools/verify.mjs。
模型侧用法
工具名:flow_canvas。参数(JSON):
{
"title": "登录方案评审", // 可选,覆盖 flow.title
"flow": { // 必填
"title": "登录方案",
"start": "prd",
"nodes": [
// Markdown 信息节点:节点上只显示简介,点击展开全文(PRD / 方案 / 背景)
{ "id": "prd", "type": "markdown", "label": "PRD 摘要",
"summary": "手机号验证码登录 + 老用户密码登录,二期接企业微信",
"content": "# 背景\n\n当前只有验证码登录…\n\n## 目标\n- 降低登录流失\n- 兼容存量密码用户" },
// 图片信息节点:缩略图无边框,鼠标移入出现全屏按钮
{ "id": "mock", "type": "image", "label": "登录页草图",
"caption": "交互线框 v2",
"url": "https://example.com/mock-login.png" },
// 决策节点:allowCustom 允许用户自己填一条
{ "id": "style", "type": "decision", "label": "登录方式",
"detail": "影响后续改造量",
"allowCustom": true, "customHint": "也可以写自己的登录方式",
"options": [
{ "id": "sms", "label": "验证码", "description": "最快、无需记密码" },
{ "id": "pwd", "label": "密码", "description": "存量用户熟悉" }
] },
// 多选决策节点:选中多个后,每个分支的下游决策点都要各自选完
{ "id": "scope", "type": "decision", "label": "灰度范围", "mode": "multi",
"options": [
{ "id": "web", "label": "Web", "description": "先发 Web" },
{ "id": "app", "label": "App", "description": "App 同步跟进" }
] },
{ "id": "end", "type": "end", "label": "进入开发" }
],
"edges": [
{ "from": "prd", "to": "mock" },
{ "from": "mock", "to": "style" },
{ "from": "style", "to": "scope", "option": "sms" },
{ "from": "style", "to": "scope", "option": "pwd" },
{ "from": "scope", "to": "end", "option": "web" },
{ "from": "scope", "to": "end", "option": "app" }
]
},
"multi": false, // 可选,决策点默认单选(节点 mode 可覆盖)
"allowComment": true // 可选,允许用户补充文字
}
节点类型
| type | 作用 | 关键字段 |
|---|---|---|
start / step / end |
流程节点(无选择) | label、detail |
decision |
决策点 | options[]、mode(single/multi)、allowCustom、customHint |
image |
图片信息节点(决策前置/输出) | url(https/http 或 data:image/...)、caption |
markdown |
文本信息节点(PRD/说明) | content(Markdown 全文)、summary(节点上展示的简介) |
信息节点(image / markdown)不参与选择,沿普通边连接,可以放在决策之前当「前置条件」,也可以放在决策之后当「输出」。 图片只接受 URL:本地文件路径浏览器读不到,需要先转成可访问的 URL 或
data:URI。
工具立即返回:
{ "flowId": "fc_xxx", "status": "pending" }
用户提交后,会话里出现一条消息(多选、自定义输入都会体现):
[flow_canvas 提交] flowId=fc_xxx
提交时间: 2026-10-23T12:41:05.000Z
- style: sms(验证码)+ 自定义「也支持企业微信扫码」(节点:登录方式)
- scope: web(Web), app(App)(节点:灰度范围)
补充说明: 先小流量
(用户已在画布上完成选择,请据此继续。)
模型按这条消息继续即可。技能里调用同一个工具,唯一要做的就是生成 flow 数据。
目录结构
src/
├─ shared/
│ ├─ flow.ts # host/client 共用数据契约(FlowSpec / 选择 / 结果)
│ └─ path.ts # 校验、归一化、路径推导、选择摘要(纯函数)
├─ host/
│ ├─ index.ts # Cordis 宿主插件入口(注册工具)
│ └─ flow-tool.ts # flow_canvas 工具定义 + presentationMeta
├─ client/
│ ├─ index.ts # 客户端插件入口(注册 toolview + 注入提交通道)
│ └─ FlowCard.tsx # 对话内卡片
└─ ui/ # 全屏画布(React,可独立预览)
├─ FlowCanvas.tsx
└─ flow-canvas/ # 图解析、布局、选择状态机、SVG 渲染、样式
tools/
├─ build.mjs # esbuild 打包:host ESM + client ModuleLoader bundle
├─ mount.mjs / unmount.mjs
ui-demo/ # 画布组件独立 Vite 预览工程
test/ # 工具与路径纯逻辑单测 + 流程样本
docs/ # 方案、客户端手册、画布说明、工具与回传方案、截图
.verify/ # 隔离实例验证脚本(临时件)
开发与验证
npm run typecheck # tsc --noEmit
node tools/build.mjs # 构建
node --test # 工具/路径单测(Node 24 自带 test runner)
cd ui-demo; npm install; npm run dev # 画布独立预览 http://127.0.0.1:5178/
node .verify/check.mjs <token-url> # 隔离实例:确认 bundle 被宿主组合进客户端图
node .verify/e2e.mjs # 隔离实例:卡片→画布→提交 全链路
文档:
docs/01-需求与方案.md:需求、DSH 插件机制调研、方案对比docs/02-客户端插件开发手册.md:客户端 bundle 格式、平台模块表、挂载与排错docs/03-画布UI说明.md:画布组件 API、状态机、主题、集成docs/04-工具与回传方案.md:工具 schema、presentationMeta、回传通道结论
已知限制
- 非阻塞:工具立即返回
pending,用户提交是新一轮用户消息,不是同一轮内返回的工具结果。想做「模型挂起等结果」需要自定义 remote 或事件时序 POC(见 docs/04 §5)。 - 提交状态只在本浏览器:卡片「已提交」是本地状态,刷新页面后回放会恢复成「待选择」(可以再次提交,服务端没有幂等保护;客户端已做连点防护)。
- 画布布局:自研最长路径分层 + 重心排序,不是 dagre;回边做了简化绕行。
- 流程规模:建议 ≤ 30 个节点;更大规模布局会变密。
- 模型兼容性:提交结果通过一条用户消息进入会话,模型需要按
[flow_canvas 提交]标记解析;技能里可以在系统提示中约定这一点。
后续(技能复用)
这个插件被设计成「表现层」,技能只需要:
- 在自己的
SKILL.md里说明何时调用flow_canvas; - 生成
flowJSON(可以来自技能内的模板文件); - 收到
[flow_canvas 提交]消息后按选择分支继续。
skills/flow-canvas-choice/SKILL.md 是一份可直接改用的技能模板。









No comments yet. Be the first to write one.