跳转到内容

输入关键词开始搜索文档。

部署到 Cloudflare

最后更新 查看 Markdown 源文件

在 Cloudflare Workers 上构建和部署 Flue 智能体。本指南将带你了解可以构建的不同类型的智能体 — 从简单的提示 - 响应端点到由持久化存储和远程沙箱支持的完整编码智能体。

完成本指南后,你将拥有一个在 Cloudflare Workers 上运行的 Flue 智能体,并且你将了解如何添加子智能体、R2 支持的上下文、Cloudflare 沙箱和 Durable Object 支持的会话。

在 Cloudflare 上,Flue 是两个并排的 Vite 插件:来自 @flue/vite 的 flue() 加上官方的 @cloudflare/vite-plugin。flue() 扫描你的 'use agent' 模块,生成 Worker 入口(每个智能体一个 Durable Object 类),并将其贡献合并到你的 Wrangler 配置中;Cloudflare 插件负责 workerd 开发和可部署的 Worker 输出。

Hello World

最简单的智能体 — 没有容器,没有存储,只有提示和回复。

1. 设置你的项目

mkdir my-flue-worker && cd my-flue-worker
npm init -y
npm install @flue/runtime hono
npm install -D @flue/vite @cloudflare/vite-plugin vite wrangler

Flue 基于 agents(Cloudflare 的 Agents SDK)构建 — 它使用 SDK 的 Durable Object 基类和原生生命周期功能,同时保留应用路由的所有权。@flue/vite 将 SDK 作为自己的依赖项发布,因此每个 Flue 版本都针对经过测试的 SDK 次要版本运行,你的项目不需要声明它。要运行不同的 SDK 版本,添加你自己的 agents 依赖 — 在你项目中安装的副本总是优先;生成的 worker 在运行时检查 SDK 是否提供 Flue 依赖的持久化 API(如 runFiber),如果不存在则会显式报错。如果你还需要远程沙箱,还需安装 @cloudflare/sandbox(参见下面的连接远程沙箱)。

vite.config.ts
import { cloudflare } from '@cloudflare/vite-plugin';
import { flue, flueWorkerConfig } from '@flue/vite';
import { defineConfig } from 'vite';

// flue() 必须在 cloudflare() 之前:它准备生成的 Worker 入口
// 和合并的 wrangler 配置,供 Cloudflare 插件使用。
// 将 flueWorkerConfig() 传递给 cloudflare(),以便插件获取
// 生成的 Worker 入口和每个智能体的 Durable Object 绑定。
export default defineConfig({
  plugins: [flue(), cloudflare({ config: flueWorkerConfig() })],
});
package.json
{
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": "vite build",
    "deploy": "vite build && wrangler deploy",
  },
}

"type": "module" 是必需的:@cloudflare/vite-plugin 仅支持 ESM,没有它 Vite 会用 require() 加载 vite.config.ts 并无法解析插件。

2. 创建你的第一个智能体

智能体模块是一个普通的 TypeScript 文件加上一行:'use agent' 指令。该指令是智能体加入应用的方式 — 构建扫描你的源根目录查找标记的模块,每个导出的名称大写的函数都是一个智能体,构建为每个智能体生成一个 Durable Object 类。函数的名称成为智能体的持久化标识(可选的 Translator.agentName = '...' 字符串字面量静态属性可以覆盖它)。

src/agents/translator.ts
'use agent';
import { useModel } from '@flue/runtime';

export function Translator() {
  useModel('anthropic/claude-sonnet-4-6');
  return '将用户消息翻译为请求的语言。只回复翻译结果。';
}

需要文件系统的智能体可以附加一个由 just-bash 驱动的内存虚拟沙箱 — 不需要容器。

3. 创建 app.ts — 路由映射

app.ts 是唯一必需的文件。它的默认导出拥有请求流水线;每个挂载的智能体路由解析生成的绑定并转发到该智能体的 Durable Object,其他一切都只是在 Worker 隔离区中运行的 Hono 应用:

src/app.ts
import { createAgentRouter } from '@flue/runtime/routing';
import { Hono } from 'hono';
import { Translator } from './agents/translator.ts';

const app = new Hono();

app.route('/agents/translator', createAgentRouter(Translator));
app.get('/api/ping', (c) => c.text('pong'));

export default app;

挂载路径由你选择;导出的函数名称(智能体的持久化标识)是对话和 Durable Object 类的键。参见路由。

4. 配置 Durable Object 迁移

Cloudflare 要求在 Worker 添加 Durable Object 类时进行显式迁移。Flue 为扫描的智能体生成类和绑定,但你的项目拥有 wrangler.jsonc 中有序的迁移历史:

wrangler.jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-flue-worker",
  "compatibility_date": "2026-06-01",
  "compatibility_flags": ["nodejs_compat"],
  "migrations": [
    { "tag": "flue-class-FlueTranslatorAgent", "new_sqlite_classes": ["FlueTranslatorAgent"] },
  ],
}

类名从智能体标识派生(导出的函数名称,或其 agentName 静态覆盖),驼峰边界为绑定拆分:Translator 智能体产生类 FlueTranslatorAgent 和绑定 FLUE_TRANSLATOR_AGENT,而 IssueTriage 智能体将产生 FlueIssueTriageAgent 和 FLUE_ISSUE_TRIAGE_AGENT。Flue 要求 nodejs_compat 和 2026-04-01 或更新的 compatibility_date,并在构建时验证两者。

添加智能体是一个三件套:'use agent' 文件、app.route(...) 挂载,以及新类的唯一标记迁移。保持已部署的迁移条目有序并追加,永远不要重写。生成的 Flue 智能体类需要 Durable Object SQLite:通过 new_sqlite_classes 引入它们,而不是旧版的 new_classes。

重命名智能体函数是存储标识的更改 — 类名跟随标识,标识跟随函数名,除非 agentName 静态属性固定了它。使用 wrangler 原生的 renamed_classes({ "from": "FlueOldNameAgent", "to": "FlueNewNameAgent" })来表达标识更改,以保留已部署的 Durable Objects。仅重命名文件不会改变任何东西,将智能体重新挂载到不同的 URL 也不是标识更改 — 两者都不需要迁移。

5. 添加你的 API key

对于本地 Cloudflare 开发,将 provider API key 放在 Wrangler 配置旁边的 .dev.vars 中:

cat > .dev.vars <<'EOF'
ANTHROPIC_API_KEY="your-api-key"
EOF

printf '\n.dev.vars*\n.env*\n' >> .gitignore

使用你的 provider 期望的变量名 — Anthropic 使用 ANTHROPIC_API_KEY,OpenAI 使用 OPENAI_API_KEY,依此类推。不要提交本地密钥文件。Cloudflare 也支持基于 .env 的本地变量,但使用 .dev.vars 或 .env 其中之一,不要同时使用;当 .dev.vars 存在时,.env 值不会加载到本地 Worker 绑定中。Worker 运行时变量遵循 Cloudflare 的本地变量规则(.dev.vars、.env、CLOUDFLARE_ENV)。

或者,通过 Workers AI 绑定(cloudflare/... 模型标识符)路由模型流量,完全跳过 API key。

对于已部署的 Worker,通过 Wrangler 添加密钥,而不是将本地开发文件视为生产配置:

npx wrangler secret put ANTHROPIC_API_KEY

对于 CI 或托管部署流水线,当你的流水线提供受保护的密钥文件时,wrangler deploy --secrets-file <path> 也可用。

6. 本地试用

vite dev 通过官方 Cloudflare Vite 集成在本地 workerd 中运行 Worker,使用 Flue 生成的入口和合并的配置:

npx vite dev

然后与智能体对话 — 对话存在于挂载路径加上你选择的任何 id:

curl -X POST 'http://localhost:5173/agents/translator/demo-1' \
  -H "Content-Type: application/json" \
  -d '{"kind": "user", "body": "翻译为法语: Hello world"}'
# → 202 { "streamUrl": "...", "offset": "...", "submissionId": "..." }

curl 'http://localhost:5173/agents/translator/demo-1'   # 读取对话

应用代码应使用 Flue Agent SDK — createFlueClient({ url }).send(...) 加上 wait()/observe() 处理准入和流式传输。

添加或删除智能体文件会自动重新生成 Worker 入口和 wrangler 配置;智能体内部的主体编辑是普通的热更新。

