Cloudflare agents 框架完全指南:在 Cloudflare 全球网络上构建持久化 AI Agent
【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents
agents是 Cloudflare 开源的 Agent 运行时与 SDK(本仓库位于packages/agents),它的核心承诺是"构建会思考、会行动(Build software that thinks and does)"的软件:让 LLM 不再只是"一次请求、一次返回"的临时工具,而是有记忆、能推理、可自主调度并主动行动的持久化实体。本指南以 packages/agents/README.md 为主线,结合 源码实现 与 官方文档,带你从零掌握:如何用Agent类编写有状态的服务端智能体、如何用@callable()暴露 RPC 方法、如何通过 React/原生 JS 客户端实时同步状态,以及如何接入调度、子 Agent、Workflows、AI Chat 与 MCP 等进阶能力。
为什么需要 Agent:从无状态函数到持久智能体
LLM 已经能推理、规划、使用工具,但它们需要与之匹配的基础设施。传统 Serverless 是无状态且短暂的——一个函数从请求到返回,生命周期即告终结;而 Agent 是持久且有目的的实体:
From request handlers → to autonomous entities From stateless functions → to persistent intelligence Traditional serverless: Request → Response → Gone Agents: Thinking, remembering, acting — continuously二者的本质差异在于:
- 按活跃度计费:Agent 在请求之间会休眠(hibernate)。你可以在单个 Worker 上拥有数百万个 Agent——每用户一个、每会话一个、每游戏房间一个——空闲时零成本,被唤醒时才开始计费。
- 持久状态:基于 Cloudflare Durable Objects 构建,Agent 运行在距离用户最近的全球节点,状态在重启、部署甚至休眠后依然存活。
这一设计直接回答了"为什么是现在":模型能力已经就绪,而传统无服务器模型无法承载"持续思考、跨请求记忆"这类应用形态。
快速上手:创建项目与安装
方式一:从模板创建全新项目
npm create cloudflare@latest -- --template cloudflare/agents-starter方式二:加入现有项目
npm install agentsREADME 中的示例大量使用@callable()装饰器,这需要工程配置配合:
- Vite 项目必须继承
agents/tsconfig,并在vite.config.ts中加入agents/vite插件; - 其他打包器必须支持 TC39 装饰器(
2023-11版本)。
详细配置见 将 Agents 加入现有项目。在 getting-started 文档 中给出了两份关键配置:
tsconfig.json—— 继承agents/tsconfig(设置target: "ES2021"等推荐选项):
{ "extends": "agents/tsconfig" }vite.config.ts—— 加入agents()插件处理 TC39 装饰器转换(Vite 8 的 Oxc 转译器目前尚不支持装饰器,因此必须依赖该插件):
import { cloudflare } from "@cloudflare/vite-plugin"; import react from "@vitejs/plugin-react"; import agents from "agents/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [agents(), react(), cloudflare()] });从 package.json 的exports字段可以看出,agents包采用了多入口设计:agents(核心)、agents/client(原生 JS 客户端)、agents/react(React Hook)、agents/vite、agents/tsconfig、agents/mcp、agents/mcp/server、agents/mcp/client、agents/voice、agents/channels、agents/workflows、agents/schedule、agents/agent-tools、agents/email等。这让打包器可以按需加载,应用只加载真正用到的集成模块。
快速示例:一个带实时状态同步的计数器 Agent
服务端:定义 Agent 与可调用方法
// server.ts import { Agent, callable } from "agents"; export type State = { count: number }; export class CounterAgent extends Agent<Env, State> { initialState: State = { count: 0 }; @callable() increment() { this.setState({ count: this.state.count + 1 }); return this.state.count; } @callable() decrement() { this.setState({ count: this.state.count - 1 }); return this.state.count; } }客户端:React Hook 实时同步
// client.tsx import { useAgent } from "agents/react"; import { useState } from "react"; import type { CounterAgent, State } from "./server"; function Counter() { const [count, setCount] = useState(0); const agent = useAgent<CounterAgent, State>({ agent: "counter-agent", name: "my-counter", onStateUpdate: (state) => setCount(state.count) }); return ( <div> <span>{count}</span> <button onClick={() => agent.stub.increment()}>+</button> <button onClick={() => agent.stub.decrement()}>-</button> </div> ); }状态变更会自动同步到所有已连接的客户端,方法调用就像调用本地函数一样简单。整个交互链路是:
- 客户端通过 WebSocket 调用
agent.stub.increment() - Agent执行
increment(),用setState()更新状态 - 状态自动持久化到 SQLite
- 广播推送给所有已连接客户端
- React组件重新渲染出最新的
agent.state
完整流程与排查指南(如 "Agent not found"、状态不同步、"Method X is not callable" 等常见问题的解决方案)见 Getting Started。
注册 Agent 到 wrangler.jsonc
{ "name": "my-agent", "main": "src/server.ts", "compatibility_date": "2025-01-01", "compatibility_flags": ["nodejs_compat"], "durable_objects": { "bindings": [ { "name": "Counter", "class_name": "Counter" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["Counter"] } ] }Agent 使用SQLite 存储(new_sqlite_classes),因此每个命名实例都拥有独立的持久存储。
你能构建什么:典型应用场景
| 应用场景 | 为什么用 Agent |
|---|---|
| 多人游戏房间 | 按房间隔离状态、实时同步、房间空置时休眠 |
| 客服机器人 | 记住对话历史、可升级转人工 |
| 协作编辑器 | 在线存在感、光标、文档状态 |
| 审批工作流 | 长时间运行、暂停等待人工输入、可持久化 |
| 个人 AI 助手 | 每用户记忆、通过 MCP 接入工具 |
| 通知系统 | 定时投递、用户偏好、重试逻辑 |
状态管理:持久化 + 实时广播
状态跨请求持久化,并同步到所有已连接客户端:
import { Agent, callable, type Connection } from "agents"; type State = { items: string[] }; export class MyAgent extends Agent<Env, State> { initialState: State = { items: [] }; @callable() addItem(item: string) { this.setState({ items: [...this.state.items, item] }); } onStateChanged(state: State, source: Connection | "server") { // Called after state is persisted and broadcast } }从源码看,Agent 的状态管理有一整套内部设计。在 src/index.ts 中:
- 状态存储在 DO 的 SQLite 内,内部键(如
cf_state_row_id)与用户状态键隔离; - 连接状态中保留一批
_cf_前缀的内部键(CF_READONLY_KEY、CF_NO_PROTOCOL_KEY等),setState时会被自动剥离/保留,避免用户代码误操作内部状态; - 错误广播会经过
sanitizeErrorString截断与剥离控制字符(最长 500 字符),防止来自外部 MCP OAuth 提供商的不可信内容带来 XSS 风险。
细节参考 State Management 文档。
@callable() 方法:类型安全的 RPC
用@callable()装饰器向客户端暴露方法:
@callable() async processOrder(orderId: string, items: Item[]) { // Full type safety - clients call this like a local function const result = await this.validateAndProcess(orderId, items); return result; }// Client const result = await agent.stub.processOrder("order-123", items);从 callable-decorator.ts 的源码可以看到装饰器背后的元数据机制:
CallableMetadata目前支持两个可选字段:description(方法功能描述)与streaming(方法是否支持流式响应);- 装饰器通过
WeakMap<Function, CallableMetadata>注册方法元数据,getCallableMetadata/isCallableMethod用于运行时识别可调用方法,copyCallableMetadata保证框架包装方法后注册信息不丢失; - 旧的
unstable_callable已更名为callable,会在下一个大版本移除。
更多细节见 Callable Methods 文档。
调度与后台任务
调度(Scheduling)
支持延迟执行、固定间隔与 cron 表达式:
// In 60 seconds this.schedule(60, "sendReminder", { userId: "123" }); // Every hour this.scheduleEvery(3600, "syncData"); // Daily at 9am UTC this.schedule("0 9 * * *", "dailyReport"); // At a specific date this.schedule(new Date("2025-12-31"), "yearEndTask");从源码实现看,schedules 模块 使用cron-schedule包解析 cron 表达式(见 schedule-timing.ts),并由 scheduler.ts 统一调度。调度器有一个hungScheduleTimeoutSeconds参数(默认 30 秒):如果一次周期性任务回调被认为"卡死",会被强制重置。如果你的回调合法地需要超过 30 秒,应调大该值。参考 Scheduling 文档。
后台任务(Queue)
立即排队执行后台任务:
await this.queue("processUpload", { fileId: "abc" }); // Returns immediately, task runs in background从 src/index.ts 中QueueItem类型可以看出队列条目的结构:id、payload、callback(Agent 上的方法名)、created_at,以及可选的retry: RetryOptions。任务项支持独立配置重试策略,框架对schedule()、queue()、this.retry()提供了默认重试配置(见下文"配置"一节)。参考 Queue 文档。
子 Agent(Sub-agents / Facets)
父 Agent 可以派生子 Durable Object(称为 facet)。每个子 Agent 拥有独立的 SQLite 存储并可并行运行,但都统一挂在父 Agent 的 URL 下:
export class Inbox extends Agent { @callable() async createChat() { const id = crypto.randomUUID(); await this.subAgent(Chat, id); return id; } override async onBeforeSubAgent(_req, { className, name }) { if (!this.hasSubAgent(className, name)) { return new Response("Not found", { status: 404 }); } } } export class Chat extends Agent { async writePreview(text: string) { const inbox = await this.parentAgent(Inbox); await inbox.savePreview(this.name, text); } }客户端通过useAgent({ sub: [...] })连接子 Agent:
const inbox = useAgent({ agent: "Inbox", name: userId }); const chat = useAgent({ agent: "Inbox", name: userId, sub: [{ agent: "Chat", name: chatId }] });路由后的 URL 形如/agents/inbox/{userId}/sub/chat/{chatId}。
子 Agent 的 WebSocket 客户端可以使用相同的 URL 结构。父 Agent 始终是公开地址,但子 Agent 依然能收到作用域限定在自己客户端上的onConnect、onMessage、onClose、broadcast()、getConnections()回调。父 Agent 的广播不会泄漏到定向子 Agent 的 socket;当连接从休眠中恢复时,子 Agent 的连接标签、只读状态和协议消息设置都会被保留。子 Agent URL 支持通过重复的/sub/{agent}/{name}段进行嵌套,但受平台当前 facet 嵌套层数限制。
路由实现细节可参考 sub-routing.ts(导出buildAgentPath、routeSubAgentRequest、SUB_PREFIX等),以及 Sub-agents 文档。
Agent Tools:把子 Agent 变成工具
父聊天 Agent 可以把具备聊天能力的子 Agent 作为工具运行(支持 Think Agent 与AIChatAgent子类)。子 Agent 保留自己的消息、工具、SQLite 存储和可恢复的流;父 Agent 广播agent-tool-event帧,让 UI 可以内联渲染子 Agent 的时间线:
import { Think } from "@cloudflare/think"; import { agentTool } from "agents/agent-tools"; import { z } from "zod"; export class Researcher extends Think<Env> { getSystemPrompt() { return "Research the requested topic and end with a concise summary."; } } export class Assistant extends Think<Env> { getTools() { return { research: agentTool(Researcher, { description: "Research one topic in depth.", inputSchema: z.object({ query: z.string().min(3) }) }) }; } }inputSchema接受 AI SDK 支持的各类 schema:Zod、Standard JSON Schema 兼容 schema、通过jsonSchema()传入的原始 JSON Schema,以及 AI SDK 适配器暴露的 schema。例如使用@ai-sdk/valibot(AI SDK 6 用 v2,AI SDK 7 用 v3):
import { valibotSchema } from "@ai-sdk/valibot"; import * as v from "valibot"; const researchInput = valibotSchema( v.object({ query: v.pipe(v.string(), v.minLength(3)) }) ); agentTool(Researcher, { description: "Research one topic in depth.", inputSchema: researchInput });注意:工具输入除了运行时校验外,还需要面向模型的 JSON Schema。因此仅做校验的 Standard Schema 是不够的,必须使用其 Standard JSON Schema 扩展或 AI SDK 适配器。
确定性扇出(deterministic fan-out)可以直接调用this.runAgentTool(Researcher, { input })。父 Agent 在重启后会协调并清理残留的子任务行,把无法恢复的运行标记为interrupted而不是一直挂起。React 端用useAgentToolEvents({ agent })渲染保留并重放的子 Agent 时间线。AIChatAgent子任务以无头模式运行,因此浏览器端客户端工具需要一个独立的桥接,而服务端工具正常工作。完整指南见 Agent Tools。
WebSocket 连接与邮件处理
实时连接
async onConnect(connection: Connection) { console.log(`Client ${connection.id} connected`); } async onMessage(connection: Connection, message: unknown) { // Handle incoming messages connection.send(JSON.stringify({ received: true })); } async onClose(connection: Connection) { console.log(`Client ${connection.id} disconnected`); }邮件收发
Agent 可以接收并回复邮件:
import type { AgentEmail } from "agents/email"; async onEmail(email: AgentEmail) { const from = email.from; const subject = email.headers.get("subject"); // Process incoming email }邮件头解析基于postal-mime、邮件构造基于mimetext(见 package.json 依赖)。邮件相关完整文档见 Email 文档。
客户端 SDK
React Hook
import { useAgent } from "agents/react"; import { useState } from "react"; function App() { const [state, setState] = useState<MyState | null>(null); const agent = useAgent<MyState>({ agent: "my-agent", name: "instance-name", onStateUpdate: (newState) => setState(newState) }); return ( <div> <pre>{JSON.stringify(state, null, 2)}</pre> <button onClick={() => agent.stub.doSomething()}>Call Agent</button> </div> ); }原生 JavaScript
import { AgentClient } from "agents/client"; const client = new AgentClient({ agent: "my-agent", name: "instance-name", onStateUpdate: (state) => console.log("State:", state) }); // Call methods const result = await client.call("processData", [payload]); // Or use the stub const result = await client.stub.processData(payload);useAgent通过 WebSocket 连接 Agent,agent.state是响应式的(状态变化触发组件重渲染),agent.stub.methodName()调用服务端@callable()方法。完整 API 参考见 Client SDK。
Workflows 集成:持久化多步任务
对需要跨故障存活、且能暂停等待人工审批的多步任务,可以集成 Cloudflare Workflows:
import { AgentWorkflow } from "agents"; export class OrderWorkflow extends AgentWorkflow<OrderAgent, OrderParams> { async run(event, step) { // Step 1: Validate (retries automatically on failure) const validated = await step.do("validate", async () => { return validateOrder(event.payload); }); // Step 2: Wait for human approval await this.reportProgress({ step: "approval", status: "pending" }); const approval = await this.waitForApproval(step, { timeout: "7 days" }); // Step 3: Process the approved order await step.do("process", async () => { return processOrder(validated, approval); }); } }Workflows 提供的能力:
- 持久化执行(Durable execution)—— 步骤失败自动重试,状态跨故障保留
- 人在回路(Human-in-the-loop)—— 通过
waitForApproval()暂停等待审批 - 长时任务—— 可以运行数天甚至数周
- 进度追踪—— 通过
reportProgress()向 Agent 汇报状态
AgentWorkflow类在 workflows.ts 中定义。参考 Workflows 文档 与 Human in the Loop。
AI Chat 集成:持久会话与流式响应
对于需要持久会话、流式响应和工具支持的 AI 聊天体验,参见@cloudflare/ai-chat:
import { AIChatAgent } from "@cloudflare/ai-chat"; import { createWorkersAI } from "workers-ai-provider"; import { convertToModelMessages, streamText } from "ai"; export class ChatAgent extends AIChatAgent<Env> { async onChatMessage() { const workersai = createWorkersAI({ binding: this.env.AI }); const result = streamText({ model: workersai("@cf/moonshotai/kimi-k2.7-code"), messages: await convertToModelMessages(this.messages) }); return result.toUIMessageStreamResponse(); } }// Client import { useAgentChat } from "@cloudflare/ai-chat/react"; const { messages, sendMessage } = useAgentChat({ agent: useAgent({ agent: "chat-agent" }) });特性:
- 自动消息持久化
- 可恢复流式响应(断开连接后可以恢复)
- 服务端与客户端工具执行
- 敏感工具的人工审批(human-in-the-loop)
MCP(Model Context Protocol)
Agent 可以扮演 MCP 服务器(向 AI 助手提供工具),也可以扮演 MCP 客户端(使用其他服务的工具)。
创建无状态 MCP 服务器
import { McpServer } from "@modelcontextprotocol/server"; import { createMcpHandler } from "agents/mcp/server"; import { z } from "zod"; function createServer() { const server = new McpServer({ name: "my-tools", version: "1.0.0" }); server.registerTool( "lookup", { description: "Look up data", inputSchema: { query: z.string() } }, async ({ query }) => ({ content: [{ type: "text", text: await lookup(query) }] }) ); return server; } export default { fetch(request, env, ctx) { return createMcpHandler(createServer)(request, env, ctx); } } satisfies ExportedHandler;只有当需要保留 Legacy 会话行为时,才从
agents/mcp使用McpAgent、createLegacyMcpHandler和WorkerTransport。
使用 MCP 工具(作为客户端)
// Connect to external MCP servers await this.addMcpServer( "weather-service", "https://weather-mcp.example.com/mcp", { transport: { type: "streamable-http" } } ); // Use with AI SDK const result = await generateText({ model: openai("gpt-4o"), tools: this.mcp.getAITools(), prompt: "What's the weather in Tokyo?" });从 src/index.ts 中AddMcpServerOptions的源码可见addMcpServer支持丰富选项:可选稳定id(用于存储与工具名命名空间,如 connector 风格的tool_github_create_pull_request)、callbackHost/callbackPath(OAuth 回调地址)、agentsPrefix(路由前缀,默认agents)、client(MCP 客户端选项)、transport(含headers认证头、传输类型"sse" | "streamable-http" | "auto",以及一个安全弱化的skipIssuerMetadataValidation兼容开关)、retry(连接与重连重试策略)。normalizeServerId与MCP_SERVER_ID_MAX_LENGTH约束了服务器 ID 的规范化与长度上限。参考 MCP Client 文档 与 MCP Servers 文档。
配置与路由
在 wrangler.jsonc 中注册 Agent
{ "durable_objects": { "bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }] }将请求路由到 Agent
import { routeAgentRequest } from "agents"; export default { async fetch(request: Request, env: Env) { return ( (await routeAgentRequest(request, env)) ?? new Response("Not found", { status: 404 }) ); } };路由与运行时选项(源码级)
routeAgentRequest的实现位于 agent-routing.ts,它的行为由AgentOptions控制:
prefix—— URL 前缀,默认"agents",路由匹配形如/agents/{binding}/{name}的路径;cors—— 设为true时启用默认宽松 CORS 头(Access-Control-Allow-Origin: *、方法GET, POST, HEAD, OPTIONS、Max-Age: 86400);带凭证的请求应显式传入HeadersInit指定具体 origin;对匹配路由的 OPTIONS 预检请求会自动处理;jurisdiction/locationHint—— Durable Object 的管辖区域与放置位置提示;props—— 生命周期启动前注入的属性(通过x-agents-lifecycle-props头编码传递);onBeforeRequest/onBeforeConnect—— 路由前的拦截钩子,可以改写请求或直接返回响应;routingRetry—— 默认开启(maxAttempts: 3、baseDelayMs: 100、maxDelayMs: 800,指数退避 + 随机抖动),仅对 Durable Object 标记为retryable的瞬时基础设施错误生效;传false关闭。
此外,Agent 类本身有一组可通过static options覆盖的默认静态配置(定义在 src/index.ts 的DEFAULT_AGENT_STATIC_OPTIONS):
| 选项 | 默认值 | 说明 |
|---|---|---|
sendIdentityOnConnect | true | 连接建立时是否向客户端发送身份信息(agent 名称与实例名) |
hungScheduleTimeoutSeconds | 30 | 周期调度回调被认为"卡死"并被强制重置的超时(秒) |
keepAliveIntervalMs | 30000 | keepAlive()心跳 alarm 的间隔(毫秒),越低恢复越快但 alarm 越频繁 |
retry | { maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 3000 } | schedule()、queue()、this.retry()的默认重试策略 |
fiberRecoveryHookTimeoutMs | 10000 | 框架内部 fiber 恢复钩子的超时 |
fiberRecoveryScanDeadlineMs | 10000 | 单次中断 fiber 恢复扫描的软截止时间 |
fiberRecoveryMaxAgeMs | 86400000(24h) | 未管理的中断 fiber 行的最大恢复年龄,防止反复抛错的钩子无限重试(设为0可永久保留) |
agentToolReattachNoProgressTimeoutMs | 120000 | 部署/父恢复后重挂到仍在运行的 agent-tool 子任务的"无进展"预算,每次收到转发 chunk 会重置 |
agentToolReattachMaxWindowMs | Infinity | 单次重挂的硬墙钟上限(默认不设上限) |
detachedMaxBudgetMs | 86400000(24h) | 分离(后台)agent-tool 运行的绝对预算上限 |
detachedNoProgressBudgetMs | 3600000(1h) | 分离运行报告过一次进度后、再次静默的放弃窗口 |
maxAlarmMemoryLimitStrikes | 3 | 连续因内存限制重置的 alarm 调用次数上限,防止平台自动重试循环(断路器) |
总结与下一步
agents把 Cloudflare Durable Objects、SQLite、WebSocket、调度器、Workflows 和 MCP 整合为一套面向 AI 智能体的运行时抽象,让开发者用普通类的写法就能获得:持久状态、实时同步、类型安全的远程方法调用、后台调度、子 Agent 编排与多通道通信。官方发布包内还包含完整的文档树(docs/index.md)。
继续深入可以参考以下文档:
- Getting Started · State Management · Scheduling
- Callable Methods · Durable Object Lifecycle · MCP Integration
- Full Documentation · Agent Class · Sub-agents · Workflows · Channels
项目的完整开源许可见根目录 LICENSE(MIT 协议)。
【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考