普通的无状态 Agent 每次请求都"失忆":用户刚说完的话、Agent 跑了一半的进度,刷新一次就没了。Cloudflare 的 Agents SDK(npm 包 agents)把每个 Agent 实例变成一个 Durable Object——自带一个 SQLite 数据库,状态自动持久化,断线、休眠、重启都不丢。本教程带你从零跑通:先写一个会"数数"的有状态 Agent,再升级成流式聊天 Agent,最后接上 React 前端。
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:安装与初始化项目
新建一个目录,初始化并安装核心包。聊天类 Agent 还需要 @cloudflare/ai-chat、ai、@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
npx wrangler dev 即可在本地起一个 Durable Object 运行时,不用先部署到 Cloudflare。Step 2:配置 wrangler.jsonc(绑定 Durable Object)
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)
下面这个 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:本地跑通
本地起运行时,然后用 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)
把 Counter 换成 AIChatAgent,重写 onChatMessage 用 streamText 流式返回,并自动持久化消息历史:
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:加记忆与定时任务
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 前端
前端用官方 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 |
| 模型调用 401 | OPENAI_API_KEY 未配置或模型名不可用 |
| 前端连不上 | agent 名与 wrangler 绑定名不一致 |