路由中间件(在 app.ts 中智能体挂载路径上应用的普通 Hono 中间件,在 createAgentRouter(...) 挂载之前)在 Flue 将接受的工作转发到其 Durable Object 之前看到原始入站 HTTP 请求。Durable 智能体处理是后续边界:准入后,Flue 使用确定性的内部请求,并且不持久化或重建调用者的原始 headers、cookies、查询参数、URL 或 body。在准入之前进行身份验证,并在应用拥有的输入或存储中携带你稍后需要的任何非密钥关联信息。

flue run 不模拟 Cloudflare:它是 Node 本地的,导入 cloudflare:* 的智能体模块在它下面会失败并指向 vite dev。

7. 构建和部署

npx vite build
npx wrangler deploy

vite build 将 Workers 兼容的制品及其最终的 Wrangler 配置写入 dist/,Cloudflare Vite 插件记录一个部署重定向,以便 wrangler deploy(和 wrangler dev)读取该构建输出 — 从项目根目录部署,无需 --config 标志。先运行 wrangler deploy --dry-run 进行验证。

Flue 永远不会重写你编写的 wrangler.jsonc。它读取它,将其贡献(生成的 main,每个扫描的智能体一个 Durable Object 绑定)分层到一个生成的、gitignore 的 Vite 输入配置(.flue-vite.wrangler.jsonc)中,并将其交给 Cloudflare 插件。迁移历史从你的文件原样传递。名称与 Flue 生成的 FLUE_*_AGENT 名称冲突的 Durable Object 绑定是构建错误。

从同一 Worker 提供静态资源

Workers 静态资源在你的 Worker 脚本之前提供,除非 assets.run_worker_first 另有说明。如果单个 Worker 提供前端构建和调用 Flue 的应用路由,请在 run_worker_first 中包含每个应用拥有的 API 前缀 — 包括 app.ts 挂载智能体或通道的每个前缀 — 以便这些请求到达 Hono 而不是资源处理器或 SPA 回退:

wrangler.jsonc
{
  "assets": {
    "directory": "./dist/client",
    "binding": "ASSETS",
    "not_found_handling": "single-page-application",
    "run_worker_first": ["/api/*", "/agents/*", "/channels/*"],
  },
}

调整前缀以匹配你的 app.ts 路由映射。

扩展生成的 Cloudflare Durable Objects

Flue 通常拥有每个生成的智能体 Durable Object 类。当智能体需要原生 Cloudflare Agents SDK 功能(如 onStart()、schedule()、scheduleEvery() 或 queue())时,从其模块导出一个 cloudflare 扩展描述符:

src/agents/heartbeat.ts
'use agent';
import { useModel } from '@flue/runtime';
import { extend } from '@flue/runtime/cloudflare';

export function Heartbeat() {
  useModel('anthropic/claude-sonnet-4-6');
}

export const cloudflare = extend({
  base: (Base) =>
    class extends Base {
      async onStart() {
        await this.scheduleEvery(60, 'heartbeat');
      }

      async heartbeat() {
        this.setState({ ...this.state, lastHeartbeatAt: Date.now() });
      }
    },
});

这是一个高级的 Cloudflare 专用扩展点。Flue 首先应用 base,然后定义自己的 Durable Object 子类,带有生成的绑定和类标识。对于 Heartbeat 智能体,编写的 Worker 代码可以通过 env.FLUE_HEARTBEAT_AGENT 访问命名空间,Wrangler 将该名称绑定到 FlueHeartbeatAgent。使用 base 用于原生 SDK 生命周期钩子和额外的命名方法。不要覆盖 fetch()、onRequest()、onFiberRecovered() 或 alarm():Flue 和 Agents SDK 使用这些方法进行路由、中断恢复和告警复用。

当集成需要包装最终的 Flue 生成的 Durable Object 类时使用 wrap:

import * as Sentry from '@sentry/cloudflare';

export const cloudflare = extend({
  wrap: (Final) =>
    Sentry.instrumentDurableObjectWithSentry((env: Env) => ({ dsn: env.SENTRY_DSN }), Final),
});

base 和 wrap 都是可选的。这个模块本地导出不同于下面可选的源根 cloudflare.ts 部署模块。原生 SDK 回调作为 Durable Object 活动运行:它们不会自动接收 Flue harness 或会话。

