測試版對於接收意見回饋和進行反覆運作很有用,但歸根結底,並非每個人都願意做實驗品,或者能忍受測試版軟體偶爾出現的尖銳問題。有時您需要大而閃亮的「正式推出」標籤(或部落格文章),現在輪到 Workflows 了。
我們的無伺服器持久執行引擎 Workflows 現已正式推出,您可在 Workers 上構建長期執行、多步驟的應用程式(有些人稱之為「步驟函數」)。
簡而言之,這意味著它可用於生產環境,但並不意味著 Workflows 將會僵化。我們將繼續擴展 Workflows(包括更多並行執行個體),推出新功能(例如全新的 waitForEvent
API),並使用我們的 Agents SDK 和 Workflows,更輕鬆地構建 AI 代理程式。
如果您更喜歡程式碼而非散文,可以快速安裝 Workflows 入門專案,並使用單一命令來開始探索程式碼和 API :
npm create cloudflare@latest workflows-starter --
--template="cloudflare/workflows-starter"
Workflows 如何運作?我可以用來構建哪些內容?如何看待使用 Workflows 和 Agents SDK 來構建 AI 代理程式?請繼續閱讀。
使用 Workflows 構建
Workflows 是在 Cloudflare Workers 基礎上構建的持久執行引擎,可讓您構建具有復原能力的多步驟應用程式。
Workflows 的核心是實作一個基於步驟的架構,其中應用程式中的每個步驟都可獨立重試,其狀態會在步驟之間自動保留。這意味著,即使某個步驟因暫時性錯誤或網路問題失敗,Workflows 可以僅重試該步驟,而無需從頭開始重新啟動整個應用程式。
定義 Workflow 後,您將應用程式分解為合乎邏輯的步驟。
每個步驟都可執行程式碼 (
step.do
),讓您的 Workflow 進入休眠狀態(step.sleep
或 step.sleepUnutil),
或者等待某個事件 (step.waitForEvent
)。在 Workflow 執行時,它會自動保留從每個步驟傳回的狀態,從而確保您的應用程式可從中斷的位置繼續執行,即使在失敗或休眠期之後亦是如此。
對於在多個系統之間協調運作、按順序處理資料,或者需要處理可能持續數分鐘、數小時甚至數天的長時間執行任務的應用程式,這種持久的執行模型尤其強大。
需要處理傳統無狀態功能難以處理的複雜業務程序時,Workflows 尤其有用。
例如,電子商務訂單處理工作流程可能會在單獨的步驟中完成庫存檢查、以某種付款方式收費、傳送電子郵件確認,以及更新資料庫等操作。如果付款處理步驟因暫時性中斷而失敗,Workflows 會在付款服務再次可用時自動重試該步驟,而無需重複庫存檢查或重新啟動整個程序。
您可在下方看到其運作方式:對服務的每次呼叫都可建模為一個步驟,獨立重試,並在需要時從該步驟開始復原:
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
// The params we expect when triggering this Workflow
type OrderParams = {
orderId: string;
customerId: string;
items: Array<{ productId: string; quantity: number }>;
paymentMethod: {
type: string;
id: string;
};
};
// Our Workflow definition
export class OrderProcessingWorkflow extends WorkflowEntrypoint<Env, OrderParams> {
async run(event: WorkflowEvent<OrderParams>, step: WorkflowStep) {
// Step 1: Check inventory
const inventoryResult = await step.do('check-inventory', async () => {
console.log(`Checking inventory for order ${event.payload.orderId}`);
// Mock: In a real workflow, you'd query your inventory system
const inventoryCheck = await this.env.INVENTORY_SERVICE.checkAvailability(event.payload.items);
// Return inventory status as state for the next step
return {
inStock: true,
reservationId: 'inv-123456',
itemsChecked: event.payload.items.length,
};
});
// Exit workflow if items aren't in stock
if (!inventoryResult.inStock) {
return { status: 'failed', reason: 'out-of-stock' };
}
// Step 2: Process payment
// Configure specific retry logic for payment processing
const paymentResult = await step.do(
'process-payment',
{
retries: {
limit: 3,
delay: '30 seconds',
backoff: 'exponential',
},
timeout: '2 minutes',
},
async () => {
console.log(`Processing payment for order ${event.payload.orderId}`);
// Mock: In a real workflow, you'd call your payment processor
const paymentResponse = await this.env.PAYMENT_SERVICE.processPayment({
customerId: event.payload.customerId,
orderId: event.payload.orderId,
amount: calculateTotal(event.payload.items),
paymentMethodId: event.payload.paymentMethod.id,
});
// If payment failed, throw an error that will trigger retry logic
if (paymentResponse.status !== 'success') {
throw new Error(`Payment failed: ${paymentResponse.message}`);
}
// Return payment info as state for the next step
return {
transactionId: 'txn-789012',
amount: 129.99,
timestamp: new Date().toISOString(),
};
},
);
// Step 3: Send email confirmation
await step.do('send-confirmation-email', async () => {
console.log(`Sending confirmation email for order ${event.payload.orderId}`);
console.log(`Including payment confirmation ${paymentResult.transactionId}`);
return await this.env.EMAIL_SERVICE.sendOrderConfirmation({ ... })
});
// Step 4: Update database
const dbResult = await step.do('update-database', async () => {
console.log(`Updating database for order ${event.payload.orderId}`);
await this.updateOrderStatus(...)
return { dbUpdated: true };
});
// Return final workflow state
return {
orderId: event.payload.orderId,
processedAt: new Date().toISOString(),
};
}
}
由於集耐用性、自動重試和狀態持久性於一身,Workflows 成為構建可靠的分散式應用程式的理想選擇,這些應用程式可從容地處理現實世界出現的失敗問題。
人機互動
Workflows 即程式碼,這使得其變得極其強大:您可以動態、即時地定義步驟、有條件地分支,並對您需要的任何系統進行 API 呼叫。但有時您也需要一個 Workflow 來等待現實世界中發生的事情。
例如:
取得人工核准以繼續。
傳入的 Webhook,例如,Stripe 付款或 GitHub 事件。
狀態變更,例如,檔案上傳至 R2 會觸發事件通知,然後將檔案引用推送至 Workflow,以便其處理檔案(或透過 AI 模型執行)。
Workflows 中的全新 waitForEvent
API 可讓您完成以下操作:
let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" })
然後,您可透過能發出 HTTP 請求的任何外部服務,將事件傳送至特定執行個體:
curl -d '{"transaction":"complete","id":"1234-6789"}' \
-H "Authorization: Bearer ${CF_TOKEN}" \
\ "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances/{instance_id}/events/{event_type}"
… 或透過 Worker 本身內的 Workers API 完成:
interface Env {
MY_WORKFLOW: Workflow;
}
interface Payload {
transaction: string;
id: string;
}
export default {
async fetch(req: Request, env: Env) {
const instanceId = new URL(req.url).searchParams.get("instanceId")
const webhookPayload = await req.json<Payload>()
let instance = await env.MY_WORKFLOW.get(instanceId);
// Send our event, with `type` matching the event type defined in
// our step.waitForEvent call
await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
return Response.json({
status: await instance.status(),
});
},
};
您甚至可使用 type
參數來等待多個事件,及/或使用 Promise.race
競爭多個事件來繼續操作,具體取決於先收到哪個事件:
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
let state = await step.do("get some data", () => { /* step call here /* })
// Race the events, resolving the Promise based on which event
// we receive first
let value = Promise.race([
step.waitForEvent("payment success", { type: "payment-success-webhook", timeout: "4 hours" ),
step.waitForEvent("payment failure", { type: "payment-failure-webhook", timeout: "4 hours" ),
])
// Continue on based on the value and event received
}
}
為了更詳細地視覺化 waitForEvent
,假設我們有一個 Workflow,由監視 GitHub 存放庫的程式碼審查代理程式觸發。
如果不能等待事件,Workflow 就無法輕鬆取得人工核准來寫回建議(甚至無法提交自己的 PR)。它可能會輪詢某些已更新的狀態,但這意味著我們必須在任意時段內呼叫 step.sleep
,輪詢儲存服務以取得更新值,以及在不存在時重複操作。程式碼數量龐大,而且容易出錯:

如果沒有 waitForEvent,則更難以向執行中的 Workflow 執行個體傳送資料
如果修改相同範例以整合新的 waitForEvent API,我們可將其用於等待人工核准,再進行變更:

將 waitForEvent 新增至我們的程式碼審查 Workflow,以便其尋求明確的核准。
在這裡,您甚至可以想像 AI 代理程式本身代表人類傳送及/或採取行動:waitForEvent
只是為 Workflow 提供了一種方法,以在繼續(或不繼續)操作之前擷取和暫停某些要變更的內容。
關鍵是,您可以像 Workflows 中的任何其他步驟一樣呼叫 waitForEvent
:您可以有條件地呼叫、及/或多次呼叫、及/或在互動中呼叫。Workflows 就像是 Workers:您擁有程式設計語言的全部功能,並且不受領域特定語言 (DSL) 或設定語言的限制。
定價
好消息:自發佈原始測試版以來,我們並未做出太多變更!我們將依據 Workflows 儲存的狀態新增儲存定價,並保留基於 CPU 和基於請求(叫用)的定價,如下所示:
單位 | Workers Free | Workers Paid |
CPU 時間(毫秒) | 每個 Workflow 10 毫秒 | 每月含 3000 萬 CPU 毫秒 + 每增加 100 萬 CPU 毫秒 0.02 美元 |
請求 | 每天 100,000 次 Workflow 叫用(與 Workers 共用) | 每月包含 1,000 萬個 每增加 100 萬個 + 0.3 美元 |
儲存 (GB) | 1 GB | 每月含 1 GB + 每月每 GB 0.20 美元 |
由於儲存定價為新的定價,我們不會在 2025 年 9 月 15 日之前主動計費。在收取儲存費用之前,我們將通知超過 1 GB 限制的使用者,依預設,Workflows 的儲存狀態將在三 (3) 天(免費方案)或三十 (30) 天(付費方案)後到期。
這裡的「CPU 時間」是指:您的 Workflow 主動取用運算資源的時間。這不包括等待 API 呼叫、推理 LLM 或其他 I/O(例如寫入資料庫)所花費的時間。這看起來似乎是一件小事,但在實踐中,它會共同產生影響:大多數應用程式的 CPU 時間只有幾毫秒,而操作時間有幾秒:一個或兩個 API 需要 100 - 250 毫秒才能回應!

Workflow 空閒或等待時依 CPU 計費,而不是花費的時間。
特別是 Workflow 引擎,往往需要花費大量時間等待:從物件儲存(例如 Cloudflare R2)讀取資料、呼叫第三方 API 或 LLM(例如 o3-mini 或 Claude 3.7),甚至查詢 D1、Postgres 或 MySQL 等資料庫。使用 Workflows 就像Workers一樣:您不必為應用程式等待的時間付費。
開始構建
現在您已非常瞭解 Workflows 及其運作方式,並想要開始構建。接下來做什麼?
瀏覽 Workflows 文件以瞭解其運作方式、 Workflows API 和最佳做法
檢閱入門專案中的程式碼
最後,只需按幾下滑鼠,即可將啟動程式部署至您自己的 Cloudflare 帳戶中: