Workflow 引擎:声明式多步编排与暂停/恢复
30 秒导读: Agent(第 1 章)是"让模型自由发挥、边想边做手脚";Workflow 反过来—— 用代码提前把步骤写死成一条流水线,该走哪步、走几步、并行还是串行,全由你声明,不靠模型临场决定。 换来的是确定性和一个杀手锏:每步跑完都存一份快照,于是流程能在任意步暂停、等人审批, 之后跨进程恢复接着跑,甚至回到历史某一步重放。
本章讲这套引擎怎么搭、怎么转、怎么在中断后原地复活。不重复 Agent 一次生成的内部细节(见 01-agent-runtime.md),只讲"步骤如何把 agent 当积木嵌进来、状态如何持久化以支持恢复"。
1. 这是什么(零基础也能懂)
一句话定义
Workflow 是一条用链式代码声明出来的多步流水线:你把每一步(跑个函数、调个 agent、并行几件事、 按条件分支、循环、睡一会儿……)一节一节接起来,引擎负责按顺序驱动、在步与步之间传数据、 并在需要时把整条流水线冻结成快照。
解决什么问题 / 给谁用
假设你要做一个"用户入职"自动化:拉用户资料 → 生成欢迎语(要用大模型)→ 停下来等管理员点批准 →
发通知。这里的难点不是任何单步,而是那个"停下来等人"——审批可能几分钟也可能三天,你不能让一个
进程干等着。Workflow 让你把"停下来"写成一行 suspend(),引擎把现场存进数据库、进程可以退出;
等审批来了,再用一行 resume() 从那一步接着跑。
它给这几类人用:
- 要把 LLM 调用和普通业务逻辑混编成可靠管线的工程师。
- 需要 human-in-the-loop(人工审批、补充输入)的流程。
- 需要崩溃可恢复、可回放调试的生产级编排。
它能做什么(功能一览)
| 能力 | 对应原语 | 白话 |
|---|---|---|
| 跑一段函数 | andThen | 最基本的一步 |
| 调一个 agent | andAgent | 把第 1 章的 Agent 当一步嵌进来 |
| 条件执行 | andWhen | 满足条件才跑这步 |
| 多路分支 | andBranch | 所有命中条件的分支都跑 |
| 并行等全部 | andAll | 几件事同时做,等齐 |
| 并行取最快 | andRace | 谁先完成用谁 |
| 遍历数组 | andForEach | 对每个元素跑一步(可限并发) |
| 循环 | andDoWhile / andDoUntil | 反复跑直到条件 |
| 取数据塑形 | andMap | 从 input/步骤/上下文拼出新对象 |
| 定时等待 | andSleep / andSleepUntil | 睡一段/睡到某时刻 |
| 护栏校验 | andGuardrail | 校验/清洗数据 |
| 旁路观察 | andTap | 看一眼数据但不改它 |
| 嵌套子流程 | andWorkflow | 把另一条 workflow 当一步 |
用起来什么样
一段最小的真实风格代码,直观感受"链式声明":
// 示意,非源码:一条把 agent 嵌进流水线的最小 workflow
const workflow = createWorkflowChain({
id: "user-processing",
input: z.object({ userId: z.string() }), // 入口数据形状
result: z.object({ content: z.string() }), // 出口数据形状
})
.andThen({ // 第 1 步:拉资料
id: "fetch-user",
execute: async ({ data }) => {
const userInfo = await fetchUserInfo(data.userId);
return { ...data, userInfo }; // 返回值 = 下一步的 data
},
})
.andAgent( // 第 2 步:让 agent 写欢迎语
({ data }) => `给 ${data.userInfo.name} 写一句欢迎语`,
agent,
{ schema: z.object({ content: z.string() }) },
);
const { result } = await workflow.run({ userId: "123" });
三个关键直觉,先记住: