cloudflare-agents

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Agents SDK

Cloudflare Agents SDK

Status: Production Ready ✅ Last Updated: 2026-01-09 Dependencies: cloudflare-worker-base (recommended) Latest Versions: agents@0.3.3, @modelcontextprotocol/sdk@latest Production Tested: Cloudflare's own MCP servers (https://github.com/cloudflare/mcp-server-cloudflare)
Recent Updates (2025-2026):
  • Jan 2026: Agents SDK v0.3.6 with callable methods fix, protocol version support updates
  • Nov 2025: Agents SDK v0.2.24+ with resumable streaming (streams persist across disconnects, page refreshes, and sync across tabs/devices), MCP client improvements, schedule fixes
  • Sept 2025: AI SDK v5 compatibility, automatic message migration
  • Aug 2025: MCP Elicitation support, http-streamable transport, task queues, email integration
  • April 2025: MCP support (MCPAgent class),
    import { context }
    from agents
  • March 2025: Package rename (agents-sdk → agents)
状态:已就绪可用于生产环境 ✅ 最后更新:2026-01-09 依赖项:cloudflare-worker-base(推荐) 最新版本:agents@0.3.3, @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流式传输、任务队列、邮件集成
  • 2025年4月:支持MCP(新增MCPAgent类),可从agents中
    import { context }
  • 2025年3月:包重命名(agents-sdk → agents)

Resumable Streaming (agents@0.2.24+)

可恢复式流式传输(agents@0.2.24+)

AIChatAgent now supports resumable streaming, enabling clients to reconnect and continue receiving streamed responses without data loss. This solves critical real-world scenarios:
  • Long-running AI responses that exceed connection timeout
  • Users on unreliable networks (mobile, airplane WiFi)
  • Users switching between devices mid-conversation
  • Background tasks where users navigate away and return
  • Real-time collaboration where multiple clients need to stay in sync
Key capability: Streams persist across page refreshes, broken connections, and sync across open tabs and devices.
Implementation (automatic in AIChatAgent):
typescript
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
  }
}
No code changes needed - just use AIChatAgent with agents@0.2.24 or later.

AIChatAgent现在支持可恢复式流式传输,客户端重新连接后可继续接收流式响应,不会丢失数据。这解决了以下关键实际场景问题:
  • 超过连接超时的长时AI响应
  • 网络不稳定的用户(移动网络、飞机WiFi)
  • 对话中途切换设备的用户
  • 用户离开后返回的后台任务
  • 多客户端需要保持同步的实时协作场景
核心能力:流式连接可在页面刷新、连接中断、多标签/设备间同步保留。
实现方式(AIChatAgent中自动生效):
typescript
export class ChatAgent extends AIChatAgent<Env> {
  async onChatMessage(onFinish) {
    return streamText({
      model: openai('gpt-4o-mini'),
      messages: this.messages,
      onFinish
    }).toTextStreamResponse();

    // ✅ 流自动可恢复
    // - 客户端断开?流会保留
    // - 页面刷新?流会继续
    // - 多标签页面?全部保持同步
  }
}
无需修改代码 - 只需使用agents@0.2.24或更高版本的AIChatAgent即可。

What is Cloudflare Agents?

什么是Cloudflare Agents?

The Cloudflare Agents SDK enables building AI-powered autonomous agents that run on Cloudflare Workers + Durable Objects. Agents can:
  • Communicate in real-time via WebSockets and Server-Sent Events
  • Persist state with built-in SQLite database (up to 1GB per agent)
  • Schedule tasks using delays, specific dates, or cron expressions
  • Run workflows by triggering asynchronous Cloudflare Workflows
  • Browse the web using Browser Rendering API + Puppeteer
  • Implement RAG with Vectorize vector database + Workers AI embeddings
  • Build MCP servers implementing the Model Context Protocol
  • Support human-in-the-loop patterns for review and approval
  • Scale to millions of independent agent instances globally
Each agent instance is a globally unique, stateful micro-server that can run for seconds, minutes, or hours.

Cloudflare Agents SDK用于构建运行在Cloudflare Workers + Durable Objects上的AI驱动自主Agent。Agent具备以下能力:
  • 实时双向通信:通过WebSocket和Server-Sent Events实现
  • 状态持久化:内置SQLite数据库(每个Agent最多1GB)
  • 任务调度:支持延迟执行、指定日期或cron表达式
  • 工作流运行:触发异步Cloudflare Workflows
  • 网页浏览:使用Browser Rendering API + Puppeteer
  • RAG实现:结合Vectorize向量数据库 + Workers AI嵌入
  • MCP服务器构建:实现Model Context Protocol
  • 人在回路模式:支持审核与批准流程
  • 全球百万级扩展:支持独立Agent实例的全球大规模扩展
每个Agent实例都是全局唯一的有状态微服务,可运行数秒、数分钟或数小时。

Do You Need Agents SDK?

是否需要Agents SDK?

STOP: Before using Agents SDK, ask yourself if you actually need it.
注意:在使用Agents SDK之前,请先确认你是否真的需要它。

Use JUST Vercel AI SDK (Simpler) When:

仅使用Vercel AI SDK(更简单)的场景:

  • ✅ Building a basic chat interface
  • ✅ Server-Sent Events (SSE) streaming is sufficient (one-way: server → client)
  • ✅ No persistent agent state needed (or you manage it separately with D1/KV)
  • ✅ Single-user, single-conversation scenarios
  • ✅ Just need AI responses, no complex workflows or scheduling
This covers 80% of chat applications. For these cases, use Vercel AI SDK directly on Workers - it's simpler, requires less infrastructure, and handles streaming automatically.
Example (no Agents SDK needed):
typescript
// 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.
}
Result: 100 lines of code instead of 500. No Durable Objects setup, no WebSocket complexity, no migrations.

  • ✅ 构建基础聊天界面
  • ✅ Server-Sent Events (SSE)流式传输已足够(单向:服务器→客户端)
  • ✅ 不需要持久化Agent状态(或通过D1/KV单独管理)
  • ✅ 单用户、单对话场景
  • ✅ 仅需要AI响应,无需复杂工作流或调度
这覆盖了80%的聊天应用场景。对于这些场景,直接在Workers上使用Vercel AI SDK即可 - 它更简单,所需基础设施更少,且自动处理流式传输。
示例(无需Agents SDK):
typescript
// worker.ts - 仅使用AI SDK的简单聊天
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(); // 自动SSE流式传输
  }
}

// client.tsx - 带有内置钩子的React客户端
import { useChat } from 'ai/react';

function ChatPage() {
  const { messages, input, handleSubmit } = useChat({ api: '/api/chat' });
  // 完成。无需Agents SDK。
}
结果:只需约100行代码,而非500行。无需Durable Objects配置、WebSocket复杂度或迁移操作。

Use Agents SDK When You Need:

需要使用Agents SDK的场景:

  • WebSocket connections (true bidirectional real-time communication)
  • Durable Objects (globally unique, stateful agent instances)
  • Built-in state persistence (SQLite storage up to 1GB per agent)
  • Multi-agent coordination (agents calling and communicating with each other)
  • Scheduled tasks (delays, cron expressions, recurring jobs)
  • Human-in-the-loop workflows (approval gates, review processes)
  • Long-running agents (background processing, autonomous workflows)
  • MCP servers with stateful tool execution
This is ~20% of applications - when you need the infrastructure that Agents SDK provides.

  • WebSocket连接(真正的双向实时通信)
  • Durable Objects(全局唯一的有状态Agent实例)
  • 内置状态持久化(每个Agent最多1GB的SQLite存储)
  • 多Agent协同(Agent之间可调用与通信)
  • 任务调度(延迟执行、cron表达式、周期性任务)
  • 人在回路工作流(审批 gate、审核流程)
  • 长时运行Agent(后台处理、自主工作流)
  • MCP服务器:支持有状态工具执行
这约占20%的应用场景 - 当你需要Agents SDK提供的基础设施能力时使用。

Key Understanding: What Agents SDK IS vs IS NOT

核心认知:Agents SDK能做什么 vs 不能做什么

Agents SDK IS:
  • 🏗️ Infrastructure layer for WebSocket connections, Durable Objects, and state management
  • 🔧 Framework for building stateful, autonomous agents
  • 📦 Wrapper around Durable Objects with lifecycle methods
