Dynamic Workers 中的 Durable Objects:为每个 AI 生成的应用提供独立的数据库
本文另有 English、Deutsch、Español、Español (Latinoamérica)、Français、Italiano、日本語、한국어、繁體中文和Nederlands.

几周前,我们宣布推出 Dynamic Workers,Workers 平台的一项全新特性,支持您将 Worker 代码实时加载至安全沙盒中。动态 Worker 加载器 API 本质上提供了对 Worker 底层一直以来所依赖的基础计算隔离组件的直接访问能力:即隔离环境(isolates),而非容器(containers)。Isolates 比容器轻量得多,因此加载速度可提升 100 倍,且仅占用 1/10 的内存。它们非常高效,可被视作“一次性”资源:启动一个实例运行几行代码,执行完毕即销毁。类似于安全版本的 eval () 函数。
Dynamic Workers 有很多用途。在最初的发布公告中,我们重点介绍了如何利用 Dynamic Workers 运行由 AI 智能体生成的代码,以此作为工具调用的替代方案。在此场景中,AI 智能体可根据您的请求,通过编写并执行几行代码来完成相应操作。此类代码为一次性使用,仅用于单次执行一项任务,且在执行完毕后会立即被销毁。
但如果您希望 AI 生成更具持久性的代码,又该如何实现?那如果您希望 AI 构建一款带有自定义 UI、可供用户交互的小型应用,又该如何实现?如果您希望该应用具备持久化状态,又该如何实现?但是,当然,您仍然希望它在安全的沙盒环境中运行。
其中一种方法是使用 Dynamic Workers,并且只需为 Worker 提供一个RPC API,让其可以访问存储。使用 绑定(bindings),您可以为 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 Object 的命名空间?随意使用遍布全球的无限制存储?
您很可能不希望这样做。您可能需要一些管控。您可能希望限制,或至少追踪其创建的 Durable Object 数量。也许您希望将其限制为一个对象(对于采用氛围编码的个人应用可能已经足够)。您可能希望添加日志记录及其他可观测性能力。指标,账单,等等。
要实现这一切,您真正想要的是:将发往这些 Durable Object 的请求首先转发至您的代码,由您的代码完成所有调度与管控逻辑处理后,再将请求转发至智能体代码。您需要编写一个监管程序,使其作为每个 Durable Object 的一部分运行。
解决方案:Durable Object Facets
我们今天推出公测版的这一全新功能就是为了解决上述问题。

Durable Object Facets 允许您动态加载和实例化 Durable Object 类,同时为其提供 SQLite 数据库用于存储。通过 Facets:
- 首先,创建一个普通的 Durable Object 命名空间,指向您编写的一个类。
- 在该类中,您将智能体的代码作为 Dynamic Worker 加载,并对其进行调用。
- Dynamic Worker 的代码可以直接实现一个 Durable Object 类。换句话说,它导出了一个
继承自 DurableObject的类。 - 您正在将该类实例化为您的 Durable Object 的一个facet(面)。
- 该面拥有自己的 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的每个实例都是一个由 两个 SQLite 数据库组成的 Durable Object:一个属于父级 (AppRunner本身),一个属于该 facet (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",
},
],
}
开始构建
Facets 是 Dynamic Workers 的一项特性,测试版已向 Workers Paid 计划用户开放。
请查看文档以了解有关 Dynamic Workers 和 Facets 的更多信息。
