Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

为 agentic 时代重新设计 Workflows 控制平面

原文:Rearchitecting the Workflows control plane for the agentic era / 2026-04-15 Source: https://blog.cloudflare.com/workflows-v2/

最初构建 Workflows——我们用于多步骤应用的可靠执行引擎——时,它面向的世界是工作流由人类行为触发,例如用户注册或下单。对于像 onboarding 流这样的用例,工作流只需支持每人一个实例——而人点击的速度终究有限。

随着时间推移,我们实际看到的是工作负载和访问模式上的量化转变:人触发的工作流变少了,机器速度创建的、agent 触发的工作流变多了。

随着 agent 成为持久且自治的基础设施,代表用户运行数小时或数天,它们需要一个可靠的、异步的执行引擎来承担正在做的工作。Workflows 正提供这个:每一步都可独立重试,工作流可以暂停等待 human-in-the-loop 审批,每个实例都能挺过失败而不丢失进度。

更进一步,工作流本身也被用来实现 agent 循环,作为管理并保活 agent 的可靠脚手架。我们的 Agents SDK 集成 加速了这一点,让 agent 容易拉起 workflow 实例并实时获得进度。一个 agent 会话现在可以触发数十个工作流,许多 agent 并发运行意味着几秒内创建数千个实例。随着 Project Think 上线,我们预计速度只会更快。

为了帮助开发者在 Workflows 上扩展他们的 agent 和应用,我们激动地宣布我们现在支持:

  • 50,000 并发实例(并行运行的工作流执行数),最初为 4,500

  • 每账户 300 实例/秒创建速率,之前为 100

  • 每个工作流 200 万排队实例(指已创建或唤醒、正等待并发槽的实例),从 100 万上调

我们基于使用数据和基本原理重新设计了 Workflows 控制平面以支持这些提升。控制平面 V1 中,单个 Durable Object(DO)可以充当整个账户的中央注册和协调器。V2 我们构建了两个新组件以帮助系统横向扩展并缓解 V1 引入的瓶颈,然后在不中断现网流量的情况下把所有客户无缝迁移到新版本。

V1:Workflows 的初始架构

正如我们在 公测博客 中所述,我们完全在自家开发者平台上构建了 Workflows。从根本上,workflow 就是一系列可靠步骤,每一步可独立重试,可以执行任务、等待外部事件,或休眠到预定时间。

export class MyWorkflow extends WorkflowEntrypoint {

  async run(event, step) {
    const data = await step.do("fetch-data", async () => {
      return fetchFromAPI();
    });

    const approval = await step.waitForEvent("approval", {
      type: "approval",
      timeout: "24 hours",
    });

    await step.do("process-and-save", async () => {
      return store(transform(data));
    });
  }
}

为了触发每个实例、执行其逻辑、存储其元数据,我们利用基于 SQLite 的 Durable Objects,它是分布式系统中协调与存储的简单但强大的原语。

在控制平面里,有些 Durable Objects——比如 Engine,它实际执行 workflow 实例,包括其 step、retry 和 sleep 逻辑——按 1:1 比例为每个实例拉起。另一边,Account 是账户级的 Durable Object,管理该账户的所有 workflow 和 workflow 实例。

要了解 V1 控制平面的更多内容,请参阅我们的 Workflows 发布博客

把 Workflows 推入 beta 后,我们高兴地看到客户快速扩展他们对产品的使用,但我们也意识到使用单个 Durable Object 存储所有账户级信息引入了瓶颈。许多客户每分钟需要创建并执行数百乃至数千个 Workflow 实例,这很快会压垮我们最初架构里的 Account。最初的限额——4,500 并发槽和每 10 秒 100 次实例创建——是这个限制的产物。

在 V1 控制平面上,这些限额是硬上限。所有依赖 Account 的操作,包括 create、update 和 list,都必须通过那一个 DO。高并发工作负载的用户可能在任何时刻有数千个实例在启动和结束,叠加成对 Account 每秒数千个请求。为解决这个问题,我们重新设计了 workflow 控制平面,使其可以横向扩展到更高的并发与创建速率限额。

