Scope
本文是“如何新增一个 workflow node”的实施指南,面向当前仓库,不讲抽象原则,直接对应代码入口。
适用两种场景:
- 新增内建节点
- 新增插件节点
不覆盖复杂 UI 美化,只覆盖最小可运行链路。
Choose The Path First
新增 workflow node 先选实现路径:
-
内建节点
适合:- 需要深度接入执行引擎
- 需要访问 server 内部能力
- 需要长期稳定维护
-
服务端插件节点
适合:- 希望通过插件独立分发
- 执行逻辑主要在 server
-
客户端插件节点
适合:- 依赖 Electron / 本地客户端能力
- 必须跑在用户机器上
Minimal End-to-End Chain
一个新 node 真正可用,至少要打通这条链:
NodeTypeDefinition
-> 前端 registry 可见
-> 节点选择器能创建
-> 属性面板能编辑
-> 节点渲染正常
-> 执行时 dispatchNode() 能识别
-> 结果能回写 output / execution log
如果少任何一环,会出现:
- 能看到不能创建
- 能创建不能编辑
- 能编辑不能执行
- 能执行但输出结构不稳定
Step 1: Define The Node Type
共享定义类型在 packages/shared/src/types/workflow.ts。
你不一定要改 shared 类型本身,但要理解 NodeTypeDefinition 可声明的关键字段:
typelabelcategoryicondescriptionpropertieshandlesallowInputFieldsallowedInputFieldTypesoutputscustomViewmanualCreatesingletondebuggablecompound
普通节点最少只需要:
typelabelcategoryicondescriptionproperties
Step 2: Register Frontend Definition
内建节点 definition 放在:
packages/web/src/lib/workflow-nodes/definitions/*.ts
注册入口:
packages/web/src/lib/workflow-nodes/registry.ts
做法:
- 把 node 加到合适的 definitions 文件
例如:- 流程类放
flow-control.ts - 展示类放
display.ts - AI 类放
ai.ts
- 流程类放
- 确保该 definition 被
registry.ts的allNodeDefinitions收进来
最小例子:
{
type: 'my_node',
label: 'nodes.my_node.label',
category: 'nodes.categories.utilities',
icon: 'Wrench',
description: 'nodes.my_node.description',
properties: [
{ key: 'text', label: 'nodes.my_node.props.text', type: 'text', required: true },
],
outputs: [{ key: 'result', type: 'string' }],
}
Step 3: Add i18n Strings
definition 使用的是 i18n key,不是直接文本。
至少补到:
packages/web/src/locales/zh/workflows.jsonpackages/web/src/locales/en/workflows.json
至少包含:
nodes.my_node.labelnodes.my_node.descriptionnodes.my_node.props.*
如果少这一步:
- 节点仍然可能工作
- 但 UI 会出现裸 key,研究和调试成本会上升
Step 4: Verify Creation Path
节点创建入口主要有两条:
- 简单 store 版本:
packages/web/src/stores/workflow-editor/edit.ts - 实际画布创建主路径:
packages/web/src/components/workflow/use-workflow-node-operations.ts
当前真正应关注的是后者:
WorkflowNodeSelectDialog
-> use-workflow-node-operations.ts
-> createNodesForDefinition()
-> shared createWorkflowNodesForDefinition()
普通节点:
createWorkflowNodesForDefinition()只生成一个 root node
复合节点:
- 需要 definition 里写
compound - factory 会自动生成 child nodes / generated edges
相关约束:
manualCreate === false的节点不会在选择器里出现singleton === true的节点一个 workflow 只能有一个
Step 5: Check Properties Panel Compatibility
大部分节点不需要手写属性面板。
只要 properties 使用现有字段类型,packages/web/src/components/workflow/workflow-properties-panel.tsx 就能直接生成表单。
现成字段类型包括:
texttextareanumberselectcheckboxcodeconditionsarrayoutput_fieldsagentsqliteknowledge-base
只有这几种情况才需要额外代码:
- 输出字段需要根据属性动态派生
- 例:
set_variable
- 例:
- 有特殊预设或执行结果回填
- 需要 custom view
Step 6: Decide Whether Custom View Is Needed
渲染主入口:
packages/web/src/components/workflow/workflow-node.tsx
默认情况下:
- 节点会按通用壳子渲染
- definition 决定图标、标题、handles、日志面板等
只有这些情况才需要 custom view:
- 节点体需要特殊布局
- 有复杂可视化内容
- 需要与普通字段表单不同的交互
如果要做 custom view:
- 可使用
customView - 插件节点还能走
plugin-workflow-custom-view
Step 7: Implement Server Execution
内建节点要在:
packages/server/src/services/execution-manager.ts
里补 dispatchNode() 分支。
最常见模式:
case 'my_node':
return executeMyNode(resolvedData)
建议做法:
- 先把纯逻辑放到单独 helper 文件
参考:execution-node-helpers.tsexecution-agent-runner.ts
dispatchNode()只做分派- 返回值尽量是对象,字段名与
outputs对齐
如果少这一步:
- 前端可见
- 运行时会
Unsupported node type
Step 8: Define Output Contract Clearly
新增 node 时要同时想清楚:
definition.outputs怎么写- server 返回值是什么结构
最佳实践:
outputs与执行结果 key 保持一致- 输出尽量稳定,不要一会儿返回标量、一会儿返回对象
例如:
- 好:
outputs: [{ key: 'result', type: 'string' }],执行返回{ result: '...' } - 差:definition 声明
result,执行时有时返回字符串、有时返回数组
Step 9: Support Debugging If Needed
默认 debug 路径走:
workflow:debug-nodeuse-workflow-editor-execution.tsExecutionManager.debugNode()
如果你的 node:
- 不适合单步执行
- 依赖复合结构
- 只是内部结构节点
可以在 definition 里设置:
debuggable: false
典型例子:
loop_body
Step 10: For Plugin Nodes
如果是插件节点,不走内建 definition 文件,而是走 plugin API:
- 前端拉取:
packages/web/src/components/workflow/workflow-editor.tsxpluginApi.getWorkflowNodes(plugin.id)registerPluginNodeDefinitions(allNodes)
- 服务端接口:
packages/server/src/routes/plugin.tsGET /:pluginId/workflow-nodes
- 服务端实现:
packages/server/src/services/plugin.tsgetWorkflowNodes()canExecuteWorkflowNode()getWorkflowNodeDefinitionByType()
服务端插件节点要么:
- 提供 server handler
- 要么声明需要 client execution
Step 11: For Client Plugin Nodes
如果节点必须在客户端执行,要满足:
- node definition 已注册
node.data.pluginType为client或bothnode.data.pluginId可解析
执行链:
dispatchNode()
-> executeClientNode()
-> ClientNodeManager.request()
-> workflow:client-node
-> web use-workflow-editor-execution.ts
-> electronAPI.clientPlugins.executeNode()
-> client_node_response
因此这类节点不适合:
- 无客户端连接
- 纯后台调度
- 服务器独立运行的场景
Step 12: If It Is A Composite Node
如果新节点不是普通节点,而是 composite node:
- 在 definition 中声明
compound - 明确:
- root role
- children
- generated edges
- 哪个 child 是
scopeBoundary
- 验证:
- 创建
- 拖拽
- 删除
- 插入新节点
- 边连接
- 执行 scope
不要只停在“能创建出来”。
Recommended Minimal Checklist
新增一个内建普通 node,至少检查:
- definition 已注册,节点选择器可见。
- 中英文文案完整。
- 能创建、保存、重新加载。
- 属性面板能编辑并落到
node.data。 dispatchNode()有分支。- 执行结果与
outputs一致。 - debug node 正常。
- execution log 能展示结果。
Recommended File Touch Map
内建普通节点最小改动通常落在这些文件:
packages/web/src/lib/workflow-nodes/definitions/*.tspackages/web/src/locales/zh/workflows.jsonpackages/web/src/locales/en/workflows.jsonpackages/server/src/services/execution-manager.ts- 可选:新的 server helper 文件
插件节点最小改动通常落在:
- 插件
workflow.js - 插件 server handler
- 必要时插件 custom view / client bridge
Files To Read Next
packages/shared/src/types/workflow.ts- definition 字段能力边界
packages/shared/src/types/workflow-node-factory.ts- composite node 创建逻辑
packages/web/src/lib/workflow-nodes/registry.ts- definition 注册入口
packages/web/src/components/workflow/use-workflow-node-operations.ts- 节点创建真实入口
packages/web/src/components/workflow/workflow-node.tsx- 节点渲染入口
packages/web/src/components/workflow/workflow-properties-panel.tsx- 通用属性面板
packages/server/src/services/execution-manager.ts- 执行分派入口
packages/server/src/services/plugin.ts- 插件节点接入
Common Failure Modes
- 只加 definition,忘了 server dispatch
- 结果:
Unsupported node type
- 结果:
- 只加 server 执行,忘了 definition
- 结果:编辑器不可见
- 输出结构与
outputs不一致- 结果:变量引用和执行预览混乱
- 复合节点没处理 generated edges / scope
- 结果:创建、删除或执行异常
- 客户端节点当后台节点用
- 结果:无连接时直接失败