扩展 Worker

当你的部署需要 Flue 生成的类之外的原生 Cloudflare 功能时,添加一个可选的 src/cloudflare.ts 模块(路径可通过 flue.config.ts 中的 cloudflare 字段配置)。命名导出成为顶层 Worker 导出,这允许同一个 Worker 定义应用拥有的 Durable Objects:

src/cloudflare.ts
import { DurableObject } from 'cloudflare:workers';

export class SalesforceAuthCache extends DurableObject {
  async refreshIfNeeded() {
    return await this.ctx.storage.get('token');
  }
}

在你的项目根目录的 wrangler.jsonc 中声明相应的绑定和迁移:

{
  "durable_objects": {
    "bindings": [{ "name": "SALESFORCE_AUTH_CACHE", "class_name": "SalesforceAuthCache" }],
  },
  "migrations": [{ "tag": "v2", "new_sqlite_classes": ["SalesforceAuthCache"] }],
}

你的智能体通过 env.SALESFORCE_AUTH_CACHE 接收命名空间。在 Wrangler 配置中保持绑定、容器和有序的迁移历史;cloudflare.ts 提供 Worker 代码导出,但不推断部署拓扑。

可选的默认导出添加非 HTTP Worker 处理器:

export default {
  async scheduled(_controller, env) {
    await env.SALESFORCE_AUTH_CACHE.getByName('default').refreshIfNeeded();
  },
};

使用 app.ts 进行自定义 HTTP 路由和中间件。cloudflare.ts 不得导出默认的 fetch 处理器,因为 Flue 将 HTTP 组合保留在 app.ts 中。

子智能体

useSubagent(...) 声明一个命名的委托,模型可以通过任务将专注的工作交给它:

src/agents/assistant.ts
'use agent';
import { useModel, useSubagent } from '@flue/runtime';

function Triager() {
  return '彻底搜索,引用来源,保持简洁。';
}

export function Assistant() {
  useModel('anthropic/claude-sonnet-4-6');
  useSubagent({
    name: 'triager',
    description: '彻底研究一个主题并带回引用来源的报告。',
    agent: Triager,
  });
  return '通过任务将研究委托给 `triager` 子智能体。';
}

使用沙箱

默认情况下,虚拟沙箱启动时是空的 — 没有文件,没有技能,没有上下文。这对于像上面的翻译器那样的无状态提示 - 响应智能体来说没问题。但许多智能体需要文件来工作。

因为智能体有 shell 访问权限,它可以即时设置自己的工作空间,并且一个 harness 连接的工具(useTool({ harness: true }))可以在提示之前播种上下文:

src/agents/support.ts
'use agent';
import { useModel, useTool } from '@flue/runtime';
import * as v from 'valibot';

export function Support() {
  useModel('anthropic/claude-sonnet-4-6');
  useTool({
    name: 'answer',
    description: '使用工作空间文章回答一个支持请求。',
    input: v.object({ message: v.string() }),
    harness: true,
    async run({ harness, data }) {
      await harness.sandbox.writeFile(
        '/workspace/articles/reset-password.md',
        '# 重置你的密码\n\n使用账户设置页面请求密码重置邮件。',
      );

      const { text } = await harness.prompt(
        `搜索工作空间中与此请求相关的文章,然后撰写有用的回复。\n\n客户:${data.message}`,
      );
      return text;
    },
  });
  return '对于每个支持请求,使用客户消息调用 `answer` 工具。';
}

智能体可以使用其内置工具 — grep、glob、read — 来搜索和读取这些文件。这仍然在虚拟沙箱上运行(没有容器),因此速度快且成本低。如果应用需要持久化外部存储或完整的 Linux 环境,请选择并拥有适合该需求的沙箱适配器。

连接远程沙箱

上面的示例都运行在虚拟沙箱上 — 不需要容器。但对于需要完整 Linux 环境的智能体 — git、Node.js、浏览器、系统包 — 你需要一个远程沙箱。

Cloudflare 通过 @cloudflare/sandbox 提供原生容器支持。每个会话获得自己的隔离容器,带有持久化文件系统、shell 和完整的 Linux 用户空间。

