Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Client SDK

最終更新 Markdown で表示Agent セットアップ

ブラウザー、Node.js、Deno、Bun、エッジ関数など、任意の JavaScript ランタイムから、WebSockets または HTTP でエージェントに接続します。SDK はリアルタイムの状態同期、RPC メソッド呼び出し、ストリーミング応答を提供します。

概要

クライアント SDK は、WebSocket 接続でつなぐ 2 つの方法と、HTTP リクエストを送る 1 つの方法を提供します。

クライアント 用途
useAgent 自動再接続と状態管理付きの React フック
AgentClient 任意の環境向けのバニラ JavaScript / TypeScript クラス
agentFetch WebSocket が不要なときの HTTP リクエスト

すべてのクライアントが提供するもの:

  • 双方向の状態同期 - 状態更新をリアルタイムで送信・受信します
  • RPC 呼び出し - 型付きの引数と戻り値でエージェントメソッドを呼び出します
  • ストリーミング - 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 パラメーターはエージェントクラス名です。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" });

接続オプション

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

非同期クエリパラメーター

認証トークンやその他の非同期データには、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 接続が閉じる(自動キャッシュ無効化)
  • コンポーネントが再マウントされる

状態同期

エージェントは、接続中のすべてのクライアントと双方向に同期する状態を保持できます。

現在の状態を読む

useAgentAgentClient の両方は、現在のエージェント状態を反映する 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() を呼び出すと:

  1. 状態が WebSocket 経由でエージェントへ送られます
  2. エージェントの onStateChanged() メソッドが呼ばれます
  3. エージェントが新しい状態を接続中のすべてのクライアントへブロードキャストします
  4. onStateUpdate コールバックが source: "client" で発火します

状態の流れ

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

エージェントメソッドの呼び出し(RPC)

@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 を渡します。

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

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 状態同期
永続接続が不要 複数の RPC 呼び出し

MCP サーバー連携

エージェントが 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...");
	},
});

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. 再接続は自動です

クライアントは自動再接続し、エージェントは各接続時に現在の状態を自動送信します。onStateUpdate コールバックは最新状態で発火します。手動の再同期は不要です。認証に非同期の query 関数を使う場合、切断時にキャッシュが自動無効化され、再接続時に新しいトークンが取得されます。

3. クエリキャッシュを最適化する

// 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 フックは、次のプロパティとメソッドを持つオブジェクトを返します。

プロパティ / メソッド 説明
agent string kebab-case のエージェント名
name string インスタンス名
setState(state) void 状態をエージェントへ送信します
call(method, args?, options?) Promise エージェントメソッドを呼び出します
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 のメソッド

プロパティ / メソッド 説明
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", () => {});

非 React クライアントからのチャット

AgentClient 接続は、任意のフレームワークの AI SDK チャット UI を駆動できます。agents/chat/transportWebSocketChatTransport をエクスポートし、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 });

次のステップ

役に立ちましたか?