工作流画布(workflow-canvas)
把「一句话的需求」变成看得见、改得动、跑得起来的节点工作流。形态对标 OpenClaw 的 Canvas:节点=一个动作,边=数据流,mapping=参数映射;人在环上,随时能改结构,也随时能改中间值。
0. 三条核心主张
- 画出来才算想清楚 —— 先出图,再动手。图上缺的边、缺的参数,一眼就能看出来。
- 人始终能改 —— 改结构(拖拽/增删/连线)和改中间值(执行时暂停后覆盖字段)是两条独立的入口,都给到用户。
- 确定性自己做,判断交出去 —— 取数/计算/落盘由引擎跑;需要推理的
agent.*节点转成待办清单交给模型;不可逆动作前放human.gate真停顿。
1. 什么时候用 / 不用
用:任务 ≥3 步、要反复跑、步骤之间有依赖、需要人确认、或者用户想「看得见流程长什么样」。
不用:一次性的单步操作(读个文件、算个数、写封邮件)直接做掉,不要套流程。用户明确说「只要结果不要图」时也别硬画。
2. 五阶段工作流
Stage 1 · 需求 → workflow.json
从用户描述里抽出这些信息,缺的用合理默认值补上并明确告知假设:
| 要抽出来的 | 例子 |
|---|---|
| 触发时机 | 每个工作日 09:00 / 手动 / 收到邮件时 / 溢价超过 3% 时 |
| 数据从哪来 | HTTP 接口 / 本地文件 / 连接器 / 人工填 |
| 中间算什么 | 溢价率、涨跌幅、汇总统计、筛选 |
| 哪些环节要判断 | 分档、异常识别、趋势研判 → agent.* 或 logic.condition |
| 产出什么 | 自包含 HTML 报告 / 表格 / Markdown / 文件 |
| 要不要通知、通知到哪 | 邮件 / IM / 桌面提醒 |
| 哪一步不可逆、要先问人 | 外发、发布、下单 → human.gate |
| 要不要挂定时 | 要 → schedule.register |
节点类型从 references/node-catalog.md 选(只选目录里有的类型,写错类型校验器会报 error)。
写 JSON 时遵守 references/workflow-schema.md:id 全流程唯一、边要指明 fromPort(分支节点是 true/false/approved/rejected/case/each)、必填参数不能空。
把业务口径写成 util.note 便签钉在图上(阈值、分母用哪天的净值、单位是元还是分)。这一步很关键:口径散在对话里会丢,钉在图上不会。
Stage 2 · 出画布
python scripts/build_canvas.py 我的流程.workflow.json 我的流程.canvas.html
产出单文件、零依赖、离线可用的 HTML(约 120 KB,模板 + 数据内联)。用 present_files 交给用户,双击即可在浏览器里编辑。
Stage 3 · 人工改(这一步由用户在画布上完成)
用户能做的事:拖节点改位置、从节点库加节点、右键删/复制/停用、从右侧圆点拖到另一个节点左侧圆点连线、右侧面板改参数与映射、框选批量操作、自动布局、模拟执行、导出 JSON + 变更报告。
编辑器会自动生成一份变更报告(新增/删除/改名/改参数/改连线),用户导出时一并给出。看到变更报告就知道用户动过什么,不用自己 diff。
Stage 4 · 回读与校验
用户把导出的 JSON(+ 变更报告)交回来,或说「我改完了,按新流程跑」。这时:
- 先读变更报告,理解意图;报告缺失就读 JSON 与上一版对比。
- 跑校验:
bash python scripts/validate_workflow.py 我的流程.workflow.json - 有 error 必须拦住并解释(缺必填参数、有环、分支只接了一条、类型写错…),不要让流程带病跑。warn 提示但不阻断。
- 如果用户的改动引入结构问题(比如删掉了必需的上游),说清楚后果再问,不要默默按坏图执行。
Stage 5 · 落地执行 / 挂定时
# 先看计划,不动任何东西
python scripts/run_workflow.py 我的流程.workflow.json --dry-run
# 真跑
python scripts/run_workflow.py 我的流程.workflow.json
跑起来后有三种「停」:
- AI 节点 → 引擎把任务写进
runs/<runId>/agent_tasks.md,流程继续往下或停在收尾。这时由你(模型)读清单、执行 AI 节点、把结果写进runs/<runId>/agent_results.json,再--resume续跑。 - 人工关卡 /
gate: true→ 引擎真停,退出。等人处理后续跑(详见下方「暂停与续跑」)。 - 失败 → 默认中断;节点
onError: continue则跳过继续。
要挂定时:启用 schedule.register 节点并执行,它会产出建议参数;实际注册用 automation_update,把节点里写的 rrule / prompt / cwds 照搬过去,不要自己另编排程。
3. 暂停与续跑(人改中间值的落地方式)
# 停在指定节点(调试用)
python scripts/run_workflow.py wf.json --pause-at gen_report
# 人工关卡:看数据 → 续跑
python scripts/run_workflow.py wf.json --resume runs/20261013-090000 --approve human_review
python scripts/run_workflow.py wf.json --resume runs/20261013-090000 --reject human_review
# 顺手改中间值:改完下游就吃到新值(人工优先级最高,覆盖节点自己算出来的结果)
python scripts/run_workflow.py wf.json --resume runs/xxx --approve human_review \
--set premium_pct=1.32 --set decide.decision.value=加倍买入
--set 字段=值:扁平字段改全局;节点id.字段.子字段直接写进那个节点的输出。- 人工改过的值优先级最高:即使某节点会重算出这个字段,也以人改的为准(输出会递归同步,不会出现同一字段两个值)。
--reject会让该关卡的下游整条支路跳过(不是只跳一个节点)。- 续跑会重跑暂停节点一次以取最新上游数据(AI 回填的结果在暂停后才到,旧快照会漏掉)。
4. 受限节点:默认不执行
source.shell(跑命令)、transform.code(跑任意 Python)、notify.*(真实外发)、source.connector(调连接器)默认只出草稿不执行,需要显式开关:
python scripts/run_workflow.py wf.json --allow-shell --allow-code --allow-send
别顺手全开。 先按默认跑一遍,确认要做的事,再决定开哪个开关;要开之前跟用户说一句。
5. 编排铁律(写流程时照做)
- 口径上墙:业务阈值、分母口径、单位换算写进
util.note,别只留在聊天里。 - 不可逆动作前必有
human.gate:外发、发布、下单、删数据。 - AI 节点要写清三件事:指令(要它做什么)、视角/专家(用谁的框架)、输出格式(自由文本还是 JSON)。指令含糊 → 产出含糊。
- 一个节点一件事:
transform.calc只算数,agent.analyze只分析。想在一段代码里干完所有事,就拆不开、也改不动了。 - 节点数控制在 20 以内,超了用
util.subflow收成子流程。 - 产出统一自包含 HTML:内联样式、内联 SVG、不引 CDN,双击就能看、能直接发给别人。
- 涉及投资/医疗/法律内容的报告,必须带免责声明(
output.html的includeDisclaimer保持开启)。 - 代码里不写密钥:只写引用名(
env:XXX),真值由用户自己放环境变量。
6. 本环境的已知适配(别踩)
- 取数用 Python
urllib+ 关闭证书校验,不要先试curl(本沙箱常拦 curl);HTTP 节点已内置该逻辑。见references/patterns.md末尾。 - 沙箱对
npx大包安装、python -m venv不稳定,本 skill 全程零依赖,不要引入第三方库。 - 出 HTML/可视化产物后要目视校验:用真实渲染器截图看一眼,别只看代码。
- 表达式里写
vars.code这类点号取值时,引擎会自动转成下标访问 —— 你自己写脚本时注意:eval里的dict.attr取不到值且会静默返回 None。
7. 目录与产物
workflow-canvas/
├── SKILL.md
├── assets/
│ ├── node_catalog.json ← 节点定义唯一真相源(编辑器/校验器/文档共用)
│ ├── canvas_template.html ← 画布模板(注入 workflow.json 后成单文件编辑器)
│ └── node-catalog.md ← 由 JSON 自动派生,不要手改
├── references/
│ ├── node-catalog.md ← 全部节点类型 + 参数 + 何时用(选节点时读这个)
│ ├── workflow-schema.md ← workflow.json 结构规范(写 JSON 时读这个)
│ ├── patterns.md ← 常见流程模式库(设计流程时读这个)
│ └── canvas-guide.md ← 画布操作手册与快捷键(教用户用时读这个)
├── templates/
│ ├── workflow.daily-fund-premium.json ← 旗舰样板:每日基金溢价分析流水线
│ └── workflow.minimal.json ← 最小骨架
└── scripts/
├── build_canvas.py ← workflow.json → 单文件画布 HTML
├── validate_workflow.py ← 结构校验(错误会阻断执行)
└── run_workflow.py ← 执行引擎(dry-run / 暂停 / 续跑 / 改中间值)
运行期产物落在工作流文件同级的 runs/<runId>/:state.json(逐节点状态与输出)、run.log、agent_tasks.md、agent_results.json、drafts/(未外发的通知草稿)、产物文件写到 output.* 节点指定的路径。
8. 上手最快的路径
不清楚怎么做时,读这两份现成的:
templates/workflow.daily-fund-premium.json—— 旗舰样板,19 个可执行节点覆盖了触发器 / 并行扇出 / 取数 / 合并 / 计算 / 汇总 / AI 判定 / AI 分析 / AI 生成 / 人工关卡 / 双分支 / 落盘 / 通知 / 注册定时 / 便签钉口径。要什么照着改。templates/workflow.minimal.json—— 5 节点的最小骨架,用来验证链路通不通。
9. 故障排查
| 现象 | 原因与处理 |
|---|---|
| 校验报「必填参数未填」 | 参数键名要对上 catalog 里的 key,不是 label |
| 校验报「存在环」 | 边连反了,或回路想表达循环 → 用 logic.loop 不要用回边 |
| 校验报「没有出口 true/false」 | logic.condition 两条分支都要接上才能跑 |
| 画布打开是白页/提示未注入 | 打开的是模板,不是构建产物 → 跑 build_canvas.py |
| 表达式算不出来、静默变 null | 上游字段名对不上(看 --dry-run 的字段名),或用了 dict.attr 写法 |
| 执行到某节点就失败 | 读 runs/<runId>/state.json 里该节点的 error 字段,run.log 有完整流水 |
| 流程一直重复同一段 | 大概率是把回边当循环用了;真实循环用 logic.loop,并设 maxIterations 保险丝 |