|
Add this skill
npx mdskills install cloudflare/building-ai-agent-on-cloudflareComprehensive guide with state management, real-time WebSockets, scheduling, and practical examples
1---2name: building-ai-agent-on-cloudflare3description: |4 Builds AI agents on Cloudflare using the Agents SDK with state management,5 real-time WebSockets, scheduled tasks, tool integration, and chat capabilities.6 Generates production-ready agent code deployed to Workers.78 Use when: user wants to "build an agent", "AI agent", "chat agent", "stateful9 agent", mentions "Agents SDK", needs "real-time AI", "WebSocket AI", or asks10 about agent "state management", "scheduled tasks", or "tool calling".11---1213# Building Cloudflare Agents1415Creates AI-powered agents using Cloudflare's Agents SDK with persistent state, real-time communication, and tool integration.1617## When to Use1819- User wants to build an AI agent or chatbot20- User needs stateful, real-time AI interactions21- User asks about the Cloudflare Agents SDK22- User wants scheduled tasks or background AI work23- User needs WebSocket-based AI communication2425## Prerequisites2627- Cloudflare account with Workers enabled28- Node.js 18+ and npm/pnpm/yarn29- Wrangler CLI (`npm install -g wrangler`)3031## Quick Start3233```bash34npm create cloudflare@latest -- my-agent --template=cloudflare/agents-starter35cd my-agent36npm start37```3839Agent runs at `http://localhost:8787`4041## Core Concepts4243### What is an Agent?4445An Agent is a stateful, persistent AI service that:46- Maintains state across requests and reconnections47- Communicates via WebSockets or HTTP48- Runs on Cloudflare's edge via Durable Objects49- Can schedule tasks and call tools50- Scales horizontally (each user/session gets own instance)5152### Agent Lifecycle5354```55Client connects → Agent.onConnect() → Agent processes messages56 → Agent.onMessage()57 → Agent.setState() (persists + syncs)58Client disconnects → State persists → Client reconnects → State restored59```6061## Basic Agent Structure6263```typescript64import { Agent, Connection } from "agents";6566interface Env {67 AI: Ai; // Workers AI binding68}6970interface State {71 messages: Array<{ role: string; content: string }>;72 preferences: Record<string, string>;73}7475export class MyAgent extends Agent<Env, State> {76 // Initial state for new instances77 initialState: State = {78 messages: [],79 preferences: {},80 };8182 // Called when agent starts or resumes83 async onStart() {84 console.log("Agent started with state:", this.state);85 }8687 // Handle WebSocket connections88 async onConnect(connection: Connection) {89 connection.send(JSON.stringify({90 type: "welcome",91 history: this.state.messages,92 }));93 }9495 // Handle incoming messages96 async onMessage(connection: Connection, message: string) {97 const data = JSON.parse(message);9899 if (data.type === "chat") {100 await this.handleChat(connection, data.content);101 }102 }103104 // Handle disconnections105 async onClose(connection: Connection) {106 console.log("Client disconnected");107 }108109 // React to state changes110 onStateUpdate(state: State, source: string) {111 console.log("State updated by:", source);112 }113114 private async handleChat(connection: Connection, userMessage: string) {115 // Add user message to history116 const messages = [117 ...this.state.messages,118 { role: "user", content: userMessage },119 ];120121 // Call AI122 const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {123 messages,124 });125126 // Update state (persists and syncs to all clients)127 this.setState({128 ...this.state,129 messages: [130 ...messages,131 { role: "assistant", content: response.response },132 ],133 });134135 // Send response136 connection.send(JSON.stringify({137 type: "response",138 content: response.response,139 }));140 }141}142```143144## Entry Point Configuration145146```typescript147// src/index.ts148import { routeAgentRequest } from "agents";149import { MyAgent } from "./agent";150151export default {152 async fetch(request: Request, env: Env) {153 // routeAgentRequest handles routing to /agents/:class/:name154 return (155 (await routeAgentRequest(request, env)) ||156 new Response("Not found", { status: 404 })157 );158 },159};160161export { MyAgent };162```163164Clients connect via: `wss://my-agent.workers.dev/agents/MyAgent/session-id`165166## Wrangler Configuration167168```toml169name = "my-agent"170main = "src/index.ts"171compatibility_date = "2024-12-01"172173[ai]174binding = "AI"175176[durable_objects]177bindings = [{ name = "AGENT", class_name = "MyAgent" }]178179[[migrations]]180tag = "v1"181new_classes = ["MyAgent"]182```183184## State Management185186### Reading State187188```typescript189// Current state is always available190const currentMessages = this.state.messages;191const userPrefs = this.state.preferences;192```193194### Updating State195196```typescript197// setState persists AND syncs to all connected clients198this.setState({199 ...this.state,200 messages: [...this.state.messages, newMessage],201});202203// Partial updates work too204this.setState({205 preferences: { ...this.state.preferences, theme: "dark" },206});207```208209### SQL Storage210211For complex queries, use the embedded SQLite database:212213```typescript214// Create tables215await this.sql`216 CREATE TABLE IF NOT EXISTS documents (217 id INTEGER PRIMARY KEY AUTOINCREMENT,218 title TEXT NOT NULL,219 content TEXT,220 created_at DATETIME DEFAULT CURRENT_TIMESTAMP221 )222`;223224// Insert225await this.sql`226 INSERT INTO documents (title, content)227 VALUES (${title}, ${content})228`;229230// Query231const docs = await this.sql`232 SELECT * FROM documents WHERE title LIKE ${`%${search}%`}233`;234```235236## Scheduled Tasks237238Agents can schedule future work:239240```typescript241async onMessage(connection: Connection, message: string) {242 const data = JSON.parse(message);243244 if (data.type === "schedule_reminder") {245 // Schedule task for 1 hour from now246 const { id } = await this.schedule(3600, "sendReminder", {247 message: data.reminderText,248 userId: data.userId,249 });250251 connection.send(JSON.stringify({ type: "scheduled", taskId: id }));252 }253}254255// Called when scheduled task fires256async sendReminder(data: { message: string; userId: string }) {257 // Send notification, email, etc.258 console.log(`Reminder for ${data.userId}: ${data.message}`);259260 // Can also update state261 this.setState({262 ...this.state,263 lastReminder: new Date().toISOString(),264 });265}266```267268### Schedule Options269270```typescript271// Delay in seconds272await this.schedule(60, "taskMethod", { data });273274// Specific date275await this.schedule(new Date("2025-01-01T00:00:00Z"), "taskMethod", { data });276277// Cron expression (recurring)278await this.schedule("0 9 * * *", "dailyTask", {}); // 9 AM daily279await this.schedule("*/5 * * * *", "everyFiveMinutes", {}); // Every 5 min280281// Manage schedules282const schedules = await this.getSchedules();283await this.cancelSchedule(taskId);284```285286## Chat Agent (AI-Powered)287288For chat-focused agents, extend `AIChatAgent`:289290```typescript291import { AIChatAgent } from "agents/ai-chat-agent";292293export class ChatBot extends AIChatAgent<Env> {294 // Called for each user message295 async onChatMessage(message: string) {296 const response = await this.env.AI.run("@cf/meta/llama-3-8b-instruct", {297 messages: [298 { role: "system", content: "You are a helpful assistant." },299 ...this.messages, // Automatic history management300 { role: "user", content: message },301 ],302 stream: true,303 });304305 // Stream response back to client306 return response;307 }308}309```310311Features included:312- Automatic message history313- Resumable streaming (survives disconnects)314- Built-in `saveMessages()` for persistence315316## Client Integration317318### React Hook319320```tsx321import { useAgent } from "agents/react";322323function Chat() {324 const { state, send, connected } = useAgent({325 agent: "my-agent",326 name: userId, // Agent instance ID327 });328329 const sendMessage = (text: string) => {330 send(JSON.stringify({ type: "chat", content: text }));331 };332333 return (334 <div>335 {state.messages.map((msg, i) => (336 <div key={i}>{msg.role}: {msg.content}</div>337 ))}338 <input onKeyDown={(e) => e.key === "Enter" && sendMessage(e.target.value)} />339 </div>340 );341}342```343344### Vanilla JavaScript345346```javascript347const ws = new WebSocket("wss://my-agent.workers.dev/agents/MyAgent/user123");348349ws.onopen = () => {350 console.log("Connected to agent");351};352353ws.onmessage = (event) => {354 const data = JSON.parse(event.data);355 console.log("Received:", data);356};357358ws.send(JSON.stringify({ type: "chat", content: "Hello!" }));359```360361## Common Patterns362363See [references/agent-patterns.md](references/agent-patterns.md) for:364- Tool calling and function execution365- Multi-agent orchestration366- RAG (Retrieval Augmented Generation)367- Human-in-the-loop workflows368369## Deployment370371```bash372# Deploy373npx wrangler deploy374375# View logs376wrangler tail377378# Test endpoint379curl https://my-agent.workers.dev/agents/MyAgent/test-user380```381382## Troubleshooting383384See [references/troubleshooting.md](references/troubleshooting.md) for common issues.385386## References387388- [references/examples.md](references/examples.md) — Official templates and production examples389- [references/agent-patterns.md](references/agent-patterns.md) — Advanced patterns390- [references/state-patterns.md](references/state-patterns.md) — State management strategies391- [references/troubleshooting.md](references/troubleshooting.md) — Error solutions392
Full transparency — inspect the skill content before installing.