如果你想连接到外部提供商(例如 Daytona)而不是在 Cloudflare 上运行沙箱,请参见连接 Daytona 沙箱。

设置

容器是 Workers 付费功能,使用 vite dev 进行本地开发需要 Docker 守护进程运行来构建和启动容器镜像。

你拥有容器配置。这意味着四件事:

  1. 安装 @cloudflare/sandbox:npm install @cloudflare/sandbox。
  2. 从 src/cloudflare.ts 导出 Sandbox 类。
  3. 在项目根目录的 wrangler.jsonc 中声明 Durable Object 绑定、迁移和容器镜像。
  4. 在你的 containers[].image 指向的路径提交一个 Dockerfile。

将 Sandbox 迁移追加到你用于生成的 Flue 类的同一个顶层历史中;不要替换已经部署的迁移。

示例

src/cloudflare.ts
export { Sandbox } from '@cloudflare/sandbox';

wrangler.jsonc(在项目根目录,与 package.json 同级):

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-agent",
  "compatibility_date": "2026-06-01",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [{ "class_name": "Sandbox", "name": "Sandbox" }],
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["FlueAssistantAgent"] },
    { "tag": "v2", "new_sqlite_classes": ["Sandbox"] },
  ],
  "containers": [{ "class_name": "Sandbox", "image": "./Dockerfile" }],
}

Dockerfile(在项目根目录):

FROM docker.io/cloudflare/sandbox:0.9.2

基础镜像由 Cloudflare 发布,捆绑了 @cloudflare/sandbox 与容器通信所需的控制面 HTTP 服务器,以及 node、git、curl 和工作目录 /workspace。将标签固定为与你 package.json 中 @cloudflare/sandbox 版本匹配 — 它们是同步版本控制的。根据需要添加你自己的 RUN 行来安装额外工具。

src/agents/assistant.ts
'use agent';
import { env } from 'cloudflare:workers';
import { type AgentProps, useModel, useSandbox } from '@flue/runtime';
import { cloudflareSandbox } from '@flue/runtime/cloudflare';
import { getSandbox } from '@cloudflare/sandbox';

interface Env {
  Sandbox: DurableObjectNamespace;
}

export function Assistant({ id }: AgentProps) {
  useModel('anthropic/claude-opus-4-7');
  const { Sandbox } = env as unknown as Env;
  useSandbox(cloudflareSandbox(getSandbox(Sandbox, id)));
  return '你有一个完整的 Linux 沙箱。用它来完成用户要求的任何事情。';
}

多个沙箱

不同的智能体可以使用不同的容器镜像。为每个 Sandbox 类导出一个单独的别名,然后声明每个绑定和容器条目:

src/cloudflare.ts
export { Sandbox as PyBoxSandbox } from '@cloudflare/sandbox';
export { Sandbox as NodeSandbox } from '@cloudflare/sandbox';
{
  "durable_objects": {
    "bindings": [
      { "class_name": "PyBoxSandbox", "name": "PyBox" },
      { "class_name": "NodeSandbox", "name": "NodeBox" },
    ],
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["FlueAssistantAgent"] },
    { "tag": "v2", "new_sqlite_classes": ["PyBoxSandbox", "NodeSandbox"] },
  ],
  "containers": [
    { "class_name": "PyBoxSandbox", "image": "./docker/python.Dockerfile" },
    { "class_name": "NodeSandbox", "image": "./docker/node.Dockerfile" },
  ],
}

每个智能体获取它需要的沙箱:cloudflareSandbox(getSandbox(env.PyBox, id)) 或 cloudflareSandbox(getSandbox(env.NodeBox, id))。

使用 outbound Workers 进行安全出口

当你的智能体在容器中运行时,它可能需要调用外部 API — GitHub、npm 注册表、内部服务。传统方法是将 API 令牌作为环境变量注入,但这意味着智能体(和 LLM)可以直接访问这些密钥。

Cloudflare 沙箱通过 outbound Workers 解决这个问题 — 一个可编程的出口代理,拦截容器发出的 HTTP/HTTPS 请求。密钥在代理层注入,因此容器永远看不到它们。这是在 Cloudflare Sandbox 类上配置的,在你的 Flue 智能体代码之外:

import { Sandbox } from '@cloudflare/sandbox';

