通过 WebSocket 或 HTTP,从任意 JavaScript runtime(browser、Node.js、Deno、Bun 或 edge function)连接 Agent。SDK 提供实时 state 同步、RPC 方法调用与流式响应。
客户端 SDK 提供两种 WebSocket 连接方式,以及一种 HTTP 请求方式。
| 客户端 | 用例 |
|---|---|
useAgent |
带自动重连与 state 管理的 React hook |
AgentClient |
适用于任意环境的原生 JavaScript/TypeScript 类 |
agentFetch |
不需要 WebSocket 时的 HTTP 请求 |
所有客户端提供:
- 双向 state 同步 — 实时推送与接收 state 更新
- RPC 调用 — 以类型化参数与返回值调用 Agent 方法
- 流式 — 处理 AI 补全的分块响应
- 自动重连 — 带指数退避的自动重连
import { useAgent } from "agents/react";
function Chat() {
const agent = useAgent({
agent: "ChatAgent",
name: "room-123",
onStateUpdate: (state) => {
console.log("New state:", state);
},
});
const sendMessage = async () => {
const response = await agent.call("sendMessage", ["Hello!"]);
console.log("Response:", response);
};
return <button onClick={sendMessage}>Send</button>;
}import { useAgent } from "agents/react";
function Chat() {
const agent = useAgent({
agent: "ChatAgent",
name: "room-123",
onStateUpdate: (state) => {
console.log("New state:", state);
},
});
const sendMessage = async () => {
const response = await agent.call("sendMessage", ["Hello!"]);
console.log("Response:", response);
};
return <button onClick={sendMessage}>Send</button>;
}import { AgentClient } from "agents/client";
const client = new AgentClient({
agent: "ChatAgent",
name: "room-123",
host: "your-worker.your-subdomain.workers.dev",
onStateUpdate: (state) => {
console.log("New state:", state);
},
});
// Call a method
const response = await client.call("sendMessage", ["Hello!"]);import { AgentClient } from "agents/client";
const client = new AgentClient({
agent: "ChatAgent",
name: "room-123",
host: "your-worker.your-subdomain.workers.dev",
onStateUpdate: (state) => {
console.log("New state:", state);
},
});
// Call a method
const response = await client.call("sendMessage", ["Hello!"]);agent 参数为 Agent 类名,自动从 camelCase 转为 kebab-case 用于 URL:
// These are equivalent:
useAgent({ agent: "ChatAgent" }); // → /agents/chat-agent/...
useAgent({ agent: "MyCustomAgent" }); // → /agents/my-custom-agent/...
useAgent({ agent: "LOUD_AGENT" }); // → /agents/loud-agent/...// These are equivalent:
useAgent({ agent: "ChatAgent" }); // → /agents/chat-agent/...
useAgent({ agent: "MyCustomAgent" }); // → /agents/my-custom-agent/...
useAgent({ agent: "LOUD_AGENT" }); // → /agents/loud-agent/...name 参数标识特定 Agent 实例。省略时默认为 "default":
// Connect to a specific chat room
useAgent({ agent: "ChatAgent", name: "room-123" });
// Connect to a user's personal agent
useAgent({ agent: "UserAgent", name: userId });
// Uses "default" instance
useAgent({ agent: "ChatAgent" });// Connect to a specific chat room
useAgent({ agent: "ChatAgent", name: "room-123" });
// Connect to a user's personal agent
useAgent({ agent: "UserAgent", name: userId });
// Uses "default" instance
useAgent({ agent: "ChatAgent" });useAgent 与 AgentClient 均接受连接选项:
useAgent({
agent: "ChatAgent",
name: "room-123",
// Connection settings
host: "my-worker.workers.dev", // Custom host (defaults to current origin)
path: "/custom/path", // Custom path prefix
// Query parameters (sent on connection)
query: {
token: "abc123",
version: "2",
},
// Event handlers
onOpen: () => console.log("Connected"),
onClose: () => console.log("Disconnected"),
onError: (error) => console.error("Error:", error),
});useAgent({
agent: "ChatAgent",
name: "room-123",
// Connection settings
host: "my-worker.workers.dev", // Custom host (defaults to current origin)
path: "/custom/path", // Custom path prefix
// Query parameters (sent on connection)
query: {
token: "abc123",
version: "2",
},
// Event handlers
onOpen: () => console.log("Connected"),
onClose: () => console.log("Disconnected"),
onError: (error) => console.error("Error:", error),
});认证 token 或其他异步数据请传入返回 Promise 的函数:
useAgent({
agent: "ChatAgent",
name: "room-123",
// Async query - called before connecting
query: async () => {
const token = await getAuthToken();
return { token };
},
// Dependencies that trigger re-fetching the query
queryDeps: [userId],
// Cache TTL for the query result (default: 5 minutes)
cacheTtl: 60 * 1000, // 1 minute
});useAgent({
agent: "ChatAgent",
name: "room-123",
// Async query - called before connecting
query: async () => {
const token = await getAuthToken();
return { token };
},
// Dependencies that trigger re-fetching the query
queryDeps: [userId],
// Cache TTL for the query result (default: 5 minutes)
cacheTtl: 60 * 1000, // 1 minute
});query 函数会被缓存,仅在以下情况重新调用:
queryDeps变化cacheTtl过期- WebSocket 连接关闭(自动缓存失效)
- 组件重新挂载
Agent 可维护与所有已连接客户端双向同步的 state。
useAgent 与 AgentClient 均暴露反映当前 Agent state 的 state 属性。在收到服务端第一条 state 消息前为 undefined。
const agent = useAgent({ agent: "GameAgent", name: "game-123" });
// Read the current state at any time
console.log("Current score:", agent.state?.score);const agent = useAgent({ agent: "GameAgent", name: "game-123" });
// Read the current state at any time
console.log("Current score:", agent.state?.score);使用 useAgent 时状态更新触发 React 重渲染,agent.state 在 JSX 中始终为最新值。使用 AgentClient 时,每次收到服务端广播或 setState 调用会同步更新 state 字段。
const agent = useAgent({
agent: "GameAgent",
name: "game-123",
onStateUpdate: (state, source) => {
// state: The new state from the agent
// source: "server" (agent pushed) or "client" (you pushed)
console.log(`State updated from ${source}:`, state);
setGameState(state);
},
});const agent = useAgent({
agent: "GameAgent",
name: "game-123",
onStateUpdate: (state, source) => {
// state: The new state from the agent
// source: "server" (agent pushed) or "client" (you pushed)
console.log(`State updated from ${source}:`, state);
setGameState(state);
},
});// Update the agent's state from the client
agent.setState({ score: 100, level: 5 });// Update the agent's state from the client
agent.setState({ score: 100, level: 5 });调用 setState() 时:
- 状态经 WebSocket 发送到 Agent
- 调用 Agent 的
onStateChanged() - Agent 向所有已连接客户端广播新状态
onStateUpdate回调以source: "client"触发
sequenceDiagram
participant Client
participant Agent
Client->>Agent: setState()
Agent-->>Client: onStateUpdate (broadcast)
调用 Agent 上以 @callable() 装饰的方法。
// Basic call
const result = await agent.call("getUser", [userId]);
// Call with multiple arguments
const result = await agent.call("createPost", [title, content, tags]);
// Call with no arguments
const result = await agent.call("getStats");// Basic call
const result = await agent.call("getUser", [userId]);
// Call with multiple arguments
const result = await agent.call("createPost", [title, content, tags]);
// Call with no arguments
const result = await agent.call("getStats");stub 属性提供更简洁的方法调用语法:
// Instead of:
const user = await agent.call("getUser", ["user-123"]);
// You can write:
const user = await agent.stub.getUser("user-123");
// Multiple arguments work naturally:
const post = await agent.stub.createPost(title, content, tags);// Instead of:
const user = await agent.call("getUser", ["user-123"]);
// You can write:
const user = await agent.stub.getUser("user-123");
// Multiple arguments work naturally:
const post = await agent.stub.createPost(title, content, tags);要完整类型安全,将 Agent 类作为类型参数传入:
const agent = useAgent({
agent: "MyAgent",
name: "instance-1",
});
// Now stub methods are fully typed
const result = await agent.stub.processData({ input: "test" });import type { MyAgent } from "./agents/my-agent";
const agent = useAgent<MyAgent>({
agent: "MyAgent",
name: "instance-1",
});
// Now stub methods are fully typed
const result = await agent.stub.processData({ input: "test" });对返回 StreamingResponse 的方法,在 chunk 到达时处理:
// Agent-side:
class MyAgent extends Agent {
@callable({ streaming: true })
async generateText(stream, prompt) {
for await (const chunk of llm.stream(prompt)) {
await stream.write(chunk);
}
}
}
// Client-side:
await agent.call("generateText", [prompt], {
onChunk: (chunk) => {
// Called for each chunk
appendToOutput(chunk);
},
onDone: (finalResult) => {
// Called when stream completes
console.log("Complete:", finalResult);
},
onError: (error) => {
// Called if streaming fails
console.error("Stream error:", error);
},
});// Agent-side:
class MyAgent extends Agent {
@callable({ streaming: true })
async generateText(stream: StreamingResponse, prompt: string) {
for await (const chunk of llm.stream(prompt)) {
await stream.write(chunk);
}
}
}
// Client-side:
await agent.call("generateText", [prompt], {
onChunk: (chunk) => {
// Called for each chunk
appendToOutput(chunk);
},
onDone: (finalResult) => {
// Called when stream completes
console.log("Complete:", finalResult);
},
onError: (error) => {
// Called if streaming fails
console.error("Stream error:", error);
},
});无需维持 WebSocket 连接的一次性请求:
import { agentFetch } from "agents/client";
// GET request
const response = await agentFetch({
agent: "DataAgent",
name: "instance-1",
host: "my-worker.workers.dev",
});
const data = await response.json();
// POST request with body
const response = await agentFetch(
{
agent: "DataAgent",
name: "instance-1",
host: "my-worker.workers.dev",
},
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "process" }),
},
);import { agentFetch } from "agents/client";
// GET request
const response = await agentFetch({
agent: "DataAgent",
name: "instance-1",
host: "my-worker.workers.dev",
});
const data = await response.json();
// POST request with body
const response = await agentFetch(
{
agent: "DataAgent",
name: "instance-1",
host: "my-worker.workers.dev",
},
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "process" }),
},
);何时使用 agentFetch 与 WebSocket:
使用 agentFetch |
使用 useAgent/AgentClient |
|---|---|
| 一次性请求 | 需要实时更新 |
| 服务端到服务端调用 | 双向通信 |
| 简单 REST 风格 API | State 同步 |
| 无需持久连接 | 多次 RPC 调用 |
若 Agent 使用 MCP(Model Context Protocol)server,可接收其 state 更新:
const agent = useAgent({
agent: "AssistantAgent",
name: "session-123",
onMcpUpdate: (mcpServers) => {
// mcpServers is a record of server states
for (const [serverId, server] of Object.entries(mcpServers)) {
console.log(`${serverId}: ${server.connectionState}`);
console.log(`Tools: ${server.tools?.map((t) => t.name).join(", ")}`);
}
},
});const agent = useAgent({
agent: "AssistantAgent",
name: "session-123",
onMcpUpdate: (mcpServers) => {
// mcpServers is a record of server states
for (const [serverId, server] of Object.entries(mcpServers)) {
console.log(`${serverId}: ${server.connectionState}`);
console.log(`Tools: ${server.tools?.map((t) => t.name).join(", ")}`);
}
},
});const agent = useAgent({
agent: "MyAgent",
onError: (error) => {
console.error("WebSocket error:", error);
},
onClose: () => {
console.log("Connection closed, will auto-reconnect...");
},
});const agent = useAgent({
agent: "MyAgent",
onError: (error) => {
console.error("WebSocket error:", error);
},
onClose: () => {
console.log("Connection closed, will auto-reconnect...");
},
});try {
const result = await agent.call("riskyMethod", [data]);
} catch (error) {
// Error thrown by the agent method
console.error("RPC failed:", error.message);
}try {
const result = await agent.call("riskyMethod", [data]);
} catch (error) {
// Error thrown by the agent method
console.error("RPC failed:", error.message);
}await agent.call("streamingMethod", [data], {
onChunk: (chunk) => handleChunk(chunk),
onError: (errorMessage) => {
// Stream-specific error handling
console.error("Stream error:", errorMessage);
},
});await agent.call("streamingMethod", [data], {
onChunk: (chunk) => handleChunk(chunk),
onError: (errorMessage) => {
// Stream-specific error handling
console.error("Stream error:", errorMessage);
},
});// Prefer this:
const user = await agent.stub.getUser(id);
// Over this:
const user = await agent.call("getUser", [id]);// Prefer this:
const user = await agent.stub.getUser(id);
// Over this:
const user = await agent.call("getUser", [id]);客户端自动重连,Agent 在每次连接时自动发送当前 state。onStateUpdate 会以最新 state 触发——无需手动重新同步。若对认证使用异步 query 函数,断开时缓存自动失效,确保重连时获取新 token。
// For auth tokens that expire hourly:
useAgent({
query: async () => ({ token: await getToken() }),
cacheTtl: 55 * 60 * 1000, // Refresh 5 min before expiry
queryDeps: [userId], // Refresh if user changes
});// For auth tokens that expire hourly:
useAgent({
query: async () => ({ token: await getToken() }),
cacheTtl: 55 * 60 * 1000, // Refresh 5 min before expiry
queryDeps: [userId], // Refresh if user changes
});原生 JS 中完成后关闭连接:
const client = new AgentClient({ agent: "MyAgent", host: "..." });
// When done:
client.close();const client = new AgentClient({ agent: "MyAgent", host: "..." });
// When done:
client.close();React 的 useAgent 在卸载时自动清理。
type UseAgentOptions<State> = {
// Required
agent: string; // Agent class name
// Optional
name?: string; // Instance name (default: "default")
host?: string; // Custom host
path?: string; // Custom path prefix
// Query parameters
query?: Record<string, string> | (() => Promise<Record<string, string>>);
queryDeps?: unknown[]; // Dependencies for async query
cacheTtl?: number; // Query cache TTL in ms (default: 5 min)
// Callbacks
onStateUpdate?: (state: State, source: "server" | "client") => void;
onMcpUpdate?: (mcpServers: MCPServersState) => void;
onOpen?: () => void;
onClose?: () => void;
onError?: (error: Event) => void;
onMessage?: (message: MessageEvent) => void;
};useAgent hook 返回包含以下属性与方法的对象:
| Property/Method | 类型 | 描述 |
|---|---|---|
agent |
string |
kebab-case Agent 名 |
name |
string |
实例名 |
setState(state) |
void |
向 Agent 推送 state |
call(method, args?, options?) |
Promise |
调用 Agent 方法 |
stub |
Proxy |
类型化方法调用 |
send(data) |
void |
发送原始 WebSocket 消息 |
close() |
void |
关闭连接 |
reconnect() |
void |
强制重连 |
type AgentClientOptions<State> = {
// Required
agent: string; // Agent class name
host: string; // Worker host
// Optional
name?: string; // Instance name (default: "default")
path?: string; // Custom path prefix
query?: Record<string, string>;
// Callbacks
onStateUpdate?: (state: State, source: "server" | "client") => void;
};| Property/Method | 类型 | 描述 |
|---|---|---|
agent |
string |
kebab-case Agent 名 |
name |
string |
实例名 |
setState(state) |
void |
向 Agent 推送 state |
call(method, args?, options?) |
Promise |
调用 Agent 方法 |
send(data) |
void |
发送原始 WebSocket 消息 |
close() |
void |
关闭连接 |
reconnect() |
void |
强制重连 |
客户端还支持 WebSocket 事件监听器:
client.addEventListener("open", () => {});
client.addEventListener("close", () => {});
client.addEventListener("error", () => {});
client.addEventListener("message", () => {});client.addEventListener("open", () => {});
client.addEventListener("close", () => {});
client.addEventListener("error", () => {});
client.addEventListener("message", () => {});若聊天 UI 渲染 Agent 作为工具 的保留子运行,与 useAgent()、useAgentChat() 一起使用 useAgentToolEvents()。该 hook 订阅父连接、重放保留的子时间线,并按父 tool 调用 ID 分组运行。
import { useAgent, useAgentToolEvents } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
const agent = useAgent({ agent: "Assistant", name: userId });
const { messages } = useAgentChat({ agent });
const agentTools = useAgentToolEvents({ agent });import { useAgent, useAgentToolEvents } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
const agent = useAgent({ agent: "Assistant", name: userId });
const { messages } = useAgentChat({ agent });
const agentTools = useAgentToolEvents({ agent });