几周前,我们宣布推出 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 的更多信息。