export class MySandbox extends Sandbox {
  static outboundByHost = {
    'api.github.com': (request, env, ctx) => {
      const headers = new Headers(request.headers);
      headers.set('Authorization', `Bearer ${env.GITHUB_TOKEN}`);
      return fetch(request, { headers });
    },
  };
}

这是一个零信任模型 — 令牌永远不会授予不受信任的沙箱。代理在与容器相同的机器上运行,因此延迟最小。你还可以使用 outbound Workers 记录请求、阻止特定域或强制执行在会话生命周期内变化的动态策略。

完整详情请参见 outbound Workers 文档。

何时使用远程沙箱

虚拟沙箱在毫秒内启动,从 R2 或内联文件工作,提供 grep、glob、read 和基本 shell,适合高流量、大规模的智能体。远程沙箱需要几秒钟启动(缓存的镜像更快),以换取完整的 Linux 环境 — git、Node.js、Python、浏览器 — 和真正的持久化文件系统,这是编码智能体和复杂开发环境所需要的。

大多数智能体不需要远程沙箱。从虚拟沙箱开始,只有在你需要完整环境时才转向远程沙箱。

对话持久化

生成的 Cloudflare 应用在 Durable Object SQLite 中为每个智能体实例存储一个仅追加的规范对话流,附件字节存储在单独的不可变存储中。会话从该流中选择对话;没有第二个会话转录或持久化的对话快照。db.ts 是 Node 专用的约定 — 在 Cloudflare 上,Durable Object SQLite 是持久化层。

文件系统持久化仍然是一个单独的决策。默认的轻量级沙箱使用内存文件系统,不能仅仅因为对话状态存储在 Durable Object 中就将其视为持久化的。当文件或安装的制品必须在后续活动中保留时,请使用持久化工作空间或容器支持的集成。

客户端通过在对话 URL 上发送 GET 来读取物化历史或投影更新;参见流式协议。

中断和恢复语义

部署或代码更新可以在操作运行时重置 Durable Object。Flue 保守地处理中断的 Cloudflare 操作:

操作中断后
直接附加的 HTTP 提示接受的提示独立于其传输排队。Flue 仅在可以证明规范输入不存在时重新排队,识别可以证明已完成的规范输出,并在其他情况下记录可见的终端中断,而不会盲目重放 provider 工作。
分发的智能体输入持久化传递和内部去重由 submissionId 和持久化的提交状态键控。对同一智能体实例的直接和分发输入共享一个接受的顺序。协调使用相同的保守重放规则。

Cloudflare 直接提示和分发输入进入由目标智能体 Durable Object 拥有的一个 SQLite 支持的提交队列。附加的传输观察接受的后端工作但不拥有它:丢失 HTTP 响应不会取消已接受的提交。对话记录被持久化存储,对话流可以通过 Durable Streams 协议从任何偏移量重放。

在 provider 处理开始之前,Flue 持久化规范提交的输入并记录操作输入应用边界。中断后,Flue 仅在可以证明 provider 工作没有跨越该边界时重试。如果重放安全性不确定,它会将框架中断建议附加到规范会话历史中,并终止操作提交,而不是冒险重复模型工作或外部效果。对同一智能体实例的后续提示可以看到该事实建议。

外部效果仍然由应用拥有。中断可能使已启动的模型或工具活动的结果不确定,显式的调用者重试可能会重复效果。对于分发的智能体工作,将效果与 submissionId 或应用级幂等键关联。

提交有效负载在排队和运行时是持久的应用数据。已结算的提交数据被无限期保留。接收行也被无限期保留,为一个 submissionId 的重复转发提供重复传递保护;没有公开的提交查找 API。将持久化的输入视为敏感数据:不要提交密钥,除非你的应用保留和访问策略允许存储它们。

Flue 不会自动将 trace 载波与分发输入一起传播,也不会在持久化准入后保留原始附加的直接请求。有关 trace 解释和应用拥有的 HTTP 提取,请参见 OpenTelemetry。

对于需要持久化步骤级继续的工作,请使用 Cloudflare Workflows 实现这些步骤。

持久化格式边界

