你的 Agent 跑一个 20 分钟的长任务,跑到第 18 分钟进程崩了——进度全没,还得从零重来。更糟的是,付费、发邮件这种「副作用步骤」重复执行会出事故。Cloudflare Workflows 就是为这种场景生的:它把工作流每一步的状态都持久化在云端,任何一步失败都能从断点重试,且带 exactly-once 语义(同一步不会重复执行副作用)。
先搞懂:Workflows 解决什么?
和普通「写一个 async 函数顺序调 API」不同,Workflows 把每个 step.do() 当成一个可持久化、可恢复的检查点。一旦某步失败,引擎会从那一步(而不是开头)重启;引擎崩溃也无所谓,因为它不依赖内存里的状态。
| 裸 async 函数 | Cloudflare Workflows |
|---|---|
| 崩了从零重跑,副作用重复 | 从失败那步续跑,exactly-once |
| 重试/超时要自己写 | step 内置 retries / timeout |
| 人工审批要外接队列 | waitForApproval 原生支持 |
它不等于普通的「定时任务」。Workflows 面向有状态、多步骤、需要容错与人工介入的业务流;纯无状态短任务用 Workers Cron 更轻。
Step 1:建项目并装 agents/workflows
npm create cloudflare@latest -- my-agent-flow
# 选 "Hello World" Worker 模板
cd my-agent-flow
npm install agents
# agents 包内含 workflows 子模块
wrangler login。Workflows 是 Cloudflare 托管能力,本地 dev 能跑,但完整持久化要在云端账号下生效。Step 2:写第一个 Workflow(step.do 链)
在 src/index.ts 里定义一个继承 WorkflowEntrypoint 的类,用 step.do() 包装每一步:
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
import { Agent } from 'agents';
export class MyFlow extends WorkflowEntrypoint<Env, { url: string }> {
async run(event: WorkflowEvent<{ url: string }>, step: WorkflowStep) {
const url = event.payload.url;
const raw = await step.do('download', async () => {
const r = await fetch(url);
return r.text();
});
const summary = await step.do('summarize', async () => {
const agent = new Agent(...);
return agent.run('总结这段内容:' + raw.slice(0, 4000));
});
return { summary };
}
}
每个 step 的函数必须是「纯副作用 + 可重入」的:同样输入必须产出同样结果,因为失败时会重跑。不要把随机数、时间戳塞进 step 逻辑里还指望它不变。
Step 3:给步骤加重试与超时
const raw = await step.do('download', {
retries: { limit: 3, delay: '5s', backoff: 'exponential' },
timeout: '30s',
}, async () => {
const r = await fetch(url);
if (!r.ok) throw new Error('fetch failed: ' + r.status);
return r.text();
});
Step 4:人工审批 waitForApproval
发邮件、扣款、对外发布前,用 waitForApproval 暂停流程等人确认:
const approved = await step.waitForApproval({
type: 'approval',
timeout: '24h',
// 可在 Dashboard 或 API 里 approve / reject
});
if (!approved) {
return { status: 'rejected' };
}
await step.do('send-email', async () => { /* 真正发送 */ });
审批等待期间,工作流实例是「挂起」而非「空转」——不占计算、不计费空等,被人点通过后才从断点继续。这正是持久化的价值。
Step 5:从 Agent 类触发 + 共享状态
在 agents 的 Agent 类里,用 runWorkflow() 触发,并用 updateAgentState / mergeAgentState 在多步间共享记忆:
class MyAgent extends Agent<Env, State> {
async onRequest(req) {
const instance = await this.runWorkflow('my-flow', {
payload: { url: 'https://example.com/report' },
});
await this.setState({ lastRunId: instance.instanceId });
return new Response('started ' + instance.instanceId);
}
}
Step 6 & 7:本地调试与云端部署
wrangler dev
# 另开终端触发一次
curl -X POST localhost:8787/start -d '{"url":"https://example.com"}'
在本地能看到 step 的执行顺序与日志。确认无误后部署:
wrangler deploy
# 去 Cloudflare Dashboard → Workflows 看每次运行的甘特图与每步输入/输出
常见问题速查
| 现象 | 大概率原因 & 解决 |
|---|---|
| step 重跑后结果变了 | step 函数不纯(含随机/时间),把这类值提到 step 外或作为参数传入 |
| 本地能跑、云端卡住 | 未 wrangler login 或 Workflows 功能未在该账号区域开启 |
| 审批一直 pending | 没人去 Dashboard / API 点 approve;检查 timeout 是否过期 |
| 想并行多步 | 用 Promise.all 包多个 step.do,或拆成子工作流 |