工作流引擎:用 Block 编排多步骤
30 秒导读: 前面几章讲的是"一个任务怎么跑"(02-agent-loop-planning.md 讲单步 Agent 循环)。但真实业务很少是一个任务——通常是"登录 → 循环处理每一行 → 抽取数据 → 发邮件"这种多步骤流程。工作流引擎就是把这些步骤拼成一条可复用、可参数化的流水线的东西。它的最小积木叫 Block;block 与 block 之间用参数传数据。本章讲清楚:block 是编排单元,参数是数据管道。
1. 这是什么(零基础也能懂)
一句话定义: 工作流(workflow)= 一串按顺序执行的 Block;每个 Block 是一个自带输入输出的"步骤积木"。
它解决什么问题: 假设你要每天从一个供应商门户下载账单。这件事是:登录 → 进报表页 → 对每个月循环下载 → 把下载的文件解析 成表格 → 邮件发给财务。你不想每次都写一遍代码,也不想让一个巨型 Agent"一口气自由发挥"(不可控、不可复用)。工作流让你把它拆成命名好的步骤,存成一份定义,以后换参数就能重跑。
一个 Block 长什么样(概念示意,非源码):
# 示意:一个"导航到登录页并登录"的 block
- block_type: login
label: do_login # 块的唯一名字,别的块靠它引用输出
url: "https://portal.example.com"
navigation_goal: "用给定账号登录"
parameters: [username, password]
一句话直觉: 把工作流想成一条工厂流水线——每个工位(Block)干一件事,上一个工位的产出(OutputParameter)顺着传送带流到下一个工位。有的工位是"派个工人去网页上干活"(浏览器任务类 block),有的工位是"纯机器计算"(代码/HTTP/解析类 block),还有的是"控制传送带走向"(循环、分支)。
它和"单步 Agent"的分工:
| 关切 | 谁负责 | 本章讲不讲 |
|---|---|---|
| 网页里的一步动作(点哪、填什么) | 单步 Agent 循环 | 不讲,见 02 |
| 一句话目标自主拆解成整条流程 | Skyvern 2.0 规划器 | 不讲,见 06 |
| 把多个任务显式编排成可复用流水线 | 工作流引擎 / Block | 本章 |
2. 顶层全景(它大概怎么转)
怎么读下面这张图: 从上到下是"一次工作流运行"的生命周期;左边是用户给的东西,右边是引擎内部。
用户 引擎入口 编排层 积木层
┌──────┐ run_workflow ┌──────────────┐ execute_workflow ┌──────────────────┐
│ 参数 │ ───────────────> │ prepare_ │ ─────────────────> │ 逐个 block: │
│ 值 │ library/ │ workflow │ service.py │ block.execute_ │
└──────┘ skyvern.py │ 建 WorkflowRun│ │ safe() │
└──────────────┘ └────────┬─────────┘
│ │
│ 初始化 ┌────────▼─────────┐
▼ │ 浏览器任务类 │
┌───────────────────┐ 取/写参数值 │ → 落到单步 Agent │
│ WorkflowRunContext │ <──────────────>│ 控制流类 → 循环/ │
│ (参数 + 值的仓库) │ │ 分支 │
└───────────────────┘ │ 副作用类 → 直接干│
└──────────────────┘
部件一句话职责:
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
run_workflow | 公共入口:接参数、建运行、丢给执行器 | skyvern/services/workflow_service.py:83;库封装 skyvern/library/skyvern.py:351 |
WorkflowService.execute_workflow | 编排:按顺序取出 block 挨个跑,处理失败/终止/取消 | skyvern/forge/sdk/workflow/service.py:1615 |
Block / 各子类 | 积木本身:每类 block 一件事 | skyvern/forge/sdk/workflow/models/block.py:379 |
WorkflowRunContext | 参数与值的中央仓库,block 间传数据靠它 | skyvern/forge/sdk/workflow/context_manager.py:89 |
block_yaml_to_block | 把 no-code 的 YAML 定义翻译成运行时 Block 对象 | skyvern/forge/sdk/workflow/workflow_definition_converter.py:458 |
主线走一遍(高层,不进代码):
- 用户调
run_workflow,给一份 workflow 的 id + 一个参数字典。 - 引擎建一个
WorkflowRun,初始化WorkflowRunContext(把用户参数值灌进去)。 - 编排层从 workflow 定义里拿到 block 列表,
for block in blocks挨个调block.execute_safe(...)。 - 每个 block 跑完把产出写回 context(键叫
<label>_output),下一个 block 用 Jinja 模板{{ do_login.output }}就能引用到。 - 某个 block 失败/终止 → 看它的
continue_on_failure,决定是整条流程停,还是跳过继续。