跳到主要内容

Activepieces 工作流构建器 — 架构与原理

30 秒导读: Activepieces 是一个开源的 Zapier 替代品。它的"工作流构建器"让你把"当 X 发生 → 做 Y → 再做 Z"拖成一张流程图。本组文档讲清楚这张图在代码里到底是什么(一棵递归树)、怎么被改(一组纯函数操作)、怎么同时在浏览器和服务器上保持一致(同一个 reducer 跑两遍),以及怎么被执行和被 AI 搭建

1. 这是什么(零基础也能懂)

一句话定义: 工作流构建器是 Activepieces 里那块画布 + 它背后的数据结构 + 一套"怎么改这张图"的规则。

解决什么问题 / 给谁用: 假设你想自动化一件事——"每当 Gmail 收到带附件的邮件,就把附件存到 Google Drive,如果发件人是老板就再发条 Slack"。你不想写代码,只想拖几个方块、连几条线。构建器就是那块让你拖方块的地方;但更有意思的是方块拖完之后,这件事在系统里被存成什么、怎么被可靠地改、怎么被一字不差地跑起来。

一条工作流由两类东西组成:

概念白话代码里的类型
Trigger(触发器)"什么时候开始" —— 一条流有且仅有一个FlowTrigger(EMPTYPIECE_TRIGGER)
Action(动作)"开始之后做什么" —— 一串,可嵌套(循环 / 分支)FlowAction(PIECE / CODE / LOOP_ON_ITEMS / ROUTER)
Piece(块)第三方集成包(Gmail、Slack…),提供具体的 trigger 和 action400+ 个,在 packages/pieces/

用起来什么样: 在 UI 上你拖方块;在数据里,这条流被存成一个 FlowVersion,它的 trigger 字段指向第一个节点,每个节点用 nextAction 指向下一个——就是一条链表,循环和分支让它变成

一句话直觉/类比: 把工作流想成一份用 JSON 写的递归提纲——trigger 是标题,每个 action 是一条,nextAction 是"下一条",循环和路由是"缩进的子条目"。构建器的全部工作,就是安全地编辑这份提纲,并让它在浏览器、数据库、执行引擎三处始终是同一份

2. 顶层全景(它大概怎么转)

整个构建器围绕一个核心动作转:"对工作流做一次编辑"。无论是人在画布上拖,还是 AI 通过 MCP 建流,最终都变成同一种东西——一个 FlowOperationRequest(操作请求),交给同一个纯函数 flowOperations.apply(flowVersion, operation) 去算出新的工作流。

┌──────────── 谁在编辑 ────────────┐
人拖画布 AI 通过 MCP 建流
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ FlowOperationRequest(一次编辑的描述) │ 例:ADD_ACTION / UPDATE_TRIGGER / MOVE_ACTION
└─────────────────────────────────────────┘

├──── 浏览器:先本地跑一遍(乐观更新,UI 立刻变)
│ flowOperations.apply(...) ← 同一份代码

└──── 服务器:再权威跑一遍(校验 + 落库)
preApplyOperation → prepareRequest
→ flowOperations.apply(...) ← 同一份代码
→ 存进 Postgres(一个 FlowVersion 行)


┌─────────────────────────────────────────┐
│ FlowVersion(trigger → action 递归树) │
└─────────────────────────────────────────┘

├──── 画布:树 → 节点/连线图(布局算法算坐标)

└──── 引擎:沿 nextAction 链表 + 递归子树 执行

怎么读这张图: 从上往下是一次编辑的生命周期。最关键的一点是中间那个 flowOperations.apply —— 它是一份代码,被前端和后端各跑一次,这是整个构建器最妙的设计(§3 详述,03 章深入)。

部件一句话职责:

部件干什么在哪个文件(克隆根相对)
FlowVersion / FlowAction / FlowTrigger工作流的数据模型(递归树)packages/core/execution/src/lib/flows/flow-version.tsactions/action.tstriggers/trigger.ts
flowOperations.apply纯函数 reducer:(树, 操作) → 新树packages/core/execution/src/lib/flows/operations/index.ts
flowStructureUtil.transferFlow递归遍历器:对树里每个节点跑一个函数packages/core/execution/src/lib/flows/util/flow-structure-util.ts
前端 flow-state乐观更新 + 防抖排队把操作发给服务器packages/web/src/app/builder/state/flow-state.ts
服务器 flowVersionService.applyOperation校验 + 落库,并把宏操作展开packages/server/api/src/app/flows/flow-version/flow-version.service.ts
画布布局 flowCanvasUtils.computeStepPositions树 → 每个节点的 (x,y) 坐标packages/core/execution/src/lib/flows/util/flow-canvas-util.ts
flowMigrations.apply把旧 schema 的流升级到最新版packages/server/api/src/app/flows/flow-version/migrations/index.ts
引擎 flowExecutor.execute沿链表执行;router/loop 递归子树packages/server/engine/src/lib/handler/flow-executor.ts

主线走一遍(高层):

  1. 用户在画布拖一个 "Slack 发消息" 到某个步骤后面。
  2. 前端构造 { type: ADD_ACTION, request: { parentStep, action, ... } }
  3. 前端立刻本地 flowOperations.apply,UI 瞬间显示新方块(乐观);同时把这个操作防抖入队发给服务器。
  4. 服务器收到,跑 preApplyOperation(副作用/校验)→ prepareRequest(补全/清洗)→ flowOperations.apply(权威计算)→ 存成新的 FlowVersion
  5. 下次读这条流时,若它的 schemaVersion 落后,flowMigrations 自动把它升级到最新。
  6. 发布后运行时,引擎拿到这棵树,从 trigger.nextAction 开始沿链表走,遇到 router/loop 就递归进子树。

3. 核心思想:一棵树 + 一个 reducer + 跑两遍

如果只记三件事,记这三个:

① 工作流是递归树,不是节点数组。 没有"边表"。结构本身就是数据嵌套:trigger.nextAction.nextAction… 是主干,router.children[i]loop.firstLoopAction 是分叉的子树(详见 01 章)。这让"一条流"天然是一份可序列化的 JSON,复制/导入/导出都只是深拷贝。

② 所有编辑都是"操作",apply 是纯函数。 你永远不直接改树;你描述一个 FlowOperationRequest,交给 flowOperations.apply 算出一棵全新的树(它内部先 JSON.parse(JSON.stringify(...)) 深拷贝,绝不原地改)。复杂操作(移动、复制)是——展开成一串基本操作依次 apply(详见 02 章)。

③ 同一份 reducer 在前端和后端各跑一遍。 前端跑是为了乐观更新(UI 零延迟);后端跑是为了权威落库(加校验)。因为是同一份代码,两边结果天然一致,不会"前端显示的和存下来的不一样"(详见 03 章)。

4. 阅读地图(建议顺序)

本项目较复杂,拆成 4 章,由浅入深:

  1. 01-data-model.md — 数据模型(先读)。工作流到底长什么样:FlowVersion、递归的 FlowAction 联合类型、router 的 children、loop 的 firstLoopAction、失败分支 continueOnFailureBranches。读完你能在脑子里画出这棵树。
  2. 02-operations-engine.md — 操作引擎(核心)。FlowOperationRequest 有哪些、flowOperations.apply 这个 reducer 怎么写、transferFlow 这个递归遍历器如何用一个函数实现增删改、宏操作(move = delete + add)如何展开。
  3. 03-dual-runtime-and-canvas.md — 双端运行 + 画布。前端 flow-state 的乐观更新 + 防抖 PromiseQueue;服务器的校验流水线;以及画布如何把树布局成节点和连线(纯函数算坐标)。
  4. 04-migrations-and-agents.md — 迁移与 agent。schema 版本号 + 22 个迁移如何把老流升级;引擎运行时如何走这棵树;以及 MCP 工具如何让 AI 用同一套操作从零搭一条流。

想最快抓住精华:读 01 的"递归树"那节 + 02 的"apply 是 reducer"+"transferFlow"两节,就掌握了 80%。