关于 cloudflare-agents
面向开发者的 Cloudflare Agents SDK 技能指南,用于在 Workers 和 Durable Objects 上构建有状态、实时运行的 Agent。它提供持久化状态、调度、多 Agent 协作和 MCP 支持,但不提供 AI 推理。
主要功能
- 01构建有状态的 Agent
- 02实时 WebSocket 通信
- 03SQLite 状态持久化
- 04任务调度与多 Agent 协作
- 05支持构建 MCP 服务
适用场景
- 01实现需要持久状态的 Agent
- 02构建实时双向聊天应用
- 03安排周期性或延迟任务
- 04构建支持 MCP 工具执行的 Agent
MCP 相关
Agents SDK 支持实现 MCP 服务,可用于有状态的工具执行。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill cloudflare-agents安装后在 Claude Code 中输入/cloudflare-agents确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.md.claude-plugin/plugin.jsonrules/cloudflare-agents.mdtemplates/basic-agent.tsbrowser-agent.tscalling-agents-worker.tschat-agent-streaming.tshitl-agent.tsmcp-server-basic.tsrag-agent.tsreact-useagent-client.tsxscheduled-agent.tssimple-chat-no-agents-sdk.tsstate-sync-agent.tswebsocket-agent.tsworkflow-agent.ts详细介绍
Cloudflare Agents SDK
状态:生产就绪 ✅ 最后更新:2026-01-09 依赖项:cloudflare-worker-base(推荐) 最新版本:[email protected]、@modelcontextprotocol/sdk@latest 生产环境测试:Cloudflare 自有的 MCP 服务(https://github.com/cloudflare/mcp-server-cloudflare)
近期更新(2025-2026):
- 2026 年 1 月:Agents SDK v0.3.6 修复可调用方法问题,并更新协议版本支持
- 2025 年 11 月:Agents SDK v0.2.24+ 支持可恢复流式传输(流在断开连接和刷新页面后仍会保留,并可在标签页和设备间同步),改进 MCP 客户端,修复计划任务问题
- 2025 年 9 月:兼容 AI SDK v5,自动迁移消息
- 2025 年 8 月:支持 MCP Elicitation、http-streamable 传输、任务队列和电子邮件集成
- 2025 年 4 月:支持 MCP(MCPAgent 类),从 agents 导入
context - 2025 年 3 月:包更名(agents-sdk → agents)
可恢复流式传输([email protected]+)
AIChatAgent 现在支持可恢复流式传输,客户端可以重新连接并继续接收流式响应,而不会丢失数据。这解决了关键的实际场景:
- 长时间运行的 AI 响应超过连接超时
- 用户处于不稳定的网络环境中(移动网络、飞机 Wi-Fi)
- 用户在对话过程中切换设备
- 用户离开页面后又返回的后台任务
- 多个客户端需要保持同步的实时协作
核心能力:流在刷新页面、连接中断后仍会保留,并可在打开的标签页和设备间同步。
实现方式(AIChatAgent 中自动处理):
export class ChatAgent extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
return streamText({
model: openai('gpt-4o-mini'),
messages: this.messages,
onFinish
}).toTextStreamResponse();
// ✅ Stream automatically resumable
// - Client disconnects? Stream preserved
// - Page refresh? Stream continues
// - Multiple tabs? All stay in sync
}
}
无需更改代码,只需使用 [email protected] 或更高版本的 AIChatAgent。
什么是 Cloudflare Agents?
Cloudflare Agents SDK 支持构建由 AI 驱动的自主 Agent,并在 Cloudflare Workers + Durable Objects 上运行。Agent 可以:
- 通过 WebSockets 和 Server-Sent Events 实时通信
- 使用内置 SQLite 数据库持久化状态(每个 Agent 最多 1GB)
- 使用延迟、指定日期或 cron 表达式安排任务
- 通过触发异步 Cloudflare Workflows 运行工作流
- 使用 Browser Rendering API + Puppeteer 浏览网页
- 使用 Vectorize 向量数据库 + Workers AI 嵌入实现 RAG
- 构建实现 Model Context Protocol 的 MCP 服务
- 支持人机协同审核与批准模式
- 在全球扩展至数百万个独立 Agent 实例
每个 Agent 实例都是一个全球唯一、有状态的微型服务,可运行数秒、数分钟或数小时。
你需要 Agents SDK 吗?
请先停下来:使用 Agents SDK 之前,先问问自己是否真的需要它。
以下情况仅使用 Vercel AI SDK(更简单):
- ✅ 构建基础聊天界面
- ✅ Server-Sent Events(SSE)流式传输已满足需求(单向:服务器 → 客户端)
- ✅ 无需持久化 Agent 状态(或使用 D1/KV 单独管理)
- ✅ 单用户、单对话场景
- ✅ 只需要 AI 响应,不需要复杂工作流或计划任务
这适用于 80% 的聊天应用。 对于这些场景,可直接在 Workers 上使用 Vercel AI SDK,它更简单、所需基础设施更少,还能自动处理流式传输。
示例(无需 Agents SDK):
// worker.ts - Simple chat with AI SDK only
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
export default {
async fetch(request: Request, env: Env) {
const { messages } = await request.json();
const result = streamText({
model: openai('gpt-4o-mini'),
messages
});
return result.toTextStreamResponse(); // Automatic SSE streaming
}
}
// client.tsx - React with built-in hooks
import { useChat } from 'ai/react';
function ChatPage() {
const { messages, input, handleSubmit } = useChat({ api: '/api/chat' });
// Done. No Agents SDK needed.
}
结果:代码量从 500 行减少到 100 行。无需设置 Durable Objects,无需处理 WebSocket 复杂性,也无需迁移。
在以下场景中使用 Agents SDK:
- ✅ WebSocket 连接(真正的双向实时通信)
- ✅ Durable Objects(全局唯一、有状态的 Agent 实例)
- ✅ 内置状态持久化(每个 Agent 最多可使用 1GB 的 SQLite 存储)
- ✅ 多 Agent 协调(Agent 之间相互调用和通信)
- ✅ 计划任务(延迟、cron 表达式、定期作业)
- ✅ 人在回路的工作流(审批关卡、审核流程)
- ✅ 长时间运行的 Agent(后台处理、自主工作流)
- ✅ MCP 服务,支持有状态的工具执行
这类场景约占应用的 20%——只有在需要 Agents SDK 提供的基础设施时才使用它。
关键认知:Agents SDK 是什么,不是什么
Agents SDK 是:
- 🏗️ 用于 WebSocket 连接、Durable Objects 和状态管理的基础设施层
- 🔧 用于构建有状态、自主 Agent 的框架
- 📦 带有生命周期方法的 Durable Objects封装层
Agents SDK 不是:
- ❌ AI 推理提供商(你需要自行选择:AI SDK、Workers AI、OpenAI 等)
- ❌ 流式响应处理器(使用 AI SDK 自动解析)
- ❌ 大模型集成(这是另一项独立的工作)
可以这样理解:
- Agents SDK = 建筑(WebSocket、状态、房间)
- AI SDK / Workers AI = AI 大脑(推理、思考、响应)
你可以将它们搭配使用(大多数情况下推荐这样做),也可以直接使用 Workers AI(前提是你愿意手动处理 SSE 解析)。
决策流程图
Building an AI application?
│
├─ Need WebSocket bidirectional communication? ───────┐
│ (Client sends while server streams, agent-initiated messages)
│
├─ Need Durable Objects stateful instances? ──────────┤
│ (Globally unique agents with persistent memory)
│
├─ Need multi-agent coordination? ────────────────────┤
│ (Agents calling/messaging other agents)
│
├─ Need scheduled tasks or cron jobs? ────────────────┤
│ (Delayed execution, recurring tasks)
│
├─ Need human-in-the-loop workflows? ─────────────────┤
│ (Approval gates, review processes)
│
└─ If ALL above are NO ─────────────────────────────→ Use AI SDK directly
(Much simpler approach)
If ANY above are YES ────────────────────────────→ Use Agents SDK + AI SDK
(More infrastructure, more power)
架构对比
| 特性 | 仅使用 AI SDK | Agents SDK + AI SDK |
|---|---|---|
| 设置复杂度 | 🟢 低(npm install 即可) | 🔴 较高(Durable Objects、迁移、绑定) |
| 代码量 | 🟢 约 100 行 | 🟡 约 500 行以上 |
| 流式传输 | ✅ 自动(SSE) | ✅ 自动(AI SDK)或手动(Workers AI) |
| 状态管理 | ⚠️ 手动(D1/KV) | ✅ 内置(SQLite) |
| WebSocket | ❌ 需手动设置 | ✅ 内置 |
| React Hooks | ✅ useChat、useCompletion | ⚠️ 需要自定义 Hooks |
| 多 Agent | ❌ 不支持 | ✅ 内置(routeAgentRequest) |
| 计划任务 | ❌ 外部实现(Queue/Workflow) | ✅ 内置(this.schedule) |
| 适用场景 | 简单聊天、补全 | 复杂的有状态工作流 |
还不确定?
从 AI SDK 开始。 如果之后发现需要 WebSocket 或 Durable Objects,随时可以迁移到 Agents SDK。之后再添加基础设施,比移除它更容易。
对于大多数开发者:如果你要构建聊天界面,且没有 WebSocket、多 Agent 协调或计划任务方面的特定需求,请直接使用 AI SDK。这样能更快交付,复杂度也更低。
只有在确认需要其基础设施功能时,才使用 Agents SDK。
快速开始(10 分钟)
1. 使用模板搭建项目
npm create cloudflare@latest my-agent -- \
--template=cloudflare/agents-starter \
--ts \
--git \
--deploy false
此操作会创建:
- 完整的 Agent 项目结构
- TypeScript 配置
- 带有 Durable Objects 绑定的 wrangler.jsonc
- 示例聊天 Agent 实现
- 带有 useAgent Hook 的 React 客户端
2. 或添加到现有 Worker
cd my-existing-worker
npm install agents
然后创建一个 Agent 类:
// src/index.ts
import { Agent, AgentNamespace } from "agents";
export class MyAgent extends Agent {
async onRequest(request: Request): Promise<Response> {
return new Response("Hello from Agent!");
}
}
export default MyAgent;
3. 配置 Durable Objects 绑定
创建或更新 wrangler.jsonc:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-agent",
"main": "src/index.ts",
"compatibility_date": "2025-10-21",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{
"name": "MyAgent", // MUST match class name
"class_name": "MyAgent" // MUST match exported class
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyAgent"] // CRITICAL: Enables SQLite storage
}
]
}
关键配置规则:
- ✅
name和class_name必须相同 - ✅
new_sqlite_classes必须包含在首次迁移中(之后无法再添加) - ✅ Agent 类必须导出(否则绑定会失败)
- ✅ 迁移标记不能重复使用(每次迁移都需要唯一标记)
4. 部署
npx wrangler@latest deploy
你的 Agent 现已运行于:https://my-agent.<subdomain>.workers.dev
架构概览:各部分如何协同工作
了解每种工具的作用,有助于避免混淆并选择合适的组合。
技术栈
┌─────────────────────────────────────────────────────────┐
│ Your Application │
│ │
│ ┌────────────────┐ ┌──────────────────────┐ │
│ │ Agents SDK │ │ AI Inference │ │
│ │ (Infra Layer) │ + │ (Brain Layer) │ │
│ │ │ │ │ │
│ │ • WebSockets │ │ Choose ONE: │ │
│ │ • Durable Objs │ │ • Vercel AI SDK ✅ │ │
│ │ • State (SQL) │ │ • Workers AI ⚠️ │ │
│ │ • Scheduling │ │ • OpenAI Direct │ │
│ │ • Multi-agent │ │ • Anthropic Direct │ │
│ └────────────────┘ └──────────────────────┘ │
│ ↓ ↓ │
│ Manages connections Generates responses │
│ and state and handles streaming │
└─────────────────────────────────────────────────────────┘
↓
Cloudflare Workers + Durable Objects
各工具提供的功能
1. Agents SDK(此 Skill)
用途:为有状态、实时的 Agent 提供基础设施
提供:
- ✅ WebSocket 连接管理(双向实时通信)
- ✅ Durable Objects 封装器(全局唯一的 Agent 实例)
- ✅ 内置状态持久化(SQLite,最大支持 1 GB)
- ✅ 生命周期方法(
onStart、onConnect、onMessage、onClose) - ✅ 任务调度(使用
this.schedule()设置 cron 或延迟) - ✅ 多 Agent 协调(
routeAgentRequest()) - ✅ 客户端库(
useAgent、AgentClient、agentFetch)
不提供:
- ❌ AI 推理(不调用大模型)
- ❌ 流式响应解析(需自行实现)
- ❌ 提供商集成(OpenAI、Anthropic 等)
可以把它看作:建筑和基础设施(房间、门、管道),但不包括住户(AI)。
2. Vercel AI SDK(AI 场景推荐)
用途:自动处理流式传输的 AI 推理
提供:
- ✅ 自动处理流式响应(为你解析 SSE)
- ✅ 支持多个提供商(OpenAI、Anthropic、Google 等)
- ✅ React Hooks(
useChat、useCompletion、useAssistant) - ✅ 跨提供商的统一 API
- ✅ 工具调用/函数调用
- ✅ 可在 Cloudflare Workers 上运行 ✅
示例:
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = streamText({
model: openai('gpt-4o-mini'),
messages: [...]
});
// Returns SSE stream - no manual parsing needed
return result.toTextStreamResponse();
与 Agents SDK 配合使用的场景:
- ✅ 大多数聊天应用
- ✅ 需要 React Hooks 时
- ✅ 使用多个 AI 提供商时
- ✅ 希望通过简洁、抽象化的方式调用 AI 时
与 Agents SDK 组合使用:
import { AIChatAgent } from "agents/ai-chat-agent";
import { streamText } from "ai";
export class MyAgent extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
// Agents SDK provides: WebSocket, state, this.messages
// AI SDK provides: Automatic streaming, provider abstraction
return streamText({
model: openai('gpt-4o-mini'),
messages: this.messages // Managed by Agents SDK
}).toTextStreamResponse();
}
}
3. Workers AI(AI 场景的替代方案)
用途:Cloudflare 平台内的 AI 推理
提供:
- ✅ 高性价比推理(包含在 Workers 订阅中)
- ✅ 无需外部 API 密钥
- ✅ 模型:LLaMA 3、Qwen、Mistral、嵌入模型等
- ✅ 在 Cloudflare 网络上运行(低延迟)
不提供:
- ❌ 自动解析流式传输(返回原始 SSE 格式)
- ❌ React Hooks
- ❌ 多提供商抽象
需要手动解析:
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [...],
stream: true
});
// Returns raw SSE format - YOU must parse
for await (const chunk of response) {
const text = new TextDecoder().decode(chunk); // Uint8Array → string
if (text.startsWith('data: ')) { // Check SSE format
const data = JSON.parse(text.slice(6)); // Parse JSON
if (data.response) { // Extract .response field
fullResponse += data.response;
}
}
}
适用场景:
- ✅ 成本至关重要(嵌入模型、高调用量)
- ✅ 需要 Cloudflare 专属模型
- ✅ 愿意手动处理 SSE 解析
- ✅ 不允许使用外部依赖
权衡:节省费用,但需要花时间手动解析。
推荐组合
选项 A:Agents SDK + Vercel AI SDK(推荐 ⭐)
适用场景:需要 WebSocket/状态管理,同时希望简洁地集成 AI
import { AIChatAgent } from "agents/ai-chat-agent";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export class ChatAgent extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
return streamText({
model: openai('gpt-4o-mini'),
messages: this.messages, // Agents SDK manages history
onFinish
}).toTextStreamResponse();
}
}
优点:
- ✅ 最佳开发体验
- ✅ 自动处理流式传输
- ✅ 使用 Agents SDK 提供 WebSocket 和状态管理
- ✅ 代码简洁、易于维护
缺点:
- ⚠️ 需要外部 API 密钥
- ⚠️ AI 提供商会产生额外费用
选项 B:Agents SDK + Workers AI
适用场景:需要 WebSocket/状态管理,同时成本至关重要
import { Agent } from "agents";
export class BudgetAgent extends Agent<Env> {
async onMessage(connection, message) {
const response = await this.env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [...],
stream: true
});
// Manual SSE parsing required (see Workers AI section above)
for await (const chunk of response) {
// ... manual parsing ...
}
}
}
优点:
- ✅ 高性价比
- ✅ 无需外部依赖
- ✅ 使用 Agents SDK 提供 WebSocket 和状态管理
缺点:
- ❌ 手动解析 SSE,增加复杂性
- ❌ 模型选择有限
- ❌ 需要维护更多代码
选项 C:仅使用 Vercel AI SDK(不使用 Agents)
适用场景:不需要 WebSocket 或 Durable Objects
// worker.ts - Simple Workers route
export default {
async fetch(request: Request, env: Env) {
const { messages } = await request.json();
const result = streamText({
model: openai('gpt-4o-mini'),
messages
});
return result.toTextStreamResponse();
}
}
// client.tsx - Built-in React hooks
import { useChat } from 'ai/react';
function Chat() {
const { messages, input, handleSubmit } = useChat({ api: '/api/chat' });
return <form onSubmit={handleSubmit}>...</form>;
}
优点:
- ✅ 实现方式最简单
- ✅ 代码最少
- ✅ 实现速度快
- ✅ 内置 React Hooks
缺点:
- ❌ 不支持 WebSocket(仅支持 SSE)
- ❌ 不支持 Durable Objects 状态
- ❌ 不支持多 Agent 协调
最适合:80% 的聊天应用
决策矩阵
| 需求 | 推荐技术栈 | 复杂度 | 成本 |
|---|---|---|---|
| 简单聊天,无状态管理 | 仅 AI SDK | 🟢 低 | $$(AI 提供商) |
| 聊天 + WebSocket + 状态管理 | Agents SDK + AI SDK | 🟡 中 | $$$(基础设施 + AI) |
| 聊天 + WebSocket + 预算有限 | Agents SDK + Workers AI | 🔴 高 | $(仅基础设施) |
| 多 Agent 工作流 | Agents SDK + AI SDK | 🔴 高 | $$$(基础设施 + AI) |
| 带工具的 MCP 服务 | Agents SDK(McpAgent) | 🟡 中 | $(仅基础设施) |
关键要点
**Agents SDK 是基础设施,而非 AI。**你需要将它与 AI 推理工具结合使用:
- 获得最佳开发体验:Agents SDK + Vercel AI SDK ⭐
- 节省成本:Agents SDK + Workers AI(接受手动解析)
- 简单易用:只使用 AI SDK(如果你不需要 WebSocket/状态)
本技能的其余部分聚焦于 Agents SDK(基础设施层)。关于 AI 推理模式,请参阅 ai-sdk-core 或 cloudflare-workers-ai 技能。
配置(wrangler.jsonc)
关键必需配置:
{
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] } // MUST be in first migration
]
}
常见可选绑定:ai、vectorize、browser、workflows、d1_databases、r2_buckets
关键迁移规则:
- ✅
new_sqlite_classes必须位于标签 "v1" 中(无法向已部署的类添加 SQLite) - ✅
name和class_name必须完全一致 - ✅ 迁移是原子的(所有实例会同时更新)
- ✅ 每个标签都必须唯一,不能编辑或移除之前的标签
参阅:https://developers.cloudflare.com/agents/api-reference/configuration/
Agent 核心模式
Agent 类基础知识——扩展 Agent<Env, State> 并实现生命周期方法:
onStart()——初始化 AgentonRequest()——处理 HTTP 请求onConnect/onMessage/onClose()——处理 WebSocketonStateUpdate()——响应状态变化
关键属性:
this.env——环境绑定(AI、DB 等)this.state——当前 Agent 状态(只读)this.setState()——更新持久化状态this.sql——内置 SQLite 数据库this.name——Agent 实例标识符this.schedule()——安排未来任务
参阅:Agent 官方 API 文档:https://developers.cloudflare.com/agents/api-reference/agents-api/
WebSocket 与实时通信
Agents 支持 WebSocket,可实现双向实时通信。适用于以下场景:
- 客户端可以在服务端持续推送内容时发送消息
- Agent 主动发送消息(通知、更新)
- 需要长连接和状态
基本模式:
export class ChatAgent extends Agent<Env, State> {
async onConnect(connection: Connection, ctx: ConnectionContext) {
// Auth check, add to participants, send welcome
}
async onMessage(connection: Connection, message: WSMessage) {
// Process message, update state, broadcast response
}
}
SSE 替代方案:对于单向服务端 → 客户端流式传输(更简单,基于 HTTP),请使用服务器发送事件,而非 WebSocket。
参阅:https://developers.cloudflare.com/agents/api-reference/websockets/
状态管理
两种状态机制:
-
this.setState(newState)——可序列化为 JSON 的状态(最大 1GB)- 自动持久化,并同步到 WebSocket 客户端
- 用于:用户偏好、会话数据、小型数据集
-
this.sql——内置 SQLite 数据库(最大 1GB)- 标记模板字面量可防止 SQL 注入
- 用于:关系型数据、大型数据集、复杂查询
状态规则:
- ✅ 仅支持可序列化为 JSON 的数据(对象、数组、原始值、null)
- ✅ 重启后仍会保留,并立即保持一致
- ❌ 不支持函数或循环引用
- ❌ 总上限为 1GB(状态与 SQL 合计)
SQL 模式:
await this.sql`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, email TEXT)`
await this.sql`INSERT INTO users (email) VALUES (${userEmail})` // ← Prepared statement
const users = await this.sql`SELECT * FROM users WHERE email = ${email}` // ← Returns array
状态类型安全注意事项
关键:为状态方法提供类型参数,并不会验证结果是否符合你的类型定义。在 TypeScript 中,不存在或不符合所提供类型的属性(字段)会被静默丢弃。
interface MyState {
count: number;
name: string;
}
export class MyAgent extends Agent<Env, MyState> {
initialState = { count: 0, name: "default" };
async increment() {
// TypeScript allows this, but runtime may differ
const currentState = this.state; // Type is MyState
// If state was corrupted/modified externally:
// { count: "invalid", otherField: 123 }
// TypeScript still shows it as MyState
// count field doesn't match (string vs number)
// otherField is dropped silently
}
}
预防措施:为关键状态操作添加运行时验证:
// Validate state shape at runtime
function validateState(state: unknown): state is MyState {
return (
typeof state === 'object' &&
state !== null &&
'count' in state &&
typeof (state as MyState).count === 'number' &&
'name' in state &&
typeof (state as MyState).name === 'string'
);
}
async increment() {
if (!validateState(this.state)) {
console.error('State validation failed', this.state);
// Reset to valid state
await this.setState(this.initialState);
return;
}
// Safe to use
const newCount = this.state.count + 1;
await this.setState({ ...this.state, count: newCount });
}
参阅:https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/
安排任务
Agents 可以使用 this.schedule() 安排未来运行的任务。
延迟(秒)
export class MyAgent extends Agent {
async onRequest(request: Request): Promise<Response> {
// Schedule task to run in 60 seconds
const { id } = await this.schedule(60, "checkStatus", { requestId: "123" });
return Response.json({ scheduledTaskId: id });
}
// This method will be called in 60 seconds
async checkStatus(data: { requestId: string }) {
console.log('Checking status for request:', data.requestId);
// Perform check, update state, send notification, etc.
}
}
指定日期
export class MyAgent extends Agent {
async scheduleReminder(reminderDate: string) {
const date = new Date(reminderDate);
const { id } = await this.schedule(date, "sendReminder", {
message: "Time for your appointment!"
});
return id;
}
async sendReminder(data: { message: string }) {
console.log('Sending reminder:', data.message);
// Send email, push notification, etc.
}
}
Cron 表达式
export class MyAgent extends Agent {
async setupRecurringTasks() {
// Every 10 minutes
await this.schedule("*/10 * * * *", "checkUpdates", {});
// Every day at 8 AM
await this.schedule("0 8 * * *", "dailyReport", {});
// Every Monday at 9 AM
await this.schedule("0 9 * * 1", "weeklyReport", {});
// Every hour on the hour
await this.schedule("0 * * * *", "hourlyCheck", {});
}
async checkUpdates(data: any) {
console.log('Checking for updates...');
}
async dailyReport(data: any) {
console.log('Generating daily report...');
}
async weeklyReport(data: any) {
console.log('Generating weekly report...');
}
async hourlyCheck(data: any) {
console.log('Running hourly check...');
}
}
管理计划任务
export class MyAgent extends Agent {
async manageSchedules() {
// Get all scheduled tasks
const allTasks = this.getSchedules();
console.log('Total tasks:', allTasks.length);
// Get specific task by ID
const taskId = "some-task-id";
const task = await this.getSchedule(taskId);
if (task) {
console.log('Task:', task.callback, 'at', new Date(task.time));
console.log('Payload:', task.payload);
console.log('Type:', task.type); // "scheduled" | "delayed" | "cron"
// Cancel the task
const cancelled = await this.cancelSchedule(taskId);
console.log('Cancelled:', cancelled);
}
// Get tasks in time range
const upcomingTasks = this.getSchedules({
timeRange: {
start: new Date(),
end: new Date(Date.now() + 24 * 60 * 60 * 1000) // Next 24 hours
}
});
console.log('Upcoming tasks:', upcomingTasks.length);
// Filter by type
const cronTasks = this.getSchedules({ type: "cron" });
const delayedTasks = this.getSchedules({ type: "delayed" });
}
}
计划任务限制:
- 每个任务对应一行 SQL 数据库记录(每个任务最大 2 MB)
- 任务总量限制为:
(task_size * count) + other_state < 1GB - Cron 任务会持续运行,直到被明确取消
- 回调方法必须存在于 Agent 类中(否则会抛出错误)
关键错误:如果回调方法不存在:
// ❌ BAD: Method doesn't exist
await this.schedule(60, "nonExistentMethod", {});
// ✅ GOOD: Method exists
await this.schedule(60, "existingMethod", {});
async existingMethod(data: any) {
// Implementation
}
运行工作流
Agent 可以触发异步的 Cloudflare Workflows。
工作流绑定配置
wrangler.jsonc:
{
"workflows": [
{
"name": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}
]
}
如果 Workflow 位于不同的脚本中:
{
"workflows": [
{
"name": "EMAIL_WORKFLOW",
"class_name": "EmailWorkflow",
"script_name": "email-workflows" // Different project
}
]
}
触发工作流
import { Agent } from "agents";
import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from "cloudflare:workers";
interface Env {
MY_WORKFLOW: Workflow;
MyAgent: AgentNamespace<MyAgent>;
}
export class MyAgent extends Agent<Env> {
async onRequest(request: Request): Promise<Response> {
const userId = new URL(request.url).searchParams.get('userId');
// Trigger a workflow immediately
const instance = await this.env.MY_WORKFLOW.create({
id: `user-${userId}`,
params: { userId, action: "process" }
});
// Or schedule a delayed workflow trigger
await this.schedule(300, "runWorkflow", { userId });
return Response.json({ workflowId: instance.id });
}
async runWorkflow(data: { userId: string }) {
const instance = await this.env.MY_WORKFLOW.create({
id: `delayed-${data.userId}`,
params: data
});
// Monitor workflow status periodically
await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
}
async checkWorkflowStatus(data: { id: string }) {
// Check workflow status (see Workflows docs for details)
console.log('Checking workflow:', data.id);
}
}
// Workflow definition (can be in same or different file/project)
export class MyWorkflow extends WorkflowEntrypoint<Env> {
async run(event: WorkflowEvent<{ userId: string }>, step: WorkflowStep) {
// Workflow implementation
const result = await step.do('process-data', async () => {
return { processed: true };
});
return result;
}
}
Agent 与工作流
| 功能 | Agent | 工作流 |
|---|---|---|
| 用途 | 交互式,面向用户 | 后台处理 |
| 持续时间 | 数秒到数小时 | 数分钟到数小时 |
| 状态 | SQLite 数据库 | 基于步骤的检查点 |
| 交互 | WebSockets、HTTP | 无直接交互 |
| 重试 | 手动 | 每个步骤自动重试 |
| 使用场景 | 聊天、实时 UI | ETL、批处理 |
最佳实践:使用 Agent 协调多个工作流。Agent 可以触发、监控工作流并响应其结果,同时保持与用户的交互。
浏览网页
Agent 可以使用 Browser Rendering 进行网页抓取和自动化:
绑定:将 "browser": { "binding": "BROWSER" } 添加到 wrangler.jsonc
软件包:@cloudflare/puppeteer
使用场景:网页抓取、截图,以及在 Agent 工作流中自动浏览网页
参见:cloudflare-browser-rendering 技能,了解完整的 Puppeteer + Workers 集成指南。
检索增强生成(RAG)
Agent 可以使用 Vectorize(向量数据库)+ Workers AI(嵌入)实现 RAG:
模式:摄取文档 → 生成嵌入 → 存储到 Vectorize → 查询 → 检索上下文 → 传递给 AI
绑定:
"ai": { "binding": "AI" }- 用于生成嵌入的 Workers AI"vectorize": { "bindings": [{ "binding": "VECTORIZE", "index_name": "my-vectors" }] }- 向量搜索
典型工作流:
- 使用 Workers AI 生成嵌入(
@cf/baai/bge-base-en-v1.5) - 将向量插入或更新至 Vectorize(
this.env.VECTORIZE.upsert(vectors)) - 查询相似向量(
this.env.VECTORIZE.query(queryVector, { topK: 5 })) - 在 AI 提示词中使用检索到的上下文
参见:cloudflare-vectorize 技能,了解完整的 RAG 实现指南。
使用 AI 模型
Agent 可以通过以下方式调用 AI 模型:
- Vercel AI SDK(推荐):支持多家提供商、自动流式传输和工具调用
- Workers AI:Cloudflare 的平台内推理(成本效益高,需手动解析)
架构说明:Agents SDK 提供基础设施(WebSockets、状态、调度)。AI 推理属于单独的一层——使用 AI SDK 作为“大脑”。
参见:
ai-sdk-core技能,了解完整的 AI SDK 集成模式cloudflare-workers-ai技能,了解 Workers AI 流式解析
调用 Agent
两种主要模式:
-
routeAgentRequest(request, env)- 通过 URL 模式/agents/:agent/:name自动路由- 示例:
/agents/my-agent/user-123路由到 MyAgent 实例“user-123”
- 示例:
-
getAgentByName<Env, T>(env.AgentBinding, instanceName)- 自定义路由- 返回 Agent 存根,用于调用方法或传递请求
- 示例:
const agent = getAgentByName(env.MyAgent, 'user-${userId}')
多 Agent 通信:
export class AgentA extends Agent<Env> {
async processData(data: any) {
const agentB = getAgentByName<Env, AgentB>(this.env.AgentB, 'processor-1');
return await (await agentB).analyze(data);
}
}
重要安全事项:务必在创建或访问 Agent 之前,先在 Worker 中完成身份验证。Agent 应假定调用者已获授权。
参见:https://developers.cloudflare.com/agents/api-reference/calling-agents/
客户端 API
浏览器/React 集成:
AgentClient(来自agents/client)- 用于浏览器的 WebSocket 客户端agentFetch(来自agents/client)- 向 Agent 发送 HTTP 请求useAgent(来自agents/react)- 用于 WebSocket 连接和状态同步的 React HookuseAgentChat(来自agents/ai-react)- 预构建的聊天 UI Hook
所有客户端库都会自动处理:WebSocket 连接、状态同步和重新连接逻辑。
参见:https://developers.cloudflare.com/agents/api-reference/client-apis/
模型上下文协议(MCP)
使用 Agents SDK 构建 MCP 服务。
MCP 服务设置
npm install @modelcontextprotocol/sdk agents
基本 MCP 服务
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({ name: "Demo", version: "1.0.0" });
async init() {
// Define a tool
this.server.tool(
"add",
"Add two numbers together",
{
a: z.number().describe("First number"),
b: z.number().describe("Second number")
},
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
}
}
有状态 MCP 服务
type State = { counter: number };
export class StatefulMCP extends McpAgent<Env, State> {
server = new McpServer({ name: "Counter", version: "1.0.0" });
initialState: State = { counter: 0 };
async init() {
// Resource
this.server.resource(
"counter",
"mcp://resource/counter",
(uri) => ({
contents: [{ uri: uri.href, text: String(this.state.counter) }]
})
);
// Tool
this.server.tool(
"increment",
"Increment the counter",
{ amount: z.number() },
async ({ amount }) => {
this.setState({
...this.state,
counter: this.state.counter + amount
});
return {
content: [{
type: "text",
text: `Counter is now ${this.state.counter}`
}]
};
}
);
}
}
MCP 传输配置
import { Hono } from 'hono';
const app = new Hono();
// Modern streamable HTTP transport (recommended)
app.mount('/mcp', MyMCP.serve('/mcp').fetch, { replaceRequest: false });
// Legacy SSE transport (deprecated)
app.mount('/sse', MyMCP.serveSSE('/sse').fetch, { replaceRequest: false });
export default app;
传输方式对比:
- /mcp:流式 HTTP(现代方式,推荐)
- /sse:服务器发送事件(旧版,已弃用)
MCP 协议版本支持
Agents SDK 支持多个 MCP 协议版本。从 [email protected] 起,版本验证较为宽松,可接受更新的协议版本:
- 支持的版本:
2024-11-05、2025-11-25及未来版本 - 错误(0.3.x 之前):
Error: Unsupported MCP protocol version: 2025-11-25 - 已修复:版本验证现在接受所有非过旧的协议版本
这与 MCP 社区转向无状态传输的方向一致。如果遇到协议版本错误,请更新到 [email protected] 或更高版本。
使用 OAuth 的 MCP
import { OAuthProvider } from '@cloudflare/workers-oauth-provider';
export default new OAuthProvider({
apiHandlers: {
'/sse': MyMCP.serveSSE('/sse'),
'/mcp': MyMCP.serve('/mcp')
},
// OAuth configuration
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
// ... other OAuth settings
});
测试 MCP 服务
# Run MCP inspector
npx @modelcontextprotocol/inspector@latest
# Connect to: http://localhost:8788/mcp
Cloudflare 的 MCP 服务:查看参考资料中的生产环境示例。
关键规则
务必做到 ✅
- 导出 Agent 类 - 必须导出,绑定才能生效
- 在 v1 迁移中包含 new_sqlite_classes - 之后无法再添加 SQLite
- 使绑定名称与类名一致 - 防止出现“找不到绑定”错误
- 在 Worker 中而非 Agent 中进行身份验证 - 安全最佳实践
- 使用带标签的模板字面量编写 SQL - 防止 SQL 注入
- 处理 WebSocket 断开连接 - 状态会保留,连接不会
- 确认已存在计划任务回调 - 如果方法缺失,会抛出错误
- 对用户专属 Agent 使用 idFromName() - 对持久状态绝不要使用 newUniqueId()(参见 Issue #23)
- 检查状态大小限制 - 每个 Agent 总计最多 1GB
- 监控任务负载大小 - 每个计划任务最多 2MB
- 正确使用工作流绑定 - 必须在 wrangler.jsonc 中配置
- 插入数据前创建 Vectorize 索引 - 元数据过滤所必需
- 关闭浏览器实例 - 防止资源泄漏
- 使用 setState() 实现持久化 - 不要只修改 this.state
- 先在本地测试迁移 - 迁移是原子操作,无法回滚
- 精简 WebSocket 消息历史记录 - 累积负载保持在 1MB 以内(参见 Issue #17)
- 在运行时验证状态结构 - TypeScript 类型不会强制执行运行时验证(参见 State Type Safety)
绝不要做 ❌
- 不要向现有的已部署类添加 SQLite - 必须在首次迁移中添加
- 不要逐步部署迁移 - 只能原子部署
- 不要跳过 Worker 中的身份验证 - 访问 Agent 前务必先进行身份验证
- 不要手动拼接 SQL 字符串 - 使用带标签的模板字面量
- 不要让每个 Agent 的状态超过 1GB - 硬性限制
- 不要为不存在的回调安排任务 - 会导致运行时错误
- 不要对用户专属 Agent 使用 newUniqueId() - 状态无法持久化(参见 Issue #23)
- 不要将 SSE 用于 MCP - 已弃用,请使用 /mcp 传输方式
- 不要忘记浏览器绑定 - 网页浏览必需
- 不要直接修改 this.state - 改用 setState()
- 不要让 WebSocket 负载超过 1MB - 连接会崩溃(参见 Issue #17)
- 不要依赖 TypeScript 类型验证状态 - 添加运行时检查(参见 State Type Safety)
避免已知问题
此技能可避免 23 个已记录的问题:
问题 1:迁移不是原子操作
错误:“Cannot gradually deploy migration”
来源:https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/
原因:迁移会同时应用于所有实例
预防方法:将迁移与代码更改分开部署,使用 npx wrangler versions deploy
问题 2:缺少 new_sqlite_classes
错误:"Cannot enable SQLite on existing class"
来源:https://developers.cloudflare.com/agents/api-reference/configuration/
原因:必须在首次迁移中启用 SQLite
预防措施:在标签为 "v1" 的迁移中加入 new_sqlite_classes
问题 3:未导出 Agent 类
错误:"Binding not found" 或 "Cannot access undefined"
来源:https://developers.cloudflare.com/agents/api-reference/agents-api/
原因:Durable Objects 要求导出类
预防措施:export class MyAgent extends Agent(包含 export 关键字)
问题 4:绑定名称不匹配
错误:"Binding 'X' not found"
来源:https://developers.cloudflare.com/agents/api-reference/configuration/
原因:绑定名称必须与类名完全一致
预防措施:确保 wrangler.jsonc 中的 name 和 class_name 完全一致
问题 5:不了解全局唯一性
错误:Agent 实例行为异常 来源:https://developers.cloudflare.com/agents/api-reference/agents-api/ 原因:相同名称始终会返回同一个全局 Agent 实例 预防措施:使用唯一标识符(userId、sessionId)作为实例名称
问题 6:WebSocket 状态未持久化
错误:断开连接后连接状态丢失 来源:https://developers.cloudflare.com/agents/api-reference/websockets/ 原因:WebSocket 连接不会持久化,但 Agent 状态会 预防措施:通过 setState() 将重要数据存储在 Agent 状态中,而不是连接状态中
问题 7:计划任务回调不存在/长时间 AI 请求超时
错误:"Method X does not exist on Agent" 或 "IoContext timed out due to inactivity" 或 "A call to blockConcurrencyWhile() waited for too long"
来源:https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ 和 GitHub 问题 #600
原因:this.schedule() 调用了未定义的方法;或者,由于 blockConcurrencyWhile 包装器,AI 请求超过 30 秒时计划回调会失败(已在 [email protected] 中通过 PR #653 修复)
预防措施:安排任务前,确保回调方法已存在。对于长时间运行的 AI 请求,请更新到 [email protected] 或更高版本。
历史问题(0.2.x 之前):计划回调被 blockConcurrencyWhile 包装,该包装器会强制执行 30 秒的限制。AI 请求超过此时长时会失败,即使这些请求是有效的异步操作。
// ✅ Fixed in 0.2.x - schedule callbacks can now run for their full duration
export class MyAgent extends Agent<Env> {
async onRequest(request: Request) {
await this.schedule(60, "processAIRequest", { query: "..." });
}
async processAIRequest(data: { query: string }) {
// This can now take > 30s without timeout
const result = await streamText({
model: openai('gpt-4o'),
messages: [{ role: 'user', content: data.query }]
});
}
}
问题 8:超出状态大小限制
错误:"Maximum database size exceeded" 来源:https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/ 原因:Agent 状态和计划任务总计超过 1GB 预防措施:监控状态大小;对于大型数据,请使用外部存储(D1、R2)
问题 9:计划任务过大
错误:"Task payload exceeds 2MB" 来源:https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ 原因:每个任务都映射到数据库行,大小限制为 2MB 预防措施:尽量精简任务负载,将大型数据存储在 Agent 状态/SQL 中
问题 10:缺少工作流绑定
错误:"Cannot read property 'create' of undefined" 来源:https://developers.cloudflare.com/agents/api-reference/run-workflows/ 原因:wrangler.jsonc 中未配置工作流绑定 预防措施:使用 this.env.WORKFLOW 前,先添加工作流绑定
问题 11:需要 Browser 绑定
错误:"BROWSER binding undefined"
来源:https://developers.cloudflare.com/agents/api-reference/browse-the-web/
原因:Browser Rendering 需要显式绑定
预防措施:在 wrangler.jsonc 中添加 "browser": { "binding": "BROWSER" }
问题 12:未找到 Vectorize 索引
错误:"Index does not exist"
来源:https://developers.cloudflare.com/agents/api-reference/rag/
原因:使用前必须创建 Vectorize 索引
预防措施:部署 Agent 前运行 wrangler vectorize create
问题 13:MCP 传输方式混淆
错误:“SSE 传输方式已弃用”
来源:https://developers.cloudflare.com/agents/model-context-protocol/transport/
原因:SSE 传输方式已属旧版,建议使用可流式传输的 HTTP
预防措施:使用 /mcp 端点和 MyMCP.serve('/mcp'),而非 /sse
问题 14:绕过身份验证
错误:安全漏洞 来源:https://developers.cloudflare.com/agents/api-reference/calling-agents/ 原因:在 Agent 中而非 Worker 中进行身份验证 预防措施:调用 getAgentByName() 前,务必在 Worker 中进行身份验证
问题 15:实例命名错误
错误:用户间数据泄露
来源:https://developers.cloudflare.com/agents/api-reference/calling-agents/
原因:实例命名不当,导致访问错误的 Agent
预防措施:使用带命名空间的名称,例如 user-${userId},并验证所有权
问题 16:Workers AI 流式传输需要手动解析
错误:“Cannot read property 'response' of undefined”或 AI 响应为空
来源:https://developers.cloudflare.com/workers-ai/platform/streaming/
原因:Workers AI 以 Server-Sent Events(SSE)格式将流式响应作为 Uint8Array 返回,而非普通对象
预防措施:使用 TextDecoder + SSE 解析模式(参见上文“Workers AI(AI 的替代方案)”部分)
问题 - 直接访问流块会失败:
const response = await env.AI.run(model, { stream: true });
for await (const chunk of response) {
console.log(chunk.response); // ❌ undefined - chunk is Uint8Array, not object
}
解决方案 - 手动解析 SSE 格式:
const response = await env.AI.run(model, { stream: true });
for await (const chunk of response) {
const text = new TextDecoder().decode(chunk); // Step 1: Uint8Array → string
if (text.startsWith('data: ')) { // Step 2: Check SSE format
const jsonStr = text.slice(6).trim(); // Step 3: Extract JSON from "data: {...}"
if (jsonStr === '[DONE]') break; // Step 4: Handle termination
const data = JSON.parse(jsonStr); // Step 5: Parse JSON
if (data.response) { // Step 6: Extract .response field
fullResponse += data.response;
}
}
}
更好的替代方案:使用 Vercel AI SDK 自动处理:
import { streamText } from 'ai';
import { createCloudflare } from '@ai-sdk/cloudflare';
const cloudflare = createCloudflare();
const result = streamText({
model: cloudflare('@cf/meta/llama-3-8b-instruct', { binding: env.AI }),
messages
});
// No manual parsing needed ✅
适合接受手动解析的情况:
- 成本至关重要(Workers AI 更便宜)
- 不允许使用外部依赖
- 愿意维护 SSE 解析代码
适合改用 AI SDK 的情况:
- 开发者时间比计算成本更重要
- 希望自动处理流式传输
- 需要支持多个提供商
问题 17:WebSocket 负载大小限制(1MB)
错误:Error: internal error; reference = [reference ID]
来源:GitHub Issue #119
原因:当累计消息负载超过约 1 MB 时,WebSocket 连接会失败。连续 5–6 次工具调用返回大量数据(例如,每次超过 200KB)后,负载会超出限制,连接因“internal error”而崩溃。
预防措施:在客户端精简消息历史,确保大小不超过 950KB
问题:包括大型工具结果在内的所有消息都会回传给客户端和大模型,以便继续对话,导致累计负载不断增长。
// Workaround: Prune old messages client-side
function pruneMessages(messages: Message[]): Message[] {
let totalSize = 0;
const pruned = [];
// Keep recent messages until we hit size limit
for (let i = messages.length - 1; i >= 0; i--) {
const msgSize = JSON.stringify(messages[i]).length;
if (totalSize + msgSize > 950_000) break; // 950KB limit
pruned.unshift(messages[i]);
totalSize += msgSize;
}
return pruned;
}
// Use before sending to agent
const prunedMessages = pruneMessages(allMessages);
更好的解决方案(提议):在服务端管理上下文,只向客户端发送消息摘要,而非完整的工具结果。
问题 18:使用 needsApproval 工具时出现重复的助手消息
错误:出现具有相同 toolCallId 的重复消息,原始消息卡在 input-available 状态
来源:GitHub Issue #790
原因:对工具使用 needsApproval: true 时,系统会创建重复的助手消息,而不是更新原始消息。客户端批准后,服务端生成的消息(状态为 input-available)不会转换为 approval-responded。
预防措施:请注意这是已知限制。问题修复前,在 UI 中跟踪这两条消息的 ID。
// Current behavior ([email protected])
export class MyAgent extends AIChatAgent<Env> {
tools = {
sensitiveAction: tool({
needsApproval: true, // ⚠️ Causes duplicate messages
execute: async (args) => {
return { result: "action completed" };
}
})
};
}
// Result: Two messages persist
// 1. Server message: ID "assistant_1768917665170_4mub00d32", state "input-available"
// 2. Client message: ID "oFwQwEpvLd8f1Gwd", state "approval-responded"
临时解决方法:在 UI 中处理这两条消息,或在问题解决前避免使用 needsApproval。
问题 19:客户端工具执行时出现重复消息(已在 0.2.31+ 中修复)
错误:Duplicate item found with id rs_xxx(OpenAI API 拒绝请求)
来源:GitHub Issue #728
修复版本:[email protected]+
原因:使用 useAgentChat 搭配缺少服务端 execute 函数的客户端工具时,agents 库会创建共享相同推理 ID 的重复助手消息,导致 OpenAI API 拒绝请求。
预防措施:更新到 [email protected] 或更高版本
历史问题(0.2.31 之前):客户端工具执行会创建新消息,而不是更新现有消息,导致原始消息一直处于未完成状态,并产生重复的 providerMetadata.openai.itemId 值。
// ✅ Fixed in 0.2.31+ - no changes needed
// Just ensure you're on [email protected] or later
问题 20:异步查询未遵循缓存 TTL(已修复)
错误:即使设置了 cacheTtl,令牌过期后仍出现 401 Unauthorized 错误
来源:GitHub Issue #725
修复版本:查看 agents 发布说明
原因:useAgent 钩子的缓存存在问题:每个 cacheKey 只计算一次 queryPromise,并永久保留,即使 TTL 已过期也是如此。useMemo 实现未将时间纳入依赖项,因此 TTL 从未生效。
预防措施:更新到最新版本的 agents
历史解决方法(修复前):
// Force cache invalidation by including token version in queryDeps
const [tokenVersion, setTokenVersion] = useState(0);
const { state } = useAgent({
query: async () => ({ token: await getJWT() }),
queryDeps: [tokenVersion], // ✅ Force new cache key
cacheTtl: 60_000,
});
// Manually refresh token before expiry
useEffect(() => {
const interval = setInterval(() => {
setTokenVersion(v => v + 1);
}, 50_000); // Refresh every 50s
return () => clearInterval(interval);
}, []);
问题 21:DO 休眠后 jsonSchemaValidator 失效(已修复)
错误:TypeError: validator.getValidator is not a function
来源:GitHub Issue #663
修复版本:查看 agents 发布说明
原因:Durable Object 休眠并恢复后,Agents SDK 会使用 JSON.stringify() 序列化 MCP 连接选项,将 CfWorkerJsonSchemaValidator 等类实例转换为没有方法的普通对象。通过 JSON.parse() 恢复后,验证器会丢失其方法。
预防措施:更新到最新版本的 agents(验证器现已内置)
历史问题(修复前):
// ❌ Before fix - manual validator caused errors
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/cloudflare-worker';
const mcpAgent = new McpAgent({
client: {
jsonSchemaValidator: new CfWorkerJsonSchemaValidator(), // Got serialized to {}
}
});
当前方案(已修复):
// ✅ Now automatic - no manual validator needed
const mcpAgent = new McpAgent({
// SDK handles validator internally
});
问题 22:WorkerTransport 在休眠后丢失 ClientCapabilities(已在 0.3.5+ 中修复)
错误:Error: Client does not support form elicitation
来源:GitHub Issue #777
修复版本:[email protected]+
原因:在无服务器环境中将 WorkerTransport 与 MCP 服务搭配使用时,由于 TransportState 接口只存储 sessionId 和 initialized 状态,而不存储 clientCapabilities,客户端能力无法在 Durable Object 的休眠周期之间持久保存。
预防措施:更新到 [email protected] 或更高版本
历史问题(0.3.5 之前):
// Client advertised elicitation capability during handshake,
// but after hibernation, capability info was lost
await server.elicitInput({ /* form */ }); // ❌ Error: capabilities lost
解决方案(已在 0.3.5 中修复):
// TransportState now includes clientCapabilities
interface TransportState {
sessionId: string;
initialized: boolean;
clientCapabilities?: ClientCapabilities; // ✅ Now persisted
}
问题 23:idFromName() 与 newUniqueId() 的关键用法
错误:状态从不持久保存,每次请求都会创建新的 Agent 实例
来源:Cloudflare 博客 - 使用 OpenAI 构建 Agent
原因:如果使用 newUniqueId() 而不是 idFromName(),每次都会获得一个新的 Agent 实例,内存/状态也永远不会持久保存。这是常见的早期错误,会在不易察觉的情况下破坏有状态能力。
预防措施:用户专属的 Agent 一律使用 idFromName(),不要使用 newUniqueId()
// ❌ WRONG: Creates new agent every time (state never persists)
export default {
async fetch(request: Request, env: Env) {
const id = env.MyAgent.newUniqueId(); // New ID = new instance
const agent = env.MyAgent.get(id);
// State never persists - different instance each time
return agent.fetch(request);
}
}
// ✅ CORRECT: Same user = same agent = persistent state
export default {
async fetch(request: Request, env: Env) {
const userId = getUserId(request);
const id = env.MyAgent.idFromName(userId); // Same ID for same user
const agent = env.MyAgent.get(id);
// State persists across requests for this user
return agent.fetch(request);
}
}
重要性:
newUniqueId():每次调用都会生成随机的唯一 ID → 新的 Agent 实例idFromName(string):根据字符串生成确定性 ID → 相同输入对应同一个 Agent
经验法则:99% 的情况下都使用 idFromName()。只有确实需要一次性、临时的 Agent 实例时,才使用 newUniqueId()。
依赖项
必需
- cloudflare-worker-base - 基础框架(Hono、Vite、Workers 配置)
可选(按功能)
- cloudflare-workers-ai - 用于调用 Workers AI 模型
- cloudflare-vectorize - 用于使用 Vectorize 实现 RAG
- cloudflare-d1 - 用于存储 Agent 状态之外的其他持久化数据
- cloudflare-r2 - 用于文件存储
- cloudflare-queues - 用于消息队列
NPM 包
agents- Agents SDK(必需)@modelcontextprotocol/sdk- 用于构建 MCP 服务@cloudflare/puppeteer- 用于网页浏览ai- 用于调用模型的 AI SDK@ai-sdk/openai- OpenAI 模型@ai-sdk/anthropic- Anthropic 模型
官方文档
- Agents SDK:https://developers.cloudflare.com/agents/
- API 参考:https://developers.cloudflare.com/agents/api-reference/
- Durable Objects:https://developers.cloudflare.com/durable-objects/
- 工作流:https://developers.cloudflare.com/workflows/
- Vectorize:https://developers.cloudflare.com/vectorize/
- 浏览器渲染:https://developers.cloudflare.com/browser-rendering/
- Model Context Protocol:https://modelcontextprotocol.io/
- Cloudflare MCP 服务:https://github.com/cloudflare/mcp-server-cloudflare
捆绑资源
模板(templates/)
wrangler-agents-config.jsonc- 完整的配置示例basic-agent.ts- 最精简的 HTTP Agentwebsocket-agent.ts- WebSocket 处理程序state-sync-agent.ts- 状态管理模式scheduled-agent.ts- 任务调度workflow-agent.ts- 工作流集成browser-agent.ts- 网页浏览rag-agent.ts- RAG 实现chat-agent-streaming.ts- 流式聊天calling-agents-worker.ts- Agent 路由react-useagent-client.tsx- React 客户端mcp-server-basic.ts- MCP 服务hitl-agent.ts- 人机协作
参考资料(references/)
agent-class-api.md- 完整的 Agent 类参考client-api-reference.md- 浏览器客户端 APIstate-management-guide.md- 状态与 SQL 深入指南websockets-sse.md- WebSocket 与 SSE 对比scheduling-api.md- 任务调度详情workflows-integration.md- 工作流指南browser-rendering.md- 网页浏览模式rag-patterns.md- RAG 最佳实践mcp-server-guide.md- MCP 服务开发mcp-tools-reference.md- MCP 工具 APIhitl-patterns.md- 人机协作工作流best-practices.md- 生产环境模式
示例(examples/)
chat-bot-complete.md- 完整的聊天 Agentmulti-agent-workflow.md- Agent 编排scheduled-reports.md- 定期任务browser-scraper-agent.md- 网页抓取rag-knowledge-base.md- RAG 系统mcp-remote-server.md- 生产环境 MCP 服务
最后验证时间:2026-01-21 包版本:[email protected] 合规性:符合 Cloudflare Agents SDK 官方文档 变更:根据社区调研新增 7 个问题(WebSocket 负载限制、状态类型安全、idFromName 陷阱),并更新至 [email protected]