V2:用横向扩展承载更高吞吐

为了新版本,我们以“为高量级 workflow 优化“为目标,从头重新思考了每一个操作。最终,Workflows 应该可扩展到开发者所需的任何规模——无论是每秒创建数千个实例,还是同时运行数百万个实例。我们也希望确保 V2 允许灵活的限额,我们可以切换并继续提高,而不是 V1 那种硬上限。许多设计迭代后,我们为新架构定下以下支柱:

  • 一个给定实例存在的“事实来源“应该且仅应该是它的 Engine

    • 在 V1 控制平面架构中,我们在把实例入队前没有检查它的 Engine 是否真的存在。这允许出现一种坏状态:一个实例已被入队,而它对应的 Engine 还没拉起。

    • 实例生命周期与存活机制必须按工作流横向可扩展,并跨多个区域分布。

  • 新的 Account 单例应只存储最少必要的元数据,并保证有一个不变的最大并发请求数。

V2 控制平面有两个新的关键组件让我们能改善 Workflows 的可扩展性:SousChefGatekeeper。第一个组件 SousChefAccount 的“二把手“。回想一下,以前 Account 管理一个账户内所有 workflow 跨所有实例的元数据和生命周期。引入 SousChef 是为了在一个账户内的某个工作流内,跟踪 一个子集 的实例的元数据与生命周期。在一个账户内,一群 SousChefs 可以以更高效、更可管理的方式向 Account 汇报。(这种设计还有一个附加好处:我们不仅本来就有按账户隔离,还无意中获得了同一账户内的“按工作流隔离“,因为每个 SousChef 只管一个特定的工作流。)

第二个组件 Gatekeeper 是一种把并发“槽“(由并发限额衍生)分发给账户内所有 SousChefs 的机制。它充当租约系统。当一个实例被创建,它被随机分配给该账户内的某个 SousChefs。然后该 SousChefAccount 请求触发该实例。要么槽被授予,要么实例被排队。一旦槽被授予,SousChef 就触发实例执行,并承担确保该实例不会卡住的责任。

需要 Gatekeeper 是为了确保 Engines 永不压垮它们的 Account(V1 上的紧迫风险),所以 SousChefs 和它们的 Account 之间每次通信都按周期进行,每秒一次——每个周期还会批量处理所有槽请求,确保只发起一次 JSRPC 调用。这确保实例创建速率绝不会压垮或影响最重要的组件 Account(顺便一提:如果 SousChef 数量太高,我们对调用做限速,或在不同时间段内分布到不同 SousChefs)。同时,这种周期性属性让我们能保持对较老实例的公平性,并通过众多 SousChefs 确保 max-min 公平,让它们都能进展。例如,如果一个实例被唤醒,它应该比一个新创建的实例优先获得槽,但每个 SousChef 也确保它自己的实例不会卡住。

这个架构更分布式,因此更可扩展。现在,当一个实例被创建,请求路径是:

  1. 检查控制平面版本

  2. 检查该位置是否有 workflow 与版本细节的缓存版本

    1. 若没有,检查 Account 拿到 workflow 名、唯一 ID 和版本,并缓存这些信息
  3. 把仅必要的元数据(实例 payload、创建日期)存储到它自己的 Engine

那么,Engine 怎么告诉控制平面它现在存在?这在实例元数据被设置后在后台发生。由于 Durable Object 上的后台操作可能因驱逐或服务器故障而失败,我们也在创建热路径上为 Engine 设了一个“alarm“。这样,如果后台任务没完成,alarm 确保 实例会启动。

Durable Object alarm 允许 Durable Object 实例在未来某个细粒度时间被唤醒,采用 at-least-once 执行模型,内置自动重试。我们大量使用这种“任务“+ alarms 的组合,把操作从热路径上移开,同时仍然确保一切都会按计划发生。这就是我们如何让像 创建实例 这样的关键操作保持快速,而从不在可靠性上妥协。