Flue 在第一次打开 Durable Object 数据库时,在其 flue_meta 表的一行中盖上其持久化格式版本,并拒绝打开由未知或更新格式版本盖印的数据库(例如,在回滚部署之后)。没有就地格式迁移:由不同格式版本盖印的状态必须被清除,或其类被弃用。KV 支持的 Durable Object 类仍在此边界之外,因为 Cloudflare 无法将它们就地转换为 SQLite — 生成的 Flue 智能体类必须通过 new_sqlite_classes 引入。

沙箱上下文

AGENTS.md 和技能是可选的工作空间上下文文件,智能体在 init() 时从其沙箱中读取。它们位于智能体使用的任何沙箱中的约定路径 — Flue 查找 <cwd>/AGENTS.md 和 <cwd>/.agents/skills/<name>/SKILL.md。有什么就加载什么;没有的就不加载。大多数智能体不需要两者来完成有用的工作。

如果你想使用它们,将它们放在你的沙箱中。如何做到取决于你使用的沙箱:通过 harness.sandbox 为虚拟沙箱写入,或通过 COPY 为容器复制进去。

技能是定义在 .agents/skills/ 中的可重用智能体任务,作为 markdown 文件:

.agents/skills/greet/SKILL.md:

---
name: greet
description: 为给定名称生成个性化问候。
---

根据参数中提供的名称,生成一个温暖、个性化的问候。
保持在一到两句话内。

沙箱根目录的 AGENTS.md 是智能体的系统提示 — 它提供关于项目的全局上下文。

从 Action 或工具主体中使用 harness.prompt(...) 指导技能 — 它共享智能体自己的对话上下文,因此只需命名技能就足以让模型激活它:

const { data } = await harness.prompt('对名称 "World" 应用 greet 技能。', {
  result: v.object({ greeting: v.string() }),
});

构建和部署

# 本地开发(从 .dev.vars 或 .env 读取本地变量)
npx vite dev

# 构建可部署的 Worker 输出
npx vite build

# 交互式配置已部署的密钥,然后部署构建的输出
npx wrangler secret put ANTHROPIC_API_KEY
npx wrangler deploy

每个挂载的智能体的对话都可以在你选择的挂载路径上访问:

curl -X POST 'https://my-agent.<your-subdomain>.workers.dev/agents/translator/customer-123' \
  -H "Content-Type: application/json" \
  -d '{"kind": "user", "body": "翻译为法语: Hello world"}'

使用 GET .../agents/translator/customer-123(历史)读取对话,或使用 ?view=updates&offset=...&live=sse 跟踪实时更新(参见流式协议)。

选择沙箱策略

以下是 Cloudflare 上可用的沙箱类型进展,从最简单到最强大:

  1. 空虚拟沙箱 — 只有 useModel(...) 的智能体函数。快速、便宜、无状态。适合提示 - 响应智能体。
  2. 带 shell 设置的虚拟沙箱 — 使用 harness.sandbox 写入文件和配置工作空间。仍然快速且便宜,适合需要少量静态上下文的智能体。
  3. 容器沙箱 — 通过 @cloudflare/sandbox 提供完整的 Linux 环境。适用于编码智能体、复杂开发环境和任何需要真实系统工具的场景。

从简单开始。需要时再升级。

可观测性

在 wrangler.jsonc 中为已部署的 Worker 启用 Cloudflare 的可观测性产品:

wrangler.jsonc
{
  "observability": {
    "enabled": true,
    "traces": { "enabled": true },
  },
}

启用日志后,来自智能体工作的工具和钩子日志会出现在 Workers Observability 仪表板中,归属于写入它们的工作。启用 traces(公测中)后,每个智能体响应产生一个 trace — 端到端运行响应的 Durable Object 调用,Workers AI 调用和其他子请求作为其中的 span。

启用 traces 后,每个 trace 还携带智能体级 span — 每个模型轮次的 invoke_agent、chat,每个工具调用的 execute_tool — 包含对话内容;createCloudflareTracing() 涵盖自定义内容捕获,tracing: false 选择退出。参见 Cloudflare 的 Workers Logs 和 Traces 文档了解采样、保留和定价。

有关运行时级视图 — token 使用、工具有效负载、结算和 Sentry、OpenTelemetry 等导出器 — 请参见可观测性。有关智能体执行如何映射到平台调用,请参见 Cloudflare 目标指南。