关于 cloudflare-agents
Cloudflare Agents SDK 用于在 Cloudflare Workers 和 Durable Objects 上构建有状态的 AI Agent,面向开发者。它结合持久化状态、实时通信、任务调度与 MCP 服务构建能力。
主要功能
- 01基于 Durable Objects 构建 Agent
- 02通过 WebSockets 和 SSE 实时通信
- 03使用 SQLite 持久化状态
- 04按延迟、日期或 cron 调度任务
- 05触发异步 Cloudflare Workflows
- 06构建 MCP 服务
适用场景
- 01构建实时聊天 Agent
- 02安排定时检查或提醒
- 03将交互式 Agent 与后台工作流结合
- 04实现支持人工审核的流程
MCP 相关
可用于构建实现 Model Context Protocol 的 MCP 服务,供 MCP 客户端连接。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill cloudflare-agents安装后在 Claude Code 中输入/cloudflare-agents确认已加载。
附带文件README.mdSKILL.json
详细介绍
Cloudflare Agents SDK
状态:已可用于生产环境 ✅ 最后更新:2025-10-21 依赖项:cloudflare-worker-base(推荐) 最新版本:agents@latest,@modelcontextprotocol/sdk@latest 已通过生产环境测试:Cloudflare 自己的 MCP 服务(https://github.com/cloudflare/mcp-server-cloudflare)
什么是 Cloudflare Agents?
Cloudflare Agents SDK 可用于构建由 AI 驱动、运行在 Cloudflare Workers + Durable Objects 上的自主 Agent。Agent 可以:
- 实时通信,通过 WebSockets 和 Server-Sent Events
- 持久化状态,使用内置 SQLite 数据库(每个 Agent 最大 1GB)
- 安排任务,使用延迟时间、指定日期或 cron 表达式
- 运行工作流,通过触发异步 Cloudflare Workflows
- 浏览网页,使用 Browser Rendering API + Puppeteer
- 实现 RAG,使用 Vectorize 向量数据库 + Workers AI 嵌入
- 构建 MCP 服务,实现 Model Context Protocol
- 支持人在回路中的模式,用于审核和批准
- 扩展到数百万个独立的 Agent 实例,覆盖全球
每个 Agent 实例都是一个全局唯一的有状态微型服务,可运行数秒、数分钟或数小时。
快速开始(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
配置详解
完整的 wrangler.jsonc 示例
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-agent",
"main": "src/index.ts",
"account_id": "YOUR_ACCOUNT_ID",
"compatibility_date": "2025-10-21",
"compatibility_flags": ["nodejs_compat"],
// Durable Objects configuration (REQUIRED)
"durable_objects": {
"bindings": [
{
"name": "MyAgent",
"class_name": "MyAgent"
}
]
},
// Migrations (REQUIRED)
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyAgent"] // Enables state persistence
}
],
// Optional: Workers AI binding (for AI model calls)
"ai": {
"binding": "AI"
},
// Optional: Vectorize binding (for RAG)
"vectorize": {
"bindings": [
{
"binding": "VECTORIZE",
"index_name": "my-agent-vectors"
}
]
},
// Optional: Browser Rendering binding (for web browsing)
"browser": {
"binding": "BROWSER"
},
// Optional: Workflows binding (for async workflows)
"workflows": [
{
"name": "MY_WORKFLOW",
"class_name": "MyWorkflow",
"script_name": "my-workflow-script" // If in different project
}
],
// Optional: D1 binding (for additional persistent data)
"d1_databases": [
{
"binding": "DB",
"database_name": "my-agent-db",
"database_id": "your-database-id"
}
],
// Optional: R2 binding (for file storage)
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-agent-files"
}
],
// Optional: Environment variables
"vars": {
"ENVIRONMENT": "production"
},
// Optional: Secrets (set with: wrangler secret put KEY)
// OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
// Observability
"observability": {
"enabled": true
}
}
迁移最佳实践
原子部署:迁移是原子操作,无法逐步部署。
{
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["MyAgent"] // Initial: enable SQLite
},
{
"tag": "v2",
"renamed_classes": [
{"from": "MyAgent", "to": "MyRenamedAgent"}
]
},
{
"tag": "v3",
"deleted_classes": ["OldAgent"]
},
{
"tag": "v4",
"transferred_classes": [
{
"from": "AgentInOldScript",
"from_script": "old-worker",
"to": "AgentInNewScript"
}
]
}
]
}
迁移规则:
- ✅ 每次迁移都需要唯一的
tag - ✅ 无法为已部署的现有类启用 SQLite(必须在第一次迁移中启用)
- ✅ 部署期间按顺序应用迁移
- ✅ 无法编辑或移除之前的迁移标签
- ❌ 切勿逐步部署新迁移(只能原子部署)
特定环境的迁移
{
"migrations": [{"tag": "v1", "new_sqlite_classes": ["MyAgent"]}],
"env": {
"staging": {
"migrations": [
{"tag": "v1", "new_sqlite_classes": ["MyAgent"]},
{"tag": "v2-staging", "renamed_classes": [{"from": "MyAgent", "to": "StagingAgent"}]}
]
}
}
}
Agent 类 API
Agent 类是 Agents SDK 的基础。继承它即可创建 Agent。
基本 Agent 结构
import { Agent } from "agents";
interface Env {
// Environment variables and bindings
OPENAI_API_KEY: string;
AI: Ai;
VECTORIZE: Vectorize;
DB: D1Database;
}
interface State {
// Your agent's persistent state
counter: number;
messages: string[];
lastUpdated: Date | null;
}
export class MyAgent extends Agent<Env, State> {
// Optional: Set initial state (first time agent is created)
initialState: State = {
counter: 0,
messages: [],
lastUpdated: null
};
// Optional: Called when agent instance starts or wakes from hibernation
async onStart() {
console.log('Agent started:', this.name, 'State:', this.state);
}
// Handle HTTP requests
async onRequest(request: Request): Promise<Response> {
return Response.json({ message: "Hello from Agent", state: this.state });
}
// Handle WebSocket connections (optional)
async onConnect(connection: Connection, ctx: ConnectionContext) {
console.log('Client connected:', connection.id);
// Connections are automatically accepted
}
// Handle WebSocket messages (optional)
async onMessage(connection: Connection, message: WSMessage) {
if (typeof message === 'string') {
connection.send(`Echo: ${message}`);
}
}
// Handle WebSocket errors (optional)
async onError(connection: Connection, error: unknown): Promise<void> {
console.error('Connection error:', error);
}
// Handle WebSocket close (optional)
async onClose(connection: Connection, code: number, reason: string, wasClean: boolean): Promise<void> {
console.log('Connection closed:', code, reason);
}
// Called when state is updated from any source (optional)
onStateUpdate(state: State, source: "server" | Connection) {
console.log('State updated:', state, 'Source:', source);
}
// Custom methods (call from any handler)
async customMethod(data: any) {
this.setState({
...this.state,
counter: this.state.counter + 1,
lastUpdated: new Date()
});
}
}
访问 Agent 属性
在任何 Agent 方法中:
export class MyAgent extends Agent<Env, State> {
async someMethod() {
// Access environment variables and bindings
const apiKey = this.env.OPENAI_API_KEY;
const ai = this.env.AI;
// Access current state (read-only)
const counter = this.state.counter;
// Update state (persisted automatically)
this.setState({ ...this.state, counter: counter + 1 });
// Access SQL database
const results = await this.sql`SELECT * FROM users`;
// Get agent instance name
const instanceName = this.name; // e.g., "user-123"
// Schedule tasks
await this.schedule(60, "runLater", { data: "example" });
// Call other methods
await this.customMethod({ foo: "bar" });
}
}
HTTP 与 Server-Sent Events
HTTP 请求处理
export class MyAgent extends Agent<Env> {
async onRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
const method = request.method;
if (method === "POST" && url.pathname === "/increment") {
const counter = (this.state.counter || 0) + 1;
this.setState({ ...this.state, counter });
return Response.json({ counter });
}
if (method === "GET" && url.pathname === "/status") {
return Response.json({ state: this.state, name: this.name });
}
return new Response("Not Found", { status: 404 });
}
}
Server-Sent Events(SSE)流式传输
export class MyAgent extends Agent<Env> {
async onRequest(request: Request): Promise<Response> {
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
// Send events to client
controller.enqueue(encoder.encode('data: {"message": "Starting"}\n\n'));
await new Promise(resolve => setTimeout(resolve, 1000));
controller.enqueue(encoder.encode('data: {"message": "Processing"}\n\n'));
await new Promise(resolve => setTimeout(resolve, 1000));
controller.enqueue(encoder.encode('data: {"message": "Complete"}\n\n'));
controller.close();
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
});
}
}
SSE 与 WebSockets 对比:
| 特性 | SSE | WebSockets |
|---|---|---|
| 方向 | 仅服务器 → 客户端 | 双向 |
| 协议 | HTTP | ws:// 或 wss:// |
| 重新连接 | 自动 | 手动 |
| 二进制数据 | 有限 | 完全支持 |
| 使用场景 | 流式响应、通知 | 聊天、实时协作 |
建议:大多数 Agent 应用都使用 WebSockets(全双工,更适合长会话)。
WebSockets
完整的 WebSocket 示例
import { Agent, Connection, ConnectionContext, WSMessage } from "agents";
interface ChatState {
messages: Array<{ id: string; text: string; sender: string; timestamp: number }>;
participants: string[];
}
export class ChatAgent extends Agent<Env, ChatState> {
initialState: ChatState = {
messages: [],
participants: []
};
async onConnect(connection: Connection, ctx: ConnectionContext) {
// Access original HTTP request for auth
const authHeader = ctx.request.headers.get('Authorization');
const userId = ctx.request.headers.get('X-User-ID') || 'anonymous';
// Connections are automatically accepted
// Optionally close connection if unauthorized:
// if (!authHeader) {
// connection.close(401, "Unauthorized");
// return;
// }
// Add to participants
this.setState({
...this.state,
participants: [...this.state.participants, userId]
});
// Send welcome message
connection.send(JSON.stringify({
type: 'welcome',
message: 'Connected to chat',
participants: this.state.participants
}));
}
async onMessage(connection: Connection, message: WSMessage) {
if (typeof message === 'string') {
try {
const data = JSON.parse(message);
if (data.type === 'chat') {
// Add message to state
const newMessage = {
id: crypto.randomUUID(),
text: data.text,
sender: data.sender || 'anonymous',
timestamp: Date.now()
};
this.setState({
...this.state,
messages: [...this.state.messages, newMessage]
});
// Broadcast to this connection (state sync will broadcast to all)
connection.send(JSON.stringify({
type: 'message_added',
message: newMessage
}));
}
} catch (e) {
connection.send(JSON.stringify({ type: 'error', message: 'Invalid message format' }));
}
}
}
async onError(connection: Connection, error: unknown): Promise<void> {
console.error('WebSocket error:', error);
// Optionally log to external monitoring
}
async onClose(connection: Connection, code: number, reason: string, wasClean: boolean): Promise<void> {
console.log(`Connection ${connection.id} closed:`, code, reason, wasClean);
// Clean up connection-specific state if needed
}
}
连接管理
export class MyAgent extends Agent {
async onMessage(connection: Connection, message: WSMessage) {
// Connection properties
const connId = connection.id; // Unique connection ID
const connState = connection.state; // Connection-specific state
// Update connection state (not agent state)
connection.setState({ ...connection.state, lastActive: Date.now() });
// Send to this connection only
connection.send("Message to this client");
// Close connection programmatically
connection.close(1000, "Goodbye");
}
}
状态管理
使用 setState()
interface UserState {
name: string;
email: string;
preferences: { theme: string; notifications: boolean };
loginCount: number;
lastLogin: Date | null;
}
export class UserAgent extends Agent<Env, UserState> {
initialState: UserState = {
name: "",
email: "",
preferences: { theme: "dark", notifications: true },
loginCount: 0,
lastLogin: null
};
async onRequest(request: Request): Promise<Response> {
if (request.method === "POST" && new URL(request.url).pathname === "/login") {
// Update state
this.setState({
...this.state,
loginCount: this.state.loginCount + 1,
lastLogin: new Date()
});
// State is automatically persisted and synced to connected clients
return Response.json({ success: true, state: this.state });
}
return Response.json({ state: this.state });
}
onStateUpdate(state: UserState, source: "server" | Connection) {
console.log('State updated:', state);
console.log('Source:', source); // "server" or Connection object
// React to state changes
if (state.loginCount > 10) {
console.log('Frequent user!');
}
}
}
状态规则:
- ✅ 状态可序列化为 JSON(对象、数组、字符串、数字、布尔值、null)
- ✅ Agent 重启后状态仍会保留
- ✅ Agent 内部的状态可立即保持一致
- ✅ 状态会自动同步到已连接的 WebSocket 客户端
- ❌ 状态不能包含函数或循环引用
- ❌ 状态总大小受数据库大小限制(每个 Agent 最大 1GB)
使用 SQL 数据库
每个 Agent 都内置了一个可通过 this.sql 访问的 SQLite 数据库:
export class MyAgent extends Agent {
async onStart() {
// Create tables on first start
await this.sql`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
await this.sql`
CREATE INDEX IF NOT EXISTS idx_email ON users(email)
`;
}
async addUser(name: string, email: string) {
// Insert with prepared statement (prevents SQL injection)
const result = await this.sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
`;
return result;
}
async getUser(email: string) {
// Query returns array of results
const users = await this.sql`
SELECT * FROM users WHERE email = ${email}
`;
return users[0] || null;
}
async getAllUsers() {
const users = await this.sql`
SELECT * FROM users ORDER BY created_at DESC
`;
return users;
}
async updateUser(id: number, name: string) {
await this.sql`
UPDATE users SET name = ${name} WHERE id = ${id}
`;
}
async deleteUser(id: number) {
await this.sql`
DELETE FROM users WHERE id = ${id}
`;
}
}
SQL 最佳实践:
- ✅ 使用带标签的模板字面量(可防止 SQL 注入)
- ✅ 为频繁查询的列创建索引
- ✅ 对多个相关操作使用事务
- ✅ 查询结果始终是数组(即使只有一行)
- ❌ 不要手动构造 SQL 字符串
- ❌ 注意数据库大小上限为 1GB
计划任务
Agent 可以使用 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 与 Workflow
| 特性 | Agent | Workflow |
|---|---|---|
| 用途 | 交互式,面向用户 | 后台处理 |
| 持续时间 | 几秒到几小时 | 几分钟到几小时 |
| 状态 | SQLite 数据库 | 基于步骤的检查点 |
| 交互方式 | WebSockets、HTTP | 无直接交互 |
| 重试 | 手动 | 每个步骤自动重试 |
| 使用场景 | 聊天、实时 UI | ETL、批处理 |
最佳实践:使用 Agent 协调多个 Workflow。Agent 可以触发、监控 Workflow,并响应其结果,同时保持与用户的交互。
浏览网页
Agent 可以使用 Browser Rendering 浏览网页。
Browser Rendering 绑定
wrangler.jsonc:
{
"browser": {
"binding": "BROWSER"
}
}
安装
npm install @cloudflare/puppeteer
网页抓取示例
import { Agent } from "agents";
import puppeteer from "@cloudflare/puppeteer";
interface Env {
BROWSER: Fetcher;
OPENAI_API_KEY: string;
}
export class BrowserAgent extends Agent<Env> {
async browse(urls: string[]) {
const responses = [];
for (const url of urls) {
const browser = await puppeteer.launch(this.env.BROWSER);
const page = await browser.newPage();
await page.goto(url);
await page.waitForSelector("body");
const bodyContent = await page.$eval("body", el => el.innerHTML);
// Extract data with AI
const data = await this.extractData(bodyContent);
responses.push({ url, data });
await browser.close();
}
return responses;
}
async extractData(html: string): Promise<any> {
// Use OpenAI or Workers AI to extract structured data
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.env.OPENAI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [{
role: 'user',
content: `Extract product info from HTML: ${html.slice(0, 4000)}`
}],
response_format: { type: "json_object" }
})
});
const result = await response.json();
return JSON.parse(result.choices[0].message.content);
}
async onRequest(request: Request): Promise<Response> {
const url = new URL(request.url).searchParams.get('url');
if (!url) {
return new Response("Missing url parameter", { status: 400 });
}
const results = await this.browse([url]);
return Response.json(results);
}
}
截图
export class ScreenshotAgent extends Agent<Env> {
async captureScreenshot(url: string): Promise<Buffer> {
const browser = await puppeteer.launch(this.env.BROWSER);
const page = await browser.newPage();
await page.goto(url);
const screenshot = await page.screenshot({ fullPage: true });
await browser.close();
return screenshot;
}
}
检索增强生成(RAG)
使用 Vectorize + Workers AI 嵌入实现 RAG。
Vectorize 绑定
wrangler.jsonc:
{
"ai": {
"binding": "AI"
},
"vectorize": {
"bindings": [
{
"binding": "VECTORIZE",
"index_name": "my-agent-vectors"
}
]
}
}
创建索引
npx wrangler vectorize create my-agent-vectors \
--dimensions=768 \
--metric=cosine
完整的 RAG 实现
import { Agent } from "agents";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
interface Env {
AI: Ai;
VECTORIZE: Vectorize;
OPENAI_API_KEY: string;
}
export class RAGAgent extends Agent<Env> {
// Ingest documents
async ingestDocuments(documents: Array<{ id: string; text: string; metadata: any }>) {
const vectors = [];
for (const doc of documents) {
// Generate embedding with Workers AI
const { data } = await this.env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: [doc.text]
});
vectors.push({
id: doc.id,
values: data[0],
metadata: { ...doc.metadata, text: doc.text }
});
}
// Insert into Vectorize
await this.env.VECTORIZE.upsert(vectors);
return { ingested: vectors.length };
}
// Query knowledge base
async queryKnowledge(userQuery: string, topK: number = 5) {
// Generate query embedding
const { data } = await this.env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: [userQuery]
});
// Search Vectorize
const results = await this.env.VECTORIZE.query(data[0], { topK });
// Extract relevant documents
const context = results.matches.map(match => match.metadata.text).join('\n\n');
return context;
}
// RAG Chat
async chat(userMessage: string) {
// Retrieve relevant context
const context = await this.queryKnowledge(userMessage);
// Generate response with context
const { text } = await generateText({
model: openai('gpt-4o-mini'),
messages: [
{
role: 'system',
content: `You are a helpful assistant. Use the following context to answer questions:\n\n${context}`
},
{
role: 'user',
content: userMessage
}
]
});
return { response: text, context };
}
async onRequest(request: Request): Promise<Response> {
const { message } = await request.json();
const result = await this.chat(message);
return Response.json(result);
}
}
元数据筛选
// Create metadata indexes BEFORE inserting vectors
await this.env.VECTORIZE.createMetadataIndex("category");
await this.env.VECTORIZE.createMetadataIndex("language");
// Query with filters
const results = await this.env.VECTORIZE.query(queryVector, {
topK: 10,
filter: {
category: { $eq: "documentation" },
language: { $eq: "en" }
}
});
参见:cloudflare-vectorize skill,了解完整的 Vectorize 指南。
使用 AI 模型
AI SDK(Vercel)
npm install ai @ai-sdk/openai @ai-sdk/anthropic
import { Agent } from "agents";
import { generateText, streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
export class AIAgent extends Agent {
// Simple text generation
async generateResponse(prompt: string) {
const { text } = await generateText({
model: openai('gpt-4o-mini'),
prompt
});
return text;
}
// Streaming response
async streamResponse(prompt: string): Promise<Response> {
const result = streamText({
model: anthropic('claude-sonnet-4-5'),
prompt
});
return result.toTextStreamResponse();
}
// Structured output
async extractData(text: string) {
const { object } = await generateObject({
model: openai('gpt-4o-mini'),
schema: z.object({
name: z.string(),
email: z.string().email(),
age: z.number().optional()
}),
prompt: `Extract user info from: ${text}`
});
return object;
}
}
Workers AI
interface Env {
AI: Ai;
}
export class WorkersAIAgent extends Agent<Env> {
async generateText(prompt: string) {
const response = await this.env.AI.run('@cf/meta/llama-3-8b-instruct', {
messages: [{ role: 'user', content: prompt }]
});
return response;
}
async generateImage(prompt: string) {
const response = await this.env.AI.run('@cf/black-forest-labs/flux-1-schnell', {
prompt
});
return response;
}
}
参见:cloudflare-workers-ai skill,了解完整的 Workers AI 指南。
调用 Agent
使用 routeAgentRequest
根据 URL 模式 /agents/:agent/:name 自动将请求路由到 Agent:
import { Agent, AgentNamespace, routeAgentRequest } from 'agents';
interface Env {
MyAgent: AgentNamespace<MyAgent>;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Routes to: /agents/my-agent/user-123
const response = await routeAgentRequest(request, env);
if (response) {
return response;
}
return new Response("Not Found", { status: 404 });
}
} satisfies ExportedHandler<Env>;
export class MyAgent extends Agent<Env> {
async onRequest(request: Request): Promise<Response> {
return Response.json({ agent: this.name });
}
}
URL 模式:/agents/my-agent/user-123
my-agent= kebab-case 格式的类名user-123= Agent 实例名称
使用 getAgentByName
用于自定义路由,或从 Workers 调用 Agent:
import { Agent, AgentNamespace, getAgentByName } from 'agents';
interface Env {
MyAgent: AgentNamespace<MyAgent>;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const userId = new URL(request.url).searchParams.get('userId') || 'anonymous';
// Get or create agent instance
const agent = getAgentByName<Env, MyAgent>(env.MyAgent, `user-${userId}`);
// Pass request to agent
return (await agent).fetch(request);
}
} satisfies ExportedHandler<Env>;
export class MyAgent extends Agent<Env> {
// Agent implementation
}
直接调用 Agent 方法
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const agent = getAgentByName<Env, MyAgent>(env.MyAgent, 'user-123');
// Call custom methods on agent using RPC
const result = await (await agent).customMethod({ data: "example" });
return Response.json({ result });
}
}
export class MyAgent extends Agent<Env> {
async customMethod(params: { data: string }): Promise<any> {
return { processed: params.data };
}
}
多 Agent 通信
interface Env {
AgentA: AgentNamespace<AgentA>;
AgentB: AgentNamespace<AgentB>;
}
export class AgentA extends Agent<Env> {
async processData(data: any) {
// Call another agent
const agentB = getAgentByName<Env, AgentB>(this.env.AgentB, 'processor-1');
const result = await (await agentB).analyze(data);
return result;
}
}
export class AgentB extends Agent<Env> {
async analyze(data: any) {
return { analyzed: true, data };
}
}
身份验证模式
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Authenticate BEFORE invoking agent
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return new Response("Unauthorized", { status: 401 });
}
const userId = await verifyToken(authHeader);
if (!userId) {
return new Response("Forbidden", { status: 403 });
}
// Only create/access agent for authenticated users
const agent = getAgentByName<Env, MyAgent>(env.MyAgent, `user-${userId}`);
return (await agent).fetch(request);
}
}
严重提示:始终在 Worker 中进行身份验证,不要在 Agent 中进行。Agent 应假定调用方已获授权。
客户端 API
AgentClient(浏览器)
import { AgentClient } from "agents/client";
// Connect to agent instance
const client = new AgentClient({
agent: "chat-agent", // Class name in kebab-case
name: "room-123", // Instance name
host: window.location.host
});
client.onopen = () => {
console.log("Connected");
client.send(JSON.stringify({ type: "join", user: "alice" }));
};
client.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
client.onclose = () => {
console.log("Disconnected");
};
agentFetch(HTTP 请求)
import { agentFetch } from "agents/client";
async function getData() {
const response = await agentFetch(
{ agent: "my-agent", name: "user-123" },
{
method: "GET",
headers: { "Authorization": `Bearer ${token}` }
}
);
const data = await response.json();
return data;
}
useAgent Hook(React)
import { useAgent } from "agents/react";
import { useState } from "react";
function ChatUI() {
const [messages, setMessages] = useState([]);
const connection = useAgent({
agent: "chat-agent",
name: "room-123",
onMessage: (event) => {
const data = JSON.parse(event.data);
if (data.type === 'message') {
setMessages(prev => [...prev, data.message]);
}
},
onOpen: () => console.log("Connected"),
onClose: () => console.log("Disconnected")
});
const sendMessage = (text: string) => {
connection.send(JSON.stringify({ type: 'chat', text }));
};
return (
<div>
{messages.map((msg, i) => <div key={i}>{msg.text}</div>)}
<button onClick={() => sendMessage("Hello")}>Send</button>
</div>
);
}
状态同步
import { useAgent } from "agents/react";
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
const agent = useAgent({
agent: "counter-agent",
name: "my-counter",
onStateUpdate: (newState) => {
setCount(newState.counter);
}
});
const increment = () => {
agent.setState({ counter: count + 1 });
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
useAgentChat Hook
import { useAgentChat } from "agents/ai-react";
function ChatInterface() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgentChat({
agent: "ai-chat-agent",
name: "chat-session-123"
});
return (
<div>
<div>
{messages.map((msg, i) => (
<div key={i}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="Type a message..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
{isLoading ? "Thinking..." : "Send"}
</button>
</form>
</div>
);
}
模型上下文协议(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:服务器发送事件(旧版,已弃用)
使用 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(AIChatAgent)
import { AIChatAgent } from "agents/ai-chat-agent";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export class MyChatAgent extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
const result = streamText({
model: openai('gpt-4o-mini'),
messages: this.messages,
onFinish
});
return result.toTextStreamResponse();
}
// Optional: Customize message persistence
async onStateUpdate(state, source) {
console.log('Chat state updated:', this.messages.length, 'messages');
}
}
人类参与环节(HITL)
export class ApprovalAgent extends Agent {
async processRequest(data: any) {
// Process automatically
const processed = await this.autoProcess(data);
// If confidence low, request human review
if (processed.confidence < 0.8) {
this.setState({
...this.state,
pendingReview: {
data: processed,
requestedAt: Date.now(),
status: 'pending'
}
});
// Send notification to human
await this.notifyHuman(processed);
return { status: 'pending_review', id: processed.id };
}
// High confidence, proceed automatically
return this.complete(processed);
}
async approveReview(id: string, approved: boolean) {
const pending = this.state.pendingReview;
if (approved) {
await this.complete(pending.data);
} else {
await this.reject(pending.data);
}
this.setState({
...this.state,
pendingReview: null
});
}
}
工具概念
工具使 Agent 能够与外部服务交互:
export class ToolAgent extends Agent {
// Tool: Search flights
async searchFlights(from: string, to: string, date: string) {
const response = await fetch(`https://api.flights.com/search`, {
method: 'POST',
body: JSON.stringify({ from, to, date })
});
return response.json();
}
// Tool: Book flight
async bookFlight(flightId: string, passengers: any[]) {
const response = await fetch(`https://api.flights.com/book`, {
method: 'POST',
body: JSON.stringify({ flightId, passengers })
});
return response.json();
}
// Tool: Send confirmation email
async sendEmail(to: string, subject: string, body: string) {
// Use email service
return { sent: true };
}
// Orchestrate tools
async bookTrip(params: any) {
const flights = await this.searchFlights(params.from, params.to, params.date);
const booking = await this.bookFlight(flights[0].id, params.passengers);
await this.sendEmail(params.email, "Booking Confirmed", `Booking ID: ${booking.id}`);
return booking;
}
}
多 Agent 编排
interface Env {
ResearchAgent: AgentNamespace<ResearchAgent>;
WriterAgent: AgentNamespace<WriterAgent>;
EditorAgent: AgentNamespace<EditorAgent>;
}
export class OrchestratorAgent extends Agent<Env> {
async createArticle(topic: string) {
// 1. Research agent gathers information
const researcher = getAgentByName<Env, ResearchAgent>(
this.env.ResearchAgent,
`research-${topic}`
);
const research = await (await researcher).research(topic);
// 2. Writer agent creates draft
const writer = getAgentByName<Env, WriterAgent>(
this.env.WriterAgent,
`writer-${topic}`
);
const draft = await (await writer).write(research);
// 3. Editor agent reviews
const editor = getAgentByName<Env, EditorAgent>(
this.env.EditorAgent,
`editor-${topic}`
);
const final = await (await editor).edit(draft);
return final;
}
}
关键规则
始终要做 ✅
- 导出 Agent 类 - 必须导出才能使绑定生效
- 在 v1 迁移中包含 new_sqlite_classes - 之后无法再添加 SQLite
- 使绑定名称与类名一致 - 避免出现“找不到绑定”错误
- 在 Worker 中进行身份验证,而不是在 Agent 中 - 安全最佳实践
- 对 SQL 使用带标签的模板字面量 - 防止 SQL 注入
- 处理 WebSocket 断开连接 - 状态会保留,连接不会
- 确认计划任务回调存在 - 如果方法缺失则会抛出错误
- 使用全局唯一的实例名称 - 相同名称 = 全局同一个 Agent
- 检查状态大小限制 - 每个 Agent 总计最多 1GB
- 监控任务负载大小 - 每个计划任务最多 2MB
- 正确使用工作流绑定 - 必须在 wrangler.jsonc 中配置
- 在插入数据前创建 Vectorize 索引 - 元数据过滤所必需
- 关闭浏览器实例 - 避免资源泄漏
- 使用 setState() 进行持久化 - 不要只修改 this.state
- 先在本地测试迁移 - 迁移是原子操作,无法回滚
绝不要做 ❌
- 不要向已部署的类添加 SQLite - 必须在首次迁移中添加
- 不要逐步部署迁移 - 只能原子部署
- 不要在 Worker 中跳过身份验证 - 访问 Agent 前始终先进行身份验证
- 不要手动构造 SQL 字符串 - 使用带标签的模板字面量
- 不要让每个 Agent 的状态超过 1GB - 硬性限制
- 不要使用不存在的回调来安排任务 - 会导致运行时错误
- 不要以为相同名称表示不同 Agent - 名称全局唯一
- 不要将 SSE 用于 MCP - 已弃用,请使用 /mcp 传输
- 不要忘记浏览器绑定 - 网页浏览所必需
- 不要直接修改 this.state - 改用 setState()
已知问题预防
此技能可预防 15+ 个已记录的问题:
问题 1:迁移不是原子操作
错误:“无法逐步部署迁移”
来源:https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/
原因:迁移会同时应用到所有实例
预防措施:独立于代码更改部署迁移,使用 npx wrangler versions deploy
问题 2:缺少 new_sqlite_classes
错误:“无法在现有类上启用 SQLite”
来源:https://developers.cloudflare.com/agents/api-reference/configuration/
原因:必须在首次迁移中启用 SQLite
预防措施:在标记为 "v1" 的迁移中包含 new_sqlite_classes
问题 3:未导出 Agent 类
错误:“找不到绑定”或“无法访问 undefined”
来源:https://developers.cloudflare.com/agents/api-reference/agents-api/
原因:Durable Objects 要求类必须导出
预防措施:export class MyAgent extends Agent(使用 export 关键字)
问题 4:绑定名称不匹配
错误:“找不到绑定 'X'”
来源: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:计划任务回调不存在
错误:"Method X does not exist on Agent" 来源:https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ 原因:this.schedule() 调用了未定义的方法 预防措施:安排任务前,确保回调方法已存在
问题 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 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 transport deprecated"
来源: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},并验证所有权
依赖项
必需
- 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/
- Browser Rendering:https://developers.cloudflare.com/browser-rendering/
- Model Context Protocol:https://modelcontextprotocol.io/
- Cloudflare MCP Servers: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- 深入了解状态与 SQLwebsockets-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 服务
最后验证时间:2025-10-21
软件包版本:agents@latest
合规性:Cloudflare Agents SDK 官方文档