进阶 📋 7 个步骤 第 428 / 470 篇

用 Cloudflare Agents SDK 搭有状态 Agent:Durable Objects 持久化会话与记忆

用 Cloudflare Agents SDK(agents 包)把 Agent 变成自带 SQLite 的 Durable Object:状态自动持久化、可定时任务、可流式聊天,并接 React 前端。

2026.09.14· 20 分钟阅读· 约 1437 字· ☁️ Cloudflare / 🤖 有状态 Agent

普通的无状态 Agent 每次请求都"失忆":用户刚说完的话、Agent 跑了一半的进度,刷新一次就没了。Cloudflare 的 Agents SDK(npm 包 agents)把每个 Agent 实例变成一个 Durable Object——自带一个 SQLite 数据库,状态自动持久化,断线、休眠、重启都不丢。本教程带你从零跑通:先写一个会"数数"的有状态 Agent,再升级成流式聊天 Agent,最后接上 React 前端。

☁️ 本教程适合:想把 Agent 部署到边缘、需要会话/状态持久化、又不想自己管数据库的 TypeScript 开发者。你只需要 Node.js 18+ 和一个 Cloudflare 账号(免费版即可本地 wrangler dev 跑通)。

先搞懂:为什么需要"有状态 Agent"?

用一句话理解:普通 Agent 像一次性便签,用完即弃;Cloudflare Agents SDK 给每个 Agent 配了一个专属小仓库(SQLite),它会把状态写进仓库、随取随用。这意味着你可以做:跨多轮对话不丢上下文的客服、能定时发邮件的助手、多个用户同时协作的同一个 Agent 实例。

能力说明常用 API
状态持久化每个实例自带 SQLite,自动存、自动读this.state / this.setState()
定时任务到点执行、延迟执行、cron 表达this.schedule()
客户端 RPC浏览器经 WebSocket 直接调 Agent 方法@callable
流式聊天消息自动持久化、可断点续传AIChatAgent

本教程不内置鉴权。下例为最小可跑示例,直接公网暴露会有安全风险。生产环境务必在 fetch 里加 token / Cloudflare Access 等鉴权(详见步骤 7 的 warn)。

Step 1:安装与初始化项目

1 装好 agents 包

新建一个目录,初始化并安装核心包。聊天类 Agent 还需要 @cloudflare/ai-chatai@ai-sdk/openai

mkdir my-agent && cd my-agent
npm init -y
npm install agents
# 聊天智能体需要:
npm install @cloudflare/ai-chat ai @ai-sdk/openai
# 本地运行/部署用 wrangler:
npm install -D wrangler
💡 不想装全局 wrangler?用 npx wrangler dev 即可在本地起一个 Durable Object 运行时,不用先部署到 Cloudflare。

Step 2:配置 wrangler.jsonc(绑定 Durable Object)

2 告诉 Cloudflare 谁是 Agent 类

Agents SDK 依赖 Durable Objects。class_name 必须和你的 Agent 类名完全一致;new_sqlite_classes 列出所有需要 SQLite 存储的 Agent 类:

{
  "name": "my-agent",
  "main": "src/index.ts",
  "compatibility_date": "2025-01-01",
  "durable_objects": {
    "bindings": [
      { "name": "Chat", "class_name": "Chat" }
    ]
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["Chat"] }
  ]
}

生产部署前务必核对 migrations。每新增一个 Agent 类,都要在 new_sqlite_classes 里登记,否则该类没有持久化存储、状态会丢失。本地 wrangler dev 不强制,但上线会生效。

Step 3:写一个最小有状态 Agent(Counter)

3 用 this.setState 持久化状态

下面这个 Agent 每次调用 increment 都把计数 +1 并写回 SQLite。initialState 定义初始值,@callable 让客户端能直接远程调用:

import { Agent, routeAgentRequest } from "agents";

export class Counter extends Agent {
  initialState = { count: 0 };

  @callable()
  increment() {
    this.setState({ count: this.state.count + 1 });
    return this.state.count;
  }
}

