幾週前,我們宣佈推出 Dynamic Workers,這是 Workers 平台的一項新功能,可讓您將 Worker 程式碼動態載入至安全的沙箱中。本質上,Dynamic Worker Loader API 提供對 Workers 一直以來作為基礎的基本運算隔離原語的直接存取:隔離,而非容器。隔離環境比容器要輕得多,因此能夠使用 1/10 的記憶體將載入速度提升 100 倍。它們非常高效,並且可視為「一次性產品」:啟動一個並執行幾行程式碼,然後將其丟棄。就像 eval() 的安全版本。
Dynamic Workers 有多種用途。在最初的公告中,我們重點介紹了如何將其用於執行 AI 智慧體生成的程式碼,以替代工具呼叫。在此使用案例中,AI 智慧體可編寫並執行幾行程式碼,藉此依據使用者的請求來執行動作。該程式碼是一次性的,預期一次執行一項任務,執行後立即將其丟棄。
但是,如果您希望 AI 生成更持久的程式碼,該怎麼辦?如果您希望 AI 建置一個具有使用者可與之互動的自訂 UI 的小型應用程式,該怎麼辦?如果您希望應用程式具有長期存留狀態,該怎麼辦?但是,當然您仍希望其在安全的沙箱中執行。
一種方法是使用 Dynamic Workers,並為 Worker 提供一個 RPC API,讓其能夠存取儲存體。您可使用繫結,為 Dynamic Worker 提供一個 API,使其指向您的遠端 SQL 資料庫(可能由 Cloudflare D1 提供支援,或透過 Hyperdrive 存取的 Postgres 資料庫,這取決於您。
但是,Workers 還有一種獨特且極快的儲存體類型,可能非常適合此使用案例:Durable Objects。Durable Object 是一種特殊的 Worker,具有唯一的名稱,且每個名稱有一個全域執行個體。該執行個體附加了一個 SQLite 資料庫,其位於 Durable Object 執行所在機器的本機磁碟上。這使得儲存體存取速度極快:實際上是零延遲。
那麼,也許您真正想要的是讓 AI 為 Durable Object 編寫程式碼,然後您希望在 Dynamic Worker 中執行該程式碼。
這就提出了一個奇怪的問題。通常,若要使用 Durable Objects,您必須:
編寫一個擴展 DurableObject 的類別。
從 Worker 的主模組中將其匯出。
在 Wrangler 組態中指定,應為此類別佈建儲存體。這會建立一個指向類別的 Durable Object 命名空間,以處理傳入的請求。
宣告 Durable Object 命名空間繫結指向您的命名空間(或使用 ctx.exports),並將其用於向您的 Durable Object 發出請求。
這不會自然地延伸至 Dynamic Workers。首先,有一個明顯的問題:程式碼是動態的。您完全不需要叫用 Cloudflare API 即可執行。但 Durable Object 儲存體必須透過 API 佈建,且命名空間必須指向實作類別。其不能指向您的 Dynamic Worker。
但有一個更深層次的問題:即使您能以某種方式將 Durable Object 命名空間設定為直接指向 Dynamic Worker,您是否願意這樣做?您是否希望代理程式(或使用者)能夠建立一個充滿 Durable Objects 的完整命名空間?要使用分佈於世界各地的無限儲存體嗎?
您可能不需要。您可能需要某些控制權。您可能想要限制或至少追蹤其建立的物件數量。也許您想要將其限制為僅一個物件(對於 Vibe 編碼的個人應用程式可能已經足夠)。您可能想要新增記錄和其他可觀測性,包括指標、計費等等
為此,您真正需要的是,讓這些針對 Durable Objects 的請求首先到達您的程式碼,在此您可完成所有的「後勤作業」,然後將請求轉傳至代理程式的程式碼中。您想要編寫一個在每個 Durable Object 中執行的監督員。
解決方案:Durable Object Facets
今天,我們以公開測試版的形式發布了可解決此問題的功能。
Durable Object Facets 可讓您動態載入及具現化 Durable Object 類別,同時為其提供 SQLite 資料庫以用於儲存。使用 Facet:
首先,您建立一個普通的 Durable Object 命名空間,指向您編寫的一個類別。
在該類別中,您將代理程式的程式碼作為 Dynamic Worker 載入,並對其進行呼叫。
Dynamic Worker 的程式碼可直接實作 Durable Object 類別。也就是說,其可逐字彙出一個宣告為延伸 DurableObject 的類別。
您正在將該類別具現化為您自己的 Durable Object 的「面向」。
該面向有自己的 SQLite 資料庫,可透過一般的 Durable Object 儲存 API 來使用該資料庫。此資料庫與監督員的資料庫分開,但兩者作為同一個 Durable Object 的一部分儲存在一起。
以下是動態載入和執行 Durable Object 類別的應用程式平台的簡單且完整的實作:
import { DurableObject } from "cloudflare:workers";
// For the purpose of this example, we'll use this static
// application code, but in the real world this might be generated
// by AI (or even, perhaps, a human user).
const AGENT_CODE = `
import { DurableObject } from "cloudflare:workers";
// Simple app that remembers how many times it has been invoked
// and returns it.
export class App extends DurableObject {
fetch(request) {
// We use storage.kv here for simplicity, but storage.sql is
// also available. Both are backed by SQLite.
let counter = this.ctx.storage.kv.get("counter") || 0;
++counter;
this.ctx.storage.kv.put("counter", counter);
return new Response("You've made " + counter + " requests.\\n");
}
}
`;
// AppRunner is a Durable Object you write that is responsible for
// dynamically loading applications and delivering requests to them.
// Each instance of AppRunner contains a different app.
export class AppRunner extends DurableObject {
async fetch(request) {
// We've received an HTTP request, which we want to forward into
// the app.
// The app itself runs as a child facet named "app". One Durable
// Object can have any number of facets (subject to storage limits)
// with different names, but in this case we have only one. Call
// this.ctx.facets.get() to get a stub pointing to it.
let facet = this.ctx.facets.get("app", async () => {
// If this callback is called, it means the facet hasn't
// started yet (or has hibernated). In this callback, we can
// tell the system what code we want it to load.
// Load the Dynamic Worker.
let worker = this.#loadDynamicWorker();
// Get the exported class we're interested in.
let appClass = worker.getDurableObjectClass("App");
return { class: appClass };
});
// Forward request to the facet.
// (Alternatively, you could call RPC methods here.)
return await facet.fetch(request);
}
// RPC method that a client can call to set the dynamic code
// for this app.
setCode(code) {
// Store the code in the AppRunner's SQLite storage.
// Each unique code must have a unique ID to pass to the
// Dynamic Worker Loader API, so we generate one randomly.
this.ctx.storage.kv.put("codeId", crypto.randomUUID());
this.ctx.storage.kv.put("code", code);
}
#loadDynamicWorker() {
// Use the Dynamic Worker Loader API like normal. Use get()
// rather than load() since we may load the same Worker many
// times.
let codeId = this.ctx.storage.kv.get("codeId");
return this.env.LOADER.get(codeId, async () => {
// This Worker hasn't been loaded yet. Load its code from
// our own storage.
let code = this.ctx.storage.kv.get("code");
return {
compatibilityDate: "2026-04-01",
mainModule: "worker.js",
modules: { "worker.js": code },
globalOutbound: null, // block network access
}
});
}
}
// This is a simple Workers HTTP handler that uses AppRunner.
export default {
async fetch(req, env, ctx) {
// Get the instance of AppRunner named "my-app".
// (Each name has exactly one Durable Object instance in the
// world.)
let obj = ctx.exports.AppRunner.getByName("my-app");
// Initialize it with code. (In a real use case, you'd only
// want to call this once, not on every request.)
await obj.setCode(AGENT_CODE);
// Forward the request to it.
return await obj.fetch(req);
}
}
在此範例中:
AppRunner 是由平台開發人員(您)編寫的「標準」Durable Object。
AppRunner 的每個執行個體管理一個應用程式。其儲存應用程式碼並隨需載入。
應用程式本身會實作並匯出一個 Durable Object 類別,平台預期將其命名為 App。
AppRunner 使用 Dynamic Workers 載入應用程式程式碼,然後將該程式碼作為 Durable Object Facet 執行。
AppRunner 的每個執行個體都是一個 Durable Object,由 兩個 SQLite 資料庫組成:一個屬於父項(AppRunner 本身),另一個屬於面向(App)。這些資料庫是隔離的:應用程式無法讀取 AppRunner 的資料庫,只能讀取自己的資料庫。
若要執行範例,請將上面的程式碼複製到檔案 worker.js 中,將其與下列 wrangler.jsonc 配對,並使用 npx wrangler dev 在本機執行。
// wrangler.jsonc for the above sample worker.
{
"compatibility_date": "2026-04-01",
"main": "worker.js",
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": [
"AppRunner"
]
}
],
"worker_loaders": [
{
"binding": "LOADER",
},
],
}
Facet 是 Dynamic Workers 的一項功能,目前提供測試版,可供 Workers 付費方案使用者使用。
請查閱文件以瞭解有關 Dynamic Workers 和 Facets 的詳細資訊。