building-ai-agent-on-cloudflare について
Cloudflare Agents SDK でステートフルな AI エージェントを構築するためのドキュメント参照スキルです。SDK、Workers、プラットフォームの最新資料を確認し、状態管理、リアルタイム接続、定期タスク、チャットエージェントのパターンを案内します。
主な機能
- 01最新の SDK とプラットフォーム資料を案内
- 02エージェントの永続状態を扱う
- 03WebSocket と HTTP 通信を解説
- 04定期タスクとチャットエージェントのパターン
- 05サンプルとトラブルシューティングを案内
活用シーン
- 01ステートフルなチャットボットの構築
- 02定期またはバックグラウンドのエージェントタスク追加
- 03WebSocket ベースのエージェント通信の実装
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ cloudflare/ skills --skill building-ai-agent-on-cloudflareインストール後、Claude Code で/building-ai-agent-on-cloudflareを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/agent-patterns.mdexamples.mdstate-patterns.mdtroubleshooting.md詳細
Cloudflare エージェントの構築
Agents SDK に関する情報が古くなっている可能性があります。エージェント構築のタスクでは、事前学習済みの知識よりも情報の取得を優先してください。
情報の取得元
| 情報源 | 取得方法 | 用途 |
|---|---|---|
| Agents SDK ドキュメント | https://github.com/cloudflare/agents/tree/main/docs | SDK API、状態、ルーティング、スケジューリング |
| Cloudflare Agents ドキュメント | https://developers.cloudflare.com/agents/ | プラットフォーム統合、デプロイ |
| Workers ドキュメント | 検索ツールまたは https://developers.cloudflare.com/workers/ | ランタイム API、バインディング、設定 |
使用するタイミング
- ユーザーが AI エージェントやチャットボットの構築を希望している
- ユーザーがステートフルなリアルタイム AI インタラクションを必要としている
- ユーザーが Cloudflare Agents SDK について質問している
- ユーザーがスケジュールされたタスクやバックグラウンドでの AI 処理を希望している
- ユーザーが WebSocket ベースの AI 通信を必要としている
前提条件
- Workers が有効な Cloudflare アカウント
- Node.js 18 以降と npm/pnpm/yarn
- Wrangler CLI(
npm install -g wrangler)
クイックスタート
npm create cloudflare@latest -- my-agent --template=cloudflare/agents-starter
cd my-agent
npm start
エージェントは http://localhost:8787 で実行されます。
基本概念
エージェントとは?
エージェントは、次の機能を備えたステートフルで永続的な AI サービスです。
- リクエストや再接続をまたいで状態を維持する
- WebSocket または HTTP で通信する
- Durable Objects を介して Cloudflare のエッジで動作する
- タスクをスケジュールし、ツールを呼び出す
- 水平スケーリングする(各ユーザー/セッションに独自のインスタンスが割り当てられます)
エージェントのライフサイクル
Client connects → Agent.onConnect() → Agent processes messages
→ Agent.onMessage()
→ Agent.setState() (persists + syncs)
Client disconnects → State persists → Client reconnects → State restored
基本的なエージェント構成
import { Agent, Connection } from "agents";
interface Env {
AI: Ai; // Workers AI binding
}
interface State {
messages: Array<{ role: string; content: string }>;
preferences: Record<string, string>;
}
export class MyAgent extends Agent<Env, State> {
// Initial state for new instances
initialState: State = {
messages: [],
preferences: {},
};
// Called when agent starts or resumes
async onStart() {
console.log("Agent started with state:", this.state);
}
// Handle WebSocket connections
async onConnect(connection: Connection) {
connection.send(JSON.stringify({
type: "welcome",
history: this.state.messages,
}));
}
// Handle incoming messages
async onMessage(connection: Connection, message: string) {
const data = JSON.parse(message);
if (data.type === "chat") {
await this.handleChat(connection, data.content);
}
}
// Handle disconnections
async onClose(connection: Connection) {
console.log("Client disconnected");
}
// React to state changes
onStateUpdate(state: State, source: string) {
console.log("State updated by:", source);
}
private async handleChat(connection: Connection, userMessage: string) {
// Add user message to history
const messages = [
...this.state.messages,
{ role: "user", content: userMessage },
];
// Call AI
const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages,
});
// Update state (persists and syncs to all clients)
this.setState({
...this.state,
messages: [
...messages,
{ role: "assistant", content: response.response },
],
});
// Send response
connection.send(JSON.stringify({
type: "response",
content: response.response,
}));
}
}
エントリーポイントの設定
// src/index.ts
import { routeAgentRequest } from "agents";
import { MyAgent } from "./agent";
export default {
async fetch(request: Request, env: Env) {
// routeAgentRequest handles routing to /agents/:class/:name
return (
(await routeAgentRequest(request, env)) ||
new Response("Not found", { status: 404 })
);
},
};
export { MyAgent };
クライアントは次の URL で接続します:wss://my-agent.workers.dev/agents/MyAgent/session-id
Wrangler の設定
{
"name": "my-agent",
"main": "src/index.ts",
"compatibility_date": "2024-12-01",
"ai": { "binding": "AI" },
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }]
}
状態管理
状態の読み取り
// Current state is always available
const currentMessages = this.state.messages;
const userPrefs = this.state.preferences;
状態の更新
// setState persists AND syncs to all connected clients
this.setState({
...this.state,
messages: [...this.state.messages, newMessage],
});
// Partial updates work too
this.setState({
preferences: { ...this.state.preferences, theme: "dark" },
});
SQL ストレージ
複雑なクエリには、組み込みの SQLite データベースを使用します。
// Create tables
await this.sql`
CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`;
// Insert
await this.sql`
INSERT INTO documents (title, content)
VALUES (${title}, ${content})
`;
// Query
const docs = await this.sql`
SELECT * FROM documents WHERE title LIKE ${`%${search}%`}
`;
スケジュールされたタスク
エージェントは、将来の処理をスケジュールできます。
async onMessage(connection: Connection, message: string) {
const data = JSON.parse(message);
if (data.type === "schedule_reminder") {
// Schedule task for 1 hour from now
const { id } = await this.schedule(3600, "sendReminder", {
message: data.reminderText,
userId: data.userId,
});
connection.send(JSON.stringify({ type: "scheduled", taskId: id }));
}
}
// Called when scheduled task fires
async sendReminder(data: { message: string; userId: string }) {
// Send notification, email, etc.
console.log(`Reminder for ${data.userId}: ${data.message}`);
// Can also update state
this.setState({
...this.state,
lastReminder: new Date().toISOString(),
});
}
スケジュールのオプション
// Delay in seconds
await this.schedule(60, "taskMethod", { data });
// Specific date
await this.schedule(new Date("2025-01-01T00:00:00Z"), "taskMethod", { data });
// Cron expression (recurring)
await this.schedule("0 9 * * *", "dailyTask", {}); // 9 AM daily
await this.schedule("*/5 * * * *", "everyFiveMinutes", {}); // Every 5 min
// Manage schedules
const schedules = await this.getSchedules();
await this.cancelSchedule(taskId);
チャットエージェント(AI 搭載)
チャットに特化したエージェントを作成するには、AIChatAgent を継承します。
import { AIChatAgent } from "@cloudflare/ai-chat";
export class ChatBot extends AIChatAgent<Env> {
// Called for each user message
async onChatMessage(message: string) {
const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages: [
{ role: "system", content: "You are a helpful assistant." },
...this.messages, // Automatic history management
{ role: "user", content: message },
],
stream: true,
});
// Stream response back to client
return response;
}
}
次の機能が含まれています。
- メッセージ履歴の自動管理
- ストリーミングの再開(切断後も継続)
- 永続化のための組み込み
saveMessages()
クライアントとの統合
React フック
import { useAgent } from "agents/react";
function Chat() {
const { state, send, connected } = useAgent({
agent: "my-agent",
name: userId, // Agent instance ID
});
const sendMessage = (text: string) => {
send(JSON.stringify({ type: "chat", content: text }));
};
return (
<div>
{state.messages.map((msg, i) => (
<div key={i}>{msg.role}: {msg.content}</div>
))}
<input onKeyDown={(e) => e.key === "Enter" && sendMessage(e.target.value)} />
</div>
);
}
Vanilla JavaScript
const ws = new WebSocket("wss://my-agent.workers.dev/agents/MyAgent/user123");
ws.onopen = () => {
console.log("Connected to agent");
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
ws.send(JSON.stringify({ type: "chat", content: "Hello!" }));
よく使われるパターン
よく使われるパターンについては、references/agent-patterns.md を参照してください。
- ツール呼び出しと関数の実行
- マルチエージェントのオーケストレーション
- RAG(検索拡張生成)
- ヒューマン・イン・ザ・ループのワークフロー
デプロイ
# Deploy
npx wrangler deploy
# View logs
wrangler tail
# Test endpoint
curl https://my-agent.workers.dev/agents/MyAgent/test-user
トラブルシューティング
よくある問題については、references/troubleshooting.md を参照してください。
参考資料
- references/examples.md — 公式テンプレートと本番環境での例
- references/agent-patterns.md — 高度なパターン
- references/state-patterns.md — 状態管理の戦略
- references/troubleshooting.md — エラーの解決方法
コメント
Markdown 対応