跳转到内容
搜索文档

客户端 SDK

最后更新 查看 MarkdownAgent 设置

通过 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 补全的分块响应
  • 自动重连 — 带指数退避的自动重连

快速入门

React

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>;
}

原生 JavaScript

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 命名

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" });

连接选项

useAgentAgentClient 均接受连接选项:

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),
});

异步 query 参数

认证 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。

读取当前 state

useAgentAgentClient 均暴露反映当前 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);
	},
});

推送 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() 时:

  1. 状态经 WebSocket 发送到 Agent
  2. 调用 Agent 的 onStateChanged()
  3. Agent 向所有已连接客户端广播新状态
  4. onStateUpdate 回调以 source: "client" 触发

State 流转

sequenceDiagram
    participant Client
    participant Agent
    Client->>Agent: setState()
    Agent-->>Client: onStateUpdate (broadcast)

调用 Agent 方法(RPC)

调用 Agent 上以 @callable() 装饰的方法。

使用 call()

// 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 代理

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);

TypeScript 集成

要完整类型安全,将 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);
	},
});

使用 agentFetch 发起 HTTP 请求

无需维持 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 调用

MCP 服务器集成

若 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...");
	},
});

RPC 错误

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);
	},
});

最佳实践

1. 使用类型化 stub

// 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]);

2. 重连自动进行

客户端自动重连,Agent 在每次连接时自动发送当前 state。onStateUpdate 会以最新 state 触发——无需手动重新同步。若对认证使用异步 query 函数,断开时缓存自动失效,确保重连时获取新 token。

3. 优化 query 缓存

// 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
});

4. 清理连接

原生 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 在卸载时自动清理。

React 钩子参考

UseAgentOptions

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 强制重连

原生 JS 参考

AgentClientOptions

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;
};

AgentClient 方法

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", () => {});

Agent-tool 事件

若聊天 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 });

后续步骤

路由

URL 模式与自定义路由选项。

这篇文档对您有帮助吗?