# 在 Cloudflare 上构建 Agents URL: https://developers.cloudflare.com/agents/ import { CardGrid, Description, Feature, LinkButton, LinkTitleCard, PackageManagers, Plan, RelatedProduct, Render, TabItem, Tabs, TypeScriptExample, } from "~/components"; Agents SDK 让您能够构建和部署AI驱动的智能体,这些智能体可以自主执行任务、与客户端实时通信、调用AI模型、持久化状态、调度任务、运行异步工作流、浏览网络、从数据库查询数据、支持人机协作交互,以及[更多功能](/agents/api-reference/)。 ### 发布您的第一个 Agent 要使用agents-starter模板并通过Agents SDK创建您的第一个 Agent: ```sh # 安装 npm create cloudflare@latest agents-starter -- --template=cloudflare/agents-starter # 部署 npx wrangler@latest deploy ``` 前往[构建聊天 Agent](/agents/getting-started/build-a-chat-agent) 指南,了解agents-starter项目是如何构建的,以及如何将其作为您自己 Agents 的基础。 如果您已经在 [Workers](/workers/) 上进行开发,您可以直接将 `agents` 包安装到现有项目中: ```sh npm i agents ``` 然后通过创建一个继承 `Agent` 类的类来定义您的第一个 Agent: ```ts import { Agent, AgentNamespace } from "agents"; export class MyAgent extends Agent { // 在 Agent 上定义方法: // https://developers.cloudflare.com/agents/api-reference/agents-api/ // // 每个 Agent 都有通过 this.setState 和 this.sql 的内置状态 // 通过 this.schedule 的内置调度功能 // Agents 支持 WebSockets、HTTP 请求、状态同步,并且 // 可以运行几秒钟、几分钟或几小时:只要任务需要多长时间。 } ``` 深入了解 [Agent SDK 参考文档](/agents/api-reference/agents-api/),了解更多关于如何使用 Agents SDK 包和定义 `Agent` 的信息。 ### 为什么在 Cloudflare 上构建 Agents? 我们在构建 Agents SDK 时考虑了以下几个方面: - **内置状态管理**: Agents 配备了[内置状态管理](/agents/api-reference/store-and-sync-state/),能够自动在 Agent 和客户端之间同步状态、在状态变化时触发事件,以及读写每个 Agent 的 SQL 数据库。 - **通信能力**: 您可以通过 [WebSockets](/agents/api-reference/websockets/) 连接到 Agent,并将更新实时流式传输回客户端。处理推理模型的长时间运行响应、[异步工作流](/agents/api-reference/run-workflows/)的结果,或构建基于 Agents SDK 中包含的 `useAgent` hook 的聊天应用。 - **可扩展性**: Agents 就是代码。使用您想要的 [AI 模型](/agents/api-reference/using-ai-models/),带上您自己的无头浏览器服务,从托管在其他云中的数据库拉取数据,为您的 Agent 添加自己的方法并调用它们。 使用 Agents SDK 构建的 Agents 可以直接部署到 Cloudflare,并在 [Durable Objects](/durable-objects/) 上运行——您可以将其视为能够扩展到数千万个的有状态微服务器——并且能够在需要的任何地方运行。让您的 Agents 通过CDN技术以实现低延迟交互,利用CDN技术将数据存储在靠近用户的地方以提高吞吐量,或在两者之间的任何地方运行。 --- ### 在 Cloudflare 平台上构建 构建无服务器应用程序,在全球范围内即时部署,实现卓越的性能、可靠性和规模。 通过缓存、速率限制、请求重试、模型回退等功能观察和控制您的 AI 应用程序。 使用 Cloudflare 的向量数据库 Vectorize 构建全栈 AI 应用程序。添加 Vectorize 让您能够执行语义搜索、推荐、异常检测等任务,或用于为 LLM 提供上下文和记忆。 在 Cloudflare 的全球网络上运行由无服务器 GPU 驱动的机器学习模型。 构建有状态的 Agents,保证执行,包括自动重试、可运行几分钟、几小时、几天或几周的持久状态。 --- # 浏览网页 URL: https://developers.cloudflare.com/agents/api-reference/browse-the-web/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, PackageManagers, } from "~/components"; Agents 可以使用[浏览器渲染](/browser-rendering/) API 或您首选的无头浏览器服务来浏览网页。 ### 浏览器渲染 API [浏览器渲染](/browser-rendering/)允许您启动无头浏览器实例、渲染网页,并通过您的 Agent 与网站交互。 您可以定义一个使用 Puppeteer 提取网页内容、解析 DOM 并通过调用 OpenAI 模型提取相关信息的方法: ```ts interface Env { BROWSER: Fetcher; } export class MyAgent extends Agent { async browse(browserInstance: Fetcher, urls: string[]) { let responses = []; for (const url of urls) { const browser = await puppeteer.launch(browserInstance); const page = await browser.newPage(); await page.goto(url); await page.waitForSelector("body"); const bodyContent = await page.$eval( "body", (element) => element.innerHTML, ); const client = new OpenAI({ apiKey: this.env.OPENAI_API_KEY, }); let resp = await client.chat.completions.create({ model: this.env.MODEL, messages: [ { role: "user", content: `从下面的网站内容中返回一个包含产品名称、价格和 URL 的 JSON 对象,格式如下:{ "name": "产品名称", "price": "价格", "url": "URL" }。${bodyContent}`, }, ], response_format: { type: "json_object", }, }); responses.push(resp); await browser.close(); } return responses; } } ``` 您还需要安装 `@cloudflare/puppeteer` 包并将以下内容添加到您的 Agent 的 wrangler 配置中: ```jsonc { // ... "browser": { "binding": "MYBROWSER", }, // ... } ``` ### Browserbase 您还可以通过直接从 Agent 内使用 Browserbase API 来使用 [Browserbase](https://docs.browserbase.com/integrations/cloudflare/typescript)。 获得您的 [Browserbase API 密钥](https://docs.browserbase.com/integrations/cloudflare/typescript)后,您可以通过创建[密钥](/workers/configuration/secrets/)将其添加到您的 Agent: ```sh cd your-agent-project-folder npx wrangler@latest secret put BROWSERBASE_API_KEY ``` ```sh output Enter a secret value: ****** Creating the secret for the Worker "agents-example" Success! Uploaded secret BROWSERBASE_API_KEY ``` 安装 `@cloudflare/puppeteer` 包并在您的 Agent 内使用它来调用 Browserbase API: ```ts interface Env { BROWSERBASE_API_KEY: string; } export class MyAgent extends Agent { constructor(env: Env) { super(env); } } ``` --- # Agents API URL: https://developers.cloudflare.com/agents/api-reference/agents-api/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; 本页面提供了 Agent SDK API 的概述,包括 `Agent` 类、内置于 Agents SDK 的方法和属性。 Agents SDK 公开了两个主要 API: - 服务器端 `Agent` 类。Agent 封装了 Agent 的所有逻辑,包括客户端如何连接到它、如何存储状态、它公开的方法、如何调用 AI 模型以及任何错误处理。 - 客户端 `AgentClient` 类,允许您从客户端应用程序连接到 Agent 实例。客户端 API 还包括 React hooks,包括 `useAgent` 和 `useAgentChat`,并允许您在每个唯一的 Agent(运行在服务器端)和您的客户端应用程序之间自动同步状态。 :::note Agents 需要 [Cloudflare Durable Objects](/durable-objects/),参见[配置](/agents/getting-started/testing-your-agent/#add-the-agent-configuration)了解如何向您的项目添加所需的绑定。 ::: 您还可以在 [Agents API 参考](/agents/api-reference/)中找到每个 API 的更具体使用示例。 ```ts import { Agent } from "agents"; class MyAgent extends Agent { // 在 Agent 上定义方法 } export default MyAgent; ``` 一个 Agent 可以有许多(数百万个)实例:每个实例都是一个独立运行的独立微服务器。这允许 Agents 水平扩展:一个 Agent 可以与单个用户关联,或与数千个用户关联,这取决于您正在构建的 Agent。 Agent 的实例通过唯一标识符寻址:该标识符(ID)可以是用户 ID、电子邮件地址、GitHub 用户名、航班机票号码、发票 ID,或任何其他有助于唯一标识实例并代表谁行动的标识符。 ### Agent 类 API 编写 Agent 需要您定义一个扩展 Agents SDK 包中 `Agent` 类的类。Agent 封装了 Agent 的所有逻辑,包括客户端如何连接到它、如何存储状态、它公开的方法以及任何错误处理。 您还可以在 Agent 上定义自己的方法:从技术上讲,发布一个只公开您自己方法的 Agent 是有效的,并直接从 Worker 创建/获取 Agents。 您自己的方法可以通过 `this.env` 访问 Agent 的环境变量和绑定,通过 `this.setState` 访问状态,并通过 `this.yourMethodName` 调用 Agent 上的其他方法。 ```ts import { Agent } from "agents"; interface Env { // 在这里定义环境变量和绑定 } // 将 Env 作为 TypeScript 类型参数传递 // 作为绑定连接到您的 Agent 或 Worker 的任何服务 // 然后在 this.env. 上可用 // 用于创建可以维护状态、编排的 Agents 的核心类 // 复杂的 AI 工作流、调度任务,并与用户和其他 Agents 交互。 class MyAgent extends Agent { // 可选的初始状态定义 initialState = { counter: 0, messages: [], lastUpdated: null, }; // 当新的 Agent 实例启动或从休眠中唤醒时调用 async onStart() { console.log("Agent 已启动,状态:", this.state); } // 处理来到此 Agent 实例的 HTTP 请求 // 返回 Response 对象 async onRequest(request: Request): Promise { return new Response("来自 Agent 的问候!"); } // 当建立 WebSocket 连接时调用 // 通过 ctx.request 访问原始请求进行身份验证等 async onConnect(connection: Connection, ctx: ConnectionContext) { // 连接被 SDK 自动接受。 // 您也可以在这里用 connection.close() 显式关闭连接 // 在 ctx.request 上访问 Request 以检查头部、cookies 和 URL } // 为在 WebSocket 连接上接收的每条消息调用 // 消息可以是 string、ArrayBuffer 或 ArrayBufferView async onMessage(connection: Connection, message: WSMessage) { // 处理传入消息 connection.send("已收到您的消息"); } // 处理 WebSocket 连接错误 async onError(connection: Connection, error: unknown): Promise { console.error(`连接错误:`, error); } // 处理 WebSocket 连接关闭事件 async onClose( connection: Connection, code: number, reason: string, wasClean: boolean, ): Promise { console.log(`连接已关闭: ${code} - ${reason}`); } // 当 Agent 的状态从任何源更新时调用 // source 可以是 "server" 或客户端 Connection onStateUpdate(state: State, source: "server" | Connection) { console.log("状态已更新:", state, "来源:", source); } // 您可以定义自己的自定义方法,由请求、 // WebSocket 消息或调度任务调用 async customProcessingMethod(data: any) { // 处理数据、更新状态、调度任务等 this.setState({ ...this.state, lastUpdated: new Date() }); } } ``` ```ts // 带有自定义方法的基本 Agent 实现 import { Agent } from "agents"; interface MyState { counter: number; lastUpdated: Date | null; } class MyAgent extends Agent { initialState = { counter: 0, lastUpdated: null, }; async onRequest(request: Request) { if (request.method === "POST") { await this.incrementCounter(); return new Response(JSON.stringify(this.state), { headers: { "Content-Type": "application/json" }, }); } return new Response(JSON.stringify(this.state), { headers: { "Content-Type": "application/json" }, }); } async incrementCounter() { this.setState({ counter: this.state.counter + 1, lastUpdated: new Date(), }); } } ``` ### WebSocket API WebSocket API 允许您接受和管理与 Agent 的 WebSocket 连接。 #### Connection 表示与 Agent 的 WebSocket 连接。 ```ts // WebSocket 连接接口 interface Connection { // 此连接的唯一 ID id: string; // 附加到此连接的客户端特定状态 state: State; // 更新连接的状态 setState(state: State): void; // 接受传入的 WebSocket 连接 accept(): void; // 使用可选代码和原因关闭 WebSocket 连接 close(code?: number, reason?: string): void; // 向客户端发送消息 // 可以是 string、ArrayBuffer 或 ArrayBufferView send(message: string | ArrayBuffer | ArrayBufferView): void; } ``` ```ts // Example of handling WebSocket messages export class YourAgent extends Agent { async onMessage(connection: Connection, message: WSMessage) { if (typeof message === "string") { try { // Parse JSON message const data = JSON.parse(message); if (data.type === "update") { // Update connection-specific state connection.setState({ ...connection.state, lastActive: Date.now() }); // Update global Agent state this.setState({ ...this.state, connections: this.state.connections + 1, }); // Send response back to this client only connection.send( JSON.stringify({ type: "updated", status: "success", }), ); } } catch (e) { connection.send(JSON.stringify({ error: "Invalid message format" })); } } } } ``` #### WSMessage Types of messages that can be received from a WebSocket. ```ts // Types of messages that can be received from WebSockets type WSMessage = string | ArrayBuffer | ArrayBufferView; ``` #### ConnectionContext Context information for a WebSocket connection. ```ts // Context available during WebSocket connection interface ConnectionContext { // The original HTTP request that initiated the WebSocket connection request: Request; } ``` ### State synchronization API :::note To learn more about how to manage state within an Agent, refer to the documentation on [managing and syncing state](/agents/api-reference/store-and-sync-state/). ::: #### State Methods and types for managing Agent state. ```ts // State management in the Agent class class Agent { // Initial state that will be set if no state exists yet initialState: State = {} as unknown as State; // Current state of the Agent, persisted across restarts get state(): State; // Update the Agent's state // Persists to storage and notifies all connected clients setState(state: State): void; // Called when state is updated from any source // Override to react to state changes onStateUpdate(state: State, source: "server" | Connection): void; } ``` ```ts // Example of state management in an Agent interface ChatState { messages: Array<{ sender: string; text: string; timestamp: number }>; participants: string[]; settings: { allowAnonymous: boolean; maxHistoryLength: number; }; } interface Env { // Your bindings and environment variables } // Inside your Agent class export class YourAgent extends Agent { async addMessage(sender: string, text: string) { // Update state with new message this.setState({ ...this.state, messages: [ ...this.state.messages, { sender, text, timestamp: Date.now() }, ].slice(-this.state.settings.maxHistoryLength), // Maintain max history }); // The onStateUpdate method will automatically be called // and all connected clients will receive the update } // Override onStateUpdate to add custom behavior when state changes onStateUpdate(state: ChatState, source: "server" | Connection) { console.log( `State updated by ${source === "server" ? "server" : "client"}`, ); // You could trigger additional actions based on state changes if (state.messages.length > 0) { const lastMessage = state.messages[state.messages.length - 1]; if (lastMessage.text.includes("@everyone")) { this.notifyAllParticipants(lastMessage); } } } } ``` ### Scheduling API #### Scheduling tasks Schedule tasks to run at a specified time in the future. ```ts // Scheduling API for running tasks in the future class Agent { // Schedule a task to run in the future // when: seconds from now, specific Date, or cron expression // callback: method name on the Agent to call // payload: data to pass to the callback // Returns a Schedule object with the task ID async schedule( when: Date | string | number, callback: keyof this, payload?: T, ): Promise>; // Get a scheduled task by ID // Returns undefined if the task doesn't exist async getSchedule(id: string): Promise | undefined>; // Get all scheduled tasks matching the criteria // Returns an array of Schedule objects getSchedules(criteria?: { description?: string; id?: string; type?: "scheduled" | "delayed" | "cron"; timeRange?: { start?: Date; end?: Date }; }): Schedule[]; // Cancel a scheduled task by ID // Returns true if the task was cancelled, false otherwise async cancelSchedule(id: string): Promise; } ``` ```ts // Example of scheduling in an Agent interface ReminderData { userId: string; message: string; channel: string; } export class YourAgent extends Agent { // Schedule a one-time reminder in 2 hours async scheduleReminder(userId: string, message: string) { const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000); const schedule = await this.schedule( twoHoursFromNow, "sendReminder", { userId, message, channel: "email" }, ); console.log(`Scheduled reminder with ID: ${schedule.id}`); return schedule.id; } // Schedule a recurring daily task using cron async scheduleDailyReport() { // Run at 08:00 AM every day const schedule = await this.schedule( "0 8 * * *", // Cron expression: minute hour day month weekday "generateDailyReport", { reportType: "daily-summary" }, ); console.log(`Scheduled daily report with ID: ${schedule.id}`); return schedule.id; } // Method that will be called when the scheduled task runs async sendReminder(data: ReminderData) { console.log(`Sending reminder to ${data.userId}: ${data.message}`); // Add code to send the actual notification } } ``` #### Schedule object Represents a scheduled task. ```ts // Represents a scheduled task type Schedule = { // Unique identifier for the schedule id: string; // Name of the method to be called callback: string; // Data to be passed to the callback payload: T; } & ( | { // One-time execution at a specific time type: "scheduled"; // Timestamp when the task should execute time: number; } | { // Delayed execution after a certain time type: "delayed"; // Timestamp when the task should execute time: number; // Number of seconds to delay execution delayInSeconds: number; } | { // Recurring execution based on cron expression type: "cron"; // Timestamp for the next execution time: number; // Cron expression defining the schedule cron: string; } ); ``` ```ts export class YourAgent extends Agent { // Example of managing scheduled tasks async viewAndManageSchedules() { // Get all scheduled tasks const allSchedules = this.getSchedules(); console.log(`Total scheduled tasks: ${allSchedules.length}`); // Get tasks scheduled for a specific time range const upcomingSchedules = this.getSchedules({ timeRange: { start: new Date(), end: new Date(Date.now() + 24 * 60 * 60 * 1000), // Next 24 hours }, }); // Get a specific task by ID const taskId = "task-123"; const specificTask = await this.getSchedule(taskId); if (specificTask) { console.log( `Found task: ${specificTask.callback} at ${new Date(specificTask.time)}`, ); // Cancel a scheduled task const cancelled = await this.cancelSchedule(taskId); console.log(`Task cancelled: ${cancelled}`); } } } ``` ### SQL API Each Agent instance has an embedded SQLite database that can be accessed using the `this.sql` method within any method on your `Agent` class. #### SQL queries Execute SQL queries against the Agent's built-in SQLite database using the `this.sql` method within any method on your `Agent` class. ```ts // SQL query API for the Agent's embedded database class Agent { // Execute a SQL query with tagged template literals // Returns an array of rows matching the query sql>( strings: TemplateStringsArray, ...values: (string | number | boolean | null)[] ): T[]; } ``` ```ts // Example of using SQL in an Agent interface User { id: string; name: string; email: string; created_at: number; } export class YourAgent extends Agent { async setupDatabase() { // Create a table if it doesn't exist this.sql` CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, created_at INTEGER ) `; } async createUser(id: string, name: string, email: string) { // Insert a new user this.sql` INSERT INTO users (id, name, email, created_at) VALUES (${id}, ${name}, ${email}, ${Date.now()}) `; } async getUserById(id: string): Promise { // Query a user by ID const users = this.sql` SELECT * FROM users WHERE id = ${id} `; return users.length ? users[0] : null; } async searchUsers(term: string): Promise { // Search users with a wildcard return this.sql` SELECT * FROM users WHERE name LIKE ${"%" + term + "%"} OR email LIKE ${"%" + term + "%"} ORDER BY created_at DESC `; } } ``` :::note Visit the [state management API documentation](/agents/api-reference/store-and-sync-state/) within the Agents SDK, including the native `state` APIs and the built-in `this.sql` API for storing and querying data within your Agents. ::: ### Client API The Agents SDK provides a set of client APIs for interacting with Agents from client-side JavaScript code, including: - React hooks, including `useAgent` and `useAgentChat`, for connecting to Agents from client applications. - Client-side [state syncing](/agents/api-reference/store-and-sync-state/) that allows you to subscribe to state updates between the Agent and any connected client(s) when calling `this.setState` within your Agent's code. - The ability to call remote methods (Remote Procedure Calls; RPC) on the Agent from client-side JavaScript code using the `@callable` method decorator. #### AgentClient Client for connecting to an Agent from the browser. ```ts import { AgentClient } from "agents/client"; // Options for creating an AgentClient type AgentClientOptions = Omit & { // Name of the agent to connect to (class name in kebab-case) agent: string; // Name of the specific Agent instance (optional, defaults to "default") name?: string; // Other WebSocket options like host, protocol, etc. }; // WebSocket client for connecting to an Agent class AgentClient extends PartySocket { static fetch(opts: PartyFetchOptions): Promise; constructor(opts: AgentClientOptions); } ``` ```ts // Example of using AgentClient in the browser import { AgentClient } from "agents/client"; // Connect to an Agent instance const client = new AgentClient({ agent: "chat-agent", // Name of your Agent class in kebab-case name: "support-room-123", // Specific instance name host: window.location.host, // Using same host }); client.onopen = () => { console.log("Connected to agent"); // Send an initial message client.send(JSON.stringify({ type: "join", user: "user123" })); }; client.onmessage = (event) => { // Handle incoming messages const data = JSON.parse(event.data); console.log("Received:", data); if (data.type === "state_update") { // Update local UI with new state updateUI(data.state); } }; client.onclose = () => console.log("Disconnected from agent"); // Send messages to the Agent function sendMessage(text) { client.send( JSON.stringify({ type: "message", text, timestamp: Date.now(), }), ); } ``` #### agentFetch Make an HTTP request to an Agent. ```ts import { agentFetch } from "agents/client"; // Options for the agentFetch function type AgentClientFetchOptions = Omit & { // Name of the agent to connect to agent: string; // Name of the specific Agent instance (optional) name?: string; }; // Make an HTTP request to an Agent function agentFetch( opts: AgentClientFetchOptions, init?: RequestInit, ): Promise; ``` ```ts // Example of using agentFetch in the browser import { agentFetch } from "agents/client"; // Function to get data from an Agent async function fetchAgentData() { try { const response = await agentFetch( { agent: "task-manager", name: "user-123-tasks", }, { method: "GET", headers: { Authorization: `Bearer ${userToken}`, }, }, ); if (!response.ok) { throw new Error(`Error: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error("Failed to fetch from agent:", error); } } ``` ### React API The Agents SDK provides a React API for simplifying connection and routing to Agents from front-end frameworks, including React Router (Remix), Next.js, and Astro. #### useAgent React hook for connecting to an Agent. ```ts import { useAgent } from "agents/react"; // Options for the useAgent hook type UseAgentOptions = Omit< Parameters[0], "party" | "room" > & { // Name of the agent to connect to agent: string; // Name of the specific Agent instance (optional) name?: string; // Called when the Agent's state is updated onStateUpdate?: (state: State, source: "server" | "client") => void; }; // React hook for connecting to an Agent // Returns a WebSocket connection with setState method function useAgent( options: UseAgentOptions, ): PartySocket & { // Update the Agent's state setState: (state: State) => void; }; ``` ### Chat Agent The Agents SDK exposes an `AIChatAgent` class that extends the `Agent` class and exposes an `onChatMessage` method that simplifies building interactive chat agents. You can combine this with the `useAgentChat` React hook from the `agents/ai-react` package to manage chat state and messages between a user and your Agent(s). #### AIChatAgent Extension of the `Agent` class with built-in chat capabilities. ```ts import { AIChatAgent } from "agents/ai-chat-agent"; import { Message, StreamTextOnFinishCallback, ToolSet } from "ai"; // Base class for chat-specific agents class AIChatAgent extends Agent { // Array of chat messages for the current conversation messages: Message[]; // Handle incoming chat messages and generate a response // onFinish is called when the response is complete async onChatMessage( onFinish: StreamTextOnFinishCallback, ): Promise; // Persist messages within the Agent's local storage. async saveMessages(messages: Message[]): Promise; } ``` ```ts // Example of extending AIChatAgent import { AIChatAgent } from "agents/ai-chat-agent"; import { Message } from "ai"; interface Env { AI: any; // Your AI binding } class CustomerSupportAgent extends AIChatAgent { // Override the onChatMessage method to customize behavior async onChatMessage(onFinish) { // Access the AI models using environment bindings const { openai } = this.env.AI; // Get the current conversation history const chatHistory = this.messages; // Generate a system prompt based on knowledge base const systemPrompt = await this.generateSystemPrompt(); // Generate a response stream const stream = await openai.chat({ model: "gpt-4o", messages: [{ role: "system", content: systemPrompt }, ...chatHistory], stream: true, }); // Return the streaming response return new Response(stream, { headers: { "Content-Type": "text/event-stream" }, }); } // Helper method to generate a system prompt async generateSystemPrompt() { // Query knowledge base or use static prompt return `You are a helpful customer support agent. Respond to customer inquiries based on the following guidelines: - Be friendly and professional - If you don't know an answer, say so - Current company policies: ...`; } } ``` ### Chat Agent React API #### useAgentChat React hook for building AI chat interfaces using an Agent. ```ts import { useAgentChat } from "agents/ai-react"; import { useAgent } from "agents/react"; import type { Message } from "ai"; // Options for the useAgentChat hook type UseAgentChatOptions = Omit< Parameters[0] & { // Agent connection from useAgent agent: ReturnType; }, "fetch" >; // React hook for building AI chat interfaces using an Agent function useAgentChat(options: UseAgentChatOptions): { // Current chat messages messages: Message[]; // Set messages and synchronize with the Agent setMessages: (messages: Message[]) => void; // Clear chat history on both client and Agent clearHistory: () => void; // Append a new message to the conversation append: ( message: Message, chatRequestOptions?: any, ) => Promise; // Reload the last user message reload: (chatRequestOptions?: any) => Promise; // Stop the AI response generation stop: () => void; // Current input text input: string; // Set the input text setInput: React.Dispatch>; // Handle input changes handleInputChange: ( e: React.ChangeEvent, ) => void; // Submit the current input handleSubmit: ( event?: { preventDefault?: () => void }, chatRequestOptions?: any, ) => void; // Additional metadata metadata?: Object; // Whether a response is currently being generated isLoading: boolean; // Current status of the chat status: "submitted" | "streaming" | "ready" | "error"; // Tool data from the AI response data?: any[]; // Set tool data setData: ( data: any[] | undefined | ((data: any[] | undefined) => any[] | undefined), ) => void; // Unique ID for the chat id: string; // Add a tool result for a specific tool call addToolResult: ({ toolCallId, result, }: { toolCallId: string; result: any; }) => void; // Current error if any error: Error | undefined; }; ``` ```tsx // Example of using useAgentChat in a React component import { useAgentChat } from "agents/ai-react"; import { useAgent } from "agents/react"; import { useState } from "react"; function ChatInterface() { // Connect to the chat agent const agentConnection = useAgent({ agent: "customer-support", name: "session-12345", }); // Use the useAgentChat hook with the agent connection const { messages, input, handleInputChange, handleSubmit, isLoading, error, clearHistory, } = useAgentChat({ agent: agentConnection, initialMessages: [ { role: "system", content: "You're chatting with our AI assistant." }, { role: "assistant", content: "Hello! How can I help you today?" }, ], }); return (
{messages.map((message, i) => (
{message.role === "user" ? "👤" : "🤖"} {message.content}
))} {isLoading &&
AI is typing...
} {error &&
Error: {error.message}
}
); } ```
### Next steps - [Build a chat Agent](/agents/getting-started/build-a-chat-agent/) using the Agents SDK and deploy it to Workers. - Learn more [using WebSockets](/agents/api-reference/websockets/) to build interactive Agents and stream data back from your Agent. - [Orchestrate asynchronous workflows](/agents/api-reference/run-workflows) from your Agent by combining the Agents SDK and [Workflows](/workflows). --- # 调用 Agents URL: https://developers.cloudflare.com/agents/api-reference/calling-agents/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; 了解如何从 Workers 调用您的 Agents,包括如何即时创建 Agents、寻址它们以及将请求路由到 Agent 的特定实例。 ### 调用您的 Agent Agents 是即时创建的,可以并发处理多个请求。每个 Agent 实例与其他实例隔离,可以维护自己的状态,并有唯一的地址。 您可以使用以下方式之一直接从 Worker 创建和运行 Agent 实例: - `routeAgentRequest` 助手:这将基于 `/agents/:agent/:name` URL 模式自动将请求映射到单个 Agent。`:agent` 的值将是您的 Agent 类名转换为 `kebab-case`,`:name` 的值将是您想要创建或检索的 Agent 实例的名称。 - `getAgentByName`,如果该名称不存在 Agent 实例,它将创建一个新的 Agent 实例,或检索现有实例的句柄。 查看以下示例中的使用模式: ```ts import { Agent, AgentNamespace, getAgentByName, routeAgentRequest, } from "agents"; interface Env { // 在环境中定义您的 Agent // 将您的 Agent 类作为 TypeScript 类型参数传递,允许您调用 // 在您的 Agent 上定义的方法。 MyAgent: AgentNamespace; } export default { async fetch(request, env, ctx): Promise { // 路由寻址 // 自动将 HTTP 请求和/或 WebSocket 连接路由到 /agents/:agent/:name // 最适合:使用 agents/react 的 useAgent 将 React 应用直接连接到 Agents return ( (await routeAgentRequest(request, env)) || Response.json({ msg: "no agent here" }, { status: 404 }) ); // 命名寻址 // 最适合:通过名称/ID 创建或检索 Agent 的便利方法。 // 带上您自己的路由、中间件和/或插入现有的 // 应用程序或框架。 let namedAgent = getAgentByName( env.MyAgent, "my-unique-agent-id", ); // 将传入请求直接传递给您的 Agent let namedResp = (await namedAgent).fetch(request); return namedResp; }, } satisfies ExportedHandler; export class MyAgent extends Agent { // 您的 Agent 实现在这里 } ``` :::note[调用其他 Agents] 您也可以从一个 Agent 内调用其他 Agents 并构建多 Agent 系统。 调用其他 Agents 使用与直接调用 Agent 相同的 API。 ::: ### 在 Agents 上调用方法 使用 `getAgentByName` 时,您可以传递请求(包括 WebSocket)连接,并使用原生 [JavaScript RPC](/workers/runtime-apis/rpc/)(JSRPC)API 调用直接在 Agent 本身上定义的方法。 例如,一旦您有了 Agent 唯一实例的句柄(或"存根"),您可以在其上调用方法: ```ts import { Agent, AgentNamespace, getAgentByName } from "agents"; interface Env { // 在环境中定义您的 Agent // 将您的 Agent 类作为 TypeScript 类型参数传递,允许您调用 // 在您的 Agent 上定义的方法。 MyAgent: AgentNamespace; } interface UserHistory { history: string[]; lastUpdated: Date; } export default { async fetch(request, env, ctx): Promise { let namedAgent = getAgentByName( env.MyAgent, "my-unique-agent-id", ); // 直接在 Agent 上调用方法,并传递原生 JavaScript 对象 let chatResponse = namedAgent.chat("你好!"); // 无需从 HTTP 请求或 WebSocket 序列化/反序列化 // 消息然后再返回 let agentState = getState(); // agentState 的类型是 UserHistory return namedResp; }, } satisfies ExportedHandler; export class MyAgent extends Agent { // 您的 Agent 实现在这里 async chat(prompt: string) { // 调用您喜欢的 LLM return "result"; } async getState() { // 直接返回 Agent 的状态 return this.state; } // 根据需要的其他方法! } ``` 使用 TypeScript 时,确保将您的 Agent 类作为 TypeScript 类型参数传递给 AgentNamespace 类型,以便正确推断类型: ```ts interface Env { // 将您的 Agent 类作为 TypeScript 类型参数传递,允许您调用 // 在您的 Agent 上定义的方法。 MyAgent: AgentNamespace; } export class CodeReviewAgent extends Agent { // Agent 方法在这里 } ``` ### 为您的 Agents 命名 为您的 Agents 创建名称时,考虑 Agent 代表什么。一个唯一的用户?一个团队或公司?用于协作的房间或频道? 一致的命名方法允许您: - 将传入请求直接定向到正确的 Agent - 确定性地将新请求路由回该 Agent,无论客户端在世界的哪个地方。 - 避免依赖集中式会话存储或外部服务进行状态管理,因为每个 Agent 实例可以维护自己的状态。 对于给定的 Agent 定义(或下面代码中的"命名空间"),可以有数百万(或数千万)个该 Agent 的实例,每个都处理自己的请求、调用 LLM 并维护自己的状态。 例如,您可能为每个使用新的基于 AI 的代码编辑器的用户拥有一个 Agent。在这种情况下,您希望基于系统中的用户 ID 创建 Agents,这将允许该 Agent 处理该用户的所有请求。 它还确保 [Agent 内的状态](/agents/api-reference/store-and-sync-state/),包括聊天历史、语言偏好、模型配置和其他上下文可以与该用户特别关联,使状态管理变得更容易。 以下示例显示如何为请求中的每个 `userId` 创建唯一的 Agent: ```ts import { Agent, AgentNamespace, getAgentByName, routeAgentRequest, } from "agents"; interface Env { MyAgent: AgentNamespace; } export default { async fetch(request, env, ctx): Promise { let userId = new URL(request.url).searchParams.get("userId") || "anonymous"; // 使用允许您路由到请求、WebSockets 或在 Agent 上调用方法的标识符 // 您也可以在这里放置身份验证逻辑 - 例如,仅为已知用户创建或检索 Agents。 let namedAgent = getAgentByName( env.MyAgent, "my-unique-agent-id", ); return (await namedAgent).fetch(request); }, } satisfies ExportedHandler; export class MyAgent extends Agent { // 您可以在 Agent 内的任何方法中通过 this.name 访问 Agent 的名称 async onStartup() { console.log(`agent ${this.name} ready!`); } } ``` 根据您的 Agents 目标,将 `userId` 替换为 `teamName`、`channel`、`companyName` - 和/或配置身份验证以确保仅为已知的、经过身份验证的用户创建 Agents。 ### 验证 Agents 使用 Agents SDK 构建和部署 Agents 时,您通常希望在将请求传递给 Agent 之前验证客户端,以便限制 Agent 将调用谁、为特定 Agents 授权特定用户,和/或限制谁可以访问 Agent 公开的管理或调试 API。 作为最佳实践: - 在调用 Agent 之前,在您的 Workers 代码中处理身份验证。 - 使用 `routeAgentRequest` 助手时使用内置钩子 - `onBeforeConnect` 和 `onBeforeRequest` - 在使用其他方法调用 Agent 之前,使用您首选的路由器(如 Hono)和身份验证中间件或提供商来应用自定义身份验证方案。 本指南前面记录的 `routeAgentRequest` 助手公开了两个有用的钩子(`onBeforeConnect`、`onBeforeRequest`),允许您在创建或检索 Agent 之前应用自定义逻辑: ```ts import { Agent, AgentNamespace, routeAgentRequest } from "agents"; interface Env { MyAgent: AgentNamespace; } export default { async fetch(request, env, ctx): Promise { // Use the onBeforeConnect and onBeforeRequest hooks to authenticate clients // or run logic before handling a HTTP request or WebSocket. return ( (await routeAgentRequest(request, env, { // Run logic before a WebSocket client connects onBeforeConnect: (request) => { // Your code/auth code here // You can return a Response here - e.g. a HTTP 403 Not Authorized - // which will stop further request processing and will NOT invoke the // Agent. // return Response.json({"error": "not authorized"}, { status: 403 }) }, // Run logic before a HTTP client clients onBeforeRequest: (request) => { // Your code/auth code here // Returning nothing will result in the call to the Agent continuing }, // Prepend a prefix for how your Agents are named here prefix: "name-prefix-here", })) || Response.json({ msg: "no agent here" }, { status: 404 }) ); }, } satisfies ExportedHandler; ``` 如果使用 `getAgentByName` 或底层 Durable Objects 路由 API,则应在调用 `getAgentByName` 之前验证传入请求或 WebSocket 连接。 例如,如果您使用 [Hono](https://hono.dev/),您可以在调用 Agent 之前在中间件中进行身份验证: ```ts import { Agent, AgentNamespace, getAgentByName } from "agents"; import { Hono } from "hono"; const app = new Hono<{ Bindings: Env }>(); app.use("/code-review/*", async (c, next) => { // Perform auth here // e.g. validate a Bearer token, a JWT, use your preferred auth library // return Response.json({ msg: 'unauthorized' }, { status: 401 }); await next(); // continue on if valid }); app.get("/code-review/:id", async (c) => { const id = c.req.param("teamId"); if (!id) return Response.json({ msg: "missing id" }, { status: 400 }); // Call the Agent, creating it with the name/identifier from the ":id" segment // of our URL const agent = await getAgentByName(c.env.MyAgent, id); // Pass the request to our Agent instance return await agent.fetch(c.req.raw); }); ``` 这确保我们仅创建已验证用户的 Agents,并允许您验证 Agent 名称是否符合您首选的命名方案,然后再创建实例。 ### 下一步 - 查看 [API 文档](/agents/api-reference/agents-api/) 以了解如何定义 - [构建聊天 Agent](/agents/getting-started/build-a-chat-agent/) 使用 Agents SDK 并将其部署到 Workers。 - 了解如何使用 [WebSockets](/agents/api-reference/websockets/) 构建交互式 Agents 并从您的 Agent 流式传输数据。 - [或组织异步工作流](/agents/api-reference/run-workflows) 从您的 Agent 通过组合 Agents SDK 和 [工作流](/workflows)。 --- # 配置 URL: https://developers.cloudflare.com/agents/api-reference/configuration/ import { MetaInfo, Render, Type, WranglerConfig } from "~/components"; Agent 的配置与任何其他 Cloudflare Workers 项目相同,使用[wrangler 配置](/workers/wrangler/configuration/)文件来定义您的代码位置以及它将使用的服务(绑定)。 ### 项目结构 从 `npm create cloudflare@latest agents-starter -- --template cloudflare/agents-starter` 创建的 Agent 项目的典型文件结构如下: ```sh . |-- package-lock.json |-- package.json |-- public | `-- index.html |-- src | `-- index.ts // 您的 Agent 定义 |-- test | |-- index.spec.ts // 您的测试 | `-- tsconfig.json |-- tsconfig.json |-- vitest.config.mts |-- worker-configuration.d.ts `-- wrangler.jsonc // 您的 Workers 和 Agent 配置 ``` ### 示例配置 下面是一个最小的 `wrangler.jsonc` 文件,定义了 Agent 的配置,包括入口点、`durable_object` 命名空间和代码 `migrations`: ```jsonc { "$schema": "node_modules/wrangler/config-schema.json", "name": "agents-example", "main": "src/index.ts", "compatibility_date": "2025-02-23", "compatibility_flags": ["nodejs_compat"], "durable_objects": { "bindings": [ { // 必需的: "name": "MyAgent", // 您的 Agent 从 Worker 中的调用方式 "class_name": "MyAgent", // 必须与代码中 Agent 的类名匹配 // 可选:如果 Agent 在另一个 Worker 脚本中定义,请设置此项 "script_name": "the-other-worker", }, ], }, "migrations": [ { "tag": "v1", // Agent 存储状态的必要配置 "new_sqlite_classes": ["MyAgent"], }, ], "observability": { "enabled": true, }, } ``` 配置包括: - 一个 `main` 字段,指向您的 Agent 的入口点,通常是 TypeScript(或 JavaScript)文件。 - 一个 `durable_objects` 字段,定义您的 Agents 将在其中运行的 [Durable Object 命名空间](/durable-objects/reference/glossary/)。 - 一个 `migrations` 字段,定义您的 Agent 将使用的代码迁移。此字段是必需的,必须至少包含一次迁移。`new_sqlite_classes` 字段是 Agent 存储状态的必要配置。 Agents 必须在其 `wrangler.jsonc`(或 `wrangler.toml`)配置文件中定义这些字段。 --- # HTTP 和服务器发送事件 URL: https://developers.cloudflare.com/agents/api-reference/http-sse/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; Agents SDK 允许您处理 HTTP 请求,并原生支持[服务器发送事件](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)(SSE)。这允许您构建可以将数据推送到客户端并避免缓冲的应用程序。 ### 处理 HTTP 请求 Agents 可以使用 `onRequest` 方法处理 HTTP 请求,该方法在 Agent 实例接收到 HTTP 请求时被调用。该方法将 `Request` 对象作为参数并返回 `Response` 对象。 ```ts class MyAgent extends Agent { // 处理来到此 Agent 实例的 HTTP 请求 // 返回 Response 对象 async onRequest(request: Request) { return new Response("来自 Agent 的问候!"); } async callAIModel(prompt: string) { // 在这里实现 AI 模型调用 } } ``` 查看 [Agents API 参考](/agents/api-reference/agents-api/)了解更多关于 `Agent` 类及其方法的信息。 ### 实现服务器发送事件 Agents SDK 直接支持服务器发送事件:您可以使用 SSE 通过长时间运行的连接将数据流式传输回客户端。这避免了缓冲大型响应,这既可能使您的 Agent 感觉缓慢,又迫使您在内存中缓冲整个响应。 当 Agent 部署到 Cloudflare Workers 时,对于将响应流式传输回去的总时间没有有效限制:需要几分钟推理然后响应的大型 AI 模型响应不会被过早终止。 请注意,这并不意味着客户端在流式传输过程中不能潜在地断开连接:您可以通过[写入 Agent 的有状态存储](/agents/api-reference/store-and-sync-state/)和/或[使用 WebSockets](/agents/api-reference/websockets/)来解决这个问题。因为您总是可以[路由到同一个 Agent](/agents/api-reference/calling-agents/),所以您不需要使用集中式会话存储来在客户端断开连接时从中断的地方继续。 以下示例使用 AI SDK 生成文本并将其流式传输回客户端。它将在模型生成时自动将响应流式传输回客户端: ```ts import { Agent, AgentNamespace, getAgentByName, routeAgentRequest, } from "agents"; import { streamText } from "ai"; import { createOpenAI, openai } from "@ai-sdk/openai"; interface Env { MyAgent: AgentNamespace; OPENAI_API_KEY: string; } export class MyAgent extends Agent { async onRequest(request: Request) { // 通过以下方式测试: // curl -d '{"prompt": "为我写一个 Cloudflare Worker"}' let data = await request.json<{ prompt: string }>(); let stream = await this.callAIModel(data.prompt); // 这使用服务器发送事件(SSE) return stream.toTextStreamResponse({ headers: { "Content-Type": "text/x-unknown", "content-encoding": "identity", "transfer-encoding": "chunked", }, }); } async callAIModel(prompt: string) { const openai = createOpenAI({ apiKey: this.env.OPENAI_API_KEY, }); return streamText({ model: openai("gpt-4o"), prompt: prompt, }); } } export default { async fetch(request: Request, env: Env) { let agentId = new URL(request.url).searchParams.get("agent-id") || ""; const agent = await getAgentByName(env.MyAgent, agentId); return agent.fetch(request); }, }; ``` ### WebSockets 与服务器发送事件 WebSockets 和服务器发送事件(SSE)都能够在客户端和 Agents 之间进行实时通信。基于 Agents SDK 构建的 Agents 可以直接公开 WebSocket 和 SSE 端点。 - WebSockets 提供全双工通信,允许数据同时在两个方向流动。SSE 仅支持服务器到客户端的通信,如果客户端需要向服务器发送数据,需要额外的 HTTP 请求。 - WebSockets 建立一个在会话期间保持开放的单一持久连接。基于 HTTP 构建的 SSE 可能由于重新连接尝试和每次重新连接时的头部传输而经历更多开销,特别是当有大量客户端-服务器通信时。 - 虽然 SSE 对于简单的流式传输场景效果很好,但 WebSockets 更适合需要几分钟或几小时连接时间的应用程序,因为它们通过内置的 ping/pong 机制维护更稳定的连接以保持连接活跃。 - WebSockets 使用自己的协议(ws:// 或 wss://),在初始握手后与 HTTP 分离。这种分离允许 WebSockets 更好地处理二进制数据传输并为专门用例实现自定义子协议。 如果您不确定哪种更适合您的用例,我们推荐 WebSockets。[WebSockets API 文档](/agents/api-reference/websockets/)提供了有关如何在 Agents SDK 中使用 WebSockets 的详细信息。 ### 下一步 - 查看 [API 文档](/agents/api-reference/agents-api/)了解如何定义 Agents 类。 - [构建聊天 Agent](/agents/getting-started/build-a-chat-agent/)使用 Agents SDK 并将其部署到 Workers。 - 了解更多[使用 WebSockets](/agents/api-reference/websockets/)构建交互式 Agents 并从您的 Agent 流式传输数据。 - [编排异步工作流](/agents/api-reference/run-workflows)通过结合 Agents SDK 和[工作流](/workflows)从您的 Agent。 --- # 检索增强生成 URL: https://developers.cloudflare.com/agents/api-reference/rag/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; Agents 可以使用检索增强生成(RAG)来检索相关信息并使用它来增强[对 AI 模型的调用](/agents/api-reference/using-ai-models/)。存储用户的聊天历史记录以用作未来对话的上下文,总结文档以引导 Agent 的知识库,和/或使用来自 Agent 的[网页浏览](/agents/api-reference/browse-the-web/)任务的数据来增强您的 Agent 的能力。 您可以使用 Agent 自己的 [SQL 数据库](/agents/api-reference/store-and-sync-state)作为数据的事实来源,并将嵌入存储在 [Vectorize](/vectorize/)(或任何其他支持向量的数据库)中,以允许您的 Agent 检索相关信息。 ### 向量搜索 :::note 如果您对向量数据库和 Vectorize 完全陌生,请访问 [Vectorize 教程](/vectorize/get-started/intro/)学习基础知识,包括如何创建索引、插入数据和生成嵌入。 ::: 您可以从 Agent 上的任何方法查询向量索引(或索引):您附加的任何 Vectorize 索引都可以在 Agent 内的 `this.env` 上使用。如果您已经[将元数据关联](/vectorize/best-practices/insert-vectors/#metadata)到映射回存储在 Agent 中的数据的向量,您可以使用 `this.sql` 直接在 Agent 内查找数据。 以下是如何为 Agent 提供检索功能的示例: ```ts import { Agent } from "agents"; interface Env { AI: Ai; VECTOR_DB: Vectorize; } export class RAGAgent extends Agent { // 我们 Agent 上的其他方法 // ... // async queryKnowledge(userQuery: string) { // 将查询转换为嵌入 const queryVector = await this.env.AI.run("@cf/baai/bge-base-en-v1.5", { text: [userQuery], }); // 从我们的向量索引中检索结果 let searchResults = await this.env.VECTOR_DB.query(queryVector.data[0], { topK: 10, returnMetadata: "all", }); let knowledge = []; for (const match of searchResults.matches) { console.log(match.metadata); knowledge.push(match.metadata); } // 使用元数据将向量搜索结果重新关联 // 到我们 Agent 的 SQL 数据库中的数据 let results = this .sql`SELECT * FROM knowledge WHERE id IN (${knowledge.map((k) => k.id)})`; // 返回它们 return results; } } ``` 您还需要将您的 Agent 连接到您的向量索引: ```jsonc { // ... "vectorize": [ { "binding": "VECTOR_DB", "index_name": "your-vectorize-index-name", }, ], // ... } ``` 如果您有多个要提供的索引,您可以提供一个 `vectorize` 绑定数组。 #### 下一步 - 了解更多关于如何[结合 Vectorize 和 Workers AI](/vectorize/get-started/embeddings/) - 查看 [Vectorize 查询 API](/vectorize/reference/client-api/) - 使用[元数据过滤](/vectorize/reference/metadata-filtering/)为您的结果添加上下文 --- # API 参考 URL: https://developers.cloudflare.com/agents/api-reference/ import { DirectoryListing } from "~/components"; 了解更多关于 Agents 能做什么、`Agent` 类以及 Agents 公开的 API: --- # 运行工作流 URL: https://developers.cloudflare.com/agents/api-reference/run-workflows/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; Agents 可以触发异步[工作流](/workflows/),允许您的 Agent 在后台运行复杂的多步骤任务。这可以包括后处理用户上传的文件、更新[向量数据库](/vectorize/)中的嵌入,和/或管理长时间运行的用户生命周期电子邮件或短信通知工作流。 因为 Agent 就像 Worker 脚本一样,它可以创建在与 Agent 相同项目(脚本)中定义的工作流*或*在不同项目中定义的工作流。 :::note[Agents 与工作流] Agents 和工作流有一些相似性:它们都可以异步运行任务。对于直接的线性任务或需要运行到完成的任务,工作流可能是理想的:步骤可以重试,它们可以被取消,并且可以对事件做出反应。 Agents 不必运行到完成:它们可以循环、分支并永远运行,它们还可以直接与用户交互(通过 HTTP 或 WebSockets)。Agent 可以在运行时触发多个工作流,因此可以用来协调和管理工作流以实现其目标。 ::: ## 触发工作流 Agent 可以从任何方法内触发一个或多个工作流,无论是来自传入的 HTTP 请求、WebSocket 连接、延迟或调度,和/或来自 Agent 采取的任何其他操作。 从 Agent 触发工作流与[从 Worker 脚本触发工作流](/workflows/build/trigger-workflows/)没有区别: ```ts interface Env { MY_WORKFLOW: Workflow; MyAgent: AgentNamespace; } export class MyAgent extends Agent { async onRequest(request: Request) { let userId = request.headers.get("user-id"); // 触发运行工作流的调度 // 传递负载给它 let { taskId } = await this.schedule(300, "runWorkflow", { id: userId, flight: "DL264", date: "2025-02-23", }); } async runWorkflow(data) { let instance = await env.MY_WORKFLOW.create({ id: data.id, params: data, }); // 调度另一个每5分钟检查工作流状态的任务... await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id, }); } } export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { // 您的工作流代码在这里 } } ``` 您还需要确保您的 Agent [与您的工作流有绑定](/workflows/build/trigger-workflows/#workers-api-bindings),以便它可以调用它: ```jsonc { // ... // 在您的 Agent 和您的工作流之间创建绑定 "workflows": [ { // 必需的: "name": "EMAIL_WORKFLOW", "class_name": "MyWorkflow", // 可选:如果您的工作流在与您的 Agent 不同的项目中定义,请设置 script_name 字段 "script_name": "email-workflows", }, ], // ... } ``` ## 从另一个项目触发工作流 您还可以通过在 Agent 的 `workflows` 绑定中设置 `script_name` 属性,调用在与 Agent 不同的 Workers 脚本中定义的工作流: ```jsonc { // 必需的: "name": "EMAIL_WORKFLOW", "class_name": "MyWorkflow", // 可选:如果您的工作流在与您的 Agent 不同的项目中定义,请设置 script_name 字段 "script_name": "email-workflows", } ``` 有关更多示例,请参阅工作流文档的[跨脚本调用](/workflows/build/workers-api/#cross-script-calls)部分。 --- # 调度任务 URL: https://developers.cloudflare.com/agents/api-reference/schedule-tasks/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; Agent 可以通过调用 `this.schedule(when, callback, data)` 来调度未来运行的任务,其中 `when` 可以是延迟、`Date` 或 cron 字符串;`callback` 是要调用的函数名,`data` 是要传递给函数的数据对象。 调度任务可以做用户请求或消息能做的任何事情:发出请求、查询数据库、发送邮件、读写状态:调度任务可以调用您的 Agent 上的任何常规方法。 ### 调度任务 您可以在 Agent 的任何方法中调用 `this.schedule`,并为每个单独的 Agent 调度数万个任务: ```ts import { Agent } from "agents"; export class SchedulingAgent extends Agent { async onRequest(request) { // 处理传入请求 // 调度一个5分钟后的任务 // 调用"checkFlights"方法 let { taskId } = await this.schedule(600, "checkFlights", { flight: "DL264", date: "2025-02-23", }); return Response.json({ taskId }); } async checkFlights(data) { // 当我们的调度任务运行时被调用 // 我们也可以在这里调用 this.schedule 来调度另一个任务 } } ``` :::caution 为不存在的方法设置回调的任务将抛出异常:确保 `this.schedule` 的 `callback` 参数中命名的方法存在于您的 `Agent` 类上。 ::: 您可以通过多种方式调度任务: ```ts // 调度一个在10秒后运行的任务 let task = await this.schedule(10, "someTask", { message: "hello" }); // 调度一个在特定日期运行的任务 let task = await this.schedule(new Date("2025-01-01"), "someTask", {}); // 调度一个每10秒运行的任务 let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello", }); // 调度一个每10秒运行的任务,但只在周一 let task = await this.schedule("0 0 * * 1", "someTask", { message: "hello" }); // 取消调度任务 this.cancelSchedule(task.id); ``` 调用 `await this.schedule` 返回一个 `Schedule`,其中包含任务随机生成的 `id`。您可以使用此 `id` 在将来检索或取消任务。它还提供一个 `type` 属性,指示调度类型,例如,`"scheduled" | "delayed" | "cron"` 之一。 :::note[最大调度任务数] 每个任务都映射到 Agent 底层 [SQLite 数据库](/durable-objects/api/storage-api/) 中的一行,这意味着每个任务最大可达 2 MB。最大任务数必须满足 `(任务大小 * 任务数) + 所有其他状态 < 最大数据库大小`(目前每个 Agent 为 1GB)。 ::: ### 管理调度任务 您可以使用调度 API 在 Agent 内获取、取消和过滤调度任务: ```ts // 通过 ID 获取特定调度 // 如果任务不存在则返回 undefined let task = await this.getSchedule(task.id); // 获取所有调度任务 // 返回 Schedule 对象数组 let tasks = this.getSchedules(); // 通过 ID 取消任务 // 如果任务被取消则返回 true,如果不存在则返回 false await this.cancelSchedule(task.id); // 过滤特定任务 // 例如,未来一小时内开始的所有任务 let tasks = this.getSchedules({ timeRange: { start: new Date(Date.now()), end: new Date(Date.now() + 60 * 60 * 1000), }, }); ``` --- # 存储和同步状态 URL: https://developers.cloudflare.com/agents/api-reference/store-and-sync-state/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; 每个 Agent 都有内置的状态管理功能,包括内置存储和 Agent 与前端应用程序之间的同步。 Agent 内的状态具有以下特性: - 在 Agent 重启时持久化:数据永久存储在 Agent 内。 - 自动序列化/反序列化:您可以存储任何 JSON 可序列化的数据。 - 在 Agent 内立即一致:读取您自己的写入。 - 并发更新的线程安全 - 快速:状态与 Agent 运行的位置位于同一位置。读取和写入不需要穿越网络。 Agent 状态存储在嵌入每个单独 Agent 实例内的 SQL 数据库中:您可以使用更高级别的 `this.setState` API(推荐)与其交互,它允许您同步状态并在状态更改时触发事件,或直接使用 `this.sql` 查询数据库。 #### 状态 API 每个 Agent 都有内置的状态管理功能。您可以直接使用 `this.setState` 设置和更新 Agent 的状态: ```ts import { Agent } from "agents"; export class MyAgent extends Agent { // 响应事件更新状态 async incrementCounter() { this.setState({ ...this.state, counter: this.state.counter + 1, }); } // 处理传入消息 async onMessage(message) { if (message.type === "update") { this.setState({ ...this.state, ...message.data, }); } } // 处理状态更新 onStateUpdate(state, source: "server" | Connection) { console.log("状态已更新", state); } } ``` 如果您使用 TypeScript,您还可以通过将类型作为 [类型参数](https://www.typescriptlang.org/docs/handbook/2/generics.html#using-type-parameters-in-generic-constraints) 传递给 `Agent` 类定义的*第二个*类型参数来为您的 Agent 状态提供类型。 ```ts import { Agent } from "agents"; interface Env {} // 为您的 Agent 状态定义类型 interface FlightRecord { id: string; departureIata: string; arrival: Date; arrivalIata: string; price: number; } // 传入您的 Agent 状态的类型 export class MyAgent extends Agent { // 这允许 this.setState 和 onStateUpdate 方法 // 被类型化: async onStateUpdate(state: FlightRecord) { console.log("状态已更新", state); } async someOtherMethod() { this.setState({ ...this.state, price: this.state.price + 10, }); } } ``` ### 为 Agent 设置初始状态 您还可以通过 `Agent` 类上的 `initialState` 属性为 Agent 设置初始状态: ```ts type State = { counter: number; text: string; color: string; }; class MyAgent extends Agent { // 设置默认的初始状态 initialState = { counter: 0, text: "", color: "#3B82F6", }; doSomething() { console.log(this.state); // {counter: 0, text: "", color: "#3B82F6"},如果您还没有设置状态 } } ``` 任何初始状态都会同步到通过[`useAgent` hook](#synchronizing-state)连接的客户端。 ### 同步状态 客户端可以连接到 Agent 并使用作为 `agents/react` 一部分提供的 React hooks 与其状态保持同步。 React 应用程序可以调用 `useAgent` 通过 WebSockets 连接到命名的 Agent ```ts import { useState } from "react"; import { useAgent } from "agents/react"; function StateInterface() { const [state, setState] = useState({ counter: 0 }); const agent = useAgent({ agent: "thinking-agent", name: "my-agent", onStateUpdate: (newState) => setState(newState), }); const increment = () => { agent.setState({ counter: state.counter + 1 }); }; return (
计数: {state.counter}
); } ```
状态同步系统: - 自动将 Agent 的状态同步到所有连接的客户端 - 优雅地处理客户端断开连接和重新连接 - 提供立即的本地更新 - 支持多个同时的客户端连接 常见用例: - 实时协作功能 - 多窗口/选项卡同步 - 跨多个设备的实时更新 - 在客户端之间维护一致的 UI 状态 - 当新客户端连接时,它们自动从 Agent 接收当前状态,确保所有客户端都从最新数据开始。 ### SQL API 每个单独的 Agent 实例都有自己的 SQL(SQLite)数据库,运行在与 Agent 本身*相同的上下文*中。这意味着在您的 Agent 内插入或查询数据实际上是零延迟的:Agent 不必跨越大陆或世界来访问自己的数据。 您可以通过 `this.sql` 在 Agent 的任何方法中访问 SQL API。SQL API 接受模板字符串,并且 ```ts export class MyAgent extends Agent { async onRequest(request: Request) { let userId = new URL(request.url).searchParams.get("userId"); // 'users' 这里只是一个例子:您可以创建任意表并在每个 Agent 的数据库中 // 使用 SQL(SQLite 语法)定义您自己的架构。 let user = await this.sql`SELECT * FROM users WHERE id = ${userId}`; return Response.json(user); } } ``` 您还可以为查询提供一个 [TypeScript 类型参数](https://www.typescriptlang.org/docs/handbook/2/generics.html#using-type-parameters-in-generic-constraints),它将用于推断结果的类型: ```ts type User = { id: string; name: string; email: string; }; export class MyAgent extends Agent { async onRequest(request: Request) { let userId = new URL(request.url).searchParams.get("userId"); // Supply the type paramter to the query when calling this.sql // This assumes the results returns one or more User rows with "id", "name", and "email" columns const user = await this.sql`SELECT * FROM users WHERE id = ${userId}`; return Response.json(user); } } ``` 您不需要指定数组类型 (`User[]` 或 `Array`),因为 `this.sql` 将始终返回指定类型的数组。 提供类型参数不会验证结果是否匹配您的类型定义。在 TypeScript 中,不存在的属性(字段)或不符合您提供的类型定义的属性将被删除。如果您需要验证传入的事件,我们建议使用库,例如 [zod](https://zod.dev/) 或您自己的验证逻辑。 :::note Learn more about the zero-latency SQL storage that powers both Agents and Durable Objects [on our blog](https://blog.cloudflare.com/sqlite-in-durable-objects/). ::: The SQL API exposed to an Agent is similar to the one [within Durable Objects](/durable-objects/api/storage-api/#sql-api): Durable Object SQL methods available on `this.ctx.storage.sql`. You can use the same SQL queries with the Agent's database, create tables, and query data, just as you would with Durable Objects or [D1](/d1/). ### Use Agent state as model context You can combine the state and SQL APIs in your Agent with its ability to [call AI models](/agents/api-reference/using-ai-models/) to include historical context within your prompts to a model. Modern Large Language Models (LLMs) often have very large context windows (up to millions of tokens), which allows you to pull relevant context into your prompt directly. For example, you can use an Agent's built-in SQL database to pull history, query a model with it, and append to that history ahead of the next call to the model: ```ts export class ReasoningAgent extends Agent { async callReasoningModel(prompt: Prompt) { let result = this .sql`SELECT * FROM history WHERE user = ${prompt.userId} ORDER BY timestamp DESC LIMIT 1000`; let context = []; for await (const row of result) { context.push(row.entry); } const client = new OpenAI({ apiKey: this.env.OPENAI_API_KEY, }); // Combine user history with the current prompt const systemPrompt = prompt.system || "You are a helpful assistant."; const userPrompt = `${prompt.user}\n\nUser history:\n${context.join("\n")}`; try { const completion = await client.chat.completions.create({ model: this.env.MODEL || "o3-mini", messages: [ { role: "system", content: systemPrompt }, { role: "user", content: userPrompt }, ], temperature: 0.7, max_tokens: 1000, }); // Store the response in history this .sql`INSERT INTO history (timestamp, user, entry) VALUES (${new Date()}, ${prompt.userId}, ${completion.choices[0].message.content})`; return completion.choices[0].message.content; } catch (error) { console.error("Error calling reasoning model:", error); throw error; } } } ``` This works because each instance of an Agent has its _own_ database, the state stored in that database is private to that Agent: whether it's acting on behalf of a single user, a room or channel, or a deep research tool. By default, you don't have to manage contention or reach out over the network to a centralized database to retrieve and store state. ### Next steps - Review the [API documentation](/agents/api-reference/agents-api/) for the Agents class to learn how to define them. - [Build a chat Agent](/agents/getting-started/build-a-chat-agent/) using the Agents SDK and deploy it to Workers. - Learn more [using WebSockets](/agents/api-reference/websockets/) to build interactive Agents and stream data back from your Agent. - [Orchestrate asynchronous workflows](/agents/api-reference/run-workflows) from your Agent by combining the Agents SDK and [Workflows](/workflows). --- # 使用 AI 模型 URL: https://developers.cloudflare.com/agents/api-reference/using-ai-models/ import { AnchorHeading, MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, PackageManagers, } from "~/components"; Agents 可以与托管在任何提供商上的 AI 模型进行通信,包括: - [Workers AI](/workers-ai/) - [AI SDK](https://sdk.vercel.ai/docs/ai-sdk-core/overview) - [OpenAI](https://platform.openai.com/docs/quickstart?language=javascript) - [Anthropic](https://docs.anthropic.com/en/api/client-sdks#typescript) - [Google's Gemini](https://ai.google.dev/gemini-api/docs/openai) 您还可以使用 [AI Gateway](/ai-gateway/) 中的模型路由功能来跨提供商路由、评估响应和管理 AI 提供商速率限制。 因为 Agents 构建在 [Durable Objects](/durable-objects/) 之上,每个 Agent 或聊天会话都与一个有状态的计算实例相关联。传统的无服务器架构通常为聊天等实时应用程序所需的持久连接带来挑战。 用户可能在现代推理模型(如 `o3-mini` 或 DeepSeek R1)的长时间运行响应期间断开连接,或在刷新浏览器时丢失对话上下文。Agents 不依赖请求-响应模式和管理外部数据库来跟踪和存储对话状态,而是可以直接在 Agent 内存储状态。如果客户端断开连接,Agent 可以写入其自己的分布式存储,并在客户端重新连接时立即更新——即使是几小时或几天后。 ## 调用 AI 模型 您可以从 Agent 内的任何方法调用模型,包括使用 [`onRequest`](/agents/api-reference/agents-api/) 处理器处理 HTTP 请求时、运行[计划任务](/agents/api-reference/schedule-tasks/)时、在 [`onMessage`](/agents/api-reference/websockets/) 处理器中处理 WebSocket 消息时,或从您自己的任何方法中。 重要的是,Agents 可以自主调用 AI 模型,并且可以处理可能需要几分钟(或更长时间)才能完全响应的长时间运行响应。 ### 长时间运行的模型请求 {/* long-running-model-requests */} 现代[推理模型](https://platform.openai.com/docs/guides/reasoning)或"思考"模型可能需要一些时间来生成响应*并且*将响应流式传输回客户端。 您可以使用 [WebSocket API](/agents/api-reference/websockets/) 将响应流式传输回客户端,而不是缓冲整个响应或冒客户端断开连接的风险。 ```ts import { Agent } from "agents"; import { OpenAI } from "openai"; export class MyAgent extends Agent { async onConnect(connection: Connection, ctx: ConnectionContext) { // } async onMessage(connection: Connection, message: WSMessage) { let msg = JSON.parse(message); // 这可以运行任意长的时间,并返回任意数量的消息! await queryReasoningModel(connection, msg.prompt); } async queryReasoningModel(connection: Connection, userPrompt: string) { const client = new OpenAI({ apiKey: this.env.OPENAI_API_KEY, }); try { const stream = await client.chat.completions.create({ model: this.env.MODEL || "o3-mini", messages: [{ role: "user", content: userPrompt }], stream: true, }); // 将响应作为 WebSocket 消息流式传输回去 for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ""; if (content) { connection.send(JSON.stringify({ type: "chunk", content })); } } // 发送完成消息 connection.send(JSON.stringify({ type: "done" })); } catch (error) { connection.send(JSON.stringify({ type: "error", error: error })); } } } ``` 您还可以使用 `this.setState` 方法将 AI 模型响应持久化到 [Agent 的内部状态](/agents/api-reference/store-and-sync-state/)。例如,如果您运行[计划任务](/agents/api-reference/schedule-tasks/),您可以存储任务的输出并稍后读取。或者,如果用户断开连接,读取消息历史记录并在他们重新连接时发送给用户。 ### Workers AI ### 托管模型 您可以通过[配置绑定](/workers-ai/configuration/bindings/)在 Agent 中使用 [Workers AI 中可用的任何模型](/workers-ai/models/)。 Workers AI 通过设置 `stream: true` 开箱即用地支持流式响应,我们强烈推荐使用它们来避免缓冲和延迟响应,特别是对于较大的模型或需要更多时间生成响应的推理模型。 ```ts import { Agent } from "agents"; interface Env { AI: Ai; } export class MyAgent extends Agent { async onRequest(request: Request) { const response = await env.AI.run( "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", { prompt: "为我构建一个返回 JSON 的 Cloudflare Worker。", stream: true, // 流式传输响应,不阻塞客户端! }, ); // 返回流 return new Response(answer, { headers: { "content-type": "text/event-stream" }, }); } } ``` 您的 Wrangler 配置需要添加 `ai` 绑定: ```toml [ai] binding = "AI" ``` ### 模型路由 您还可以通过在调用 AI 绑定时指定 [`gateway` 配置](/ai-gateway/providers/workersai/),直接从 Agent 使用 [AI Gateway](/ai-gateway/) 中的模型路由功能。 :::note 模型路由允许您根据模型是否可达、对客户端进行速率限制,和/或是否超出了特定提供商的成本预算,将请求路由到不同的 AI 模型。 ::: ```ts import { Agent } from "agents"; interface Env { AI: Ai; } export class MyAgent extends Agent { async onRequest(request: Request) { const response = await env.AI.run( "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", { prompt: "为我构建一个返回 JSON 的 Cloudflare Worker。", }, { gateway: { id: "{gateway_id}", // 在此处指定您的 AI Gateway ID skipCache: false, cacheTtl: 3360, }, }, ); return Response.json(response); } } ``` 您的 Wrangler 配置需要添加 `ai` 绑定。这在 Workers AI 和 AI Gateway 之间共享。 ```toml [ai] binding = "AI" ``` 访问 [AI Gateway 文档](/ai-gateway/) 了解如何配置网关并检索网关 ID。 ### AI SDK [AI SDK](https://sdk.vercel.ai/docs/introduction) 为使用 AI 模型提供了统一的 API,包括文本生成、工具调用、结构化响应、图像生成等。 To use the AI SDK, install the `ai` package and use it within your Agent. The example below shows how it use it to generate text on request, but you can use it from any method within your Agent, including WebSocket handlers, as part of a scheduled task, or even when the Agent is initialized. ```ts import { Agent } from "agents"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; export class MyAgent extends Agent { async onRequest(request: Request): Promise { const { text } = await generateText({ model: openai("o3-mini"), prompt: "Build me an AI agent on Cloudflare Workers", }); return Response.json({ modelResponse: text }); } } ``` ### OpenAI compatible endpoints Agents can call models across any service, including those that support the OpenAI API. For example, you can use the OpenAI SDK to use one of [Google's Gemini models](https://ai.google.dev/gemini-api/docs/openai#node.js) directly from your Agent. Agents can stream responses back over HTTP using Server Sent Events (SSE) from within an `onRequest` handler, or by using the native [WebSockets](/agents/api-reference/websockets/) API in your Agent to responses back to a client, which is especially useful for larger models that can take over 30+ seconds to reply. ```ts import { Agent } from "agents"; import { OpenAI } from "openai"; export class MyAgent extends Agent { async onRequest(request: Request): Promise { const openai = new OpenAI({ apiKey: this.env.GEMINI_API_KEY, baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/", }); // Create a TransformStream to handle streaming data let { readable, writable } = new TransformStream(); let writer = writable.getWriter(); const textEncoder = new TextEncoder(); // Use ctx.waitUntil to run the async function in the background // so that it doesn't block the streaming response ctx.waitUntil( (async () => { const stream = await openai.chat.completions.create({ model: "4o", messages: [ { role: "user", content: "Write me a Cloudflare Worker." }, ], stream: true, }); // loop over the data as it is streamed and write to the writeable for await (const part of stream) { writer.write( textEncoder.encode(part.choices[0]?.delta?.content || ""), ); } writer.close(); })(), ); // Return the readable stream back to the client return new Response(readable); } } ``` --- # 使用 WebSockets URL: https://developers.cloudflare.com/agents/api-reference/websockets/ import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig, } from "~/components"; 用户和客户端可以通过 WebSockets 直接连接到 Agent,允许与您的 Agent 进行长时间运行的双向通信。 要使 Agent 能够接受 WebSockets,请在您的 Agent 上定义 `onConnect` 和 `onMessage` 方法。 - `onConnect(connection: Connection, ctx: ConnectionContext)` 在客户端建立新的 WebSocket 连接时被调用。原始的 HTTP 请求,包括请求头、cookies 和 URL 本身,都可以在 `ctx.request` 上获取。 - `onMessage(connection: Connection, message: WSMessage)` 为每个传入的 WebSocket 消息调用。消息是 `ArrayBuffer | ArrayBufferView | string` 之一,您可以使用 `connection.send()` 向客户端发送消息。您可以通过检查 `connection.id` 来区分客户端连接,它对每个连接的客户端都是唯一的。 以下是一个回显收到的任何消息的 Agent 示例: ```ts import { Agent, Connection } from "agents"; export class ChatAgent extends Agent { async onConnect(connection: Connection, ctx: ConnectionContext) { // 连接被 SDK 自动接受。 // 您也可以在这里用 connection.close() 显式关闭连接 // 在 ctx.request 上访问 Request 以检查头部、cookies 和 URL } async onMessage(connection: Connection, message: WSMessage) { // const response = await longRunningAITask(message) await connection.send(message); } } ``` ### 连接客户端 Agent 框架包含一个有用的辅助包,用于从客户端应用程序直接连接到您的 Agent(或其他 Agents)。导入 `agents/client`,创建 `AgentClient` 实例并使用它连接到您的 Agent 实例: ```ts import { AgentClient } from "agents/client"; const connection = new AgentClient({ agent: "dialogue-agent", name: "insight-seeker", }); connection.addEventListener("message", (event) => { console.log("接收到:", event.data); }); connection.send( JSON.stringify({ type: "inquiry", content: "您看到了什么模式?", }), ); ``` ### React 客户端 基于 React 的应用程序可以导入 `agents/react` 并使用 `useAgent` hook 直接连接到 Agent 实例: ```ts import { useAgent } from "agents/react"; function AgentInterface() { const connection = useAgent({ agent: "dialogue-agent", name: "insight-seeker", onMessage: (message) => { console.log("收到理解:", message.data); }, onOpen: () => console.log("连接已建立"), onClose: () => console.log("连接已关闭"), }); const inquire = () => { connection.send( JSON.stringify({ type: "inquiry", content: "您收集了哪些洞察?", }) ); }; return (
); } ```
`useAgent` hook 自动处理连接的生命周期,确保在组件挂载和卸载时正确初始化和清理。您还可以[将 `useAgent` 与 `useState` 结合使用](/agents/api-reference/store-and-sync-state/),自动在连接到您的 Agent 的所有客户端之间同步状态。 ### 处理 WebSocket 事件 在您的 Agent 上定义 `onError` 和 `onClose` 方法以显式处理 WebSocket 客户端错误和关闭事件。记录错误、清理状态和/或发出指标: ```ts import { Agent, Connection } from "agents"; export class ChatAgent extends Agent { // onConnect 和 onMessage 方法 // ... // WebSocket 错误和断开连接(关闭)处理。 async onError(connection: Connection, error: unknown): Promise { console.error(`WS 错误: ${error}`); } async onClose( connection: Connection, code: number, reason: string, wasClean: boolean, ): Promise { console.log(`WS 关闭: ${code} - ${reason} - wasClean: ${wasClean}`); connection.close(); } } ``` --- # 调用 LLMs URL: https://developers.cloudflare.com/agents/concepts/calling-llms/ import { Render } from "~/components"; ### 理解 LLM 提供商和模型类型 不同的 LLM 提供商提供针对特定类型任务优化的模型。在构建 AI 系统时,选择正确的模型对性能和成本效率都至关重要。 #### 推理模型 像 OpenAI 的 o1、Anthropic 的 Claude 和 DeepSeek 的 R1 这样的模型特别适合复杂的推理任务。这些模型擅长: - 将问题分解为步骤 - 遵循复杂指令 - 在长对话中维护上下文 - 生成代码和技术内容 例如,在实施旅行预订系统时,您可能使用推理模型来分析旅行需求并生成适当的预订策略。 #### 指令模型 像 GPT-4 和 Claude Instant 这样的模型经过优化,能够高效地遵循直接的指令。它们在以下方面表现良好: - 内容生成 - 简单分类任务 - 基本问答 - 文本转换 这些模型对于不需要复杂推理的直接任务通常更具成本效益。 --- # 人机协作 URL: https://developers.cloudflare.com/agents/concepts/human-in-the-loop/ ### 什么是人机协作? 人机协作(Human-in-the-Loop,HITL)工作流将人类判断和监督集成到自动化流程中。这些工作流在关键点暂停,进行人工审查、验证或决策,然后再继续。这种方法将自动化的效率与人类专业知识和监督相结合,在最重要的地方发挥作用。 ![人机协作图表](~/assets/images/agents/human-in-the-loop.svg) #### 理解人机协作工作流 在人机协作工作流中,流程不是完全自动化的。相反,它们包含指定的检查点,需要人工干预。例如,在旅行预订系统中,人类可能希望在 Agent 执行交易之前确认旅行安排。工作流管理这种交互,确保: 1. 流程在适当的审查点暂停 2. 人工审查员获得必要的上下文 3. 系统在审查期间维护状态 4. 审查决策得到适当整合 5. 获得批准后流程继续进行 ### 人机协作工作流的最佳实践 #### 长期状态持久化 人工审查流程不会按照可预测的时间表运行。审查员可能需要几天或几周的时间来做出决定,特别是对于需要额外调查或多重批准的复杂案例。您的系统需要在此期间保持完美的状态一致性,包括: - 原始请求和上下文 - 所有中间决策和行动 - 任何部分进度或临时状态 - 审查历史和反馈 :::note[提示] [Durable Objects](/durable-objects/) 为管理人机协作工作流中的状态提供了理想的解决方案,提供了可以维持几小时、几周或几个月状态的持久计算实例。 ::: #### 通过评估持续改进 人工审查员在评估和改进 LLM 性能方面发挥着关键作用。实施系统化的评估流程,不仅收集人工对最终输出的反馈,还收集对 LLM 决策过程的反馈。这可以包括: - 决策质量评估:让审查员评估 LLM 的推理过程和决策点,而不仅仅是最终输出。 - 边缘案例识别:利用人类专业知识识别可以改进 LLM 性能的场景。 - 反馈收集:收集结构化反馈,可用于微调 LLM 或调整工作流。[AI Gateway](/ai-gateway/evaluations/add-human-feedback/) 可以是设置 LLM 反馈循环的有用工具。 #### 错误处理和恢复 强大的错误处理对于维护工作流完整性至关重要。您的系统应该优雅地处理各种故障场景,包括审查员不可用、系统中断或冲突审查。实施清晰的升级路径来处理超出正常参数的异常情况。 系统应该在暂停状态期间保持稳定,确保即使在延长的审查期间也不会丢失任何工作。考虑实施自动检查点,允许工作流在任何中断后从最后的稳定状态恢复。 --- # 概念 URL: https://developers.cloudflare.com/agents/concepts/ import { DirectoryListing } from "~/components"; --- # Agents URL: https://developers.cloudflare.com/agents/concepts/what-are-agents/ import { Render } from "~/components"; ### 什么是 Agents? Agent 是一个 AI 系统,能够通过对工具使用和流程控制做出决策来自主执行任务。与遵循预定义路径的传统自动化不同,Agents 可以根据上下文和中间结果动态调整其方法。Agents 也与副驾驶(例如传统聊天应用程序)不同,它们可以完全自动化任务,而不是简单地增强和扩展人类输入。 - **Agents** → 非线性、非确定性(每次运行都可能改变) - **Workflows** → 线性、确定性执行路径 - **Co-pilots** → 需要人工干预的增强 AI 助手 ### 示例:预订假期 如果这是您第一次使用或与 Agents 交互,这个例子将说明 Agent 在预订假期这样的情境中是如何工作的。如果您已经熟悉这个主题,请继续阅读。 想象您正在尝试预订假期。您需要研究航班、寻找酒店、查看餐厅评价并跟踪您的预算。 #### 传统工作流自动化 传统自动化系统遵循预定的序列: - 接受特定输入(日期、位置、预算) - 按固定顺序调用预定义的 API 端点 - 基于硬编码标准返回结果 - 当出现意外情况时无法适应 ![传统工作流自动化图表](~/assets/images/agents/workflow-automation.svg) #### AI Co-pilot Co-pilot 作为智能助手: - 基于您的偏好提供酒店和行程推荐 - 能够理解和响应自然语言查询 - 提供指导和建议 - 需要人类决策和执行操作 ![Co-pilot 图表](~/assets/images/agents/co-pilot.svg) #### Agent Agent 结合了 AI 的判断能力和调用相关工具来执行任务的能力。Agent 的输出将是非确定性的,考虑到: - 实时可用性和价格变化 - 约束条件的动态优先级排序 - 从故障中恢复的能力 - 基于中间结果的自适应决策 ![Agent 图表](~/assets/images/agents/agent-workflow.svg) Agent 可以动态生成行程并执行预订,类似于您对旅行社的期望。 ### Agent 系统的三个主要组成部分: - **决策引擎**: 通常是 LLM(大语言模型),决定行动步骤 - **工具集成**: Agent 可以利用的 API、函数和服务 - **记忆系统**: 维护上下文并跟踪任务进度 #### Agents 如何工作 Agents 在以下连续循环中运行: 1. **观察** 当前状态或任务 2. **规划** 要采取的行动,使用 AI 进行推理 3. **执行** 使用可用工具执行这些行动(通常是 API 或 [MCPs](https://modelcontextprotocol.io/introduction)) 4. **学习** 从结果中学习(将结果存储在记忆中,更新任务进度,并为下一次迭代做准备) --- # 工具 URL: https://developers.cloudflare.com/agents/concepts/tools/ ### 什么是工具? 工具使 AI 系统能够与外部服务交互并执行操作。它们为 Agents 和工作流提供了调用 API、操作数据以及与外部系统集成的结构化方式。工具在 AI 决策能力和现实世界行动之间架起了桥梁。 ### 理解工具 在 AI 系统中,工具通常实现为 AI 可以用来完成特定任务的函数调用。例如,旅行预订 Agent 可能有以下工具: - 搜索航班可用性 - 查询酒店价格 - 处理付款 - 发送确认邮件 每个工具都有一个定义的接口,指定其输入、输出和预期行为。这使得 AI 系统能够理解何时以及如何适当地使用每个工具。 ### 常见工具模式 #### API 集成工具 最常见的工具类型是那些包装外部 API 的工具。这些工具处理 API 认证、请求格式化和响应解析的复杂性,为 AI 系统提供一个清洁的接口。 #### Model Context Protocol (MCP) [Model Context Protocol](https://modelcontextprotocol.io/introduction) 提供了定义和与工具交互的标准化方式。可以将其视为为 LLM 与外部资源交互而设计的 API 抽象层。MCP 为以下方面定义了一致的接口: - **工具发现**: 系统可以动态发现可用的工具 - **参数验证**: 工具使用 JSON Schema 指定其输入要求 - **错误处理**: 标准化的错误报告和恢复 - **状态管理**: 工具可以在调用之间维护状态 #### 数据处理工具 处理数据转换和分析的工具对许多 AI 工作流都是必需的。这些可能包括: - CSV 解析和分析 - 图像处理 - 文本提取 - 数据验证 --- # 工作流 URL: https://developers.cloudflare.com/agents/concepts/workflows/ import { Render } from "~/components"; ## 什么是工作流? 工作流是协调 Agent 组件如何协同工作的编排层。它定义了任务处理、工具调用和结果管理的结构化路径。虽然 Agents 动态决定要做什么,但工作流提供了管理这些决策如何执行的底层框架。 ### 理解 Agent 系统中的工作流 将工作流想象成公司的操作程序。公司(Agent)可以做出各种决策,但这些决策的实施方式遵循既定的流程(工作流)。例如,当您通过旅行社预订航班时,他们可能对推荐哪些航班做出不同的决策,但实际预订航班的过程遵循固定的步骤序列。 让我们检查一个基本的 Agent 工作流: ### 工作流的核心组成部分 工作流通常由几个关键元素组成: 1. **输入处理** 工作流定义了输入在被 Agent 处理之前如何被接收和验证。这包括标准化格式、检查权限,以及确保所有必需信息都存在。 2. **工具集成** 工作流管理如何访问外部工具和服务。它们处理认证、速率限制、错误恢复,并确保工具以正确的顺序使用。 3. **状态管理** 工作流维护正在进行的流程状态,跟踪多个步骤的进度,并确保操作的一致性。 4. **输出处理** 来自 Agent 行动的结果根据定义的规则进行处理,无论是存储数据、触发通知还是格式化响应。 --- # 指南 URL: https://developers.cloudflare.com/agents/guides/ import { DirectoryListing } from "~/components"; --- # 构建远程 MCP 服务器 URL: https://developers.cloudflare.com/agents/guides/remote-mcp-server/ import { Details, Render, PackageManagers } from "~/components"; ## 部署您的第一个 MCP 服务器 本指南将向您展示如何在 Cloudflare 上部署您自己的远程 MCP 服务器,提供两种选择: - **无身份验证** — 任何人都可以连接和使用服务器(无需登录)。 - **有[身份验证和授权](/agents/guides/remote-mcp-server/#add-authentication)** — 用户在访问工具之前需要登录,您可以根据用户的权限控制 Agent 可以调用哪些工具。 您可以从部署一个无身份验证的[公共 MCP 服务器](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless)开始,然后稍后添加用户身份验证和范围授权。如果您已经知道您的服务器需要身份验证,可以跳到[下一节](/agents/guides/remote-mcp-server/#add-authentication)。 下面的按钮将引导您完成将此[示例 MCP 服务器](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless)部署到您的 Cloudflare 账户所需的所有操作: [![部署到 Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless) 部署后,此服务器将在您的 workers.dev 子域名上运行(例如 remote-mcp-server-authless.your-account.workers.dev/sse)。您可以立即使用 [AI Playground](https://playground.ai.cloudflare.com/)(远程 MCP 客户端)、[MCP inspector](https://github.com/modelcontextprotocol/inspector) 或[其他 MCP 客户端](/agents/guides/remote-mcp-server/#connect-your-remote-mcp-server-to-claude-and-other-mcp-clients-via-a-local-proxy)连接到它。然后,一旦您准备好了,就可以自定义 MCP 服务器并添加您自己的[工具](/agents/model-context-protocol/tools/)。 如果您使用"部署到 Cloudflare"按钮,将在您的 GitHub 或 GitLab 账户上为您的 MCP 服务器设置一个新的 git 仓库,配置为每次您推送更改或将拉取请求合并到仓库的主分支时自动部署到 Cloudflare。然后您可以克隆此仓库,[进行本地开发](/agents/guides/remote-mcp-server/#local-development),并开始编写代码和构建。 ### 通过 CLI 设置和部署您的 MCP 服务器 或者,您可以使用如下所示的命令行在本地机器上创建新的 MCP 服务器。 现在,您已经设置了 MCP 服务器,依赖项已安装。进入该项目文件夹: ```sh cd my-mcp-server ``` #### 本地开发 在新项目的目录中,运行以下命令启动开发服务器: ```sh npm start ``` 您的 MCP 服务器现在运行在 `http://localhost:8787/sse`。 在新终端中,运行 [MCP inspector](https://github.com/modelcontextprotocol/inspector)。MCP inspector 是一个交互式 MCP 客户端,允许您连接到 MCP 服务器并从 Web 浏览器调用工具。 ```sh npx @modelcontextprotocol/inspector@latest ``` 在 Web 浏览器中打开 MCP inspector: ```sh open http://localhost:5173 ``` 在 inspector 中,输入您的 MCP 服务器的 URL `http://localhost:8787/sse`,然后点击 **Connect**。您应该看到"List Tools"按钮,它将列出您的 MCP 服务器公开的工具。 ![MCP inspector — 已认证](~/assets/images/agents/mcp-inspector-authenticated.png) #### 部署您的 MCP 服务器 您可以在示例项目中使用以下 [Wrangler CLI 命令](/workers/wrangler)将 MCP 服务器部署到 Cloudflare: ```sh npx wrangler@latest deploy ``` 如果您已经[将 git 仓库连接](/workers/ci-cd/builds/)到带有 MCP 服务器的 Worker,您可以通过推送更改或将拉取请求合并到仓库的主分支来部署您的 MCP 服务器。 部署后,获取您已部署的 MCP 服务器的 URL,并在运行在 `http://localhost:5173` 上的 MCP inspector 中输入它。您现在有了一个部署到 Cloudflare 的远程 MCP 服务器,MCP 客户端可以连接到它。 ### 通过本地代理将您的远程 MCP 服务器连接到 Claude 和其他 MCP 客户端 现在您的 MCP 服务器正在运行,您可以使用 [`mcp-remote` 本地代理](https://www.npmjs.com/package/mcp-remote)将 Claude Desktop 或其他 MCP 客户端连接到它——即使这些工具还不是*远程* MCP 客户端,并且在客户端不支持远程传输或授权。这让您可以测试与真正的 MCP 客户端交互时您的 MCP 服务器的交互体验。 更新您的 Claude Desktop 配置以指向您的 MCP 服务器的 URL。您可以使用 `localhost:8787/sse` URL 或您已部署的 MCP 服务器的 URL: ```json { "mcpServers": { "math": { "command": "npx", "args": [ "mcp-remote", "https://your-worker-name.your-account.workers.dev/sse" ] } } } ``` 更新配置文件后重启 Claude Desktop 以加载 MCP 服务器。完成后,Claude 将能够调用您的远程 MCP 服务器。您可以通过让 Claude 使用您的工具之一来测试这一点。例如:"您能使用数学工具将 23 和 19 相加吗?"。Claude 应该调用该工具并显示 MCP 服务器生成的结果。 在[此节](/agents/guides/test-remote-mcp-server)中了解更多关于将远程 MCP 服务器与 MCP 客户端一起使用的其他方式。 ## 添加身份验证 现在您已经部署了公共 MCP 服务器,让我们看看如何使用 OAuth 启用用户身份验证。 您之前部署的公共服务器示例允许任何客户端连接并调用工具而无需登录。要添加身份验证,您将更新您的 MCP 服务器以充当 OAuth 提供商,处理安全登录流程并颁发 MCP 客户端可以用来进行经过身份验证的工具调用的访问令牌。 如果用户已经需要登录才能使用您的服务,这特别有用。启用身份验证后,用户可以使用其现有账户登录,并授权其 AI Agent 使用范围权限与您的 MCP 服务器公开的工具进行交互。 在此示例中,我们使用 GitHub 作为 OAuth 提供商,但您可以将您的 MCP 服务器与任何支持 OAuth 2.0 规范的 [OAuth 提供商](/agents/model-context-protocol/authorization/#2-third-party-oauth-provider)连接,包括 Google、Slack、[Stytch](/agents/model-context-protocol/authorization/#stytch)、[Auth0](/agents/model-context-protocol/authorization/#stytch)、[WorkOS](/agents/model-context-protocol/authorization/#stytch) 等。 ### 步骤 1 — 创建和部署新的 MCP 服务器 运行以下命令创建新的 MCP 服务器: 现在,您已经设置了 MCP 服务器,依赖项已安装。进入该项目文件夹: ```sh cd my-mcp-server-github-auth ``` 然后,运行以下命令部署 MCP 服务器: ```sh npx wrangler@latest deploy ``` 您会注意到,在示例 MCP 服务器中,如果您打开 `src/index.ts`,主要区别是 `defaultHandler` 设置为 `GitHubHandler`: ```ts ins="OAuthProvider.GitHubHandler" import GitHubHandler from "./github-handler"; export default new OAuthProvider({ apiRoute: "/sse", apiHandler: MyMCP.Router, defaultHandler: GitHubHandler, authorizeEndpoint: "/authorize", tokenEndpoint: "/token", clientRegistrationEndpoint: "/register", }); ``` 这将确保您的用户被重定向到 GitHub 进行身份验证。但是要使其工作,您需要在以下步骤中创建 OAuth 客户端应用程序。 ### 步骤 2 — 创建 OAuth 应用程序 您需要创建两个 [GitHub OAuth 应用程序](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app)以使用 GitHub 作为 MCP 服务器的身份验证提供商——一个用于本地开发,一个用于生产。 #### 首先为本地开发创建新的 OAuth 应用程序 导航到 [github.com/settings/developers](https://github.com/settings/developers) 创建具有以下设置的新 OAuth 应用程序: - **Application name**: `My MCP Server (local)` - **Homepage URL**: `http://localhost:8787` - **Authorization callback URL**: `http://localhost:8787/callback` 对于您刚创建的 OAuth 应用程序,将 OAuth 应用程序的客户端 ID 添加为 `GITHUB_CLIENT_ID`,生成客户端密钥,将其添加为 `GITHUB_CLIENT_SECRET` 到项目根目录的 `.dev.vars` 文件中,该文件[将用于在本地开发中设置密钥](/workers/configuration/secrets/)。 ```sh touch .dev.vars echo 'GITHUB_CLIENT_ID="your-client-id"' >> .dev.vars echo 'GITHUB_CLIENT_SECRET="your-client-secret"' >> .dev.vars cat .dev.vars ``` #### 接下来,在本地运行您的 MCP 服务器 运行以下命令启动开发服务器: ```sh npm start ``` 您的 MCP 服务器现在运行在 `http://localhost:8787/sse`。 在新终端中,运行 [MCP inspector](https://github.com/modelcontextprotocol/inspector)。MCP inspector 是一个交互式 MCP 客户端,允许您连接到 MCP 服务器并从 Web 浏览器调用工具。 ```sh npx @modelcontextprotocol/inspector@latest ``` 在 Web 浏览器中打开 MCP inspector: ```sh open http://localhost:5173 ``` 在 inspector 中,输入您的 MCP 服务器的 URL `http://localhost:8787/sse`,然后点击 **Connect**: You should be redirected to a GitHub login or authorization page. After authorizing the MCP Client (the inspector) access to your GitHub account, you will be redirected back to the inspector. You should see the "List Tools" button, which will list the tools that your MCP server exposes. #### Second — create a new OAuth App for production You'll need to repeat these steps to create a new OAuth App for production. Navigate to [github.com/settings/developers](https://github.com/settings/developers) to create a new OAuth App with the following settings: - **Application name**: `My MCP Server (production)` - **Homepage URL**: Enter the workers.dev URL of your deployed MCP server (ex: `worker-name.account-name.workers.dev`) - **Authorization callback URL**: Enter the `/callback` path of the workers.dev URL of your deployed MCP server (ex: `worker-name.account-name.workers.dev/callback`) For the OAuth app you just created, add the client ID and client secret, using Wrangler CLI: ```sh wrangler secret put GITHUB_CLIENT_ID ``` ```sh wrangler secret put GITHUB_CLIENT_SECRET ``` #### Finally, connect to your MCP server Now that you've added the ID and secret of your production OAuth app, you should now be able to connect to your MCP server running at `worker-name.account-name.workers.dev/sse` using the [AI Playground](https://playground.ai.cloudflare.com/), MCP inspector or ([other MCP clients](/agents/guides/remote-mcp-server/#connect-your-mcp-server-to-claude-and-other-mcp-clients)), and authenticate with GitHub. ## Next steps - Add [tools](/agents/model-context-protocol/tools/) to your MCP server. - Customize your MCP Server's [authentication and authorization](/agents/model-context-protocol/authorization/). --- # 测试远程 MCP 服务器 URL: https://developers.cloudflare.com/agents/guides/test-remote-mcp-server/ import { Render } from "~/components"; 远程授权连接是 [Model Context Protocol (MCP) 规范](https://spec.modelcontextprotocol.io/specification/draft/basic/authorization/)不断发展的一部分。并非所有 MCP 客户端都支持远程连接。 本指南将向您展示如何开始使用支持远程连接的 MCP 客户端使用您的远程 MCP 服务器的选项。如果您尚未创建和部署远程 MCP 服务器,您应该首先遵循[构建远程 MCP 服务器](/agents/guides/remote-mcp-server/)指南。 ## Model Context Protocol (MCP) 检查器 [`@modelcontextprotocol/inspector` 包](https://github.com/modelcontextprotocol/inspector)是 MCP 服务器的可视化测试工具。 您可以通过运行以下命令在本地运行它: ```bash npx @modelcontextprotocol/inspector ``` 然后,输入您的远程 MCP 服务器的 URL。您可以使用在本地机器上运行在 localhost 的 MCP 服务器,或者您可以使用在 Cloudflare 上运行的远程 MCP 服务器。 ![MCP 检查器](~/assets/images/agents/mcp-inspector-enter-url.png) 一旦您通过身份验证,您将被重定向回检查器。您应该看到"List Tools"按钮,它将列出您的 MCP 服务器公开的工具。 ![MCP 检查器 — 已认证](~/assets/images/agents/mcp-inspector-authenticated.png) ## 通过本地代理将您的远程 MCP 服务器连接到 Claude Desktop 即使 [Claude Desktop](https://claude.ai/download) 尚未支持远程 MCP 客户端,您也可以使用 [`mcp-remote` 本地代理](https://www.npmjs.com/package/mcp-remote)将其连接到您的远程 MCP 服务器。这让您可以测试与真实世界 MCP 客户端的远程 MCP 服务器交互体验。 1. 打开 Claude Desktop 并导航到 Settings -> Developer -> Edit Config。这会打开控制 Claude 可以访问哪些 MCP 服务器的配置文件。 2. 将内容替换为这样的配置: ```json { "mcpServers": { "math": { "command": "npx", "args": ["mcp-remote", "http://my-mcp-server.my-account.workers.dev/sse"] } } } ``` 这告诉 Claude 与运行在 `http://localhost:8787/sse` 的 MCP 服务器通信。 3. 保存文件并重启 Claude Desktop(command/ctrl + R)。当 Claude 重启时,浏览器窗口将打开显示您的 OAuth 登录页面。完成授权流程以授予 Claude 访问您的 MCP 服务器的权限。 一旦通过身份验证,您将能够通过点击 Claude 界面右下角的工具图标来查看您的工具。 ## 将您的远程 MCP 服务器连接到 Cursor 要将 [Cursor](https://www.cursor.com/) 与您的远程 MCP 服务器连接,选择 `Type`: "Command",在 `Command` 字段中,将 command 和 args 字段合并为一个(例如 `npx mcp-remote https://your-worker-name.your-account.workers.dev/sse`)。 ## 将您的远程 MCP 服务器连接到 Windsurf 您可以通过编辑 [`mcp_config.json` 文件](https://docs.codeium.com/windsurf/mcp)并添加以下配置,将您的远程 MCP 服务器连接到 [Windsurf](https://codeium.com/windsurf): ```json { "mcpServers": { "math": { "command": "npx", "args": ["mcp-remote", "http://my-mcp-server.my-account.workers.dev/sse"] } } } ``` --- # 快速开始 URL: https://developers.cloudflare.com/agents/getting-started/ import { DirectoryListing } from "~/components"; --- # 测试您的 Agents URL: https://developers.cloudflare.com/agents/getting-started/testing-your-agent/ import { Render, PackageManagers, WranglerConfig } from "~/components"; 因为 Agents 运行在 Cloudflare Workers 和 Durable Objects 上,所以可以使用与 Workers 和 Durable Objects 相同的工具和技术来测试它们。 ## 编写和运行测试 ### 设置 :::note `agents-starter` 模板和新的 Cloudflare Workers 项目已经包含了相关的 `vitest` 和 `@cloudflare/vitest-pool-workers` 包,以及有效的 `vitest.config.js` 文件。 ::: 在编写第一个测试之前,安装必要的包: ```sh npm install vitest@~3.0.0 --save-dev --save-exact npm install @cloudflare/vitest-pool-workers --save-dev ``` 确保您的 `vitest.config.js` 文件与以下内容相同: ```js import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; export default defineWorkersConfig({ test: { poolOptions: { workers: { wrangler: { configPath: "./wrangler.toml" }, }, }, }, }); ``` ### 添加 Agent 配置 在 `vitest.config.js` 中添加 `durableObjects` 配置,包含您的 Agent 类的名称: ```js import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"; export default defineWorkersConfig({ test: { poolOptions: { workers: { main: "./src/index.ts", miniflare: { durableObjects: { NAME: "MyAgent", }, }, }, }, }, }); ``` ### 编写测试 :::note 查看 [Vitest 文档](https://vitest.dev/) 了解更多关于测试的信息,包括测试 API 参考和高级测试技术。 ::: 测试使用 `vitest` 框架。您的 Agent 的基本测试套件可以验证您的 Agent 如何响应请求,但也可以对您的 Agent 的方法和状态进行单元测试。 ```ts import { env, createExecutionContext, waitOnExecutionContext, SELF, } from "cloudflare:test"; import { describe, it, expect } from "vitest"; import worker from "../src"; import { Env } from "../src"; interface ProvidedEnv extends Env {} describe("向我的 Agent 发出请求", () => { // 单元测试方法 it("响应状态", async () => { // 提供一个有效的 URL,您的 Worker 可以使用它来路由到您的 Agent // 如果您使用 routeAgentRequest,这将是 /agent/:agent/:name const request = new Request( "http://example.com/agent/my-agent/agent-123", ); const ctx = createExecutionContext(); const response = await worker.fetch(request, env, ctx); await waitOnExecutionContext(ctx); expect(await response.text()).toMatchObject({ hello: "from your agent" }); }); it("也响应状态", async () => { const request = new Request("http://example.com/agent/my-agent/agent-123"); const response = await SELF.fetch(request); expect(await response.text()).toMatchObject({ hello: "from your agent" }); }); }); ``` ### 运行测试 运行测试使用 `vitest` CLI: ```sh $ npm run test # 或直接运行 vitest $ npx vitest ``` ```sh output MyAgent ✓ 应该返回问候语 (1 ms) Test Files 1 passed (1) ``` 查看[测试文档](/workers/testing/vitest-integration/write-your-first-test/) 了解更多示例和测试配置。 ## 本地运行 Agents 您也可以使用 `wrangler` CLI 在本地运行 Agent: ```sh $ npx wrangler dev ``` ```sh output Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development. Your worker has access to the following bindings: - Durable Objects: - MyAgent: MyAgent Starting local server... [wrangler:inf] Ready on http://localhost:53645 ``` 这会启动一个本地开发服务器,运行与 Cloudflare Workers 相同的运行时,让您可以迭代 Agent 的代码并在不部署的情况下本地测试。 访问 [`wrangler dev`](https://developers.cloudflare.com/workers/wrangler/commands/#dev) 文档以查看 CLI 标志和配置选项。 --- # McpAgent — API 参考 URL: https://developers.cloudflare.com/agents/model-context-protocol/mcp-agent-api/ import { Render, TypeScriptExample } from "~/components"; 当您在 Cloudflare 上构建 MCP 服务器时,您需要扩展来自 Agents SDK 的 [`McpAgent` 类](https://github.com/cloudflare/agents/blob/5881c5d23a7f4580600029f69307cfc94743e6b8/packages/agents/src/mcp.ts),如下所示: ```ts title="src/index.ts" 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(), b: z.number() }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }], }), ); } } ``` 这意味着您的 MCP 服务器的每个实例都有自己的持久状态,由 [Durable Object](/durable-objects/) 支持,拥有自己的 [SQL 数据库](/agents/api-reference/store-and-sync-state)。 您的 MCP 服务器不一定必须是一个 Agent。您可以构建无状态的 MCP 服务器,只需使用 `@modelcontextprotocol/typescript-sdk` 包为您的 MCP 服务器添加[工具](/agents/model-context-protocol/tools)。 但是,如果您希望您的 MCP 服务器能够: - 记住以前的工具调用和它提供的响应 - 向 MCP 客户端提供游戏,记住游戏板状态、以前的移动和分数 - 缓存以前外部 API 调用的状态,以便后续工具调用可以重复使用它 - 执行 Agent 可以做的任何事情,但允许 MCP 客户端与其通信 您可以使用下面的 API 来实现这些功能。 #### 休眠支持 `McpAgent` 实例自动支持 [WebSockets 休眠](/durable-objects/best-practices/websockets/#websocket-hibernation-api),允许有状态的 MCP 服务器在非活动期间休眠,同时保留其状态。这意味着您的 agents 只在主动处理请求时消耗计算资源,在保持完整上下文和对话历史的同时优化成本。 休眠功能默认启用,无需额外配置。 #### 身份验证和授权 McpAgent 类提供与 [OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider) 的无缝集成,用于[身份验证和授权](/agents/model-context-protocol/authorization/)。 当用户向您的 MCP 服务器进行身份验证时,他们的身份信息和令牌通过 `props` 参数提供,允许您: - 访问特定用户的数据 - 在执行操作前检查用户权限 - 根据用户属性自定义响应 - 使用身份验证令牌代表用户向外部服务发出请求 ### 状态同步 API `McpAgent` 类提供了来自 [Agents SDK](/agents/api-reference/agents-api/) 的以下方法子集: - [`state`](/agents/api-reference/store-and-sync-state/) - [`initialState`](/agents/api-reference/store-and-sync-state/#set-the-initial-state-for-an-agent) - [`setState`](/agents/api-reference/store-and-sync-state/) - [`onStateUpdate`](/agents/api-reference/store-and-sync-state/#synchronizing-state) - [`sql`](/agents/api-reference/agents-api/#sql-api) :::note[会话结束后状态重置] 目前,每个客户端会话都由 `McpAgent` 类的一个实例支持。这对您是自动处理的,如[入门指南](/agents/guides/remote-mcp-server)所示。这意味着当同一客户端重新连接时,它们将开始一个新会话,状态将被重置。 ::: 例如,以下代码实现了一个记住计数器值的 MCP 服务器,并在调用 `add` 工具时更新计数器: ```ts title="src/index.ts" import { McpAgent } from "agents/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; type State = { counter: number }; export class MyMCP extends McpAgent { server = new McpServer({ name: "Demo", version: "1.0.0", }); initialState: State = { counter: 1, }; async init() { this.server.resource(`counter`, `mcp://resource/counter`, (uri) => { return { contents: [{ uri: uri.href, text: String(this.state.counter) }], }; }); this.server.tool( "add", "Add to the counter, stored in the MCP", { a: z.number() }, async ({ a }) => { this.setState({ ...this.state, counter: this.state.counter + a }); return { content: [ { type: "text", text: String(`Added ${a}, total is now ${this.state.counter}`), }, ], }; }, ); } onStateUpdate(state: State) { console.log({ stateUpdate: state }); } } ``` ### 尚不支持的 API 以下来自 Agents SDK 的 API 在 `McpAgent` 上尚不可用: - [WebSocket API](/agents/api-reference/websockets/)(`onMessage`、`onError`、`onClose`、`onConnect`) - [调度 API](/agents/api-reference/schedule-tasks/) `this.schedule` --- # 授权 URL: https://developers.cloudflare.com/agents/model-context-protocol/authorization/ import { DirectoryListing } from "~/components"; 在构建 [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 服务器时,您需要一种允许用户登录(身份验证)的方式,以及允许他们授予 MCP 客户端访问其账户资源权限(授权)的方式。 Model Context Protocol 使用 [OAuth 2.1 的子集进行授权](https://spec.modelcontextprotocol.io/specification/draft/basic/authorization/)。OAuth 允许您的用户授予对资源的有限访问权限,而无需共享 API 密钥或其他凭据。 Cloudflare 提供了一个 [OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider),它实现了 OAuth 2.1 协议的提供方,让您可以轻松地为 MCP 服务器添加授权功能。 您可以通过三种方式使用 OAuth Provider Library: 1. **您的 Worker 自己处理授权。** 您的 MCP 服务器在 Cloudflare 上运行,处理完整的 OAuth 流程。([示例](/agents/guides/remote-mcp-server/)) 2. **直接与第三方 OAuth 提供方集成**,如 GitHub 或 Google。 3. **与您自己的 OAuth 提供方集成**,包括您可能已经依赖的授权即服务提供商,如 Stytch、Auth0 或 WorkOS。 以下部分描述了这些选项中的每一个,并链接到每个的可运行代码示例。 ## 授权选项 ### (1) 您的 MCP 服务器自己处理授权和身份验证 您的 MCP 服务器使用 [OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider),可以处理完整的 OAuth 授权流程,无需任何第三方参与。 [Workers OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider) 是一个 Cloudflare Worker,它实现了一个 [`fetch()` 处理程序](/workers/runtime-apis/handlers/fetch/),并处理传入的 MCP 服务器请求。 您为您的 MCP 服务器的 API、身份验证和授权逻辑以及 OAuth 端点的 URI 路径提供您自己的处理程序,如下所示: ```ts export default new OAuthProvider({ apiRoute: "/mcp", // 您的 MCP 服务器: apiHandler: MyMCPServer.Router, // 您的身份验证和授权处理程序: defaultHandler: MyAuthHandler, authorizeEndpoint: "/authorize", tokenEndpoint: "/token", clientRegistrationEndpoint: "/register", }); ``` 参考[入门示例](/agents/guides/remote-mcp-server/)以获取 `OAuthProvider` 的完整使用示例,包含模拟身份验证流程。 在这种情况下,授权流程的工作方式如下: ```mermaid sequenceDiagram participant B as User-Agent (Browser) participant C as MCP Client participant M as MCP Server (your Worker) C->>M: MCP Request M->>C: HTTP 401 Unauthorized Note over C: Generate code_verifier and code_challenge C->>B: Open browser with authorization URL + code_challenge B->>M: GET /authorize Note over M: User logs in and authorizes M->>B: Redirect to callback URL with auth code B->>C: Callback with authorization code C->>M: Token Request with code + code_verifier M->>C: Access Token (+ Refresh Token) C->>M: MCP Request with Access Token Note over C,M: Begin standard MCP message exchange ``` 请记住 — [身份验证与授权是不同的](https://www.cloudflare.com/learning/access-management/authn-vs-authz/)。您的 MCP 服务器可以自己处理授权,同时仍然依赖外部身份验证服务来首先验证用户身份。[入门指南中的示例](/agents/guides/remote-mcp-server)提供了一个模拟身份验证流程。您需要实现自己的身份验证处理程序 — 要么自己处理身份验证,要么使用外部身份验证服务。 ### (2) 第三方 OAuth 提供方 The [OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider) can be configured to use a third-party OAuth provider, such as GitHub or Google. You can see a complete example of this in the [GitHub example](/agents/guides/remote-mcp-server/#add-authentication). When you use a third-party OAuth provider, you must provide a handler to the `OAuthProvider` that implements the OAuth flow for the third-party provider. ```ts ins="defaultHandler: MyAuthHandler," import MyAuthHandler from "./auth-handler"; export default new OAuthProvider({ apiRoute: "/mcp", // Your MCP server: apiHandler: MyMCPServer.Router, // Replace this handler with your own handler for authentication and authorization with the third-party provider: defaultHandler: MyAuthHandler, authorizeEndpoint: "/authorize", tokenEndpoint: "/token", clientRegistrationEndpoint: "/register", }); ``` Note that as [defined in the Model Context Protocol specification](https://spec.modelcontextprotocol.io/specification/draft/basic/authorization/#292-flow-description) when you use a third-party OAuth provider, the MCP Server (your Worker) generates and issues its own token to the MCP client: ```mermaid sequenceDiagram participant B as User-Agent (Browser) participant C as MCP Client participant M as MCP Server (your Worker) participant T as Third-Party Auth Server C->>M: Initial OAuth Request M->>B: Redirect to Third-Party /authorize B->>T: Authorization Request Note over T: User authorizes T->>B: Redirect to MCP Server callback B->>M: Authorization code M->>T: Exchange code for token T->>M: Third-party access token Note over M: Generate bound MCP token M->>B: Redirect to MCP Client callback B->>C: MCP authorization code C->>M: Exchange code for token M->>C: MCP access token ``` Read the docs for the [Workers oAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider) for more details. ### (3) Bring your own OAuth Provider If your application already implements an OAuth Provider itself, or you use [Stytch](https://stytch.com/), [Auth0](https://auth0.com/), [WorkOS](https://workos.com/), or authorization-as-a-service provider, you can use this in the same way that you would use a third-party OAuth provider, described above in (2). You can use the auth provider to: - Allow users to authenticate to your MCP server through email, social logins, SSO (single sign-on), and MFA (multi-factor authentication). - Define scopes and permissions that directly map to your MCP tools. - Present users with a consent page corresponding with the requested permissions. - Enforce the permissions so that agents can only invoke permitted tools. #### Stytch Get started with a [remote MCP server that uses Stytch](https://stytch.com/docs/guides/connected-apps/mcp-servers) to allow users to sign in with email, Google login or enterprise SSO and authorize their AI agent to view and manage their company's OKRs on their behalf. Stytch will handle restricting the scopes granted to the AI agent based on the user's role and permissions within their organization. When authorizing the MCP Client, each user will see a consent page that outlines the permissions that the agent is requesting that they are able to grant based on their role. [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/mcp-stytch-b2b-okr-manager) For more consumer use cases, deploy a remote MCP server for a To Do app that uses Stytch for authentication and MCP client authorization. Users can sign in with email and immediately access the To Do lists associated with their account, and grant access to any AI assistant to help them manage their tasks. [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/mcp-stytch-consumer-todo-list) #### Auth0 Get started with a remote MCP server that uses Auth0 to authenticate users through email, social logins, or enterprise SSO to interact with their todos and personal data through AI agents. The MCP server securely connects to API endpoints on behalf of users, showing exactly which resources the agent will be able to access once it gets consent from the user. In this implementation, access tokens are automatically refreshed during long running interactions. To set it up, first deploy the protected API endpoint: [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-auth0/todos-api) Then, deploy the MCP server that handles authentication through Auth0 and securely connects AI agents to your API endpoint. [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-auth0/mcp-auth0-oidc) #### WorkOS Get started with a remote MCP server that uses WorkOS's AuthKit to authenticate users and manage the permissions granted to AI agents. In this example, the MCP server dynamically exposes tools based on the user's role and access rights. All authenticated users get access to the `add` tool, but only users who have been assigned the `image_generation` permission in WorkOS can grant the AI agent access to the image generation tool. This showcases how MCP servers can conditionally expose capabilities to AI agents based on the authenticated user's role and permission. [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authkit) ## Using Authentication Context in Your MCP Server When a user authenticates to your MCP server through Cloudflare's OAuth Provider, their identity information and tokens are made available through the `props` parameter. ```js export class MyMCP extends McpAgent { async init() { this.server.tool("userInfo", "Get user information", {}, async () => ({ content: [{ type: "text", text: `Hello, ${this.props.claims.name || "user"}!` }], })); } } ``` The authentication context can be used for: - Accessing user-specific data by using the user ID (this.props.claims.sub) as a key - Checking user permissions before performing operations - Customizing responses based on user preferences or attributes - Using authentication tokens to make requests to external services on behalf of the user - Ensuring consistency when users interact with your application through different interfaces (dashboard, API, MCP server) ## Implementing Permission-Based Access for MCP Tools You can implement fine-grained authorization controls for your MCP tools based on user permissions. This allows you to restrict access to certain tools based on the user's role or specific permissions. ```js // Create a wrapper function to check permissions function requirePermission(permission, handler) { return async (request, context) => { // Check if user has the required permission const userPermissions = context.props.permissions || []; if (!userPermissions.includes(permission)) { return { content: [{ type: "text", text: `Permission denied: requires ${permission}` }], status: 403 }; } // If permission check passes, execute the handler return handler(request, context); }; } // Use the wrapper with your MCP tools async init() { // Basic tools available to all authenticated users this.server.tool("basicTool", "Available to all users", {}, async () => { // Implementation for all users }); // Protected tool using the permission wrapper this.server.tool( "adminAction", "Administrative action requiring special permission", { /* parameters */ }, requirePermission("admin", async (req) => { // Only executes if user has "admin" permission return { content: [{ type: "text", text: "Admin action completed" }] }; }) ); // Conditionally register tools based on user permissions if (this.props.permissions?.includes("special_feature")) { this.server.tool("specialTool", "Special feature", {}, async () => { // This tool only appears for users with the special_feature permission }); } } ``` Benefits: - Authorization check at the tool level ensures proper access control - Allows you to define permission checks once and reuse them across tools - Provides clear feedback to users when permission is denied - Can choose to only present tools that the agent is able to call ## Next steps - [Learn how to use the Workers OAuth Provider Library](https://github.com/cloudflare/workers-oauth-provider) - Learn how to use a third-party OAuth provider, using the [GitHub](/agents/guides/remote-mcp-server/#add-authentication) example MCP server. --- # Model Context Protocol (MCP) URL: https://developers.cloudflare.com/agents/model-context-protocol/ 您可以在 Cloudflare 上构建和部署 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器。 ## 什么是 Model Context Protocol (MCP)? [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 是一个开放标准,用于连接 AI 系统与外部应用程序。可以将 MCP 想象成 AI 应用程序的 USB-C 接口。正如 USB-C 提供了一种标准化的方式来连接您的设备与各种配件一样,MCP 提供了一种标准化的方式来连接 AI Agents 与不同的服务。 ### MCP 术语 - **MCP Hosts**: AI 助手(如 [Claude](http://claude.ai) 或 [Cursor](http://cursor.com))、AI Agents 或需要访问外部功能的应用程序。 - **MCP Clients**: 嵌入在 MCP Hosts 中的客户端,连接到 MCP 服务器并调用工具。每个 MCP 客户端实例与单个 MCP 服务器建立连接。 - **MCP Servers**: 暴露[工具](/agents/model-context-protocol/tools/)、[提示词](https://modelcontextprotocol.io/docs/concepts/prompts)和[资源](https://modelcontextprotocol.io/docs/concepts/resources)供 MCP 客户端使用的应用程序。 ### 远程 vs. 本地 MCP 连接 MCP 标准支持两种操作模式: - **远程 MCP 连接**: MCP 客户端通过互联网连接到 MCP 服务器,[使用 HTTP 和 Server-Sent Events (SSE) 建立长连接](/agents/model-context-protocol/transport/),并使用 [OAuth](/agents/model-context-protocol/authorization/) 授权 MCP 客户端访问用户账户上的资源。 - **本地 MCP 连接**: MCP 客户端连接到同一台机器上的 MCP 服务器,使用 [stdio](https://spec.modelcontextprotocol.io/specification/draft/basic/transports/#stdio) 作为本地传输方法。 ### 最佳实践 - **工具设计**: 不要将您的 MCP 服务器视为完整 API 架构的包装器。相反,构建针对特定用户目标和可靠结果优化的工具。更少但设计良好的工具往往比许多细粒度的工具表现更好,特别是对于上下文窗口较小或延迟预算紧张的 Agents。 - **范围权限**: 部署几个专注的 MCP 服务器,每个都具有严格范围的权限,可以减少过度特权访问的风险,并使管理和审计每个服务器允许做什么变得更容易。 - **工具描述**: 详细的参数描述帮助 Agents 理解如何正确使用您的工具——包括期望的值、它们如何影响行为以及任何重要约束。这减少了错误并提高了可靠性。 - **评估测试**: 使用评估测试('evals')来衡量 Agent 正确使用您工具的能力。在对服务器或工具描述进行任何更新后运行这些测试,以便及早发现回归并跟踪改进情况。 ### 开始使用 前往[快速开始](/agents/guides/remote-mcp-server/) 指南,了解如何构建和部署您的第一个远程 MCP 服务器到 Cloudflare。 --- # 工具 URL: https://developers.cloudflare.com/agents/model-context-protocol/tools/ import { Render, TypeScriptExample } from "~/components"; Model Context Protocol (MCP) 工具是 [MCP 服务器](/agents/model-context-protocol)提供且 MCP 客户端可以调用的函数。 当您使用 `@cloudflare/model-context-protocol` 包构建 MCP 服务器时,您可以[按照 `@modelcontextprotocol/typescript-sdk` 包示例中显示的相同方式](https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#tools)定义工具。 例如,来自[此示例 MCP 服务器](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-server)的以下代码定义了一个将两个数字相加的简单 MCP 服务器: ```ts title="src/index.ts" import { McpServer } from "@modelcontextprotocol/sdk/server/mcp"; import { McpAgent } from "agents/mcp"; export class MyMCP extends McpAgent { server = new McpServer({ name: "Demo", version: "1.0.0" }); async init() { this.server.tool( "add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }], }), ); } } ``` --- # Cloudflare 自有的 MCP 服务器 URL: https://developers.cloudflare.com/agents/model-context-protocol/mcp-servers-for-cloudflare/ import { Render } from "~/components"; Cloudflare 运行着一系列托管的远程 MCP 服务器目录,您可以在 [Claude](https://modelcontextprotocol.io/quickstart/user)、[Windsurf](https://docs.windsurf.com/windsurf/cascade/mcp)、我们自己的 [AI Playground](https://playground.ai.cloudflare.com/) 或任何[支持 MCP 的 SDK](https://github.com/cloudflare/agents/tree/main/packages/agents/src/mcp) 等客户端上使用 OAuth 进行连接。 这些 MCP 服务器允许您的 MCP 客户端从您的账户读取配置、处理信息、基于数据提出建议,甚至为您做出这些建议的更改。所有这些操作都可以跨 Cloudflare 的众多服务进行,包括应用程序开发、安全和性能。 | 服务器名称 | 描述 | 服务器 URL | | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------- | | [文档服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/docs-vectorize) | 获取 Cloudflare 的最新参考信息 | `https://docs.mcp.cloudflare.com/sse` | | [Workers Bindings 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/workers-bindings) | 使用存储、AI 和计算原语构建 Workers 应用程序 | `https://bindings.mcp.cloudflare.com/sse` | | [Workers Builds 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/workers-builds) | 获取洞察并管理您的 Cloudflare Workers Builds | `https://builds.mcp.cloudflare.com/sse` | | [可观测性服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/workers-observability) | 调试并深入了解您应用程序的日志和分析 | `https://observability.mcp.cloudflare.com/sse` | | [Radar 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/radar) | 获取全球互联网流量洞察、趋势、URL 扫描和其他实用工具 | `https://radar.mcp.cloudflare.com/sse` | | [容器服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/sandbox-container) | 启动沙盒开发环境 | `https://containers.mcp.cloudflare.com/sse` | | [浏览器渲染服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/browser-rendering) | 获取网页,将其转换为 markdown 并截图 | `https://browser.mcp.cloudflare.com/sse` | | [Logpush 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/logpush) | 获取 Logpush 作业健康状况的快速摘要 | `https://logs.mcp.cloudflare.com/sse` | | [AI Gateway 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/ai-gateway) | 搜索您的日志,获取有关提示和响应的详细信息 | `https://ai-gateway.mcp.cloudflare.com/sse` | | [AutoRAG 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/autorag) | 列出并搜索您的 AutoRAG 上的文档 | `https://autorag.mcp.cloudflare.com/sse` | | [审计日志服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/auditlogs) | 查询审计日志并生成报告供审查 | `https://auditlogs.mcp.cloudflare.com/sse` | | [DNS 分析服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/dns-analytics) | 基于当前设置优化 DNS 性能并调试问题 | `https://dns-analytics.mcp.cloudflare.com/sse` | | [数字体验监控服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/dex-analysis) | 快速了解您组织的关键应用程序 | `https://dex.mcp.cloudflare.com/sse` | | [Cloudflare One CASB 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/cloudflare-one-casb) | 快速识别 SaaS 应用程序的任何安全配置错误以保护用户和数据 | `https://casb.mcp.cloudflare.com/sse` | | [GraphQL 服务器](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/graphql/) | 使用 Cloudflare 的 GraphQL API 获取分析数据 | `https://graphql.mcp.cloudflare.com/sse` | 查看我们的 [GitHub 页面](https://github.com/cloudflare/mcp-server-cloudflare),了解如何在不同的 MCP 客户端中使用 Cloudflare 的远程 MCP 服务器。 --- # 传输 URL: https://developers.cloudflare.com/agents/model-context-protocol/transport/ import { Render } from "~/components"; import { TabItem, Tabs } from "~/components"; Model Context Protocol (MCP) 规范定义了三种标准的[传输机制](https://spec.modelcontextprotocol.io/specification/draft/basic/transports/),用于客户端和服务器之间的通信: 1. **stdio,通过标准输入和标准输出进行通信** — 专为本地 MCP 连接设计。 2. **Server-Sent Events (SSE)** — 目前被大多数远程 MCP 客户端支持,但预计随时间推移将被 Streamable HTTP 替代。它需要两个端点:一个用于发送请求,另一个用于接收流式响应。 3. **Streamable HTTP** — 2025年3月[引入](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http)的新传输方法。它通过使用单个 HTTP 端点进行双向消息传递来简化通信。目前在远程 MCP 客户端中正在获得采用,预计将成为未来的标准传输方式。 使用 [Agents SDK](/agents) 构建的 MCP 服务器可以支持两种远程传输方法(SSE 和 Streamable HTTP),[`McpAgent` 类](https://github.com/cloudflare/agents/blob/2f82f51784f4e27292249747b5fbeeef94305552/packages/agents/src/mcp.ts)会自动处理传输配置。 ## 实现远程 MCP 传输 如果您正在构建新的 MCP 服务器或在 Cloudflare 上升级现有服务器,我们建议同时支持两种远程传输方法(SSE 和 Streamable HTTP),以确保与所有 MCP 客户端的兼容性。 #### 快速开始 您可以使用 "Deploy to Cloudflare" 按钮创建一个自动支持 SSE 和 Streamable HTTP 传输方法的远程 MCP 服务器。 [![Deploy to Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless) #### 远程 MCP 服务器(无身份验证) 如果您手动配置 MCP 服务器,以下是如何使用 `McpAgent` 类来处理两种传输方法: ```js export default { fetch(request: Request, env: Env, ctx: ExecutionContext) { const { pathname } = new URL(request.url); if (pathname.startsWith('/sse')) { return MyMcpAgent.serveSSE('/sse').fetch(request, env, ctx); } if (pathname.startsWith('/mcp')) { return MyMcpAgent.serve('/mcp').fetch(request, env, ctx); } }, }; ``` ```ts export default { fetch( request: Request, env: Env, ctx: ExecutionContext, ): Response | Promise { const { pathname } = new URL(request.url); if (pathname.startsWith("/sse")) { return MyMcpAgent.serveSSE("/sse").fetch(request, env, ctx); } if (pathname.startsWith("/mcp")) { return MyMcpAgent.serve("/mcp").fetch(request, env, ctx); } // Handle case where no path matches return new Response("Not found", { status: 404 }); }, }; ``` ```ts const app = new Hono() app.mount('/sse', MyMCP.serveSSE('/sse').fetch, { replaceRequest: false }) app.mount('/mcp', MyMCP.serve('/mcp').fetch, { replaceRequest: false ) export default app ``` #### 带身份验证的 MCP 服务器 如果您的 MCP 服务器使用 [Workers OAuth Provider](https://github.com/cloudflare/workers-oauth-provider) 库实现身份验证和授权,那么您可以使用 `apiHandlers` 属性将其配置为支持两种传输方法。 ```js export default new OAuthProvider({ apiHandlers: { "/sse": MyMCP.serveSSE("/sse"), "/mcp": MyMCP.serve("/mcp"), }, // ... other OAuth configuration }); ``` ### 升级现有的远程 MCP 服务器 如果您已经使用 Cloudflare Agents SDK 构建了远程 MCP 服务器,请进行以下更改以支持新的 Streamable HTTP 传输,同时保持与使用 SSE 的远程 MCP 客户端的兼容性: - 对现有的 SSE 传输使用 `MyMcpAgent.serveSSE('/sse')`。以前,这会是 `MyMcpAgent.mount('/sse')`,它已被保留作为别名。 - 使用 `MyMcpAgent.serve('/mcp')` 添加新路径以支持新的 Streamable HTTP 传输。 如果您有一个使用 Workers OAuth Provider 进行身份验证/授权的 MCP 服务器,请[更新配置](/agents/model-context-protocol/transport/#mcp-server-with-authentication)以使用 `apiHandlers` 属性,它替代了 `apiRoute` 和 `apiHandler`。 :::note 要使用 apiHandlers,请更新到 @cloudflare/workers-oauth-provider v0.0.4 或更高版本。 ::: 通过这些少量更改,您的 MCP 服务器将支持两种传输方法,使其与现有和新客户端兼容。 ### 使用 MCP 客户端进行测试 虽然大多数 MCP 客户端尚未采用新的 Streamable HTTP 传输,但您可以使用 [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) 立即开始测试,这是一个适配器,让原本只支持本地连接的 MCP 客户端可以与远程 MCP 服务器协作。 按照[此指南](/agents/guides/test-remote-mcp-server/)获取如何使用 [`mcp-remote` 本地代理](https://www.npmjs.com/package/mcp-remote)从 Claude Desktop、Cursor、Windsurf 和其他本地 MCP 客户端连接到远程 MCP 服务器的说明。 --- # 平台 URL: https://developers.cloudflare.com/agents/platform/ import { DirectoryListing } from "~/components"; --- # 限制 URL: https://developers.cloudflare.com/agents/platform/limits/ import { Render } from "~/components"; 适用于编写、部署和运行 Agents 的限制详细说明如下。 许多限制继承自应用于 Workers 脚本和/或 Durable Objects 的限制,详情请参见 [Workers 限制](/workers/platform/limits/) 文档。 | 功能 | 限制 | | ------------------------------------- | ------------------------------------------------------ | | 每个账户最大并发(运行中)Agents 数量 | 数千万个+ [^1] | | 每个账户最大定义数量 | ~250,000+ [^2] | | 每个唯一 Agent 最大存储状态 | 1 GB | | 每个 Agent 最大计算时间 | 30 秒(每次 HTTP 请求/传入 WebSocket 消息时刷新) [^3] | | 每步持续时间(实际时间) [^3] | 无限制(例如,等待数据库调用或 LLM 响应) | --- [^1]: 是的,真的。您可以同时运行数千万个 Agents,因为每个 Agent 都映射到一个[唯一的 Durable Object](/durable-objects/what-are-durable-objects/)(角色)。 [^2]: 您可以[每个账户部署最多 500 个脚本](/workers/platform/limits/),但每个脚本(项目)可以定义多个 Agents。在 [Workers 付费计划](/workers/platform/pricing/#workers) 上,每个部署的脚本最大可达 10 MB。 [^3]: 每个 Agent 的计算(CPU)时间限制为 30 秒,但当 Agent 接收到新的 HTTP 请求、运行[计划任务](/agents/api-reference/schedule-tasks/)或传入 WebSocket 消息时,这个时间会刷新。 ---