Agents SDK IS NOT:
  • AI inference provider (you bring your own: AI SDK, Workers AI, OpenAI, etc.)
  • Streaming response handler (use AI SDK for automatic parsing)
  • LLM integration (that's a separate concern)
Think of it this way:
  • Agents SDK = The building (WebSockets, state, rooms)
  • AI SDK / Workers AI = The AI brain (inference, reasoning, responses)
You can use them together (recommended for most cases), or use Workers AI directly (if you're willing to handle manual SSE parsing).

Agents SDK能做
  • 🏗️ 基础设施层:管理WebSocket连接、Durable Objects与状态
  • 🔧 开发框架:用于构建有状态的自主Agent
  • 📦 Durable Objects封装:提供生命周期方法
Agents SDK不能做
  • AI推理提供商:你需要自行引入(AI SDK、Workers AI、OpenAI等)
  • 流式响应处理器:使用AI SDK进行自动解析
  • LLM集成:这是独立的关注点
可以这样理解
  • Agents SDK = 建筑(WebSocket、状态、房间)
  • AI SDK / Workers AI = AI大脑(推理、决策、响应)
你可以将它们结合使用(大多数场景推荐),也可以直接使用Workers AI(如果你愿意手动处理SSE解析)。

Decision Flowchart

决策流程图

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应用?
├─ 是否需要WebSocket双向通信? ───────┐
│  (客户端发送消息时服务器同时流式传输,Agent主动发起消息)
├─ 是否需要Durable Objects有状态实例? ──────────┤
│  (全局唯一、带持久化记忆的Agent)
├─ 是否需要多Agent协同? ────────────────────┤
│  (Agent之间调用/通信)
├─ 是否需要任务调度或cron任务? ────────────────┤
│  (延迟执行、周期性任务)
├─ 是否需要人在回路工作流? ─────────────────┤
│  (审批 gate、审核流程)
└─ 如果以上全否 ─────────────────────────────→ 直接使用AI SDK
                                                       (更简单的方案)

   如果以上任一为是 ────────────────────────────→ 使用Agents SDK + AI SDK
                                                       (更多基础设施,更强能力)

Architecture Comparison

架构对比

FeatureAI SDK OnlyAgents SDK + AI SDK
Setup Complexity🟢 Low (npm install, done)🔴 Higher (Durable Objects, migrations, bindings)
Code Volume🟢 ~100 lines🟡 ~500+ lines
Streaming✅ Automatic (SSE)✅ Automatic (AI SDK) or manual (Workers AI)
State Management⚠️ Manual (D1/KV)✅ Built-in (SQLite)
WebSockets❌ Manual setup✅ Built-in
React Hooks✅ useChat, useCompletion⚠️ Custom hooks needed
Multi-agent❌ Not supported✅ Built-in (routeAgentRequest)
Scheduling❌ External (Queue/Workflow)✅ Built-in (this.schedule)
Use CaseSimple chat, completionsComplex stateful workflows

特性仅使用AI SDKAgents SDK + AI SDK
配置复杂度🟢 低(npm安装即可)🔴 较高(Durable Objects、迁移、绑定)
代码量🟢 ~100行🟡 ~500+行
流式传输✅ 自动(SSE)✅ 自动(AI SDK)或手动(Workers AI)
状态管理⚠️ 手动(D1/KV)✅ 内置(SQLite)
WebSocket❌ 需手动配置✅ 内置
React钩子✅ useChat, useCompletion⚠️ 需要自定义钩子
多Agent❌ 不支持✅ 内置(routeAgentRequest)
任务调度❌ 需外部工具(Queue/Workflow)✅ 内置(this.schedule)
适用场景简单聊天、补全复杂有状态工作流

Still Not Sure?

仍不确定?

Start with AI SDK. You can always migrate to Agents SDK later if you discover you need WebSockets or Durable Objects. It's easier to add infrastructure later than to remove it.
For most developers: If you're building a chat interface and don't have specific requirements for WebSockets, multi-agent coordination, or scheduled tasks, use AI SDK directly. You'll ship faster and with less complexity.
Proceed with Agents SDK only if you've identified a specific need for its infrastructure capabilities.

从AI SDK开始。如果之后发现需要WebSocket或Durable Objects,再迁移到Agents SDK。后期添加基础设施比移除更容易。
对于大多数开发者:如果你正在构建聊天界面,且没有WebSocket、多Agent协同或任务调度的特定需求,请直接使用AI SDK。你将更快交付,且复杂度更低。
仅当你明确需要其基础设施能力时,再使用Agents SDK。

Quick Start (10 Minutes)

快速开始(10分钟)

1. Scaffold Project with Template

1. 使用模板搭建项目

bash
npm create cloudflare@latest my-agent -- \
  --template=cloudflare/agents-starter \
  --ts \
  --git \
  --deploy false
What this creates:
  • Complete Agent project structure
  • TypeScript configuration
  • wrangler.jsonc with Durable Objects bindings
  • Example chat agent implementation
  • React client with useAgent hook
bash
npm create cloudflare@latest my-agent -- \
  --template=cloudflare/agents-starter \
  --ts \
  --git \
  --deploy false
创建的内容
  • 完整的Agent项目结构
  • TypeScript配置
  • 带有Durable Objects绑定的wrangler.jsonc
  • 示例聊天Agent实现
  • 带有useAgent钩子的React客户端

2. Or Add to Existing Worker

2. 或添加到现有Worker项目

bash
cd my-existing-worker
npm install agents
Then create an Agent class:
typescript
// 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;
bash
cd my-existing-worker
npm install agents
然后创建Agent类
typescript
// 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. Configure Durable Objects Binding

3. 配置Durable Objects绑定

Create or update
wrangler.jsonc
:
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
    }
  ]
}
CRITICAL Configuration Rules:
  • name
    and
    class_name
    MUST be identical
  • new_sqlite_classes
    MUST be in first migration (cannot add later)
  • ✅ Agent class MUST be exported (or binding will fail)
  • ✅ Migration tags CANNOT be reused (each migration needs unique tag)
创建或更新
wrangler.jsonc
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",        // 必须与类名匹配
        "class_name": "MyAgent"   // 必须与导出的类名匹配
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["MyAgent"]  // 关键:启用SQLite存储
    }
  ]
}
关键配置规则
  • name
    class_name
    必须完全相同
  • new_sqlite_classes
    必须在首次迁移中添加(之后无法添加)
  • ✅ Agent类 必须导出(否则绑定会失败)
  • ✅ 迁移标签 不能重复使用(每个迁移需要唯一标签)

4. Deploy

4. 部署

bash
npx wrangler@latest deploy
Your agent is now running at:
https://my-agent.<subdomain>.workers.dev

bash
npx wrangler@latest deploy
你的Agent现在运行在:
https://my-agent.<subdomain>.workers.dev

Architecture Overview: How the Pieces Fit Together

架构概述:各组件如何配合

Understanding what each tool does prevents confusion and helps you choose the right combination.
了解每个工具的作用可避免混淆,帮助你选择正确的组合。

The Stack

技术栈

┌─────────────────────────────────────────────────────────┐
│                    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
┌─────────────────────────────────────────────────────────┐
│                    你的应用程序                      │
│                                                          │
│  ┌────────────────┐         ┌──────────────────────┐   │
│  │  Agents SDK    │         │   AI推理层            │   │
│  │ (基础设施层) │   +     │   (大脑层)          │   │
│  │                │         │                      │   │
│  │ • WebSocket   │         │  选择其中一个:         │   │
│  │ • Durable Objs │         │  • Vercel AI SDK ✅   │   │
│  │ • 状态(SQL)  │         │  • Workers AI ⚠️      │   │
│  │ • 任务调度   │         │  • OpenAI Direct     │   │
│  │ • 多Agent协同  │         │  • Anthropic Direct  │   │
│  └────────────────┘         └──────────────────────┘   │
│         ↓                             ↓                │
│  管理连接与状态          生成响应并处理流式传输    │
└─────────────────────────────────────────────────────────┘
              Cloudflare Workers + Durable Objects

What Each Tool Provides

各工具提供的能力

1. Agents SDK (This Skill)

1. Agents SDK(本技能)

Purpose: Infrastructure for stateful, real-time agents
Provides:
  • ✅ WebSocket connection management (bidirectional real-time)
  • ✅ Durable Objects wrapper (globally unique agent instances)
  • ✅ Built-in state persistence (SQLite up to 1GB)
  • ✅ Lifecycle methods (
    onStart
    ,
    onConnect
    ,
    onMessage
    ,
    onClose
    )
  • ✅ Task scheduling (
    this.schedule()
    with cron/delays)
  • ✅ Multi-agent coordination (
    routeAgentRequest()
    )
  • ✅ Client libraries (
    useAgent
    ,
    AgentClient
    ,
    agentFetch
    )
Does NOT Provide:
  • ❌ AI inference (no LLM calls)
  • ❌ Streaming response parsing (bring your own)
  • ❌ Provider integrations (OpenAI, Anthropic, etc.)
Think of it as: The building and infrastructure (rooms, doors, plumbing) but NOT the residents (AI).

目的:为有状态实时Agent提供基础设施
提供的能力
  • ✅ WebSocket连接管理(双向实时)
  • ✅ Durable Objects封装(全局唯一Agent实例)
  • ✅ 内置状态持久化(SQLite最多1GB)
  • ✅ 生命周期方法(
    onStart
    ,
    onConnect
    ,
    onMessage
    ,
    onClose
  • ✅ 任务调度(
    this.schedule()
    支持cron/延迟)
  • ✅ 多Agent协同(
    routeAgentRequest()
  • ✅ 客户端库(
    useAgent
    ,
    AgentClient
    ,
    agentFetch
不提供的能力
  • ❌ AI推理(无LLM调用)
  • ❌ 流式响应解析:需自行引入
  • ❌ 提供商集成(OpenAI、Anthropic等)
可以理解为:建筑与基础设施(房间、门、管道),而非住户(AI)。

2. Vercel AI SDK (Recommended for AI)

2. Vercel AI SDK(推荐用于AI)

Purpose: AI inference with automatic streaming
Provides:
  • ✅ Automatic streaming response handling (SSE parsing done for you)
  • ✅ Multi-provider support (OpenAI, Anthropic, Google, etc.)
  • ✅ React hooks (
    useChat
    ,
    useCompletion
    ,
    useAssistant
    )
  • ✅ Unified API across providers
  • ✅ Tool calling / function calling
  • ✅ Works on Cloudflare Workers ✅
Example:
typescript
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();
When to use with Agents SDK:
  • ✅ Most chat applications
  • ✅ When you want React hooks
  • ✅ When you use multiple AI providers
  • ✅ When you want clean, abstracted AI calls
Combine with Agents SDK:
typescript
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();
  }
}

目的:带自动流式传输的AI推理
提供的能力
  • ✅ 自动流式响应处理(SSE解析已完成)
  • ✅ 多提供商支持(OpenAI、Anthropic、Google等)
  • ✅ React钩子(
    useChat
    ,
    useCompletion
    ,
    useAssistant
  • ✅ 跨提供商统一API
  • ✅ 工具调用/函数调用
  • ✅ 支持Cloudflare Workers ✅
示例
typescript
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
  model: openai('gpt-4o-mini'),
  messages: [...]
});

// 返回SSE流 - 无需手动解析
return result.toTextStreamResponse();
何时与Agents SDK结合使用
  • ✅ 大多数聊天应用
  • ✅ 想要使用React钩子
  • ✅ 使用多个AI提供商
  • ✅ 想要简洁、抽象的AI调用
与Agents SDK结合示例
typescript
import { AIChatAgent } from "agents/ai-chat-agent";
import { streamText } from "ai";

export class MyAgent extends AIChatAgent<Env> {
  async onChatMessage(onFinish) {
    // Agents SDK提供:WebSocket、状态、this.messages
    // AI SDK提供:自动流式传输、提供商抽象

    return streamText({
      model: openai('gpt-4o-mini'),
      messages: this.messages,  // Agents SDK管理历史记录
      onFinish
    }).toTextStreamResponse();
  }
}

3. Workers AI (Alternative for AI)

3. Workers AI(AI的替代选择)

Purpose: Cloudflare's on-platform AI inference
Provides:
  • ✅ Cost-effective inference (included in Workers subscription)
  • ✅ No external API keys needed
  • ✅ Models: LLaMA 3, Qwen, Mistral, embeddings, etc.
  • ✅ Runs on Cloudflare's network (low latency)
Does NOT Provide:
  • ❌ Automatic streaming parsing (returns raw SSE format)
  • ❌ React hooks
  • ❌ Multi-provider abstraction
Manual parsing required:
typescript
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;
    }
  }
}
When to use:
  • ✅ Cost is critical (embeddings, high-volume)
  • ✅ Need Cloudflare-specific models
  • ✅ Willing to handle manual SSE parsing
  • ✅ No external dependencies allowed
Trade-off: Save money, spend time on manual parsing.

目的:Cloudflare平台内置的AI推理
提供的能力
  • ✅ 高性价比推理(包含在Workers订阅中)
  • ✅ 无需外部API密钥
  • ✅ 模型支持:LLaMA 3、Qwen、Mistral、嵌入等
  • ✅ 在Cloudflare网络运行(低延迟)
不提供的能力
  • ❌ 自动流式解析:返回原始SSE格式
  • ❌ React钩子
  • ❌ 多提供商抽象
需要手动解析
typescript
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
  messages: [...],
  stream: true
});

// 返回原始SSE格式 - 你必须自行解析
for await (const chunk of response) {
  const text = new TextDecoder().decode(chunk);  // Uint8Array → 字符串
  if (text.startsWith('data: ')) {              // 检查SSE格式
    const data = JSON.parse(text.slice(6));     // 解析JSON
    if (data.response) {                        // 提取.response字段
      fullResponse += data.response;
    }
  }
}
何时使用
  • ✅ 成本至关重要(嵌入、高流量场景)
  • ✅ 需要Cloudflare特定模型
  • ✅ 愿意处理手动SSE解析
  • ✅ 不允许外部依赖
权衡:节省成本,但需花费时间处理手动解析。

Recommended Combinations

推荐组合

Option A: Agents SDK + Vercel AI SDK (Recommended ⭐)

选项A:Agents SDK + Vercel AI SDK(推荐 ⭐)

Use when: You need WebSockets/state AND want clean AI integration
typescript
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();
  }
}
Pros:
  • ✅ Best developer experience
  • ✅ Automatic streaming
  • ✅ WebSockets + state from Agents SDK
  • ✅ Clean, maintainable code
Cons:
  • ⚠️ Requires external API keys
  • ⚠️ Additional cost for AI provider

适用场景:需要WebSocket/状态,且想要简洁的AI集成
typescript
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管理历史记录
      onFinish
    }).toTextStreamResponse();
  }
}
优点
  • ✅ 最佳开发者体验
  • ✅ 自动流式传输
  • ✅ Agents SDK提供WebSocket + 状态
  • ✅ 代码简洁、可维护
缺点
  • ⚠️ 需要外部API密钥
  • ⚠️ AI提供商产生额外成本

Option B: Agents SDK + Workers AI

选项B:Agents SDK + Workers AI

Use when: You need WebSockets/state AND cost is critical
typescript
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 ...
    }
  }
}
Pros:
  • ✅ Cost-effective
  • ✅ No external dependencies
  • ✅ WebSockets + state from Agents SDK
Cons:
  • ❌ Manual SSE parsing complexity
  • ❌ Limited model selection
  • ❌ More code to maintain

适用场景:需要WebSocket/状态,且成本至关重要
typescript
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
    });

    // 需要手动解析SSE(见上方Workers AI章节)
    for await (const chunk of response) {
      // ... 手动解析 ...
    }
  }
}
优点
  • ✅ 高性价比
  • ✅ 无外部依赖
  • ✅ Agents SDK提供WebSocket + 状态
缺点
  • ❌ 手动SSE解析复杂度高
  • ❌ 模型选择有限
  • ❌ 需维护更多代码

Option C: Just Vercel AI SDK (No Agents)

选项C:仅使用Vercel AI SDK(无需Agents)

Use when: You DON'T need WebSockets or Durable Objects
typescript
// 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>;
}
Pros:
  • ✅ Simplest approach
  • ✅ Least code
  • ✅ Fast to implement
  • ✅ Built-in React hooks
Cons:
  • ❌ No WebSockets (only SSE)
  • ❌ No Durable Objects state
  • ❌ No multi-agent coordination
Best for: 80% of chat applications

适用场景:不需要WebSocket或Durable Objects
typescript
// worker.ts - 简单的Workers路由
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 - 内置React钩子
import { useChat } from 'ai/react';

function Chat() {
  const { messages, input, handleSubmit } = useChat({ api: '/api/chat' });
  return <form onSubmit={handleSubmit}>...</form>;
}
优点
  • ✅ 最简单的方案
  • ✅ 代码量最少
  • ✅ 快速实现
  • ✅ 内置React钩子
缺点
  • ❌ 无WebSocket(仅SSE)
  • ❌ 无Durable Objects状态
  • ❌ 无多Agent协同
最适合:80%的聊天应用场景

Decision Matrix

决策矩阵

Your NeedsRecommended StackComplexityCost
Simple chat, no stateAI SDK only🟢 Low$$ (AI provider)
Chat + WebSockets + stateAgents SDK + AI SDK🟡 Medium$$$ (infra + AI)
Chat + WebSockets + budgetAgents SDK + Workers AI🔴 High$ (infra only)
Multi-agent workflowsAgents SDK + AI SDK🔴 High$$$ (infra + AI)
MCP server with toolsAgents SDK (McpAgent)🟡 Medium$ (infra only)

需求推荐技术栈复杂度成本
简单聊天、无状态仅AI SDK🟢 低$$(AI提供商)
聊天 + WebSocket + 状态Agents SDK + AI SDK🟡 中$$$(基础设施 + AI)
聊天 + WebSocket + 低成本Agents SDK + Workers AI🔴 高$(仅基础设施)
多Agent工作流Agents SDK + AI SDK🔴 高$$$(基础设施 + AI)
带工具的MCP服务器Agents SDK(McpAgent)🟡 中$(仅基础设施)

Key Takeaway

核心要点

Agents SDK is infrastructure, not AI. You combine it with AI inference tools:
  • For best DX: Agents SDK + Vercel AI SDK ⭐
  • For cost savings: Agents SDK + Workers AI (accept manual parsing)
  • For simplicity: Just AI SDK (if you don't need WebSockets/state)
The rest of this skill focuses on Agents SDK (the infrastructure layer). For AI inference patterns, see the
ai-sdk-core
or
cloudflare-workers-ai
skills.

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
技能。

Configuration (wrangler.jsonc)

配置(wrangler.jsonc)

Critical Required Configuration:
jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["MyAgent"] }  // MUST be in first migration
  ]
}
Common Optional Bindings:
ai
,
vectorize
,
browser
,
workflows
,
d1_databases
,
r2_buckets
CRITICAL Migration Rules:
  • new_sqlite_classes
    MUST be in tag "v1" (cannot add SQLite to existing deployed class)
  • name
    and
    class_name
    MUST match exactly
  • ✅ Migrations are atomic (all instances updated simultaneously)
  • ✅ Each tag must be unique, cannot edit/remove previous tags

关键必填配置
jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
  },
  "migrations": [
    { "tag": "v1", "new_sqlite_classes": ["MyAgent"] }  // 必须在首次迁移中添加
  ]
}
常见可选绑定
ai
,
vectorize
,
browser
,
workflows
,
d1_databases
,
r2_buckets
关键迁移规则
  • new_sqlite_classes
    必须在标签"v1"中添加(无法为已部署的类添加SQLite)
  • name
    class_name
    必须完全匹配
  • ✅ 迁移是原子操作(所有实例同时更新)
  • ✅ 每个标签必须唯一,无法编辑/移除之前的标签

Core Agent Patterns

核心Agent模式

Agent Class Basics - Extend
Agent<Env, State>
with lifecycle methods:
  • onStart()
    - Agent initialization
  • onRequest()
    - Handle HTTP requests
  • onConnect/onMessage/onClose()
    - WebSocket handling
  • onStateUpdate()
    - React to state changes
Key Properties:
  • this.env
    - Environment bindings (AI, DB, etc.)
  • this.state
    - Current agent state (read-only)
  • this.setState()
    - Update persisted state
  • this.sql
    - Built-in SQLite database
  • this.name
    - Agent instance identifier
  • this.schedule()
    - Schedule future tasks

Agent类基础 - 继承
Agent<Env, State>
并实现生命周期方法:
  • onStart()
    - Agent初始化
  • onRequest()
    - 处理HTTP请求
  • onConnect/onMessage/onClose()
    - WebSocket处理
  • onStateUpdate()
    - 响应状态变化
核心属性
  • this.env
    - 环境绑定(AI、数据库等)
  • this.state
    - 当前Agent状态(只读)
  • this.setState()
    - 更新持久化状态
  • this.sql
    - 内置SQLite数据库
  • this.name
    - Agent实例标识符
  • this.schedule()
    - 调度未来任务

WebSockets & Real-Time Communication

WebSocket与实时通信

Agents support WebSockets for bidirectional real-time communication. Use when you need:
  • Client can send messages while server streams
  • Agent-initiated messages (notifications, updates)
  • Long-lived connections with state
Basic Pattern:
typescript
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 Alternative: For one-way server → client streaming (simpler, HTTP-based), use Server-Sent Events instead of WebSockets.

Agents支持WebSocket实现双向实时通信。在以下场景使用:
  • 客户端可在服务器流式传输时发送消息
  • Agent主动发起消息(通知、更新)
  • 带状态的长连接
基础模式
typescript
export class ChatAgent extends Agent<Env, State> {
  async onConnect(connection: Connection, ctx: ConnectionContext) {
    // 权限检查、添加参与者、发送欢迎消息
  }

  async onMessage(connection: Connection, message: WSMessage) {
    // 处理消息、更新状态、广播响应
  }
}
SSE替代方案:对于单向服务器→客户端流式传输(更简单、基于HTTP),使用Server-Sent Events而非WebSocket。

State Management

状态管理

Two State Mechanisms:
  1. this.setState(newState)
    - JSON-serializable state (up to 1GB)
    • Automatically persisted, syncs to WebSocket clients
    • Use for: User preferences, session data, small datasets
  2. this.sql
    - Built-in SQLite database (up to 1GB)
    • Tagged template literals prevent SQL injection
    • Use for: Relational data, large datasets, complex queries
State Rules:
  • ✅ JSON-serializable only (objects, arrays, primitives, null)
  • ✅ Persists across restarts, immediately consistent
  • ❌ No functions or circular references
  • ❌ 1GB total limit (state + SQL combined)
SQL Pattern:
typescript
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
两种状态机制
  1. this.setState(newState)
    - JSON可序列化状态(最多1GB)
    • 自动持久化,同步到WebSocket客户端
    • 适用场景:用户偏好、会话数据、小型数据集
  2. this.sql
    - 内置SQLite数据库(最多1GB)
    • 标签模板字面量防止SQL注入
    • 适用场景:关系型数据、大型数据集、复杂查询
状态规则
  • ✅ 仅支持JSON可序列化类型(对象、数组、基本类型、null)
  • ✅ 跨重启持久化,立即一致
  • ❌ 不支持函数或循环引用
  • ❌ 总限制1GB(状态 + SQL)
SQL模式
typescript
await this.sql`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, email TEXT)`
await this.sql`INSERT INTO users (email) VALUES (${userEmail})`  // ← 预编译语句
const users = await this.sql`SELECT * FROM users WHERE email = ${email}`  // ← 返回数组

State Type Safety Gotcha

状态类型安全陷阱

CRITICAL: Providing a type parameter to state methods does NOT validate that the result matches your type definition. In TypeScript, properties (fields) that do not exist or conform to the type you provided will be dropped silently.
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
  }
}
Prevention: Add runtime validation for critical state operations:
typescript
// 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 });
}

关键注意:为状态方法提供类型参数并不保证运行时结果与类型定义匹配。在TypeScript中,不存在或不符合类型的属性会被静默丢弃。
typescript
interface MyState {
  count: number;
  name: string;
}

export class MyAgent extends Agent<Env, MyState> {
  initialState = { count: 0, name: "default" };

  async increment() {
    // TypeScript允许此操作,但运行时可能不同
    const currentState = this.state; // 类型为MyState

    // 如果状态被外部损坏/修改:
    // { count: "invalid", otherField: 123 }
    // TypeScript仍将其显示为MyState
    // count字段类型不匹配(字符串 vs 数字)
    // otherField被静默丢弃
  }
}
预防措施:为关键状态操作添加运行时验证:
typescript
// 运行时验证状态结构
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('状态验证失败', this.state);
    // 重置为有效状态
    await this.setState(this.initialState);
    return;
  }

  // 安全使用
  const newCount = this.state.count + 1;
  await this.setState({ ...this.state, count: newCount });
}

Schedule Tasks

任务调度

Agents can schedule tasks to run in the future using
this.schedule()
.
Agents可使用
this.schedule()
调度未来任务。

Delay (Seconds)

延迟执行(秒)

typescript
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.
  }
}
typescript
export class MyAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    // 调度60秒后运行的任务
    const { id } = await this.schedule(60, "checkStatus", { requestId: "123" });

    return Response.json({ scheduledTaskId: id });
  }

  // 此方法将在60秒后被调用
  async checkStatus(data: { requestId: string }) {
    console.log('检查请求状态:', data.requestId);
    // 执行检查、更新状态、发送通知等
  }
}

Specific Date

指定日期执行

typescript
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.
  }
}
typescript
export class MyAgent extends Agent {
  async scheduleReminder(reminderDate: string) {
    const date = new Date(reminderDate);

    const { id } = await this.schedule(date, "sendReminder", {
      message: "你的预约时间到了!"
    });

    return id;
  }

  async sendReminder(data: { message: string }) {
    console.log('发送提醒:', data.message);
    // 发送邮件、推送通知等
  }
}

Cron Expressions

Cron表达式

typescript
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...');
  }
}
typescript
export class MyAgent extends Agent {
  async setupRecurringTasks() {
    // 每10分钟一次
    await this.schedule("*/10 * * * *", "checkUpdates", {});

    // 每天上午8点
    await this.schedule("0 8 * * *", "dailyReport", {});

    // 每周一上午9点
    await this.schedule("0 9 * * 1", "weeklyReport", {});

    // 每小时整点
    await this.schedule("0 * * * *", "hourlyCheck", {});
  }

  async checkUpdates(data: any) {
    console.log('检查更新...');
  }

  async dailyReport(data: any) {
    console.log('生成每日报告...');
  }

  async weeklyReport(data: any) {
    console.log('生成每周报告...');
  }

  async hourlyCheck(data: any) {
    console.log('执行每小时检查...');
  }
}

Managing Scheduled Tasks

管理调度任务

typescript
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" });
  }
}
Scheduling Constraints:
  • Each task maps to a SQL database row (max 2 MB per task)
  • Total tasks limited by:
    (task_size * count) + other_state < 1GB
  • Cron tasks continue running until explicitly cancelled
  • Callback method MUST exist on Agent class (throws error if missing)
CRITICAL ERROR: If callback method doesn't exist:
typescript
// ❌ BAD: Method doesn't exist
await this.schedule(60, "nonExistentMethod", {});

// ✅ GOOD: Method exists
await this.schedule(60, "existingMethod", {});

async existingMethod(data: any) {
  // Implementation
}

typescript
export class MyAgent extends Agent {
  async manageSchedules() {
    // 获取所有调度任务
    const allTasks = this.getSchedules();
    console.log('总任务数:', allTasks.length);

    // 通过ID获取特定任务
    const taskId = "some-task-id";
    const task = await this.getSchedule(taskId);

    if (task) {
      console.log('任务:', task.callback, '执行时间:', new Date(task.time));
      console.log('负载:', task.payload);
      console.log('类型:', task.type);  // "scheduled" | "delayed" | "cron"

      // 取消任务
      const cancelled = await this.cancelSchedule(taskId);
      console.log('已取消:', cancelled);
    }

    // 获取时间范围内的任务
    const upcomingTasks = this.getSchedules({
      timeRange: {
        start: new Date(),
        end: new Date(Date.now() + 24 * 60 * 60 * 1000)  // 未来24小时
      }
    });

    console.log('即将执行的任务数:', upcomingTasks.length);

    // 按类型筛选
    const cronTasks = this.getSchedules({ type: "cron" });
    const delayedTasks = this.getSchedules({ type: "delayed" });
  }
}
调度限制
  • 每个任务对应SQL数据库中的一行(每个任务最多2MB)
  • 总任务数限制:
    (任务大小 * 数量) + 其他状态 < 1GB
  • Cron任务会持续运行,直到显式取消
  • 回调方法必须存在于Agent类中(如果不存在会抛出错误)
关键错误:如果回调方法不存在:
typescript
// ❌ 错误:方法不存在
await this.schedule(60, "nonExistentMethod", {});

// ✅ 正确:方法存在
await this.schedule(60, "existingMethod", {});

async existingMethod(data: any) {
  // 实现代码
}

Run Workflows

运行工作流

Agents can trigger asynchronous Cloudflare Workflows.
Agents可触发异步Cloudflare Workflows

Workflow Binding Configuration

工作流绑定配置

wrangler.jsonc
:
jsonc
{
  "workflows": [
    {
      "name": "MY_WORKFLOW",
      "class_name": "MyWorkflow"
    }
  ]
}
If Workflow is in a different script:
jsonc
{
  "workflows": [
    {
      "name": "EMAIL_WORKFLOW",
      "class_name": "EmailWorkflow",
      "script_name": "email-workflows"  // Different project
    }
  ]
}
wrangler.jsonc
jsonc
{
  "workflows": [
    {
      "name": "MY_WORKFLOW",
      "class_name": "MyWorkflow"
    }
  ]
}
如果工作流在其他脚本中:
jsonc
{
  "workflows": [
    {
      "name": "EMAIL_WORKFLOW",
      "class_name": "EmailWorkflow",
      "script_name": "email-workflows"  // 不同项目
    }
  ]
}

Triggering a Workflow

触发工作流

typescript
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;
  }
}
typescript
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');

    // 立即触发工作流
    const instance = await this.env.MY_WORKFLOW.create({
      id: `user-${userId}`,
      params: { userId, action: "process" }
    });

    // 或调度延迟触发的工作流
    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
    });

    // 定期监控工作流状态
    await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
  }

  async checkWorkflowStatus(data: { id: string }) {
    // 检查工作流状态(详情见Workflows文档)
    console.log('检查工作流:', data.id);
  }
}

// 工作流定义(可在同一文件/项目或不同文件/项目中)
export class MyWorkflow extends WorkflowEntrypoint<Env> {
  async run(event: WorkflowEvent<{ userId: string }>, step: WorkflowStep) {
    // 工作流实现
    const result = await step.do('process-data', async () => {
      return { processed: true };
    });

    return result;
  }
}

Agents vs Workflows

Agents vs Workflows

FeatureAgentsWorkflows
PurposeInteractive, user-facingBackground processing
DurationSeconds to hoursMinutes to hours
StateSQLite databaseStep-based checkpoints
InteractionWebSockets, HTTPNo direct interaction
RetryManualAutomatic per step
Use CaseChat, real-time UIETL, batch processing
Best Practice: Use Agents to coordinate multiple Workflows. Agents can trigger, monitor, and respond to Workflow results while maintaining user interaction.

特性AgentsWorkflows
目的交互式、面向用户后台处理
持续时间秒到小时分钟到小时
状态SQLite数据库基于步骤的检查点
交互WebSocket、HTTP无直接交互
重试手动按步骤自动重试
适用场景聊天、实时UIETL、批处理
最佳实践:使用Agents协调多个Workflows。Agents可触发、监控并响应Workflow结果,同时保持用户交互。

Browse the Web

网页浏览

Agents can use Browser Rendering for web scraping and automation:
Binding: Add
"browser": { "binding": "BROWSER" }
to wrangler.jsonc Package:
@cloudflare/puppeteer
Use Case: Web scraping, screenshots, automated browsing within agent workflows
See:
cloudflare-browser-rendering
skill for complete Puppeteer + Workers integration guide.

Agents可使用Browser Rendering进行网页抓取与自动化:
绑定:在wrangler.jsonc中添加
"browser": { "binding": "BROWSER" }
@cloudflare/puppeteer
适用场景:网页抓取、截图、Agent工作流中的自动化浏览
参考
cloudflare-browser-rendering
技能提供完整的Puppeteer + Workers集成指南。

Retrieval Augmented Generation (RAG)

检索增强生成(RAG)

Agents can implement RAG using Vectorize (vector database) + Workers AI (embeddings):
Pattern: Ingest docs → generate embeddings → store in Vectorize → query → retrieve context → pass to AI
Bindings:
  • "ai": { "binding": "AI" }
    - Workers AI for embeddings
  • "vectorize": { "bindings": [{ "binding": "VECTORIZE", "index_name": "my-vectors" }] }
    - Vector search
Typical Workflow:
  1. Generate embeddings with Workers AI (
    @cf/baai/bge-base-en-v1.5
    )
  2. Upsert vectors to Vectorize (
    this.env.VECTORIZE.upsert(vectors)
    )
  3. Query similar vectors (
    this.env.VECTORIZE.query(queryVector, { topK: 5 })
    )
  4. Use retrieved context in AI prompt
See:
cloudflare-vectorize
skill for complete RAG implementation guide.

Agents可使用Vectorize(向量数据库) + Workers AI(嵌入)实现RAG:
模式:导入文档 → 生成嵌入 → 存储到Vectorize → 查询 → 检索上下文 → 传递给AI
绑定
  • "ai": { "binding": "AI" }
    - Workers AI用于生成嵌入
  • "vectorize": { "bindings": [{ "binding": "VECTORIZE", "index_name": "my-vectors" }] }
    - 向量搜索
典型工作流
  1. 使用Workers AI生成嵌入(
    @cf/baai/bge-base-en-v1.5
  2. 将向量插入Vectorize(
    this.env.VECTORIZE.upsert(vectors)
  3. 查询相似向量(
    this.env.VECTORIZE.query(queryVector, { topK: 5 })
  4. 在AI提示词中使用检索到的上下文
参考
cloudflare-vectorize
技能提供完整的RAG实现指南。

Using AI Models

使用AI模型

Agents can call AI models using:
  • Vercel AI SDK (recommended): Multi-provider, automatic streaming, tool calling
  • Workers AI: Cloudflare's on-platform inference (cost-effective, manual parsing)
Architecture Note: Agents SDK provides infrastructure (WebSockets, state, scheduling). AI inference is a separate layer - use AI SDK for the "brain".
See:
  • ai-sdk-core
    skill for complete AI SDK integration patterns
  • cloudflare-workers-ai
    skill for Workers AI streaming parsing

Agents可通过以下方式调用AI模型:
  • Vercel AI SDK(推荐):多提供商、自动流式传输、工具调用
  • Workers AI:Cloudflare平台内置推理(高性价比、手动解析)
架构说明:Agents SDK提供基础设施(WebSocket、状态、调度)。AI推理是独立层 - 使用AI SDK作为“大脑”。
参考
  • ai-sdk-core
    技能提供完整的AI SDK集成模式
  • cloudflare-workers-ai
    技能提供Workers AI流式解析指南

Calling Agents

调用Agents

Two Main Patterns:
  1. routeAgentRequest(request, env)
    - Auto-route via URL pattern
    /agents/:agent/:name
    • Example:
      /agents/my-agent/user-123
      routes to MyAgent instance "user-123"
  2. getAgentByName<Env, T>(env.AgentBinding, instanceName)
    - Custom routing
    • Returns agent stub for calling methods or passing requests
    • Example:
      const agent = getAgentByName(env.MyAgent, 'user-${userId}')
Multi-Agent Communication:
typescript
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);
  }
}
CRITICAL Security: Always authenticate in Worker BEFORE creating/accessing agents. Agents should assume the caller is authorized.

两种主要模式
  1. routeAgentRequest(request, env)
    - 通过URL模式
    /agents/:agent/:name
    自动路由
    • 示例:
      /agents/my-agent/user-123
      路由到MyAgent实例"user-123"
  2. getAgentByName<Env, T>(env.AgentBinding, instanceName)
    - 自定义路由
    • 返回Agent存根,用于调用方法或传递请求
    • 示例:
      const agent = getAgentByName(env.MyAgent, 'user-${userId}')
多Agent通信
typescript
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);
  }
}
关键安全注意:在Worker中始终先认证,再创建/访问Agents。Agents应假设调用者已授权。

Client APIs

客户端API

Browser/React Integration:
  • AgentClient
    (from
    agents/client
    ) - WebSocket client for browser
  • agentFetch
    (from
    agents/client
    ) - HTTP requests to agents
  • useAgent
    (from
    agents/react
    ) - React hook for WebSocket connections + state sync
  • useAgentChat
    (from
    agents/ai-react
    ) - Pre-built chat UI hook
All client libraries automatically handle: WebSocket connections, state synchronization, reconnection logic.

浏览器/React集成
  • AgentClient
    (来自
    agents/client
    ) - 浏览器WebSocket客户端
  • agentFetch
    (来自
    agents/client
    ) - 向Agents发送HTTP请求
  • useAgent
    (来自
    agents/react
    ) - WebSocket连接 + 状态同步的React钩子
  • useAgentChat
    (来自
    agents/ai-react
    ) - 预构建聊天UI钩子
所有客户端库自动处理:WebSocket连接、状态同步、重连逻辑。

Model Context Protocol (MCP)

Model Context Protocol (MCP)

Build MCP servers using the Agents SDK.
使用Agents SDK构建MCP服务器。

MCP Server Setup

MCP服务器设置

bash
npm install @modelcontextprotocol/sdk agents
bash
npm install @modelcontextprotocol/sdk agents

Basic MCP Server

基础MCP服务器

typescript
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) }]
      })
    );
  }
}
typescript
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() {
    // 定义工具
    this.server.tool(
      "add",
      "将两个数字相加",
      {
        a: z.number().describe("第一个数字"),
        b: z.number().describe("第二个数字")
      },
      async ({ a, b }) => ({
        content: [{ type: "text", text: String(a + b) }]
      })
    );
  }
}

Stateful MCP Server

有状态MCP服务器

typescript
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}`
          }]
        };
      }
    );
  }
}
typescript
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() {
    // 资源
    this.server.resource(
      "counter",
      "mcp://resource/counter",
      (uri) => ({
        contents: [{ uri: uri.href, text: String(this.state.counter) }]
      })
    );

    // 工具
    this.server.tool(
      "increment",
      "增加计数器值",
      { amount: z.number() },
      async ({ amount }) => {
        this.setState({
          ...this.state,
          counter: this.state.counter + amount
        });

        return {
          content: [{
            type: "text",
            text: `计数器当前值为 ${this.state.counter}`
          }]
        };
      }
    );
  }
}

MCP Transport Configuration

MCP传输配置

typescript
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;
Transport Comparison:
  • /mcp: Streamable HTTP (modern, recommended)
  • /sse: Server-Sent Events (legacy, deprecated)
typescript
import { Hono } from 'hono';

const app = new Hono();

// 现代流式HTTP传输(推荐)
app.mount('/mcp', MyMCP.serve('/mcp').fetch, { replaceRequest: false });

// 传统SSE传输(已废弃)
app.mount('/sse', MyMCP.serveSSE('/sse').fetch, { replaceRequest: false });

export default app;
传输对比
  • /mcp:流式HTTP(现代、推荐)
  • /sse:Server-Sent Events(传统、已废弃)

MCP Protocol Version Support

MCP协议版本支持

The Agents SDK supports multiple MCP protocol versions. As of agents@0.3.x, version validation is permissive to accept newer protocol versions:
  • Supported versions:
    2024-11-05
    ,
    2025-11-25
    , and future versions
  • Error (before 0.3.x):
    Error: Unsupported MCP protocol version: 2025-11-25
  • Fixed: Version validation now accepts any non-ancient protocol version
This aligns with the MCP community's move to stateless transports. If you encounter protocol version errors, update to agents@0.3.x or later.
Agents SDK支持多个MCP协议版本。在agents@0.3.x及以上版本中,版本验证已放宽,可接受更新的协议版本:
  • 支持版本
    2024-11-05
    ,
    2025-11-25
    及未来版本
  • 旧版本错误(0.3.x之前):
    Error: Unsupported MCP protocol version: 2025-11-25
  • 修复:版本验证现在接受所有非过时的协议版本
这与MCP社区向无状态传输的转变保持一致。如果遇到协议版本错误,请升级到agents@0.3.x或更高版本。

MCP with OAuth

MCP与OAuth结合

typescript
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
});
typescript
import { OAuthProvider } from '@cloudflare/workers-oauth-provider';

export default new OAuthProvider({
  apiHandlers: {
    '/sse': MyMCP.serveSSE('/sse'),
    '/mcp': MyMCP.serve('/mcp')
  },
  // OAuth配置
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  // ... 其他OAuth设置
});

Testing MCP Server

测试MCP服务器

bash
undefined
bash
undefined

Run MCP inspector

运行MCP检查器

npx @modelcontextprotocol/inspector@latest
npx @modelcontextprotocol/inspector@latest

**Cloudflare's MCP Servers**: See [reference](https://developers.cloudflare.com/agents/model-context-protocol/mcp-servers-for-cloudflare/) for production examples.

---

**Cloudflare的MCP服务器**:查看[参考文档](https://developers.cloudflare.com/agents/model-context-protocol/mcp-servers-for-cloudflare/)获取生产示例。

---

Critical Rules

关键规则

Always Do ✅

始终要做 ✅

  1. Export Agent class - Must be exported for binding to work
  2. Include new_sqlite_classes in v1 migration - Cannot add SQLite later
  3. Match binding name to class name - Prevents "binding not found" errors
  4. Authenticate in Worker, not Agent - Security best practice
  5. Use tagged template literals for SQL - Prevents SQL injection
  6. Handle WebSocket disconnections - State persists, connections don't
  7. Verify scheduled task callback exists - Throws error if method missing
  8. Use idFromName() for user-specific agents - NEVER use newUniqueId() for persistent state (see Issue #23)
  9. Check state size limits - Max 1GB total per agent
  10. Monitor task payload size - Max 2MB per scheduled task
  11. Use workflow bindings correctly - Must be configured in wrangler.jsonc
  12. Create Vectorize indexes before inserting - Required for metadata filtering
  13. Close browser instances - Prevent resource leaks
  14. Use setState() for persistence - Don't just modify this.state
  15. Test migrations locally first - Migrations are atomic, can't rollback
  16. Prune WebSocket message history - Stay under 1MB cumulative payload (see Issue #17)
  17. Validate state shape at runtime - TypeScript types don't enforce runtime validation (see State Type Safety)
  1. 导出Agent类 - 必须导出才能使绑定生效
  2. 在v1迁移中包含new_sqlite_classes - 之后无法添加SQLite
  3. 绑定名称与类名匹配 - 防止“绑定未找到”错误
  4. 在Worker中认证,而非Agent中 - 安全最佳实践
  5. 使用标签模板字面量编写SQL - 防止SQL注入
  6. 处理WebSocket断开 - 状态持久化,但连接不持久
  7. 验证调度任务的回调存在 - 如果方法缺失会抛出错误
  8. 对用户特定Agent使用idFromName() - 绝不要对持久化状态使用newUniqueId()(见Issue #23)
  9. 检查状态大小限制 - 每个Agent最多1GB
  10. 监控任务负载大小 - 每个调度任务最多2MB
  11. 正确使用工作流绑定 - 必须在wrangler.jsonc中配置
  12. 插入前创建Vectorize索引 - 元数据过滤必需
  13. 关闭浏览器实例 - 防止资源泄漏
  14. 使用setState()进行持久化 - 不要直接修改this.state
  15. 先在本地测试迁移 - 迁移是原子操作,无法回滚
  16. 清理WebSocket消息历史 - 累计负载保持在1MB以下(见Issue #17)
  17. 运行时验证状态结构 - TypeScript类型不强制执行运行时验证(见状态类型安全部分)

Never Do ❌

绝不要做 ❌

  1. Don't add SQLite to existing deployed class - Must be in first migration
  2. Don't gradually deploy migrations - Atomic only
  3. Don't skip authentication in Worker - Always auth before agent access
  4. Don't construct SQL strings manually - Use tagged templates
  5. Don't exceed 1GB state per agent - Hard limit
  6. Don't schedule tasks with non-existent callbacks - Runtime error
  7. Don't use newUniqueId() for user-specific agents - State won't persist (see Issue #23)
  8. Don't use SSE for MCP - Deprecated, use /mcp transport
  9. Don't forget browser binding - Required for web browsing
  10. Don't modify this.state directly - Use setState() instead
  11. Don't let WebSocket payloads exceed 1MB - Connection will crash (see Issue #17)
  12. Don't trust TypeScript types for state validation - Add runtime checks (see State Type Safety)

  1. 不要为已部署的类添加SQLite - 必须在首次迁移中添加
  2. 不要逐步部署迁移 - 仅支持原子操作
  3. 不要在Worker中跳过认证 - 访问Agent前始终先认证
  4. 不要手动构造SQL字符串 - 使用标签模板
  5. 不要超过每个Agent 1GB的状态限制 - 硬限制
  6. 不要调度不存在回调的任务 - 运行时错误
  7. 不要对用户特定Agent使用newUniqueId() - 状态不会持久化(见Issue #23)
  8. 不要对MCP使用SSE - 已废弃,使用/mcp传输
  9. 不要忘记浏览器绑定 - 网页浏览必需
  10. 不要直接修改this.state - 使用setState()替代
  11. 不要让WebSocket负载超过1MB - 连接会崩溃(见Issue #17)
  12. 不要依赖TypeScript类型进行状态验证 - 添加运行时检查(见状态类型安全部分)

Known Issues Prevention

已知问题预防

This skill prevents 23 documented issues:
本技能可预防23种已记录的问题:

Issue 1: Migrations Not Atomic

Issue 1:迁移非原子操作

Error: "Cannot gradually deploy migration" Source: https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/ Why: Migrations apply to all instances simultaneously Prevention: Deploy migrations independently of code changes, use
npx wrangler versions deploy
错误:"Cannot gradually deploy migration" 来源https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/ 原因:迁移会同时应用到所有实例 预防:独立于代码变更部署迁移,使用
npx wrangler versions deploy

Issue 2: Missing new_sqlite_classes

Issue 2:缺少new_sqlite_classes

Error: "Cannot enable SQLite on existing class" Source: https://developers.cloudflare.com/agents/api-reference/configuration/ Why: SQLite must be enabled in first migration Prevention: Include
new_sqlite_classes
in tag "v1" migration
错误:"Cannot enable SQLite on existing class" 来源https://developers.cloudflare.com/agents/api-reference/configuration/ 原因:必须在首次迁移中启用SQLite 预防:在标签"v1"的迁移中包含
new_sqlite_classes

Issue 3: Agent Class Not Exported

Issue 3:Agent类未导出

Error: "Binding not found" or "Cannot access undefined" Source: https://developers.cloudflare.com/agents/api-reference/agents-api/ Why: Durable Objects require exported class Prevention:
export class MyAgent extends Agent
(with export keyword)
错误:"Binding not found"或"Cannot access undefined" 来源https://developers.cloudflare.com/agents/api-reference/agents-api/ 原因:Durable Objects要求类必须导出 预防
export class MyAgent extends Agent
(包含export关键字)

Issue 4: Binding Name Mismatch

Issue 4:绑定名称不匹配

Error: "Binding 'X' not found" Source: https://developers.cloudflare.com/agents/api-reference/configuration/ Why: Binding name must match class name exactly Prevention: Ensure
name
and
class_name
are identical in wrangler.jsonc
错误:"Binding 'X' not found" 来源https://developers.cloudflare.com/agents/api-reference/configuration/ 原因:绑定名称必须与类名完全匹配 预防:确保wrangler.jsonc中的
name
class_name
完全相同

Issue 5: Global Uniqueness Not Understood

Issue 5:未理解全局唯一性

Error: Unexpected behavior with agent instances Source: https://developers.cloudflare.com/agents/api-reference/agents-api/ Why: Same name always returns same agent instance globally Prevention: Use unique identifiers (userId, sessionId) for instance names
错误:Agent实例行为不符合预期 来源https://developers.cloudflare.com/agents/api-reference/agents-api/ 原因:相同名称始终返回同一个全局Agent实例 预防:使用唯一标识符(userId、sessionId)作为实例名称

Issue 6: WebSocket State Not Persisted

Issue 6:WebSocket状态未持久化

Error: Connection state lost after disconnect Source: https://developers.cloudflare.com/agents/api-reference/websockets/ Why: WebSocket connections don't persist, but agent state does Prevention: Store important data in agent state via setState(), not connection state
错误:断开连接后连接状态丢失 来源https://developers.cloudflare.com/agents/api-reference/websockets/ 原因:WebSocket连接不持久,但Agent状态持久 预防:通过setState()将重要数据存储在Agent状态中,而非连接状态

Issue 7: Scheduled Task Callback Doesn't Exist / Long AI Requests Timeout

Issue 7:调度任务回调不存在 / 长时AI请求超时

Error: "Method X does not exist on Agent" or "IoContext timed out due to inactivity" or "A call to blockConcurrencyWhile() waited for too long" Source: https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ and GitHub Issue #600 Why: this.schedule() calls method that isn't defined, OR scheduled callbacks failed when AI requests exceeded 30 seconds due to
blockConcurrencyWhile
wrapper (fixed in agents@0.2.x via PR #653) Prevention: Ensure callback method exists before scheduling. For long-running AI requests, update to agents@0.2.x or later.
Historical issue (before 0.2.x): Scheduled callbacks were wrapped in
blockConcurrencyWhile
, which enforced a 30-second limit. AI requests exceeding this would fail even though they were valid async operations.
typescript
// ✅ 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 }]
    });
  }
}
错误:"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 Issue #600](https://github.com/cloudflare/agents/issues/600) 原因:this.schedule()调用了未定义的方法,或者调度回调在AI请求超过30秒时失败(agents@0.2.x中通过PR #653修复) 预防:调度前确保回调方法存在。对于长时AI请求,升级到agents@0.2.x或更高版本。
历史问题(0.2.x之前):调度回调被
blockConcurrencyWhile
包装,强制执行30秒限制。即使是有效的异步操作,超过此时间的AI请求也会失败。
typescript
// ✅ 0.2.x中已修复 - 调度回调现在可运行完整时长
export class MyAgent extends Agent<Env> {
  async onRequest(request: Request) {
    await this.schedule(60, "processAIRequest", { query: "..." });
  }

  async processAIRequest(data: { query: string }) {
    // 现在可运行超过30秒而不会超时
    const result = await streamText({
      model: openai('gpt-4o'),
      messages: [{ role: 'user', content: data.query }]
    });
  }
}

Issue 8: State Size Limit Exceeded

Issue 8:状态大小超出限制

Error: "Maximum database size exceeded" Source: https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/ Why: Agent state + scheduled tasks exceed 1GB Prevention: Monitor state size, use external storage (D1, R2) for large data
错误:"Maximum database size exceeded" 来源https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/ 原因:Agent状态 + 调度任务超过1GB 预防:监控状态大小,对大数据使用外部存储(D1、R2)

Issue 9: Scheduled Task Too Large

Issue 9:调度任务过大

Error: "Task payload exceeds 2MB" Source: https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ Why: Each task maps to database row with 2MB limit Prevention: Keep task payloads minimal, store large data in agent state/SQL
错误:"Task payload exceeds 2MB" 来源https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ 原因:每个任务对应数据库中的一行,限制2MB 预防:保持任务负载最小,将大数据存储在Agent状态/SQL中

Issue 10: Workflow Binding Missing

Issue 10:工作流绑定缺失

Error: "Cannot read property 'create' of undefined" Source: https://developers.cloudflare.com/agents/api-reference/run-workflows/ Why: Workflow binding not configured in wrangler.jsonc Prevention: Add workflow binding before using this.env.WORKFLOW
错误:"Cannot read property 'create' of undefined" 来源https://developers.cloudflare.com/agents/api-reference/run-workflows/ 原因:未在wrangler.jsonc中配置工作流绑定 预防:使用this.env.WORKFLOW前添加工作流绑定

Issue 11: Browser Binding Required

Issue 11:缺少浏览器绑定

Error: "BROWSER binding undefined" Source: https://developers.cloudflare.com/agents/api-reference/browse-the-web/ Why: Browser Rendering requires explicit binding Prevention: Add
"browser": { "binding": "BROWSER" }
to wrangler.jsonc
错误:"BROWSER binding undefined" 来源https://developers.cloudflare.com/agents/api-reference/browse-the-web/ 原因:Browser Rendering需要显式绑定 预防:在wrangler.jsonc中添加
"browser": { "binding": "BROWSER" }

Issue 12: Vectorize Index Not Found

Issue 12:Vectorize索引未找到

Error: "Index does not exist" Source: https://developers.cloudflare.com/agents/api-reference/rag/ Why: Vectorize index must be created before use Prevention: Run
wrangler vectorize create
before deploying agent
错误:"Index does not exist" 来源https://developers.cloudflare.com/agents/api-reference/rag/ 原因:必须先创建Vectorize索引才能使用 预防:部署Agent前运行
wrangler vectorize create

Issue 13: MCP Transport Confusion

Issue 13:MCP传输混淆

Error: "SSE transport deprecated" Source: https://developers.cloudflare.com/agents/model-context-protocol/transport/ Why: SSE transport is legacy, streamable HTTP is recommended Prevention: Use
/mcp
endpoint with
MyMCP.serve('/mcp')
, not
/sse
错误:"SSE transport deprecated" 来源https://developers.cloudflare.com/agents/model-context-protocol/transport/ 原因:SSE传输已过时,推荐使用流式HTTP 预防:使用
MyMCP.serve('/mcp')
的/mcp端点,而非/sse

Issue 14: Authentication Bypass

Issue 14:认证绕过

Error: Security vulnerability Source: https://developers.cloudflare.com/agents/api-reference/calling-agents/ Why: Authentication done in Agent instead of Worker Prevention: Always authenticate in Worker before calling getAgentByName()
错误:安全漏洞 来源https://developers.cloudflare.com/agents/api-reference/calling-agents/ 原因:在Agent中而非Worker中进行认证 预防:调用getAgentByName()前始终在Worker中认证

Issue 15: Instance Naming Errors

Issue 15:实例命名错误

Error: Cross-user data leakage Source: https://developers.cloudflare.com/agents/api-reference/calling-agents/ Why: Poor instance naming allows access to wrong agent Prevention: Use namespaced names like
user-${userId}
, validate ownership
错误:跨用户数据泄露 来源https://developers.cloudflare.com/agents/api-reference/calling-agents/ 原因:实例命名不当导致访问错误的Agent 预防:使用命名空间名称如
user-${userId}
,验证所有权

Issue 16: Workers AI Streaming Requires Manual Parsing

Issue 16:Workers AI流式传输需要手动解析

Error: "Cannot read property 'response' of undefined" or empty AI responses Source: https://developers.cloudflare.com/workers-ai/platform/streaming/ Why: Workers AI returns streaming responses as
Uint8Array
in Server-Sent Events (SSE) format, not plain objects Prevention: Use
TextDecoder
+ SSE parsing pattern (see "Workers AI (Alternative for AI)" section above)
The problem - Attempting to access stream chunks directly fails:
typescript
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
}
The solution - Parse SSE format manually:
typescript
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;
    }
  }
}
Better alternative: Use Vercel AI SDK which handles this automatically:
typescript
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 ✅
When to accept manual parsing:
  • Cost is critical (Workers AI is cheaper)
  • No external dependencies allowed
  • Willing to maintain SSE parsing code
When to use AI SDK instead:
  • Value developer time over compute cost
  • Want automatic streaming
  • Need multi-provider support
错误:"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的替代选择)”章节)
问题 - 直接访问流块会失败:
typescript
const response = await env.AI.run(model, { stream: true });
for await (const chunk of response) {
  console.log(chunk.response);  // ❌ undefined - chunk是Uint8Array,而非对象
}
解决方案 - 手动解析SSE格式:
typescript
const response = await env.AI.run(model, { stream: true });
for await (const chunk of response) {
  const text = new TextDecoder().decode(chunk);  // 步骤1:Uint8Array → 字符串
  if (text.startsWith('data: ')) {              // 步骤2:检查SSE格式
    const jsonStr = text.slice(6).trim();       // 步骤3:从"data: {...}"中提取JSON
    if (jsonStr === '[DONE]') break;            // 步骤4:处理终止信号
    const data = JSON.parse(jsonStr);           // 步骤5:解析JSON
    if (data.response) {                        // 步骤6:提取.response字段
      fullResponse += data.response;
    }
  }
}
更好的替代方案:使用Vercel AI SDK自动处理:
typescript
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
});
// 无需手动解析 ✅
何时接受手动解析
  • 成本至关重要(Workers AI更便宜)
  • 不允许外部依赖
  • 愿意维护SSE解析代码
何时使用AI SDK替代
  • 更看重开发者时间而非计算成本
  • 想要自动流式传输
  • 需要多提供商支持

Issue 17: WebSocket Payload Size Limit (1MB)

Issue 17:WebSocket负载大小限制(1MB)

Error:
Error: internal error; reference = [reference ID]
Source: GitHub Issue #119 Why: WebSocket connections fail when cumulative message payload exceeds approximately 1 MB. After 5-6 tool calls returning large data (e.g., 200KB+ each), the payload exceeds the limit and the connection crashes with "internal error". Prevention: Prune message history client-side to stay under 950KB
The problem: All messages—including large tool results—are streamed back to the client and LLM for continued conversations, causing cumulative payload growth.
typescript
// 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);
Better solution (proposed): Server-side context management where only message summaries are sent to the client, not full tool results.
错误
Error: internal error; reference = [reference ID]
来源GitHub Issue #119 原因:当累计消息负载超过约1MB时,WebSocket连接会失败。在5-6次返回大数据的工具调用(如200KB+)后,负载会超过限制,连接会因“内部错误”崩溃。 预防:在客户端清理旧消息,保持在950KB以下
问题:所有消息(包括大型工具结果)都会流式传输回客户端和LLM以继续对话,导致累计负载增长。
typescript
// 解决方法:在客户端清理旧消息
function pruneMessages(messages: Message[]): Message[] {
  let totalSize = 0;
  const pruned = [];

  // 保留最近的消息直到达到大小限制
  for (let i = messages.length - 1; i >= 0; i--) {
    const msgSize = JSON.stringify(messages[i]).length;
    if (totalSize + msgSize > 950_000) break; // 950KB限制
    pruned.unshift(messages[i]);
    totalSize += msgSize;
  }

  return pruned;
}

// 发送到Agent前使用
const prunedMessages = pruneMessages(allMessages);
更好的解决方案(提议):服务器端上下文管理,仅将消息摘要发送到客户端,而非完整工具结果。

Issue 18: Duplicate Assistant Messages with needsApproval Tools

Issue 18:使用needsApproval工具时出现重复助手消息

Error: Duplicate messages with identical
toolCallId
, original stuck in
input-available
state Source: GitHub Issue #790 Why: When using
needsApproval: true
on tools, the system creates duplicate assistant messages instead of updating the original one. The server-generated message (state
input-available
) never transitions to
approval-responded
when client approves. Prevention: Understand this is a known limitation. Track both message IDs in your UI until fixed.
typescript
// Current behavior (agents@0.3.3)
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"
Workaround: Handle both messages in your UI or avoid
needsApproval
until this is resolved.
错误:具有相同
toolCallId
的重复消息,原始消息卡在
input-available
状态 来源GitHub Issue #790 原因:在工具上使用
needsApproval: true
时,系统会创建重复的助手消息,而非更新原始消息。当客户端批准时,服务器生成的消息(状态
input-available
)不会转换为
approval-responded
预防:了解这是已知限制。在修复前在UI中跟踪两个消息ID。
typescript
// 当前行为(agents@0.3.3)
export class MyAgent extends AIChatAgent<Env> {
  tools = {
    sensitiveAction: tool({
      needsApproval: true,  // ⚠️ 导致重复消息
      execute: async (args) => {
        return { result: "操作已完成" };
      }
    })
  };
}

// 结果:两条消息保留
// 1. 服务器消息:ID "assistant_1768917665170_4mub00d32",状态 "input-available"
// 2. 客户端消息:ID "oFwQwEpvLd8f1Gwd",状态 "approval-responded"
解决方法:在UI中处理两条消息,或在修复前避免使用
needsApproval

Issue 19: Duplicate Messages with Client-Side Tool Execution (Fixed in 0.2.31+)

Issue 19:客户端工具执行时出现重复消息(0.2.31+已修复)

Error:
Duplicate item found with id rs_xxx
(OpenAI API rejection) Source: GitHub Issue #728 Fixed In: agents@0.2.31+ Why: When using
useAgentChat
with client-side tools lacking server-side execute functions, the agents library created duplicate assistant messages sharing identical reasoning IDs, triggering OpenAI API rejection. Prevention: Update to agents@0.2.31 or later
Historical issue (before 0.2.31): Client-side tool execution created new messages instead of updating existing ones, leaving the original stuck in incomplete state and causing duplicate
providerMetadata.openai.itemId
values.
typescript
// ✅ Fixed in 0.2.31+ - no changes needed
// Just ensure you're on agents@0.2.31 or later
错误
Duplicate item found with id rs_xxx
(OpenAI API拒绝) 来源GitHub Issue #728 修复版本:agents@0.2.31+ 原因:当使用
useAgentChat
且客户端工具缺少服务器端执行函数时,agents库会创建具有相同推理ID的重复助手消息,触发OpenAI API拒绝。 预防:升级到agents@0.2.31或更高版本
历史问题(0.2.31之前):客户端工具执行会创建新消息而非更新现有消息,导致原始消息卡在未完成状态,并产生重复的
providerMetadata.openai.itemId
值。
typescript
// ✅ 0.2.31+已修复 - 无需修改
// 只需确保使用agents@0.2.31或更高版本

Issue 20: Async Querying Cache TTL Not Honored (Fixed)

Issue 20:异步查询缓存TTL未生效(已修复)

Error:
401 Unauthorized
errors after token expiration despite
cacheTtl
setting Source: GitHub Issue #725 Fixed In: Check agents release notes Why: The
useAgent
hook had a caching problem where the queryPromise was computed once per
cacheKey
and kept forever, even after TTL expired. The
useMemo
implementation didn't include time in dependencies, so TTL was never enforced. Prevention: Update to latest agents version
Historical workaround (before fix):
typescript
// 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);
}, []);
错误:令牌过期后出现
401 Unauthorized
错误,尽管设置了
cacheTtl
来源GitHub Issue #725 修复版本:查看agents发布说明 原因
useAgent
钩子存在缓存问题,queryPromise会按
cacheKey
计算一次并永久保留,即使TTL过期。
useMemo
实现未将时间包含在依赖项中,因此TTL从未生效。 预防:升级到最新agents版本
历史解决方法(修复前):
typescript
// 通过在queryDeps中包含令牌版本强制缓存失效
const [tokenVersion, setTokenVersion] = useState(0);

const { state } = useAgent({
  query: async () => ({ token: await getJWT() }),
  queryDeps: [tokenVersion], // ✅ 强制生成新缓存键
  cacheTtl: 60_000,
});

// 在过期前手动刷新令牌
useEffect(() => {
  const interval = setInterval(() => {
    setTokenVersion(v => v + 1);
  }, 50_000); // 每50秒刷新一次
  return () => clearInterval(interval);
}, []);

Issue 21: jsonSchemaValidator Breaks After DO Hibernation (Fixed)

Issue 21:DO休眠后jsonSchemaValidator失效(已修复)

Error:
TypeError: validator.getValidator is not a function
Source: GitHub Issue #663 Fixed In: Check agents release notes Why: When a Durable Object hibernated and restored, the Agents SDK serialized MCP connection options using
JSON.stringify()
, converting class instances like
CfWorkerJsonSchemaValidator
into plain objects without methods. Upon restoration via
JSON.parse()
, the validator lost its methods. Prevention: Update to latest agents version (validator now built-in)
Historical issue (before fix):
typescript
// ❌ Before fix - manual validator caused errors
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/cloudflare-worker';

const mcpAgent = new McpAgent({
  client: {
    jsonSchemaValidator: new CfWorkerJsonSchemaValidator(), // Got serialized to {}
  }
});
Current approach (fixed):
typescript
// ✅ Now automatic - no manual validator needed
const mcpAgent = new McpAgent({
  // SDK handles validator internally
});
错误
TypeError: validator.getValidator is not a function
来源GitHub Issue #663 修复版本:查看agents发布说明 原因:当Durable Object休眠并恢复时,Agents SDK使用
JSON.stringify()
序列化MCP连接选项,将
CfWorkerJsonSchemaValidator
等类实例转换为无方法的普通对象。通过
JSON.parse()
恢复后,验证器丢失了方法。 预防:升级到最新agents版本(验证器现在内置)
历史问题(修复前):
typescript
// ❌ 修复前 - 手动验证器会导致错误
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/cloudflare-worker';

const mcpAgent = new McpAgent({
  client: {
    jsonSchemaValidator: new CfWorkerJsonSchemaValidator(), // 被序列化为{}
  }
});
当前方案(已修复):
typescript
// ✅ 现在自动处理 - 无需手动验证器
const mcpAgent = new McpAgent({
  // SDK内部处理验证器
});

Issue 22: WorkerTransport ClientCapabilities Lost After Hibernation (Fixed in 0.3.5+)

Issue 22:WorkerTransport客户端能力在休眠后丢失(0.3.5+已修复)

Error:
Error: Client does not support form elicitation
Source: GitHub Issue #777 Fixed In: agents@0.3.5+ Why: When using
WorkerTransport
with MCP servers in serverless environments, client capabilities failed to persist across Durable Object hibernation cycles because the
TransportState
interface only stored
sessionId
and
initialized
status, not
clientCapabilities
. Prevention: Update to agents@0.3.5 or later
Historical issue (before 0.3.5):
typescript
// Client advertised elicitation capability during handshake,
// but after hibernation, capability info was lost
await server.elicitInput({ /* form */ }); // ❌ Error: capabilities lost
Solution (fixed in 0.3.5):
typescript
// TransportState now includes clientCapabilities
interface TransportState {
  sessionId: string;
  initialized: boolean;
  clientCapabilities?: ClientCapabilities; // ✅ Now persisted
}
错误
Error: Client does not support form elicitation
来源GitHub Issue #777 修复版本:agents@0.3.5+ 原因:在无服务器环境中使用
WorkerTransport
与MCP服务器时,客户端能力无法在Durable Object休眠周期中持久化,因为
TransportState
接口仅存储
sessionId
initialized
状态,不存储
clientCapabilities
预防:升级到agents@0.3.5或更高版本
历史问题(0.3.5之前):
typescript
// 客户端在握手时声明了elicitation能力,
// 但休眠后能力信息丢失
await server.elicitInput({ /* 表单 */ }); // ❌ 错误:能力丢失
解决方案(0.3.5中修复):
typescript
// TransportState现在包含clientCapabilities
interface TransportState {
  sessionId: string;
  initialized: boolean;
  clientCapabilities?: ClientCapabilities; // ✅ 现在会持久化
}

Issue 23: idFromName() vs newUniqueId() Critical Pattern

Issue 23:idFromName() vs newUniqueId()关键模式

Error: State never persists, new agent instance every request Source: Cloudflare blog - Building agents with OpenAI Why: If you use
newUniqueId()
instead of
idFromName()
, you'll get a new agent instance each time, and your memory/state will never persist. This is a common early bug that silently kills statefulness. Prevention: Always use
idFromName()
for user-specific agents, never
newUniqueId()
typescript
// ❌ 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);
  }
}
Why It Matters:
  • newUniqueId()
    : Generates a random unique ID each call → new agent instance
  • idFromName(string)
    : Deterministic ID from string → same agent for same input
Rule of thumb: Use
idFromName()
for 99% of cases. Only use
newUniqueId()
when you genuinely need a one-time, ephemeral agent instance.

错误:状态从未持久化,每次请求创建新Agent实例 来源Cloudflare博客 - 使用OpenAI构建Agent 原因:如果使用
newUniqueId()
而非
idFromName()
,每次都会创建新Agent实例,内存/状态永远不会持久化。这是一个常见的早期错误,会无声地破坏状态性。 预防:始终对用户特定Agent使用idFromName(),绝不要使用newUniqueId()
typescript
// ❌ 错误:每次创建新Agent(状态从未持久化)
export default {
  async fetch(request: Request, env: Env) {
    const id = env.MyAgent.newUniqueId(); // 新ID = 新实例
    const agent = env.MyAgent.get(id);

    // 状态从未持久化 - 每次请求都是不同实例
    return agent.fetch(request);
  }
}

// ✅ 正确:同一用户 = 同一Agent = 持久化状态
export default {
  async fetch(request: Request, env: Env) {
    const userId = getUserId(request);
    const id = env.MyAgent.idFromName(userId); // 同一用户对应同一ID
    const agent = env.MyAgent.get(id);

    // 该用户的请求间状态持久化
    return agent.fetch(request);
  }
}
为什么重要
  • newUniqueId()
    :每次调用生成随机唯一ID → 新Agent实例
  • idFromName(string)
    :从字符串生成确定性ID → 同一输入对应同一Agent
经验法则:99%的场景使用
idFromName()
。仅当你确实需要一次性临时Agent实例时才使用
newUniqueId()

Dependencies

依赖项

Required

必需

  • cloudflare-worker-base - Foundation (Hono, Vite, Workers setup)
  • cloudflare-worker-base - 基础框架(Hono、Vite、Workers设置)

Optional (by feature)

可选(按功能)

  • cloudflare-workers-ai - For Workers AI model calls
  • cloudflare-vectorize - For RAG with Vectorize
  • cloudflare-d1 - For additional persistent storage beyond agent state
  • cloudflare-r2 - For file storage
  • cloudflare-queues - For message queues
  • cloudflare-workers-ai - 用于Workers AI模型调用
  • cloudflare-vectorize - 用于基于Vectorize的RAG
  • cloudflare-d1 - 用于Agent状态之外的额外持久化存储
  • cloudflare-r2 - 用于文件存储
  • cloudflare-queues - 用于消息队列

NPM Packages

NPM包

  • agents
    - Agents SDK (required)
  • @modelcontextprotocol/sdk
    - For building MCP servers
  • @cloudflare/puppeteer
    - For web browsing
  • ai
    - AI SDK for model calls
  • @ai-sdk/openai
    - OpenAI models
  • @ai-sdk/anthropic
    - Anthropic models

  • agents
    - Agents SDK(必需)
  • @modelcontextprotocol/sdk
    - 用于构建MCP服务器
  • @cloudflare/puppeteer
    - 用于网页浏览
  • ai
    - AI SDK用于模型调用
  • @ai-sdk/openai
    - OpenAI模型
  • @ai-sdk/anthropic
    - Anthropic模型

Official Documentation

官方文档

Bundled Resources

捆绑资源

Templates (templates/)

模板(templates/)

  • wrangler-agents-config.jsonc
    - Complete configuration example
  • basic-agent.ts
    - Minimal HTTP agent
  • websocket-agent.ts
    - WebSocket handlers
  • state-sync-agent.ts
    - State management patterns
  • scheduled-agent.ts
    - Task scheduling
  • workflow-agent.ts
    - Workflow integration
  • browser-agent.ts
    - Web browsing
  • rag-agent.ts
    - RAG implementation
  • chat-agent-streaming.ts
    - Streaming chat
  • calling-agents-worker.ts
    - Agent routing
  • react-useagent-client.tsx
    - React client
  • mcp-server-basic.ts
    - MCP server
  • hitl-agent.ts
    - Human-in-the-loop
  • wrangler-agents-config.jsonc
    - 完整配置示例
  • basic-agent.ts
    - 最小HTTP Agent
  • websocket-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 (references/)

参考文档(references/)

  • agent-class-api.md
    - Complete Agent class reference
  • client-api-reference.md
    - Browser client APIs
  • state-management-guide.md
    - State and SQL deep dive
  • websockets-sse.md
    - WebSocket vs SSE comparison
  • scheduling-api.md
    - Task scheduling details
  • workflows-integration.md
    - Workflows guide
  • browser-rendering.md
    - Web browsing patterns
  • rag-patterns.md
    - RAG best practices
  • mcp-server-guide.md
    - MCP server development
  • mcp-tools-reference.md
    - MCP tools API
  • hitl-patterns.md
    - Human-in-the-loop workflows
  • best-practices.md
    - Production patterns
  • agent-class-api.md
    - 完整Agent类参考
  • client-api-reference.md
    - 浏览器客户端API
  • state-management-guide.md
    - 状态与SQL深度指南
  • websockets-sse.md
    - WebSocket vs SSE对比
  • scheduling-api.md
    - 任务调度详情
  • workflows-integration.md
    - 工作流指南
  • browser-rendering.md
    - 网页浏览模式
  • rag-patterns.md
    - RAG最佳实践
  • mcp-server-guide.md
    - MCP服务器开发
  • mcp-tools-reference.md
    - MCP工具API
  • hitl-patterns.md
    - 人在回路工作流
  • best-practices.md
    - 生产模式

Examples (examples/)

示例(examples/)

  • chat-bot-complete.md
    - Full chat agent
  • multi-agent-workflow.md
    - Agent orchestration
  • scheduled-reports.md
    - Recurring tasks
  • browser-scraper-agent.md
    - Web scraping
  • rag-knowledge-base.md
    - RAG system
  • mcp-remote-server.md
    - Production MCP server

Last Verified: 2026-01-21 Package Versions: agents@0.3.6 Compliance: Cloudflare Agents SDK official documentation Changes: Added 7 new issues from community research (WebSocket payload limits, state type safety, idFromName gotcha), updated to agents@0.3.6
  • chat-bot-complete.md
    - 完整聊天Agent
  • multi-agent-workflow.md
    - Agent编排
  • scheduled-reports.md
    - 周期性任务
  • browser-scraper-agent.md
    - 网页抓取
  • rag-knowledge-base.md
    - RAG知识库
  • mcp-remote-server.md
    - 生产MCP服务器

最后验证:2026-01-21 包版本:agents@0.3.6 合规性:Cloudflare Agents SDK官方文档 变更:添加社区研究的7个新问题(WebSocket负载限制、状态类型安全、idFromName陷阱),更新到agents@0.3.6