可视化工作流引擎的数据模型怎么设计?流程复用时连线怎么复制?
简化版
按「定义」和「运行」分开建表。定义三张:流程表说这是一条什么流程(编码、状态、是否可复用、归属人),节点表说每一步做什么,连线表说步骤之间怎么流转(起点、终点、条件、优先级)。运行两张:运行记录表记这一次跑出了什么,步骤日志表记每个节点的输入输出。节点类型很多时,用一张节点表加「类型 + JSON 配置」统一建模,执行引擎按类型分发。复制流程时,节点先逐个插入、记下「旧节点 ID → 新节点 ID」的映射,连线再按映射把起点和终点换成新 ID,否则副本的连线还指向原流程的节点。
详细版
定义(配置期) 运行(执行期)
workflow 一条流程是什么 workflow_run 这一次跑出了什么
workflow_node 每一步做什么 ──▶ workflow_run_step 每一步的输入、输出、耗时
workflow_edge 步骤之间怎么流转
| 表 | 关键字段 | 设计要点 |
|---|---|---|
| 流程 | 编码(唯一)、状态、是否启用、是否可复用、来源流程、归属人 | 状态决定能不能被使用和复用 |
| 节点 | 流程、编码(流程内唯一)、类型、通用内容字段、JSON 配置、输入变量、输出变量、画布坐标、启用 | 所有类型共用一张表 |
| 连线 | 流程、起点、终点、条件表达式、排序 | 同一节点多条出边按排序依次判断 |
| 运行记录 | 流程、发起人、输入、最终上下文、状态、耗时、错误 | 失败也要落库 |
| 步骤日志 | 运行、节点、节点名和类型快照、执行前上下文、输出、状态、耗时、顺序号 | 节点后来被改,历史仍可读 |
复制流程三步:复制流程主记录(新编码、回到草稿、记来源)→ 逐个复制节点并记 ID 映射 → 按映射复制连线,映射不到的连线跳过。
完整版教学
一、为什么定义和运行要分开
一条流程会被运行成百上千次,定义只有一份。如果把运行结果写回定义表,比如在节点上存「上次输出」,同一流程两个人同时运行就会互相覆盖,也查不了历史。
定义:1 条流程 × 7 个节点 × 7 条连线 (改的时候才变)
运行:每天 500 次 × 每次 7 条步骤日志 = 3500 行 (每跑一次就追加)
两者的读写特征完全不同:定义表读多写少、要能随时编辑;运行表只追加、按时间和运行 ID 查询、量大需要清理策略。分开以后,编辑流程不影响正在查看的历史,历史数据也可以单独归档。
二、节点多态:一张表还是一类型一张表
节点类型有开始、知识检索、大模型、工具、条件、HTTP、结束等,每种要配的参数完全不同。两种建模方式:
| 方式 | 做法 | 优点 | 代价 |
|---|---|---|---|
| 一张表 + JSON 配置 | 公共字段做列,类型差异放 config_json | 加新类型不改表结构;连线、日志只需引用一张表 | 配置字段没有数据库约束,要在保存时校验 |
| 一类型一张表 | 每种节点一张子表 | 字段强类型 | 连线要引用多张表,加类型要建表改代码 |
低代码编排场景下新节点类型会不断增加,多数选第一种。常见的做法是把几个高频字段提成列:比如一个通用的「内容」字段,大模型节点存 Prompt、条件节点存判断表达式、HTTP 节点存请求体;输入变量、输出变量也是列,因为执行引擎每个节点都要读它们。其余参数(模型 ID、温度、知识库 ID、HTTP 方法、失败策略)放 JSON。
记忆钩子:执行引擎每一步都要读的字段做成列,只有某一类节点才用的参数放 JSON。
三、连线表要表达的三件事
连线不只是「从 A 到 B」,还要回答三个问题:
走不走: condition_expression 为空表示无条件通过,否则按上下文求值
先判谁: sort 同一节点有多条出边时,按 sort 从小到大依次判断
显示什么: label 画布上连线旁的文字,如「需要记录工单」
一个条件节点后面接两条出边,一条条件是 needCreateTicket == true、排序 5,另一条是 needCreateTicket == false、排序 7。执行引擎按排序依次判断,取第一条为真的边。如果两条的条件写得有交集,排序就决定了谁优先,所以排序不是摆设。
四、保存时就要挡住的非法结构
图结构的错误越早拦越好,保存连线时至少校验:
| 校验 | 防止什么 |
|---|---|
| 起点、终点都存在 | 连线指向一个不存在的节点 |
| 起点和终点不能相同 | 节点自己连自己 |
| 起终点都属于当前流程 | 跨流程连线,复制和删除时出错 |
| 节点编码在流程内唯一(联合唯一索引) | 两个节点同名同编码,日志和配置对不上 |
| 删节点时先删它的连线 | 节点没了、连线还指向它 |
有些问题在保存单条连线时发现不了,比如整条流程没有连线、连线形成了环。前者可以在「发布」时统一检查,节点数或连线数为 0 就不允许发布;后者在执行时用最大步数兜底,见「工作流执行引擎怎么按连线执行?条件分支和死循环怎么处理?」。
五、复制流程:ID 映射是关键
复用一条已发布的流程,本质是把「一张图」深拷贝一份。节点和连线都有自增主键,连线通过主键引用节点,所以不能各自照搬:
原流程节点 ID: 1 2 3 4 5 6 7
插入副本后: 8 9 10 11 12 13 14
映射表: {1→8, 2→9, 3→10, 4→11, 5→12, 6→13, 7→14}
原连线 (6 → 5) 按映射改成 (13 → 12)
原连线 (6 → 4) 按映射改成 (13 → 11)
顺序必须是先复制节点、拿到新 ID,再复制连线。连线的起点或终点在映射里找不到(比如源数据里有脏连线),就跳过这条,不能插入一条指向原流程节点的连线。副本还要处理几个字段:编码加后缀保证全局唯一,状态回到草稿、默认不可复用,记下来源流程 ID 便于溯源,归属人改成当前用户。
六、状态与权限决定「谁能用、谁能改」
流程表的状态字段不只是展示用,它控制着流程的生命周期:
| 状态 | 能做什么 |
|---|---|
| 草稿 | 归属人可以编辑节点连线,可以发布 |
| 已发布 | 可以运行;标记为可复用时,其他人也能看到并复制 |
| 已停用 | 不再出现在运行入口,也不能被复用 |
多人使用时还要分清「看得到」和「改得了」:普通用户能看到自己的流程和别人已发布可复用的流程,但别人的流程只能复用、不能编辑和删除;要改就先复制成自己的草稿。运行记录和步骤日志同样要按发起人隔离,普通用户只看得到自己发起的运行。
七、步骤日志为什么要存快照
步骤日志引用节点 ID 就能关联到节点,为什么还要存节点名称和类型?因为节点会被修改甚至删除。三个月前的一次运行,节点那时叫「生成回复草稿」,现在被改成了「生成营销文案」,如果只存 ID,历史日志显示的就是现在的名字,排查时对不上。执行时把名称、类型拍成快照写进日志,节点后续怎么改都不影响历史记录。同理,执行前的完整上下文也存进日志,才能复现「这一步当时看到了什么」。
八、常见误区与追问
- 误区:运行结果写回节点表最方便。 同一流程并发运行会互相覆盖,也查不了历史,运行数据要单独建表。
- 误区:每种节点一张表更规范。 节点类型持续增加时,连线和日志要引用多张表,低代码场景多用一张表加 JSON 配置。
- 误区:复制流程就是把节点和连线各复制一遍。 连线引用的是节点主键,必须按「旧 ID → 新 ID」映射改写,否则副本连到原流程。
- 误区:连线的排序字段可有可无。 同一节点多条出边时按排序依次判断,条件有交集时排序决定走哪条。
- 误区:步骤日志只存节点 ID 就够了。 节点会被改名或删除,日志要存名称和类型快照。
- 追问:发布时应该校验什么? 至少校验节点和连线都不为空;更严格的还可以检查有没有开始和结束节点、有没有到不了的节点。
- 追问:节点编码为什么是「流程内唯一」而不是全局唯一? 复制流程时节点编码原样照搬,全局唯一会让副本插不进去;流程内唯一既能区分节点,又不妨碍复制。
九、加强记忆
工作流数据模型记「三定义两运行」:流程表管身份和生命周期(编码唯一、草稿/已发布/已停用、可复用、来源),节点表用一张表加类型和 JSON 配置统一建模,每步都要读的输入输出变量做成列,连线表管走不走(条件)、先判谁(排序)、显示什么(标签)。运行记录和步骤日志只追加,失败也落库,日志存节点名称类型快照和执行前上下文。保存时挡住悬空、自连、跨流程的连线,发布时挡住空结构。复制流程先插节点记「旧 ID → 新 ID」映射,再按映射改写连线,映射不到就跳过,副本回到草稿、记来源。
项目实战落地
项目里怎么做的
《AI企业流程编排系统》用五张表撑起「配置 → 执行 → 观测 → 复用」:workflow 说是什么,workflow_node 说每步做什么,workflow_edge 说怎么流转,workflow_run 和 workflow_run_step 说跑出了什么。
- 流程表:
code唯一索引;status取草稿、已发布、已停用;另有enabled、reusable、source_workflow_id、owner_user_id,节点数和连线数在增删节点连线时回写; - 节点表:开始、知识检索、大模型、工具、条件、HTTP、结束七种类型共用一张表,差异只在
node_type和config_json;prompt_content按类型分别存 Prompt、判断表达式、请求体;(workflow_id, code)联合唯一; - 连线表:
condition_expression和sort;保存时校验起终点存在、不能相同、必须属于同一流程;删节点先删相关连线再删节点; - 发布与复用:发布前统计节点和连线,任一为 0 不允许发布;只有「已发布且可复用」的流程能复制,副本名称加「复用副本」、编码加
_COPY_和时间戳、回到草稿,节点逐条复制并记nodeIdMap,连线按映射换成新节点 ID,映射不到的跳过; - 步骤日志:
node_name、node_type是执行时的快照,input_json是执行本节点前的完整上下文。
示例流程「客户问题智能回复流程」是 7 个节点、7 条连线,每条连线都带条件,比如开始节点到知识检索那条是 customerQuestion != null。
为什么这样取舍
- 一张节点表:执行引擎只需按
node_type分发到不同执行方法,节点模型统一。 - 复用必须连节点带连线:主表只有名称、场景、状态,真正的结构在节点和连线里,只复制主表得到的是空壳;复制后改副本的节点、Prompt 和连线条件都不影响原流程。
面试官还会追问
- 大模型节点每次执行都新建一个 ChatClient 吗?管理员在后台改了 API Key,要重启才生效吗?
- 步骤日志表里没有用户字段,普通用户「只能看自己的节点日志」是怎么实现的?
学完《AI企业流程编排系统》,上面这些追问你都会迎刃而解。