export default {
  fetch(request, env) {
    return (
      routeAgentRequest(request, env) ??
      new Response("Not found", { status: 404 })
    );
  },
} satisfies ExportedHandler;
🔑 关键点:this.setState() 会自动把状态写进该实例的 SQLite,并广播给连着的客户端。你不用碰任何数据库代码。

Step 4:本地跑通

4 wrangler dev 起服务

本地起运行时,然后用 WebSocket 连上 Agent 调用 increment。前端可用官方 React hook useAgent(步骤 7)来简化:

npx wrangler dev
# 另一个终端:用 agents/react 的 useAgent 连上后调用
# const agent = useAgent({ agent: "Counter", name: "c1" });
# agent.increment();  // 返回 1,再调返回 2…… 重启 dev 后仍是累加值
🚀 验证"有状态"的最快办法:连上调用几次 increment,然后用 Ctrl+C 重启 wrangler dev,再连上调用——计数不会归零,因为存在 SQLite 里。

Step 5:升级为流式聊天 Agent(AIChatAgent)

5 接大模型做流式对话

Counter 换成 AIChatAgent,重写 onChatMessagestreamText 流式返回,并自动持久化消息历史:

import { AIChatAgent } from "@cloudflare/ai-chat";
import { routeAgentRequest } from "agents";
import { streamText, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";

export class Chat extends AIChatAgent {
  async onChatMessage(onFinish) {
    const result = streamText({
      model: openai("gpt-4o"),
      messages: await convertToModelMessages(this.messages),
      onFinish,
    });
    return result.toUIMessageStreamResponse();
  }
}

export default {
  fetch(request, env) {
    return (
      routeAgentRequest(request, env) ??
      new Response("Not found", { status: 404 })
    );
  },
} satisfies ExportedHandler;

模型名以你账号可用模型为准。示例写 openai("gpt-4o"),Cloudflare 与 OpenAI 文档会更新可用模型列表;换成你实际有额度/权限的模型名即可。记得在 .env 里放 OPENAI_API_KEY

Step 6:加记忆与定时任务

6 持久化偏好 + cron 触发

Agent 内可直接用 this.setState 存用户偏好,用 this.schedule 注册定时任务(Date / 秒数延迟 / cron 表达式均可):

export class Chat extends AIChatAgent {
  initialState = { preferences: {} };

  @callable()
  savePref(key: string, value: string) {
    this.setState({
      preferences: { ...this.state.preferences, [key]: value },
    });
  }

  async onStart() {
    // 每天 9 点触发一次 dailyBrief
    this.schedule("0 9 * * *", "dailyBrief");
  }

  async dailyBrief() {
    // 在这里发邮件 / 写日志 / 调接口
    console.log("定时任务执行,当前偏好:", this.state.preferences);
  }
}
🧠 onStart 在 Agent 实例创建时执行一次,适合放"注册定时任务""连接 MCP"等初始化逻辑。休眠后醒来会自动恢复 RPC 连接。

Step 7:接上 React 前端

7 useAgentChat 开箱即用

前端用官方 React hook 即可拿到流式消息、输入框和提交函数:

import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";

const agent = useAgent({ agent: "Chat", name: "my-chat" });
const { messages, input, handleSubmit } = useAgentChat({ agent });

// <form onSubmit={handleSubmit}>
//   <input value={input} onChange={(e) => agent.setState(...)} />
// </form>

公网部署务必加鉴权。本教程示例的 fetch 没有验证身份,任何人都能调用你的 Agent 并消耗模型额度。上线前请在 routeAgentRequest 之前校验 token / 接入 Cloudflare Access,并给敏感 @callable 方法加权限判断。

常见问题速查

现象原因 & 解决
状态不持久化没在 wrangler.jsonc 的 new_sqlite_classes 登记该类
本地能跑、上线报错migrations tag 冲突,按官方文档递增 tag
模型调用 401OPENAI_API_KEY 未配置或模型名不可用
前端连不上agent 名与 wrangler 绑定名不一致
← 返回教程中心