除了解锁可扩展性,这个版本的控制平面还意味着:

  • 实例列表性能更快,且与游标分页真正一致;

  • 对实例的任何操作都正好做一次网络跳转(因为可以直接到它的 Engine,确保 eyeball 请求延迟尽可能小);

  • 我们能确保更多实例并发地实际行为正确(按时运行)(并在不正确时纠正,确保 Engines 不会迟到继续执行)。

V1 → V2 迁移

现在我们有了能处理更高用户负载的新版本 Workflows 控制平面,我们需要做“无聊“的部分:把客户和实例迁移到新系统。在 Cloudflare 的规模下,这本身就是一个问题,所以“无聊“的部分变成了最大的挑战。在 Workflows 一周年前,它已经积累了数百万个实例和数千名客户。此外,V1 控制平面上的一些技术债意味着排队的实例可能还没有自己的 Engine Durable Object 被创建,这让事情更复杂。

这种迁移很棘手,因为客户在任何时刻都可能有实例在运行;我们需要一种方式把 SousChefGatekeeper 组件加进老账户而不造成任何中断或停机。

我们最终决定把现有的 Accounts(我们称之为 AccountOlds)迁移成像 SousChefs 一样行事。通过保留 Account DO,我们维持了实例元数据,并简单地把那个 DO 转换为一个 SousChef “DO”:

// You might be wondering what's this SousChef class? This is the SousChef DO class!
import { SousChef } from "@repo/souschef";

class AccountOld extends DurableObject {
  constructor(state: DurableObjectState, env: Env) {
    // We added the following snippet to the end of our AccountOld DO's
    // constructor. This ensures that if we want, we can use any primitive
    // that is available on SousChef DO
    if (this.currentVersion === ControlPlaneVersions.SOUS_CHEFS) {
      this.sousChef = new SousChef(this.ctx, this.env);
      await this.sousChef.setup()
    }
  }

  async updateInstance(params: UpdateInstanceParams) {
    if (this.currentVersion === ControlPlaneVersions.SOUS_CHEFS) {
      assert(this.sousChef !== undefined, 'SousChef must exist on v2');
      return this.sousChef.updateInstance(params);
    }

    // old logic remains the same
  }

  @RequiresVersion<AccountOld>(ControlPlaneVersions.V1)
  async getMetadata() {
    // this method can only be run if 
    // this.currentVersion === ControlPlaneVersions.V1
  }
}

我们能在 AccountOld 中实例化 SousChef 类,因为追踪实例元数据的 SQL 表在 SousChefsAccountOld DO 上都是相同的。因此,我们只需决定使用哪个版本的代码。如果不是这样,我们就被迫迁移数百万实例的元数据,这会让迁移对每个账户更困难、更耗时。那么,迁移如何工作?

首先,我们准备好让 AccountOld DO 切换为像 SousChefs 一样行事(意味着发布一个包含上面代码片段版本的版本)。然后,我们按账户启用控制平面 V2,这大致同时触发以下三步:

  • 所有新的实例创建请求被路由到新的 SousChefs(SousChefs 在收到第一个请求时被创建),新实例不再到 AccountOld;

  • AccountOld DO 开始把自己迁移成像 SousChefs 一样行事;

  • 新的 Account DO 用对应的元数据被拉起。

所有账户迁移到新控制平面版本后,我们能在它们的实例保留期到期时让 AccountOld DO 退场。一旦所有账户上的所有 AccountOlds 实例都迁移完,我们就能永久关闭那些 DO。迁移完成时没有停机,这个过程真的感觉像在开车的时候换轮子。

试用

如果你是 Workflows 新手,试试我们的 入门指南构建你的第一个可靠 agent

如果你的用例需要比我们新默认值更高的限额——50,000 并发槽和账户级 300 实例/秒(每个 workflow 100)的创建速率限额——请通过你的 account team 或 Workers Limit Request Form 联系我们。你也可以在我们的 Discord 服务器 上反馈、提需求,或分享你如何使用 Workflows。

– 原文译于 2026-04-30