ブラウザー、Node.js、Deno、Bun、エッジ関数など、任意の JavaScript ランタイムから、WebSockets または HTTP でエージェントに接続します。SDK はリアルタイムの状態同期、RPC メソッド呼び出し、ストリーミング応答を提供します。
クライアント SDK は、WebSocket 接続でつなぐ 2 つの方法と、HTTP リクエストを送る 1 つの方法を提供します。
| クライアント | 用途 |
|---|---|
useAgent |
自動再接続と状態管理付きの React フック |
AgentClient |
任意の環境向けのバニラ JavaScript / TypeScript クラス |
agentFetch |
WebSocket が不要なときの HTTP リクエスト |
すべてのクライアントが提供するもの:
- 双方向の状態同期 - 状態更新をリアルタイムで送信・受信します
- RPC 呼び出し - 型付きの引数と戻り値でエージェントメソッドを呼び出します
- ストリーミング - 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 パラメーターはエージェントクラス名です。URL 用に camelCase から kebab-case へ自動変換されます。
// 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 パラメーターは特定のエージェントインスタンスを識別します。省略すると "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),
});認証トークンやその他の非同期データには、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
});クエリ関数はキャッシュされ、次のときにだけ再呼び出しされます。
queryDepsが変わるcacheTtlが期限切れになる- WebSocket 接続が閉じる(自動キャッシュ無効化)
- コンポーネントが再マウントされる
エージェントは、接続中のすべてのクライアントと双方向に同期する状態を保持できます。
useAgent と AgentClient の両方は、現在のエージェント状態を反映する 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 の再レンダーを起こすため、JSX 内の agent.state は常に最新値を反映します。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 経由でエージェントへ送られます
- エージェントの
onStateChanged()メソッドが呼ばれます - エージェントが新しい状態を接続中のすべてのクライアントへブロードキャストします
onStateUpdateコールバックがsource: "client"で発火します
sequenceDiagram
participant Client
participant Agent
Client->>Agent: setState()
Agent-->>Client: onStateUpdate (broadcast)
@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 を渡します。
// Agent-side
import { Agent, callable } from "agents";
class MyAgent extends Agent {
@callable({ streaming: true })
async generateText(stream, prompt) {
for await (const chunk of llm.stream(prompt)) {
stream.send(chunk);
}
stream.end();
}
}
// 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
import { Agent, callable, type StreamingResponse } from "agents";
class MyAgent extends Agent {
@callable({ streaming: true })
async generateText(stream: StreamingResponse, prompt: string) {
for await (const chunk of llm.stream(prompt)) {
stream.send(chunk);
}
stream.end();
}
}
// 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 | 状態同期 |
| 永続接続が不要 | 複数の RPC 呼び出し |
エージェントが MCP(Model Context Protocol)サーバーを使う場合、その状態に関する更新を受け取れます。
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]);クライアントは自動再接続し、エージェントは各接続時に現在の状態を自動送信します。onStateUpdate コールバックは最新状態で発火します。手動の再同期は不要です。認証に非同期の 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
});バニラ 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 フックは、次のプロパティとメソッドを持つオブジェクトを返します。
| プロパティ / メソッド | 型 | 説明 |
|---|---|---|
agent |
string |
kebab-case のエージェント名 |
name |
string |
インスタンス名 |
setState(state) |
void |
状態をエージェントへ送信します |
call(method, args?, options?) |
Promise |
エージェントメソッドを呼び出します |
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;
};| プロパティ / メソッド | 型 | 説明 |
|---|---|---|
agent |
string |
kebab-case のエージェント名 |
name |
string |
インスタンス名 |
setState(state) |
void |
状態をエージェントへ送信します |
call(method, args?, options?) |
Promise |
エージェントメソッドを呼び出します |
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", () => {});AgentClient 接続は、任意のフレームワークの AI SDK チャット UI を駆動できます。agents/chat/transport は WebSocketChatTransport をエクスポートし、React のピア依存関係は不要です。
import { AgentClient } from "agents/client";
import { WebSocketChatTransport } from "agents/chat/transport";
const transport = new WebSocketChatTransport({
agent: new AgentClient({
agent: "ChatAgent",
name: "user-123",
host: window.location.host,
}),
});import { AgentClient } from "agents/client";
import { WebSocketChatTransport } from "agents/chat/transport";
const transport = new WebSocketChatTransport({
agent: new AgentClient({
agent: "ChatAgent",
name: "user-123",
host: window.location.host,
}),
});このトランスポートが扱う動作と扱わない動作は 非 React クライアント を参照してください。
チャット UI が Agents as tools からの保持された子ランを描画する場合は、useAgent() と useAgentChat() と並べて useAgentToolEvents() を使います。フックは親接続を購読し、保持された子タイムラインを再生し、親のツール呼び出し 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 });