diff --git a/src/assets/images/agents/agent-workflow.svg b/src/assets/images/agents/agent-workflow.svg
new file mode 100644
index 000000000000000..82ccd7f92120e1c
--- /dev/null
+++ b/src/assets/images/agents/agent-workflow.svg
@@ -0,0 +1,7363 @@
+
diff --git a/src/assets/images/agents/co-pilot.svg b/src/assets/images/agents/co-pilot.svg
new file mode 100644
index 000000000000000..acc6077c12bbb07
--- /dev/null
+++ b/src/assets/images/agents/co-pilot.svg
@@ -0,0 +1,7356 @@
+
diff --git a/src/assets/images/agents/human-in-the-loop.svg b/src/assets/images/agents/human-in-the-loop.svg
new file mode 100644
index 000000000000000..96a8fe98234916d
--- /dev/null
+++ b/src/assets/images/agents/human-in-the-loop.svg
@@ -0,0 +1,7367 @@
+
diff --git a/src/assets/images/agents/workflow-automation.png b/src/assets/images/agents/workflow-automation.png
new file mode 100644
index 000000000000000..50b0026e3e2d7bc
Binary files /dev/null and b/src/assets/images/agents/workflow-automation.png differ
diff --git a/src/assets/images/agents/workflow-automation.svg b/src/assets/images/agents/workflow-automation.svg
new file mode 100644
index 000000000000000..0688a2485400c86
--- /dev/null
+++ b/src/assets/images/agents/workflow-automation.svg
@@ -0,0 +1,7348 @@
+
diff --git a/src/content/changelog/agents/2025-02-14-example-ai-prompts.mdx b/src/content/changelog/agents/2025-02-14-example-ai-prompts.mdx
index c5071f049a2a5b2..b979df67b5b02d0 100644
--- a/src/content/changelog/agents/2025-02-14-example-ai-prompts.mdx
+++ b/src/content/changelog/agents/2025-02-14-example-ai-prompts.mdx
@@ -8,7 +8,7 @@ products:
date: 2025-02-14T19:00:00Z
---
-We've added an [example prompt](/agents/build/prompts/) to help you get started with building AI agents and applications on Cloudflare [Workers](/workers/), including [Workflows](/workflows/), [Durable Objects](/durable-objects/), and [Workers KV](/kv/).
+We've added an [example prompt](/workers/get-started/prompting/) to help you get started with building AI agents and applications on Cloudflare [Workers](/workers/), including [Workflows](/workflows/), [Durable Objects](/durable-objects/), and [Workers KV](/kv/).
You can use this prompt with your favorite AI model, including Claude 3.5 Sonnet, OpenAI's o3-mini, Gemini 2.0 Flash, or Llama 3.3 on Workers AI. Models with large context windows will allow you to paste the prompt directly: provide your own prompt within the `` tags.
diff --git a/src/content/docs/agents/api-reference/configuration.mdx b/src/content/docs/agents/api-reference/configuration.mdx
new file mode 100644
index 000000000000000..24563dce3c49ce7
--- /dev/null
+++ b/src/content/docs/agents/api-reference/configuration.mdx
@@ -0,0 +1,72 @@
+---
+title: Configuration
+pcx_content_type: concept
+sidebar:
+ order: 3
+---
+
+import { MetaInfo, Render, Type, WranglerConfig } from "~/components";
+
+An Agent is configured like any other Cloudflare Workers project, and uses [a wrangler configuration](/workers/wrangler/configuration/) file to define where your code is and what services (bindings) it will use.
+
+The typical file structure for an Agent project created from `npm create cloudflare@latest -- --template cloudflare/agents` follows:
+
+```sh
+.
+|-- package-lock.json
+|-- package.json
+|-- public
+| `-- index.html
+|-- src
+| `-- index.ts // your Agent definition
+|-- test
+| |-- index.spec.ts // your tests
+| `-- tsconfig.json
+|-- tsconfig.json
+|-- vitest.config.mts
+|-- worker-configuration.d.ts
+`-- wrangler.jsonc // your Workers & Agent configuration
+```
+
+Below is a minimal `wrangler.jsonc` file that defines the configuration for an Agent, including the entry point, `durable_object` namespace, and code `migrations`:
+
+
+
+```jsonc
+{
+ "$schema": "node_modules/wrangler/config-schema.json",
+ "name": "agents-example",
+ "main": "src/index.ts",
+ "compatibility_date": "2025-02-23",
+ "compatibility_flags": ["nodejs_compat"],
+ "durable_objects": {
+ "bindings": [
+ {
+ // Required:
+ "name": "MyAgent", // How your Agent is called from your Worker
+ "class_name": "MyAgent", // Must match the class name of the Agent in your code
+ // Optional: set this if the Agent is defined in another Worker script
+ "script_name": "the-other-worker"
+ },
+ ],
+ },
+ "migrations": [
+ {
+ "tag": "v1",
+ // Mandatory for the Agent to store state
+ "new_sqlite_classes": ["MyAgent"],
+ },
+ ],
+ "observability": {
+ "enabled": true,
+ },
+}
+```
+
+
+
+The configuration includes:
+
+- A `main` field that points to the entry point of your Agent, which is typically a TypeScript (or JavaScript) file.
+- A `durable_objects` field that defines the [Durable Object namespace](/durable-objects/reference/glossary/) that your Agents will run within.
+- A `migrations` field that defines the code migrations that your Agent will use. This field is mandatory and must contain at least one migration. The `new_sqlite_classes` field is mandatory for the Agent to store state.
diff --git a/src/content/docs/agents/api-reference/index.mdx b/src/content/docs/agents/api-reference/index.mdx
new file mode 100644
index 000000000000000..3db3271d4f644a3
--- /dev/null
+++ b/src/content/docs/agents/api-reference/index.mdx
@@ -0,0 +1,12 @@
+---
+title: API Reference
+pcx_content_type: navigation
+sidebar:
+ order: 5
+ group:
+ hideIndex: true
+---
+
+import { DirectoryListing } from "~/components"
+
+
diff --git a/src/content/docs/agents/api-reference/sdk.mdx b/src/content/docs/agents/api-reference/sdk.mdx
new file mode 100644
index 000000000000000..258a4d5c0753a2b
--- /dev/null
+++ b/src/content/docs/agents/api-reference/sdk.mdx
@@ -0,0 +1,162 @@
+---
+title: Agents SDK
+pcx_content_type: concept
+sidebar:
+ order: 2
+
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+At its most basic, an Agent is a JavaScript class that extends the `Agent` class from the `agents-sdk` package. An Agent encapsulates all of the logic for an Agent, including how clients can connect to it, how it stores state, the methods it exposes, and any error handling.
+
+
+
+```ts
+import { Agent } from "agents-sdk";
+
+class MyAgent extends Agent {
+ // Define methods on the Agent
+}
+
+export default MyAgent;
+```
+
+
+
+An Agent can have many (millions of) instances: each instance is a separate micro-server that runs independently of the others. This allows Agents to scale horizontally: an Agent can be associated with a single user, or many thousands of users, depending on the agent you're building.
+
+Instances of an Agent are addressed by a unique identifier: that identifier (ID) can be the user ID, an email address, GitHub username, a flight ticket number, an invoice ID, or any other identifier that helps to uniquely identify the instance and for whom it is acting on behalf of.
+
+## The Agent class
+
+Writing an Agent requires you to define a class that extends the `Agent` class from the `agents-sdk` package. An Agent encapsulates all of the logic for an Agent, including how clients can connect to it, how it stores state, the methods it exposes, and any error handling.
+
+An Agent has the following class methods:
+
+
+
+```ts
+import { Agent } from "agents-sdk";
+
+interface Env {
+ // Define environment variables & bindings here
+}
+
+// Pass the Env as a TypeScript type argument
+// Any services connected to your Agent or Worker as Bindings
+// are then available on this.env.
+class MyAgent extends Agent {
+ // Called when an Agent is started (or woken up)
+ async onStart() {
+ // Can access this.env and this.state
+ console.log('Agent started');
+ }
+
+ // Called when a HTTP request is received
+ // Can be connected to routeAgentRequest to automatically route
+ // requests to an individual Agent.
+ async onRequest(request: Request) {
+ console.log("Received request!");
+ }
+
+ // Called when a WebSocket connection is established
+ async onConnect(connection: Connection, ctx: ConnectionContext) {
+ console.log("Connected!");
+ // Check the request at ctx.request
+ // Authenticate the client
+ // Give them the OK.
+ connection.accept();
+ }
+
+ // Called for each message received on the WebSocket connection
+ async onMessage(connection: Connection, message: WSMessage) {
+ console.log(`message from client ID: ${connection.id}`)
+ // Send messages back to the client
+ connection.send("Hello!");
+ }
+
+ // WebSocket error and disconnection (close) handling.
+ async onError(connection: Connection, error: unknown): Promise {
+ console.error(`WS error: ${error}`);
+ }
+
+ async onClose(connection: Connection, code: number, reason: string, wasClean: boolean): Promise {
+ console.log(`WS closed: ${code} - ${reason} - wasClean: ${wasClean}`);
+ connection.close();
+ }
+
+ // Called when the Agent's state is updated
+ // via this.setState or the useAgent hook from the agents-sdk/react package.
+ async onStateUpdate(state: any) {
+ // 'state' will be typed if you supply a type parameter to the Agent class.
+ }
+}
+
+export default MyAgent;
+```
+
+
+
+:::note
+
+To learn more about how to manage state within an Agent, refer to the documentation on [managing and syncing state](/agents/examples/manage-and-sync-state/).
+
+:::
+
+You can also define your own methods on an Agent: it's technically valid to publish an Agent only has your own methods exposed, and create/get Agents directly from a Worker.
+
+Your own methods can access the Agent's environment variables and bindings on `this.env`, state on `this.setState`, and call other methods on the Agent via `this.yourMethodName`.
+
+## Calling Agents from Workers
+
+You can create and run an instance of an Agent directly from a Worker in one of three ways:
+
+1. Using the `routeAgentRequest` helper: this will automatically map requests to an individual Agent based on the `/agents/:agent/:name` URL pattern. The value of `:agent` will be the name of your Agent class converted to `kebab-case`, and the value of `:name` will be the name of the Agent instance you want to create or retrieve.
+2. Calling `getAgentByName`, which will create a new Agent instance if none exists by that name, or retrieve a handle to an existing instance.
+3. The [Durable Objects stub API](/durable-objects/api/id/), which provides a lower level API for creating and retrieving Agents.
+
+These three patterns are shown below: we recommend using either `routeAgentRequest` or `getAgentByName`, which help avoid some boilerplate.
+
+
+
+```ts
+import { Agent, AgentNamespace, getAgentByName, routeAgentRequest } from 'agents-sdk';
+
+interface Env {
+ // Define your Agent on the environment here
+ // Passing your Agent class as a TypeScript type parameter allows you to call
+ // methods defined on your Agent.
+ MyAgent: AgentNamespace;
+}
+
+export default {
+ async fetch(request, env, ctx): Promise {
+ // Routed addressing
+ // Automatically routes HTTP requests and/or WebSocket connections to /agents/:agent/:name
+ // Best for: connecting React apps directly to Agents using useAgent from agents-sdk/react
+ (await routeAgentRequest(request, env)) || Response.json({ msg: 'no agent here' }, { status: 404 });
+
+ // Named addressing
+ // Best for: convenience method for creating or retrieving an agent by name/ID.
+ let namedAgent = getAgentByName(env.MyAgent, 'my-unique-agent-id');
+ // Pass the incoming request straight to your Agent
+ let namedResp = (await namedAgent).fetch(request);
+
+ // Durable Objects-style addressing
+ // Best for: controlling ID generation, associating IDs with your existing systems,
+ // and customizing when/how an Agent is created or invoked
+ const id = env.MyAgent.newUniqueId();
+ const agent = env.MyAgent.get(id);
+ // Pass the incoming request straight to your Agent
+ let resp = await agent.fetch(request);
+
+ return Response.json({ hello: 'visit https://developers.cloudflare.com/agents for more' });
+ },
+} satisfies ExportedHandler;
+
+export class MyAgent extends Agent {
+ // Your Agent implementation goes here
+}
+```
+
diff --git a/src/content/docs/agents/build/prompts.mdx b/src/content/docs/agents/build/prompts.mdx
deleted file mode 100644
index ab7c97f65532b8c..000000000000000
--- a/src/content/docs/agents/build/prompts.mdx
+++ /dev/null
@@ -1,1028 +0,0 @@
----
-title: Prompting AI Models
-pcx_content_type: concept
-sidebar:
- order: 2
----
-
-import { Tabs, TabItem, GlossaryTooltip, Type, Badge, TypeScriptExample } from "~/components";
-
-## Prompting
-
-When building, iterating on or debugging applications using AI tools and Large Language Models (LLMs), a well-structured and extensive prompt helps provide the model with clearer guidelines & examples that can dramatically improve output.
-
-We're providing an extensive example prompt that can help you build apps and AI agents using Cloudflare Workers and your preferred AI model.
-
-Use the click-to-copy button at the top right of the code block below to copy the full prompt to your clipboard:
-
-```md collapse={30-10000}
-
-You are an advanced assistant specialized in generating Cloudflare Workers code. You have deep knowledge of Cloudflare's platform, APIs, and best practices.
-
-
-
-
-- Respond in a friendly and concise manner
-- Focus exclusively on Cloudflare Workers solutions
-- Provide complete, self-contained solutions
-- Default to current best practices
-- Ask clarifying questions when requirements are ambiguous
-
-
-
-
-
-- Generate code in TypeScript by default unless JavaScript is specifically requested
-- Use ES modules format exclusively (never use Service Worker format)
-- You SHALL keep all code in a single file unless otherwise specified
-- Minimize external dependencies, unless there is an official SDK or library for the service you are integrating with, then use it to simplify the implementation.
-- Do not use libraries that have FFI/native/C bindings.
-- Follow Cloudflare Workers security best practices
-- Never bake in secrets into the code
-- Include proper error handling and logging
-- Add appropriate TypeScript types and interfaces
-- Include comments explaining complex logic
-
-
-
-
-
-- Use markdown code blocks to separate code from explanations
-- Provide separate blocks for:
- 1. Main worker code (index.ts/index.js)
- 2. Configuration (wrangler.jsonc)
- 3. Type definitions (if applicable)
- 4. Example usage/tests
-- Always output complete files, never partial updates or diffs
-- Format code consistently using standard TypeScript/JavaScript conventions
-
-
-
-
-
-- When data storage is needed, integrate with appropriate Cloudflare services:
- - Workers KV for key-value storage, including configuration data, user profiles, and A/B testing
- - Durable Objects for strongly consistent state management, storage, and multiplayer co-ordination use-cases
- - D1 for relational data and for its SQL dialect
- - R2 for object storage, including storing structured data, AI assets, image assets and for user-facing uploads
- - Hyperdrive to connect to existing (PostgreSQL) databases that a developer may already have
- - Queues for asynchronous processing and background tasks
- - Vectorize for storing embeddings and to support vector search (often in combination with Workers AI)
- - Workers Analytics Engine for tracking user events, billing, metrics and high-cardinality analytics
- - Workers AI as the default AI API for inference requests. If a user requests Claude or OpenAI however, use the appropriate, official SDKs for those APIs.
- - Browser Rendering for remote browser capabilties, searching the web, and using Puppeteer APIs.
-- Include all necessary bindings in both code and wrangler.jsonc
-- Add appropriate environment variable definitions
-
-
-
-
-
-- Always provide a wrangler.jsonc (not wrangler.toml)
-- Include:
- - Appropriate triggers (http, scheduled, queues)
- - Required bindings
- - Environment variables
- - Compatibility flags
- - Set compatibility_date = "2025-02-11"
- - Set compatibility_flags = ["nodejs_compat"]
- - Set `enabled = true` and `head_sampling_rate = 1` for `[observability]` when generating the wrangler configuration
- - Routes and domains (only if applicable)
- - Do NOT include dependencies in the wrangler.jsonc file
- - Only include bindings that are used in the code
-
-
-
-// wrangler.jsonc
-{
- "name": "app-name-goes-here", // name of the app
- "main": "src/index.ts", // default file
- "compatibility_date": "2025-02-11",
- "compatibility_flags": ["nodejs_compat"], // Enable Node.js compatibility
- "observability": {
- // Enable logging by default
- "enabled": true,
- }
-}
-
-
-
-
- - Defines a name for the app the user is building
- - Sets `src/index.ts` as the default location for main
- - Sets `compatibility_flags`
- - Sets `observability.enabled = true`
-
-
-
-
-
-
-
-- Implement proper request validation
-- Use appropriate security headers
-- Handle CORS correctly when needed
-- Implement rate limiting where appropriate
-- Follow least privilege principle for bindings
-- Sanitize user inputs
-
-
-
-
-
-- Include basic test examples
-- Provide curl commands for API endpoints
-- Add example environment variable values
-- Include sample requests and responses
-
-
-
-
-
-- Optimize for cold starts
-- Minimize unnecessary computation
-- Use appropriate caching strategies
-- Consider Workers limits and quotas
-- Implement streaming where beneficial
-
-
-
-
-
-- Implement proper error boundaries
-- Return appropriate HTTP status codes
-- Provide meaningful error messages
-- Log errors appropriately
-- Handle edge cases gracefully
-
-
-
-
-
-- Always use WebSocket Hibernation API instead of legacy WebSocket API unless otherwise specified
-- You SHALL use the Durable Objects WebSocket Hibernation API when providing WebSocket handling code within a Durable Object. - Refer to for an example implementation.
-- Use `this.ctx.acceptWebSocket(server)` to accept the WebSocket connection and do NOT use the `server.accept()` method.
-- Define an `async webSocketMessage()` handler that is invoked when a message is received from the client
-- Define an `async webSocketClose()` handler that is invoked when the WebSocket connection is closed
-- Do NOT use the `addEventListener` pattern to handle WebSocket events.
-- Handle WebSocket upgrade requests explicitly
-
-
-
-
-
-
-
-Example of using the Hibernatable WebSocket API in Durable Objects to handle WebSocket connections.
-
-
-
-import { DurableObject } from "cloudflare:workers";
-
-interface Env {
-WEBSOCKET_HIBERNATION_SERVER: DurableObject;
-}
-
-// Durable Object
-export class WebSocketHibernationServer extends DurableObject {
-async fetch(request) {
-// Creates two ends of a WebSocket connection.
-const webSocketPair = new WebSocketPair();
-const [client, server] = Object.values(webSocketPair);
-
- // Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
- // request within the Durable Object. It has the effect of "accepting" the connection,
- // and allowing the WebSocket to send and receive messages.
- // Unlike `ws.accept()`, `state.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
- // is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
- // the connection is open. During periods of inactivity, the Durable Object can be evicted
- // from memory, but the WebSocket connection will remain open. If at some later point the
- // WebSocket receives a message, the runtime will recreate the Durable Object
- // (run the `constructor`) and deliver the message to the appropriate handler.
- this.ctx.acceptWebSocket(server);
-
- return new Response(null, {
- status: 101,
- webSocket: client,
- });
-
- },
-
- async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void | Promise {
- // Upon receiving a message from the client, reply with the same message,
- // but will prefix the message with "[Durable Object]: " and return the
- // total number of connections.
- ws.send(
- `[Durable Object] message: ${message}, connections: ${this.ctx.getWebSockets().length}`,
- );
- },
-
- async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) void | Promise {
- // If the client closes the connection, the runtime will invoke the webSocketClose() handler.
- ws.close(code, "Durable Object is closing WebSocket");
- },
-
- async webSocketError(ws: WebSocket, error: unknown): void | Promise {
- console.error("WebSocket error:", error);
- ws.close(1011, "WebSocket error");
- }
-
-}
-
-
-
-
-{
- "name": "websocket-hibernation-server",
- "durable_objects": {
- "bindings": [
- {
- "name": "WEBSOCKET_HIBERNATION_SERVER",
- "class_name": "WebSocketHibernationServer"
- }
- ]
- },
- "migrations": [
- {
- "tag": "v1",
- "new_classes": ["WebSocketHibernationServer"]
- }
- ]
-}
-
-
-
-
-- Uses the WebSocket Hibernation API instead of the legacy WebSocket API
-- Calls `this.ctx.acceptWebSocket(server)` to accept the WebSocket connection
-- Has a `webSocketMessage()` handler that is invoked when a message is received from the client
-- Has a `webSocketClose()` handler that is invoked when the WebSocket connection is closed
-- Does NOT use the `server.addEventListener` API unless explicitly requested.
-- Don't over-use the "Hibernation" term in code or in bindings. It is an implementation detail.
-
-
-
-
-
-Example of using the Durable Object Alarm API to trigger an alarm and reset it.
-
-
-
-import { DurableObject } from "cloudflare:workers";
-
-interface Env {
-ALARM_EXAMPLE: DurableObject;
-}
-
-export default {
- async fetch(request, env) {
- let url = new URL(request.url);
- let userId = url.searchParams.get("userId") || crypto.randomUUID();
- let id = env.ALARM_EXAMPLE.idFromName(userId);
- return await env.ALARM_EXAMPLE.get(id).fetch(request);
- },
-};
-
-const SECONDS = 1000;
-
-export class AlarmExample extends DurableObject {
-constructor(ctx, env) {
-this.ctx = ctx;
-this.storage = ctx.storage;
-}
-async fetch(request) {
-// If there is no alarm currently set, set one for 10 seconds from now
-let currentAlarm = await this.storage.getAlarm();
-if (currentAlarm == null) {
-this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
-}
-}
-async alarm(alarmInfo) {
-// The alarm handler will be invoked whenever an alarm fires.
-// You can use this to do work, read from the Storage API, make HTTP calls
-// and set future alarms to run using this.storage.setAlarm() from within this handler.
-if (alarmInfo?.retryCount != 0) {
-console.log("This alarm event has been attempted ${alarmInfo?.retryCount} times before.");
-}
-
-// Set a new alarm for 10 seconds from now before exiting the handler
-this.storage.setAlarm(Date.now() + 10 \_ SECONDS);
-}
-}
-
-
-
-
-{
- "name": "durable-object-alarm",
- "durable_objects": {
- "bindings": [
- {
- "name": "ALARM_EXAMPLE",
- "class_name": "DurableObjectAlarm"
- }
- ]
- },
- "migrations": [
- {
- "tag": "v1",
- "new_classes": ["DurableObjectAlarm"]
- }
- ]
-}
-
-
-
-
-- Uses the Durable Object Alarm API to trigger an alarm
-- Has a `alarm()` handler that is invoked when the alarm is triggered
-- Sets a new alarm for 10 seconds from now before exiting the handler
-
-
-
-
-
-Using Workers KV to store session data and authenticate requests, with Hono as the router and middleware.
-
-
-
-// src/index.ts
-import { Hono } from 'hono'
-import { cors } from 'hono/cors'
-
-interface Env {
-AUTH_TOKENS: KVNamespace;
-}
-
-const app = new Hono<{ Bindings: Env }>()
-
-// Add CORS middleware
-app.use('\*', cors())
-
-app.get('/', async (c) => {
-try {
-// Get token from header or cookie
-const token = c.req.header('Authorization')?.slice(7) ||
-c.req.header('Cookie')?.match(/auth_token=([^;]+)/)?.[1];
-if (!token) {
-return c.json({
-authenticated: false,
-message: 'No authentication token provided'
-}, 403)
-}
-
- // Check token in KV
- const userData = await c.env.AUTH_TOKENS.get(token)
-
- if (!userData) {
- return c.json({
- authenticated: false,
- message: 'Invalid or expired token'
- }, 403)
- }
-
- return c.json({
- authenticated: true,
- message: 'Authentication successful',
- data: JSON.parse(userData)
- })
-
-} catch (error) {
-console.error('Authentication error:', error)
-return c.json({
-authenticated: false,
-message: 'Internal server error'
-}, 500)
-}
-})
-
-export default app
-
-
-
-{
- "name": "auth-worker",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "kv_namespaces": [
- {
- "binding": "AUTH_TOKENS",
- "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
- "preview_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- }
- ]
-}
-
-
-
-
-- Uses Hono as the router and middleware
-- Uses Workers KV to store session data
-- Uses the Authorization header or Cookie to get the token
-- Checks the token in Workers KV
-- Returns a 403 if the token is invalid or expired
-
-
-
-
-
-
-Use Cloudflare Queues to produce and consume messages.
-
-
-
-// src/producer.ts
-interface Env {
- REQUEST_QUEUE: Queue;
- UPSTREAM_API_URL: string;
- UPSTREAM_API_KEY: string;
-}
-
-export default {
-async fetch(request: Request, env: Env) {
-const info = {
-timestamp: new Date().toISOString(),
-method: request.method,
-url: request.url,
-headers: Object.fromEntries(request.headers),
-};
-await env.REQUEST_QUEUE.send(info);
-
-return Response.json({
-message: 'Request logged',
-requestId: crypto.randomUUID()
-});
-
-},
-
-async queue(batch: MessageBatch, env: Env) {
-const requests = batch.messages.map(msg => msg.body);
-
- const response = await fetch(env.UPSTREAM_API_URL, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${env.UPSTREAM_API_KEY}`
- },
- body: JSON.stringify({
- timestamp: new Date().toISOString(),
- batchSize: requests.length,
- requests
- })
- });
-
- if (!response.ok) {
- throw new Error(`Upstream API error: ${response.status}`);
- }
-
-}
-};
-
-
-
-
-{
- "name": "request-logger-consumer",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "queues": {
- "producers": [{
- "name": "request-queue",
- "binding": "REQUEST_QUEUE"
- }],
- "consumers": [{
- "name": "request-queue",
- "dead_letter_queue": "request-queue-dlq",
- "retry_delay": 300
- }]
- },
- "vars": {
- "UPSTREAM_API_URL": "https://api.example.com/batch-logs",
- "UPSTREAM_API_KEY": ""
- }
-}
-
-
-
-
-- Defines both a producer and consumer for the queue
-- Uses a dead letter queue for failed messages
-- Uses a retry delay of 300 seconds to delay the re-delivery of failed messages
-- Shows how to batch requests to an upstream API
-
-
-
-
-
-
-Connect to and query a Postgres database using Cloudflare Hyperdrive.
-
-
-
-// Postgres.js 3.4.5 or later is recommended
-import postgres from "postgres";
-
-export interface Env {
-// If you set another name in the Wrangler config file as the value for 'binding',
-// replace "HYPERDRIVE" with the variable name you defined.
-HYPERDRIVE: Hyperdrive;
-}
-
-export default {
-async fetch(request, env, ctx): Promise {
-console.log(JSON.stringify(env));
-// Create a database client that connects to your database via Hyperdrive.
-//
-// Hyperdrive generates a unique connection string you can pass to
-// supported drivers, including node-postgres, Postgres.js, and the many
-// ORMs and query builders that use these drivers.
-const sql = postgres(env.HYPERDRIVE.connectionString)
-
- try {
- // Test query
- const results = await sql`SELECT * FROM pg_tables`;
-
- // Clean up the client, ensuring we don't kill the worker before that is
- // completed.
- ctx.waitUntil(sql.end());
-
- // Return result rows as JSON
- return Response.json(results);
- } catch (e) {
- console.error(e);
- return Response.json(
- { error: e instanceof Error ? e.message : e },
- { status: 500 },
- );
- }
-
-},
-} satisfies ExportedHandler;
-
-
-
-
-{
- "name": "hyperdrive-postgres",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "hyperdrive": [
- {
- "binding": "HYPERDRIVE",
- "id": ""
- }
- ]
-}
-
-
-
-// Install Postgres.js
-npm install postgres
-
-// Create a Hyperdrive configuration
-npx wrangler hyperdrive create --connection-string="postgres://user:password@HOSTNAME_OR_IP_ADDRESS:PORT/database_name"
-
-
-
-
-
-- Installs and uses Postgres.js as the database client/driver.
-- Creates a Hyperdrive configuration using wrangler and the database connection string.
-- Uses the Hyperdrive connection string to connect to the database.
-- Calling `sql.end()` is optional, as Hyperdrive will handle the connection pooling.
-
-
-
-
-
-
-Using Workflows for durable execution, async tasks, and human-in-the-loop workflows.
-
-
-
-import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
-
-type Env = {
-// Add your bindings here, e.g. Workers KV, D1, Workers AI, etc.
-MY_WORKFLOW: Workflow;
-};
-
-// User-defined params passed to your workflow
-type Params = {
-email: string;
-metadata: Record;
-};
-
-export class MyWorkflow extends WorkflowEntrypoint {
-async run(event: WorkflowEvent, step: WorkflowStep) {
-// Can access bindings on `this.env`
-// Can access params on `event.payload`
-const files = await step.do('my first step', async () => {
-// Fetch a list of files from $SOME_SERVICE
-return {
-files: [
-'doc_7392_rev3.pdf',
-'report_x29_final.pdf',
-'memo_2024_05_12.pdf',
-'file_089_update.pdf',
-'proj_alpha_v2.pdf',
-'data_analysis_q2.pdf',
-'notes_meeting_52.pdf',
-'summary_fy24_draft.pdf',
-],
-};
-});
-
- const apiResponse = await step.do('some other step', async () => {
- let resp = await fetch('https://api.cloudflare.com/client/v4/ips');
- return await resp.json();
- });
-
- await step.sleep('wait on something', '1 minute');
-
- await step.do(
- 'make a call to write that could maybe, just might, fail',
- // Define a retry strategy
- {
- retries: {
- limit: 5,
- delay: '5 second',
- backoff: 'exponential',
- },
- timeout: '15 minutes',
- },
- async () => {
- // Do stuff here, with access to the state from our previous steps
- if (Math.random() > 0.5) {
- throw new Error('API call to $STORAGE_SYSTEM failed');
- }
- },
- );
-
-}
-}
-
-export default {
-async fetch(req: Request, env: Env): Promise {
-let url = new URL(req.url);
-
- if (url.pathname.startsWith('/favicon')) {
- return Response.json({}, { status: 404 });
- }
-
- // Get the status of an existing instance, if provided
- let id = url.searchParams.get('instanceId');
- if (id) {
- let instance = await env.MY_WORKFLOW.get(id);
- return Response.json({
- status: await instance.status(),
- });
- }
-
- const data = await req.json()
-
- // Spawn a new instance and return the ID and status
- let instance = await env.MY_WORKFLOW.create({
- // Define an ID for the Workflow instance
- id: crypto.randomUUID(),
- // Pass data to the Workflow instance
- // Available on the WorkflowEvent
- params: data,
- });
-
- return Response.json({
- id: instance.id,
- details: await instance.status(),
- });
-
-},
-};
-
-
-
-
-{
- "name": "workflows-starter",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "workflows": [
- {
- "name": "workflows-starter",
- "binding": "MY_WORKFLOW",
- "class_name": "MyWorkflow"
- }
- ]
-}
-
-
-
-
-- Defines a Workflow by extending the WorkflowEntrypoint class.
-- Defines a run method on the Workflow that is invoked when the Workflow is started.
-- Ensures that `await` is used before calling `step.do` or `step.sleep`
-- Passes a payload (event) to the Workflow from a Worker
-- Defines a payload type and uses TypeScript type arguments to ensure type safety
-
-
-
-
-
-
- Using Workers Analytics Engine for writing event data.
-
-
-
-interface Env {
- USER_EVENTS: AnalyticsEngineDataset;
-}
-
-export default {
-async fetch(req: Request, env: Env): Promise {
-let url = new URL(req.url);
-let path = url.pathname;
-let userId = url.searchParams.get("userId");
-
- // Write a datapoint for this visit, associating the data with
- // the userId as our Analytics Engine 'index'
- env.USER_EVENTS.writeDataPoint({
- // Write metrics data: counters, gauges or latency statistics
- doubles: [],
- // Write text labels - URLs, app names, event_names, etc
- blobs: [path],
- // Provide an index that groups your data correctly.
- indexes: [userId],
- });
-
- return Response.json({
- hello: "world",
- });
- ,
-
-};
-
-
-
-
-{
- "name": "analytics-engine-example",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "analytics_engine_datasets": [
- {
- "binding": "",
- "dataset": ""
- }
- ]
- }
-}
-
-
-
-// Query data within the 'temperatures' dataset
-// This is accessible via the REST API at https://api.cloudflare.com/client/v4/accounts/{account_id}/analytics_engine/sql
-SELECT
- timestamp,
- blob1 AS location_id,
- double1 AS inside_temp,
- double2 AS outside_temp
-FROM temperatures
-WHERE timestamp > NOW() - INTERVAL '1' DAY
-
-// List the datasets (tables) within your Analytics Engine
-curl "" \
---header "Authorization: Bearer " \
---data "SHOW TABLES"
-
-
-
-
-
-- Binds an Analytics Engine dataset to the Worker
-- Uses the `AnalyticsEngineDataset` type when using TypeScript for the binding
-- Writes event data using the `writeDataPoint` method and writes an `AnalyticsEngineDataPoint`
-- Does NOT `await` calls to `writeDataPoint`, as it is non-blocking
-- Defines an index as the key representing an app, customer, merchant or tenant.
-- Developers can use the GraphQL or SQL APIs to query data written to Analytics Engine
-
-
-
-
-
-Use the Browser Rendering API as a headless browser to interact with websites from a Cloudflare Worker.
-
-
-
-import puppeteer from "@cloudflare/puppeteer";
-
-interface Env {
- BROWSER_RENDERING: Fetcher;
-}
-
-export default {
- async fetch(request, env): Promise {
- const { searchParams } = new URL(request.url);
- let url = searchParams.get("url");
-
- if (url) {
- url = new URL(url).toString(); // normalize
- const browser = await puppeteer.launch(env.MYBROWSER);
- const page = await browser.newPage();
- await page.goto(url);
- // Parse the page content
- const content = await page.content();
- // Find text within the page content
- const text = await page.$eval("body", (el) => el.textContent);
- // Do something with the text
- // e.g. log it to the console, write it to KV, or store it in a database.
- console.log(text);
-
- // Ensure we close the browser session
- await browser.close();
-
- return Response.json({
- bodyText: text,
- })
- } else {
- return Response.json({
- error: "Please add an ?url=https://example.com/ parameter"
- }, { status: 400 })
- }
- },
-} satisfies ExportedHandler;
-
-
-
-{
- "name": "browser-rendering-example",
- "main": "src/index.ts",
- "compatibility_date": "2025-02-11",
- "browser": [
- {
- "binding": "BROWSER_RENDERING",
- }
- ]
-}
-
-
-
-// Install @cloudflare/puppeteer
-npm install @cloudflare/puppeteer --save-dev
-
-
-
-
-- Configures a BROWSER_RENDERING binding
-- Passes the binding to Puppeteer
-- Uses the Puppeteer APIs to navigate to a URL and render the page
-- Parses the DOM and returns context for use in the response
-- Correctly creates and closes the browser instance
-
-
-
-
-
-
-
-
-
-
-Fan-in/fan-out for WebSockets. Uses the Hibernatable WebSockets API within Durable Objects. Does NOT use the legacy addEventListener API.
-
-
-export class WebSocketHibernationServer extends DurableObject {
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
- // Creates two ends of a WebSocket connection.
- const webSocketPair = new WebSocketPair();
- const [client, server] = Object.values(webSocketPair);
-
- // Call this to accept the WebSocket connection.
- // Do NOT call server.accept() (this is the legacy approach and is not preferred)
- this.ctx.acceptWebSocket(server);
-
- return new Response(null, {
- status: 101,
- webSocket: client,
- });
-},
-
-async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void | Promise {
- // Invoked on each WebSocket message.
- ws.send(message)
-},
-
-async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) void | Promise {
- // Invoked when a client closes the connection.
- ws.close(code, "");
-},
-
-async webSocketError(ws: WebSocket, error: unknown): void | Promise {
- // Handle WebSocket errors
-}
-}
-
-
-
-
-
-{user_prompt}
-
-```
-
-The prompt above adopts several best practices, including:
-
-* Using `` tags to structure the prompt
-* API and usage examples for products and use-cases
-* Guidance on how to generate configuration (e.g. `wrangler.jsonc`) as part of the models response.
-* Recommendations on Cloudflare products to use for specific storage or state need
-
-## Using the prompt
-
-:::note
-
-The prompt(s) here are examples and should be adapted to your specific use case. We'll continue to build out the prompts available here, including additional prompts for specific products.
-
-Depending on the model and user prompt, it may generate invalid code, configuration or other errors, and we recommend reviewing and testing the generated code before deploying it.
-
-:::
-
-You can use the prompt in several ways:
-
-* Within the user context window, with your own user prompt inserted between the `` tags (**easiest**)
-* As the `system` prompt for models that support system prompts
-* Adding it to the prompt library and/or file context within your preferred IDE:
- * Cursor: add the prompt to [your Project Rules](https://docs.cursor.com/context/rules-for-ai)
- * Zed: use [the `/file` command](https://zed.dev/docs/assistant/assistant-panel) to add the prompt to the Assistant context.
- * Windsurf: use [the `@-mention` command](https://docs.codeium.com/chat/overview) to include a file containing the prompt to your Chat.
-
-For example, you can use the prompts here with the OpenAI SDK by saving the prompt to a file within your project directory and passing the text to the `system` prompt.
-
-
-
-```ts
-import workersPrompt from "./workersPrompt.md"
-
-// Llama 3.3 from Workers AI
-const PREFERRED_MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast"
-
-export default {
- async fetch(req: Request, env: Env, ctx: ExecutionContext) {
- const openai = new OpenAI({
- apiKey: env.WORKERS_AI_API_KEY
- });
-
- const stream = await openai.chat.completions.create({
- messages: [
- {
- role: "system",
- content: workersPrompt,
- },
- {
- role: "user",
- // Imagine something big!
- content: "Build an AI Agent using Workflows. The Workflow should be triggered by a GitHub webhook on a pull request, and ..."
- }
- ],
- model: PREFERRED_MODEL,
- stream: true,
- });
-
- // Stream the response so we're not buffering the entire response in memory,
- // since it could be very large.
- const transformStream = new TransformStream();
- const writer = transformStream.writable.getWriter();
- const encoder = new TextEncoder();
-
- (async () => {
- try {
- for await (const chunk of stream) {
- const content = chunk.choices[0]?.delta?.content || '';
- await writer.write(encoder.encode(content));
- }
- } finally {
- await writer.close();
- }
- })();
-
- return new Response(transformStream.readable, {
- headers: {
- 'Content-Type': 'text/plain; charset=utf-8',
- 'Transfer-Encoding': 'chunked'
- }
- });
- }
-}
-
-```
-
-
-
-
-## Additional resources
-
-To get the most out of AI models and tools, we recommend reading the following guides on prompt engineering and structure:
-
-* OpenAI's [prompt engineering](https://platform.openai.com/docs/guides/prompt-engineering) guide and [best practices](https://platform.openai.com/docs/guides/reasoning-best-practices) for using reasoning models.
-* The [prompt engineering](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview) guide from Anthropic
-* Google's [quick start guide](https://services.google.com/fh/files/misc/gemini-for-google-workspace-prompting-guide-101.pdf) for writing effective prompts
-* Meta's [prompting documentation](https://www.llama.com/docs/how-to-guides/prompting/) for their Llama model family.
-* GitHub's guide for [prompt engineering](https://docs.github.com/en/copilot/using-github-copilot/copilot-chat/prompt-engineering-for-copilot-chat) when using Copilot Chat.
diff --git a/src/content/docs/agents/capabilities/control-web-browsers.mdx b/src/content/docs/agents/capabilities/control-web-browsers.mdx
deleted file mode 100644
index 5193b4b42722c48..000000000000000
--- a/src/content/docs/agents/capabilities/control-web-browsers.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-pcx_content_type: navigation
-title: Control Web Browsers (Browser Rendering API)
-external_link: /browser-rendering/
-sidebar:
- order: 2
-head: []
-description: The Workers Browser Rendering API allows developers to programmatically control and interact with a headless browser instance and create automation flows for their applications and products.
-
----
diff --git a/src/content/docs/agents/capabilities/mcp-server.mdx b/src/content/docs/agents/capabilities/mcp-server.mdx
deleted file mode 100644
index 7e5a1479d567e76..000000000000000
--- a/src/content/docs/agents/capabilities/mcp-server.mdx
+++ /dev/null
@@ -1,114 +0,0 @@
----
-pcx_content_type: tutorial
-title: Build an MCP Server
-sidebar:
- order: 1
- group:
- hideIndex: true
-description: Build and deploy an MCP server on Cloudflare Workers
----
-[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open standard that allows AI assistants & LLMs to interact with services directly. If you want users to access your service or product straight from their AI assistant, you can enable this by spinning up an MCP server for your application.
-
-## Building an MCP Server on Cloudflare Workers
-
-Normally, setting up an MCP server requires writing boilerplate code to handle routing, define types, and standing up a server that implements the MCP protocol. But with [Cloudflare Workers](/workers/), all the heavy lifting is done for you, so all you need to do is define your service's functionality as TypeScript methods on your Worker
-Once deployed, your Worker becomes an MCP server that AI assistants (as long as they support MCP) can connect to and use to interact with your service.
-
-## Benefits
-
-- **Minimal setup & built-in boilerplate:** The Worker automatically handles API routing, server management, and MCP protocol compliance. The [workers-mcp](https://www.npmjs.com/package/workers-mcp) package bootstraps your MCP server, allowing you to focus on your service logic.
-- **Automatic documentation:** Public methods annotated with JSDoc are automatically documented and exposed as MCP tools. This means AI assistants can quickly understand how to interact with your service, making tool discovery and integration much easier.
-- **Scalability & performance:** Deploy your MCP server on Cloudflare's global edge network so that users can make fast, performant requests from their LLMs. Cloudflare will handle traffic spikes and high load, ensuring your service remains available and responsive.
-- **Expand MCP server capabilities:** Easily connect to Workers AI, D1, Durable Objects, and other Cloudflare services to add more functionality to your MCP Server.
-
-## Get Started
-
-Follow these steps to create and deploy your own MCP server on Cloudflare Workers.
-
-
-### 1. Create a new Worker
-
-If you haven't already, install [Wrangler](https://developers.cloudflare.com/workers/wrangler/) and log in:
-
-```bash
-npm install wrangler
-wrangler login
-```
-
-Initialize a new project:
-```bash
-npx create-cloudflare@latest my-mcp-worker
-cd my-mcp-worker
-```
-
-### 2. Install the MCP Tooling
-Inside your project directory, install the [workers-mcp](https://github.com/cloudflare/workers-mcp) package:
-
-```bash
-npm install workers-mcp
-```
-
-This package provides the tools needed to run your Worker as an MCP server.
-
-### 3. Configure your Worker to support MCP
-Run the following setup command:
-
-```bash
-npx workers-mcp setup
-```
-
-This guided installation process takes a brand new or existing Workers project and adds the required tooling to turn it into an MCP server:
-- Automatic documentation generation
-- Shared-secret security using Wrangler Secrets
-- Installs a local proxy so you can access it from your MCP desktop clients (like Claude Desktop)
-
-### 4. Set up the MCP Server
-Replace the contents of your src/index.ts with the following boilerplate code:
-
-```ts
-import { WorkerEntrypoint } from 'cloudflare:workers';
-import { ProxyToSelf } from 'workers-mcp';
-
-export default class MyWorker extends WorkerEntrypoint {
- /**
- * A warm, friendly greeting from your new MCP server.
- * @param name {string} The name of the person to greet.
- * @return {string} The greeting message.
- */
- sayHello(name: string) {
- return `Hello from an MCP Worker, ${name}!`;
- }
-
- /**
- * @ignore
- */
- async fetch(request: Request): Promise {
- // ProxyToSelf handles MCP protocol compliance.
- return new ProxyToSelf(this).fetch(request);
- }
-}
-```
-
-This converts your Cloudflare Worker into an MCP server, enabling interactions with AI assistants. The key components are:
-- **WorkerEntrypoint:** The WorkerEntrypoint class handles all incoming request management and routing. This provides the structure needed to expose MCP tools within the Worker.
-- **Tool Definition:** Methods, for example, sayHello, are annotated with JSDoc, which automatically registers the method as an MCP tool. AI assistants can call this method dynamically, passing a name and receiving a greeting in response. Additional tools can be defined using the same pattern.
-- **ProxyToSelf:** MCP servers must follow a specific request/response format. ProxyToSelf ensures that incoming requests are properly routed to the correct MCP tools. Without this, you would need to manually parse requests and validate responses.
-
-
-**Note:** Every public method that is annotated with JSDoc becomes an MCP tool that is discoverable by AI assistants.
-
-
-### 5. Deploy the MCP Server
-Update your [Wrangler configuration file](/workers/wrangler/configuration/) with the appropriate configuration then deploy your Worker:
-```bash
-wrangler deploy
-```
-
-Your MCP server is now deployed globally and all your public class methods are exposed as MCP tools that AI assistants can now interact with.
-
-### Expanding the MCP Server Capabilities
-Use existing Cloudflare products to expand the functionality of your MCP server. You can:
-- Send emails using [Email Routing](/email-routing/)
-- Capture and share website previews using [Browser Rendering](/browser-rendering/)
-- Store and manage sessions, user data, or other persistent information with [Durable Objects](/durable-objects/)
-- Query and update data using a [D1 database](/d1/)
diff --git a/src/content/docs/agents/capabilities/run-models.mdx b/src/content/docs/agents/capabilities/run-models.mdx
deleted file mode 100644
index d60f7d78e66c731..000000000000000
--- a/src/content/docs/agents/capabilities/run-models.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-pcx_content_type: navigation
-title: Run models (Workers AI)
-external_link: /workers-ai/
-sidebar:
- order: 3
-head: []
-description: Run popular open-source AI models on Cloudflare's network.
----
\ No newline at end of file
diff --git a/src/content/docs/agents/capabilities/send-email.mdx b/src/content/docs/agents/capabilities/send-email.mdx
deleted file mode 100644
index ed1d9a9240f7365..000000000000000
--- a/src/content/docs/agents/capabilities/send-email.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Send Email
-external_link: /email-routing/email-workers/send-email-workers/
-sidebar:
- order: 4
-head: []
-description: Send emails from your Worker for async updates to a user.
----
-
-
diff --git a/src/content/docs/agents/capabilities/webrtc-realtime.mdx b/src/content/docs/agents/capabilities/webrtc-realtime.mdx
deleted file mode 100644
index 0da8a27eff545a4..000000000000000
--- a/src/content/docs/agents/capabilities/webrtc-realtime.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-pcx_content_type: navigation
-title: Realtime voice (WebRTC)
-external_link: /calls/
-sidebar:
- order: 5
-head: []
-description: Build real-time serverless video, audio and data applications.
----
\ No newline at end of file
diff --git a/src/content/docs/agents/concepts/calling-llms.mdx b/src/content/docs/agents/concepts/calling-llms.mdx
new file mode 100644
index 000000000000000..b9956d96b606467
--- /dev/null
+++ b/src/content/docs/agents/concepts/calling-llms.mdx
@@ -0,0 +1,34 @@
+---
+title: Calling LLMs
+pcx_content_type: concept
+sidebar:
+ order: 6
+
+---
+
+import { Render } from "~/components";
+
+### Understanding LLM providers and model types
+
+Different LLM providers offer models optimized for specific types of tasks. When building AI systems, choosing the right model is crucial for both performance and cost efficiency.
+
+#### Reasoning Models
+
+Models like OpenAI's o1, Anthropic's Claude, and DeepSeek's R1 are particularly well-suited for complex reasoning tasks. These models excel at:
+
+- Breaking down problems into steps
+- Following complex instructions
+- Maintaining context across long conversations
+- Generating code and technical content
+
+For example, when implementing a travel booking system, you might use a reasoning model to analyze travel requirements and generate appropriate booking strategies.
+
+#### Instruction Models
+
+Models like GPT-4 and Claude Instant are optimized for following straightforward instructions efficiently. They work well for:
+- Content generation
+- Simple classification tasks
+- Basic question answering
+- Text transformation
+
+These models are often more cost-effective for straightforward tasks that do not require complex reasoning.
\ No newline at end of file
diff --git a/src/content/docs/agents/concepts/human-in-the-loop.mdx b/src/content/docs/agents/concepts/human-in-the-loop.mdx
new file mode 100644
index 000000000000000..22917ef538737e8
--- /dev/null
+++ b/src/content/docs/agents/concepts/human-in-the-loop.mdx
@@ -0,0 +1,54 @@
+---
+title: Human in the Loop
+pcx_content_type: concept
+sidebar:
+ order: 5
+
+---
+
+import { Render, Note, Aside } from "~/components";
+
+### What is Human-in-the-Loop?
+
+Human-in-the-Loop (HITL) workflows integrate human judgment and oversight into automated processes. These workflows pause at critical points for human review, validation, or decision-making before proceeding. This approach combines the efficiency of automation with human expertise and oversight where it matters most.
+
+
+
+#### Understanding Human-in-the-Loop workflows
+
+In a Human-in-the-Loop workflow, processes are not fully automated. Instead, they include designated checkpoints where human intervention is required. For example, in a travel booking system, a human may want to confirm the travel before an agent follows through with a transaction. The workflow manages this interaction, ensuring that:
+
+1. The process pauses at appropriate review points
+2. Human reviewers receive necessary context
+3. The system maintains state during the review period
+4. Review decisions are properly incorporated
+5. The process continues once approval is received
+
+### Best practices for Human-in-the-Loop workflows
+
+#### Long-Term State Persistence
+
+Human review processes do not operate on predictable timelines. A reviewer might need days or weeks to make a decision, especially for complex cases requiring additional investigation or multiple approvals. Your system needs to maintain perfect state consistency throughout this period, including:
+
+- The original request and context
+- All intermediate decisions and actions
+- Any partial progress or temporary states
+- Review history and feedback
+
+:::note[Tip]
+[Durable Objects](/durable-objects/) provide an ideal solution for managing state in Human-in-the-Loop workflows, offering persistent compute instances that maintain state for hours, weeks, or months.
+:::
+
+#### Continuous Improvement Through Evals
+
+Human reviewers play a crucial role in evaluating and improving LLM performance. Implement a systematic evaluation process where human feedback is collected not just on the final output, but on the LLM's decision-making process. This can include:
+
+- Decision Quality Assessment: Have reviewers evaluate the LLM's reasoning process and decision points, not just the final output.
+- Edge Case Identification: Use human expertise to identify scenarios where the LLM's performance could be improved.
+- Feedback Collection: Gather structured feedback that can be used to fine-tune the LLM or adjust the workflow. [AI Gateway](/ai-gateway/evaluations/add-human-feedback/) can be a useful tool for setting up an LLM feedback loop.
+
+#### Error handling and recovery
+
+Robust error handling is essential for maintaining workflow integrity. Your system should gracefully handle various failure scenarios, including reviewer unavailability, system outages, or conflicting reviews. Implement clear escalation paths for handling exceptional cases that fall outside normal parameters.
+
+The system should maintain stability during paused states, ensuring that no work is lost even during extended review periods. Consider implementing automatic checkpointing that allows workflows to be resumed from the last stable state after any interruption.
diff --git a/src/content/docs/agents/capabilities/index.mdx b/src/content/docs/agents/concepts/index.mdx
similarity index 82%
rename from src/content/docs/agents/capabilities/index.mdx
rename to src/content/docs/agents/concepts/index.mdx
index dfcca68ba21de99..2f115caf1042468 100644
--- a/src/content/docs/agents/capabilities/index.mdx
+++ b/src/content/docs/agents/concepts/index.mdx
@@ -1,6 +1,6 @@
---
pcx_content_type: reference
-title: Capabilities
+title: Concepts
sidebar:
order: 2
group:
@@ -9,6 +9,4 @@ sidebar:
import { DirectoryListing } from "~/components";
-Capabilities
-
diff --git a/src/content/docs/agents/concepts/tools.mdx b/src/content/docs/agents/concepts/tools.mdx
new file mode 100644
index 000000000000000..b5a235895dd50e0
--- /dev/null
+++ b/src/content/docs/agents/concepts/tools.mdx
@@ -0,0 +1,48 @@
+---
+title: Tools
+pcx_content_type: concept
+sidebar:
+ order: 4
+
+---
+
+### What are tools?
+
+Tools enable AI systems to interact with external services and perform actions. They provide a structured way for agents and workflows to invoke APIs, manipulate data, and integrate with external systems. Tools form the bridge between AI decision-making capabilities and real-world actions.
+
+### Understanding tools
+
+In an AI system, tools are typically implemented as function calls that the AI can use to accomplish specific tasks. For example, a travel booking agent might have tools for:
+
+- Searching flight availability
+- Checking hotel rates
+- Processing payments
+- Sending confirmation emails
+
+Each tool has a defined interface specifying its inputs, outputs, and expected behavior. This allows the AI system to understand when and how to use each tool appropriately.
+
+### Common tool patterns
+
+#### API integration tools
+
+The most common type of tools are those that wrap external APIs. These tools handle the complexity of API authentication, request formatting, and response parsing, presenting a clean interface to the AI system.
+
+#### Model Context Protocol (MCP)
+
+The (Model Context Protocol)[https://modelcontextprotocol.io/introduction] provides a standardized way to define and interact with tools. Think of it as an abstraction on top of APIs designed for LLMs to interact with external resources. MCP defines a consistent interface for:
+
+- **Tool Discovery**: Systems can dynamically discover available tools
+- **Parameter Validation**: Tools specify their input requirements using JSON Schema
+- **Error Handling**: Standardized error reporting and recovery
+- **State Management**: Tools can maintain state across invocations
+
+
+#### Data processing tools
+
+Tools that handle data transformation and analysis are essential for many AI workflows. These might include:
+
+- CSV parsing and analysis
+- Image processing
+- Text extraction
+- Data validation
+
diff --git a/src/content/docs/agents/concepts/what-are-agents.mdx b/src/content/docs/agents/concepts/what-are-agents.mdx
new file mode 100644
index 000000000000000..5bd4d932fe6602e
--- /dev/null
+++ b/src/content/docs/agents/concepts/what-are-agents.mdx
@@ -0,0 +1,73 @@
+---
+title: Agents
+pcx_content_type: concept
+sidebar:
+ order: 2
+
+---
+
+import { Render } from "~/components";
+
+### What are agents?
+
+An agent is an AI system that can autonomously execute tasks by making decisions about tool usage and process flow. Unlike traditional automation that follows predefined paths, agents can dynamically adapt their approach based on context and intermediate results. Agents are also distinct from co-pilots (e.g. traditional chat applications) in that they can fully automate a task, as opposed to simply augmenting and extending human input.
+
+- **Agents** → non-linear, non-deterministic (can change from run to run)
+- **Workflows** → linear, deterministic execution paths
+- **Co-pilots** → augmentative AI assistance requiring human intervention
+
+### Example: Booking vacations
+
+If this is your first time working with, or interacting with agents, this example will illustrate how an agent works within a context like booking a vacation. If you are already familiar with the topic, read on.
+
+Imagine you're trying to book a vacation. You need to research flights, find hotels, check restaurant reviews, and keep track of your budget.
+
+#### Traditional workflow automation
+
+A traditional automation system follows a predetermined sequence:
+
+- Takes specific inputs (dates, location, budget)
+- Calls predefined API endpoints in a fixed order
+- Returns results based on hardcoded criteria
+- Cannot adapt if unexpected situations arise
+
+
+
+#### AI Co-pilot
+
+A co-pilot acts as an intelligent assistant that:
+
+- Provides hotel and itinerary recommendations based on your preferences
+- Can understand and respond to natural language queries
+- Offers guidance and suggestions
+- Requires human decision-making and action for execution
+
+
+
+#### Agent
+
+An agent combines AI's ability to make judgements and call the relevant tools to execute the task. An agent's output will be nondeterministic given:
+
+- Real-time availability and pricing changes
+- Dynamic prioritization of constraints
+- Ability to recover from failures
+- Adaptive decision-making based on intermediate results
+
+
+
+An agents can dynamically generate an itinerary and execute on booking reservations, similarly to what you would expect from a travel agent.
+
+### Three primary components of agent systems:
+
+- **Decision Engine**: Usually an LLM (Large Language Model) that determines action steps
+- **Tool Integration**: APIs, functions, and services the agent can utilize
+- **Memory System**: Maintains context and tracks task progress
+
+#### How agents work
+
+Agents operate in a continuous loop of:
+
+1. **Observing** the current state or task
+2. **Planning** what actions to take, using AI for reasoning
+3. **Executing** those actions using available tools (often APIs or [MCPs](https://modelcontextprotocol.io/introduction))
+4. **Learning** from the results (storing results in memory, updating task progress, and preparing for next iteration)
diff --git a/src/content/docs/agents/concepts/workflows.mdx b/src/content/docs/agents/concepts/workflows.mdx
new file mode 100644
index 000000000000000..a0ea8d69aa7e2d2
--- /dev/null
+++ b/src/content/docs/agents/concepts/workflows.mdx
@@ -0,0 +1,31 @@
+---
+title: Workflows
+pcx_content_type: concept
+sidebar:
+ order: 3
+---
+
+import { Render } from "~/components";
+
+## What are workflows?
+
+A workflow is the orchestration layer that coordinates how an agent's components work together. It defines the structured paths through which tasks are processed, tools are called, and results are managed. While agents make dynamic decisions about what to do, workflows provide the underlying framework that governs how those decisions are executed.
+
+### Understanding workflows in agent systems
+
+Think of a workflow like the operating procedures of a company. The company (agent) can make various decisions, but how those decisions get implemented follows established processes (workflows). For example, when you book a flight through a travel agent, they might make different decisions about which flights to recommend, but the process of actually booking the flight follows a fixed sequence of steps.
+
+Let's examine a basic agent workflow:
+
+### Core components of a workflow
+
+A workflow typically consists of several key elements:
+
+1. **Input Processing**
+The workflow defines how inputs are received and validated before being processed by the agent. This includes standardizing formats, checking permissions, and ensuring all required information is present.
+2. **Tool Integration**
+Workflows manage how external tools and services are accessed. They handle authentication, rate limiting, error recovery, and ensuring tools are used in the correct sequence.
+3. **State Management**
+The workflow maintains the state of ongoing processes, tracking progress through multiple steps and ensuring consistency across operations.
+4. **Output Handling**
+Results from the agent's actions are processed according to defined rules, whether that means storing data, triggering notifications, or formatting responses.
diff --git a/src/content/docs/agents/examples/browse-the-web.mdx b/src/content/docs/agents/examples/browse-the-web.mdx
new file mode 100644
index 000000000000000..703759ceb989095
--- /dev/null
+++ b/src/content/docs/agents/examples/browse-the-web.mdx
@@ -0,0 +1,119 @@
+---
+title: Browse the web
+pcx_content_type: concept
+sidebar:
+ order: 99
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Agents can browse the web using the [Browser Rendering](/browser-rendering/) API or your preferred headless browser service.
+
+### Browser Rendering API
+
+The [Browser Rendering](/browser-rendering/) allows you to spin up headless browser instances, render web pages, and interact with websites through your Agent.
+
+You can define a method that uses Puppeteer to pull the content of a web page, parse the DOM, and extract relevant information by calling the OpenAI model:
+
+
+
+```ts
+interface Env {
+ BROWSER: Fetcher;
+}
+
+export class MyAgent extends Agent {
+ async browse(browserInstance: Fetcher, urls: string[]) {
+ let responses = [];
+ for (const url of urls) {
+ const browser = await puppeteer.launch(browserInstance);
+ const page = await browser.newPage();
+ await page.goto(url);
+
+ await page.waitForSelector('body');
+ const bodyContent = await page.$eval('body', (element) => element.innerHTML);
+ const client = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ let resp = await client.chat.completions.create({
+ model: this.env.MODEL,
+ messages: [
+ {
+ role: 'user',
+ content: `Return a JSON object with the product names, prices and URLs with the following format: { "name": "Product Name", "price": "Price", "url": "URL" } from the website content below. ${bodyContent}`,
+ },
+ ],
+ response_format: {
+ type: 'json_object',
+ },
+ });
+
+ responses.push(resp);
+ await browser.close();
+ }
+
+ return responses;
+ }
+}
+```
+
+
+
+You'll also need to add install the `@cloudflare/puppeteer` package and add the following to the wrangler configuration of your Agent:
+
+```sh
+npm install @cloudflare/puppeteer --save-dev
+```
+
+
+
+```jsonc
+{
+ // ...
+ "browser": {
+ "binding": "MYBROWSER"
+ }
+ // ...
+}
+```
+
+
+
+### Browserbase
+
+You can also use [Browserbase](https://docs.browserbase.com/integrations/cloudflare/typescript) by using the Browserbase API directly from within your Agent.
+
+Once you have your [Browserbase API key](https://docs.browserbase.com/integrations/cloudflare/typescript), you can add it to your Agent by creating a [secret](/workers/configuration/secrets/):
+
+```sh
+cd your-agent-project-folder
+npx wrangler@latest secret put BROWSERBASE_API_KEY
+```
+```sh output
+Enter a secret value: ******
+Creating the secret for the Worker "agents-example"
+Success! Uploaded secret BROWSERBASE_API_KEY
+```
+
+Install the `@cloudflare/puppeteer` package and use it from within your Agent to call the Browserbase API:
+
+```sh
+npm install @cloudflare/puppeteer
+```
+
+
+
+```ts
+interface Env {
+ BROWSERBASE_API_KEY: string;
+}
+
+export class MyAgent extends Agent {
+ constructor(env: Env) {
+ super(env);
+ }
+}
+```
+
+
diff --git a/src/content/docs/agents/examples/index.mdx b/src/content/docs/agents/examples/index.mdx
new file mode 100644
index 000000000000000..34f41dbf12ee2ec
--- /dev/null
+++ b/src/content/docs/agents/examples/index.mdx
@@ -0,0 +1,12 @@
+---
+pcx_content_type: reference
+title: Examples
+sidebar:
+ order: 3
+---
+
+import { DirectoryListing, PackageManagers } from "~/components";
+
+Agents running on Cloudflare can:
+
+
diff --git a/src/content/docs/agents/examples/manage-and-sync-state.mdx b/src/content/docs/agents/examples/manage-and-sync-state.mdx
new file mode 100644
index 000000000000000..3262f5776f01a2b
--- /dev/null
+++ b/src/content/docs/agents/examples/manage-and-sync-state.mdx
@@ -0,0 +1,197 @@
+---
+title: Manage and sync state
+pcx_content_type: concept
+sidebar:
+ order: 6
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Every Agent has built-in state management capabilities, including built-in storage and synchronization between the Agent and frontend applications. State within an Agent is:
+
+* Persisted across Agent restarts: data is permanently persisted within the Agent.
+* Automatically serialized/deserialized: you can store any JSON-serializable data.
+* Immediately consistent within the Agent: read your own writes.
+* Thread-safe for concurrent updates
+
+Agent state is stored in SQL database that associate with each indidivual Agent instance: you can interact with it using the higher-level `this.setState` API (recommended) or by directly querying the database with `this.sql`.
+
+#### State API
+
+Every Agent has built-in state management capabilities. You can set and update the Agent's state directly using `this.setState`:
+
+
+
+```ts
+import { Agent } from "agents-sdk";
+
+export class MyAgent extends Agent {
+ // Update state in response to events
+ async incrementCounter() {
+ this.setState({
+ ...this.state,
+ counter: this.state.counter + 1,
+ });
+ }
+
+ // Handle incoming messages
+ async onMessage(message) {
+ if (message.type === "update") {
+ this.setState({
+ ...this.state,
+ ...message.data,
+ });
+ }
+ }
+
+ // Handle state updates
+ onStateUpdate(state, source: "server" | Connection) {
+ console.log("state updated", state);
+ }
+}
+```
+
+
+
+If you're using TypeScript, you can also provide a type for your Agent's state by passing in a type as a [type parameter](https://www.typescriptlang.org/docs/handbook/2/generics.html#using-type-parameters-in-generic-constraints) as the _second_ type parameter to the `Agent` class definition.
+
+
+
+```ts
+import { Agent } from "agents-sdk";
+
+interface Env {}
+
+// Define a type for your Agent's state
+interface FlightRecord {
+ id: string;
+ departureIata: string;
+ arrival: Date;;
+ arrivalIata: string;
+ price: number;
+}
+
+// Pass in the type of your Agent's state
+export class MyAgent extends Agent {
+ // This allows this.setState and the onStateUpdate method to
+ // be typed:
+ async onStateUpdate(state: FlightRecord) {
+ console.log("state updated", state);
+ }
+
+ async someOtherMethod() {
+ this.setState({
+ ...this.state,
+ price: this.state.price + 10,
+ });
+ }
+}
+```
+
+
+
+### Synchronizing state
+
+Clients can connect to an Agent and stay synchronized with its state using the React hooks provided as part of `agents-sdk/react`.
+
+A React application can call `useAgent` to connect to a named Agent over WebSockets at
+
+
+
+```ts
+import { useState } from "react";
+import { useAgent } from "agents-sdk/react";
+
+function StateInterface() {
+ const [state, setState] = useState({ counter: 0 });
+
+ const agent = useAgent({
+ agent: "thinking-agent",
+ name: "my-agent",
+ onStateUpdate: (newState) => setState(newState),
+ });
+
+ const increment = () => {
+ agent.setState({ counter: state.counter + 1 });
+ };
+
+ return (
+
+
Count: {state.counter}
+
+
+ );
+}
+```
+
+
+
+The state synchronization system:
+
+* Automatically syncs the Agent's state to all connected clients
+* Handles client disconnections and reconnections gracefully
+* Provides immediate local updates
+* Supports multiple simultaneous client connections
+
+Common use cases:
+
+* Real-time collaborative features
+* Multi-window/tab synchronization
+* Live updates across multiple devices
+* Maintaining consistent UI state across clients
+* When new clients connect, they automatically receive the current state from the Agent, ensuring all clients start with the latest data.
+
+### SQL API
+
+Every individual Agent instance has its own SQL (SQLite) database that runs _within the same context_ as the Agent itself. This means that inserting or querying data within your Agent is effectively zero-latency: the Agent doesn't have to round-trip across a continent or the world to access its own data.
+
+You can access the SQL API within any method on an Agent via `this.sql`. The SQL API accepts template literals, and
+
+
+
+```ts
+export class MyAgent extends Agent {
+ async onRequest(request: Request) {
+ let userId = new URL(request.url).searchParams.get('userId');
+
+ // 'users' is just an example here: you can create arbitrary tables and define your own schemas
+ // within each Agent's database using SQL (SQLite syntax).
+ let user = await this.sql`SELECT * FROM users WHERE id = ${userId}`
+ return Response.json(user)
+ }
+}
+```
+
+
+
+You can also supply a [TypeScript type argument](https://www.typescriptlang.org/docs/handbook/2/generics.html#using-type-parameters-in-generic-constraints) the query, which will be used to infer the type of the result:
+
+```ts
+type User = {
+ id: string;
+ name: string;
+ email: string;
+};
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request) {
+ let userId = new URL(request.url).searchParams.get('userId');
+ // Supply the type paramter to the query when calling this.sql
+ // This assumes the results returns one or more User rows with "id", "name", and "email" columns
+ const user = await this.sql`SELECT * FROM users WHERE id = ${userId}`;
+ return Response.json(user)
+ }
+}
+```
+
+You do not need to specify an array type (`User[]` or `Array`) as `this.sql` will always return an array of the specified type.
+
+Providing a type parameter does not validate that the result matches your type definition. In TypeScript, properties (fields) that do not exist or conform to the type you provided will be dropped. If you need to validate incoming events, we recommend a library such as [zod](https://zod.dev/) or your own validator logic.
+
+:::note
+
+Learn more about the zero-latency SQL storage that powers both Agents and Durable Objects [on our blog](https://blog.cloudflare.com/sqlite-in-durable-objects/).
+
+:::
+
+The SQL API exposed to an Agent is similar to the one [within Durable Objects](/durable-objects/api/sql-storage/): Durable Object SQL methods available on `this.ctx.storage.sql`. You can use the same SQL queries with the Agent's database, create tables, and query data, just as you would with Durable Objects or [D1](/d1/).
\ No newline at end of file
diff --git a/src/content/docs/agents/examples/rag.mdx b/src/content/docs/agents/examples/rag.mdx
new file mode 100644
index 000000000000000..c836faa12478995
--- /dev/null
+++ b/src/content/docs/agents/examples/rag.mdx
@@ -0,0 +1,96 @@
+---
+title: Retrieval Augmented Generation
+pcx_content_type: concept
+sidebar:
+ order: 7
+
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Agents can use Retrieval Augmented Generation (RAG) to retrieve relevant information and use it augment [calls to AI models](/agents/examples/using-ai-models/). Store a user's chat history to use as context for future conversations, summarize documents to bootstrap an Agent's knowledge base, and/or use data from your Agent's [web browsing](/agents/examples/browse-the-web/) tasks to enhance your Agent's capabilities.
+
+You can use the Agent's own [SQL database](/agents/examples/manage-and-sync-state) as the source of truth for your data and store embeddings in [Vectorize](/vectorize/) (or any other vector-enabled database) to allow your Agent to retrieve relevant information.
+
+### Vector search
+
+:::note
+
+If you're brand-new to vector databases and Vectorize, visit the [Vectorize tutorial](/vectorize/get-started/intro/) to learn the basics, including how to create an index, insert data, and generate embeddings.
+
+:::
+
+You can query a vector index (or indexes) from any method on your Agent: any Vectorize index you attach is available on `this.env` within your Agent. If you've [associated metadata](/vectorize/best-practices/insert-vectors/#metadata) with your vectors that maps back to data stored in your Agent, you can then look up the data directly within your Agent using `this.sql`.
+
+Here's an example of how to give an Agent retrieval capabilties:
+
+
+
+```ts
+import { Agent } from "agents-sdk";
+
+interface Env {
+ AI: Ai;
+ VECTOR_DB: Vectorize;
+}
+
+export class RAGAgent extends Agent {
+ // Other methods on our Agent
+ // ...
+ //
+ async queryKnowledge(userQuery: string) {
+ // Turn a query into an embedding
+ const queryVector = await this.env.AI.run('@cf/baai/bge-base-en-v1.5', {
+ text: [userQuery],
+ });
+
+ // Retrieve results from our vector index
+ let searchResults = await this.env.VECTOR_DB.query(queryVector.data[0], {
+ topK: 10,
+ returnMetadata: 'all',
+ });
+
+ let knowledge = [];
+ for (const match of searchResults.matches) {
+ console.log(match.metadata);
+ knowledge.push(match.metadata);
+ }
+
+ // Use the metadata to re-associate the vector search results
+ // with data in our Agent's SQL database
+ let results = this.sql`SELECT * FROM knowledge WHERE id IN (${knowledge.map((k) => k.id)})`;
+
+ // Return them
+ return results;
+ }
+}
+```
+
+
+
+You'll also need to connect your Agent to your vector indexes:
+
+
+
+```jsonc
+{
+ // ...
+ "vectorize": [
+ {
+ "binding": "VECTOR_DB",
+ "index_name": "your-vectorize-index-name"
+ }
+ ]
+ // ...
+}
+```
+
+
+
+If you have multiple indexes you want to make available, you can provide an array of `vectorize` bindings.
+
+#### Next steps
+
+* Learn more on how to [combine Vectorize and Workers AI](/vectorize/get-started/embeddings/)
+* Review the [Vectorize query API](/vectorize/reference/client-api/)
+* Use [metadata filtering](/vectorize/reference/metadata-filtering/) to add context to your results
diff --git a/src/content/docs/agents/examples/run-workflows.mdx b/src/content/docs/agents/examples/run-workflows.mdx
new file mode 100644
index 000000000000000..d0c9049a95fd3a4
--- /dev/null
+++ b/src/content/docs/agents/examples/run-workflows.mdx
@@ -0,0 +1,108 @@
+---
+title: Run Workflows
+pcx_content_type: concept
+sidebar:
+ order: 4
+
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Agents can trigger asynchronous [Workflows](/workflows/), allowing your Agent to run complex, multi-step tasks in the background. This can include post-processing files that a user has uploaded, updating the embeddings in a [vector database](/vectorize/), and/or managing long-running user-lifecycle email or SMS notification workflows.
+
+Because an Agent is just like a Worker script, it can create Workflows defined in the same project (script) as the Agent _or_ in a different project.
+
+:::note[Agents vs. Workflows]
+
+Agents and Workflows have some similarities: they can both run tasks asynchronously. For straightforward tasks that are linear or need to run to completion, a Workflow can be ideal: steps can be retried, they can be cancelled, and can act on events.
+
+Agents do not have to run to completion: they can loop, branch and run forever, and they can also interact directly with users (over HTTP or WebSockets). An Agent can be used to trigger multiple Workflows as it runs, and can thus be used to co-ordinate and manage Workflows to achieve its goals.
+
+:::
+
+## Trigger a Workflow
+
+An Agent can trigger one or more Workflows from within any method, whether from an incoming HTTP request, a WebSocket connection, on a delay or schedule, and/or from any other action the Agent takes.
+
+Triggering a Workflow from an Agent is no different from [triggering a Workflow from a Worker script](/workflows/build/trigger-workflows/):
+
+
+
+```ts
+interface Env {
+ MY_WORKFLOW: Workflow;
+ MyAgent: AgentNamespace;
+}
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request) {
+ let userId = request.headers.get("user-id");
+ // Trigger a schedule that runs a Workflow
+ // Pass it a payload
+ let { taskId } = await this.schedule(300, "runWorkflow", { id: userId, flight: "DL264", date: "2025-02-23" });
+ }
+
+ async runWorkflow(data) {
+ let instance = await env.MY_WORKFLOW.create({
+ id: data.id,
+ params: data,
+ })
+
+ // Schedule another task that checks the Workflow status every 5 minutes...
+ await this.schedule("*/5 * * * *", "checkWorkflowStatus", { id: instance.id });
+ }
+}
+
+export class MyWorkflow extends WorkflowEntrypoint {
+ async run(event: WorkflowEvent, step: WorkflowStep) {
+ // Your Workflow code here
+ }
+}
+```
+
+
+
+You'll also need to make sure your Agent [has a binding to your Workflow](/workflows/build/trigger-workflows/#workers-api-bindings) so that it can call it:
+
+
+
+```jsonc
+{
+ // ...
+ // Create a binding between your Agent and your Workflow
+ "workflows": [
+ {
+ // Required:
+ "name": "EMAIL_WORKFLOW",
+ "class_name": "MyWorkflow",
+ // Optional: set the script_name field if your Workflow is defined in a
+ // different project from your Agent
+ "script_name": "email-workflows"
+ }
+ ],
+ // ...
+}
+```
+
+
+
+## Trigger a Workflow from another project
+
+You can also call a Workflow that is defined in a different Workers script from your Agent by setting the `script_name` property in the `workflows` binding of your Agent:
+
+
+
+```jsonc
+{
+ // Required:
+ "name": "EMAIL_WORKFLOW",
+ "class_name": "MyWorkflow",
+ // Optional: set tthe script_name field if your Workflow is defined in a
+ // different project from your Agent
+ "script_name": "email-workflows"
+}
+```
+
+
+
+Refer to the [cross-script calls](/workflows/build/workers-api/#cross-script-calls) section of the Workflows documentation for more examples.
diff --git a/src/content/docs/agents/examples/schedule-tasks.mdx b/src/content/docs/agents/examples/schedule-tasks.mdx
new file mode 100644
index 000000000000000..f654eb984bd4268
--- /dev/null
+++ b/src/content/docs/agents/examples/schedule-tasks.mdx
@@ -0,0 +1,106 @@
+---
+title: Schedule tasks
+pcx_content_type: concept
+sidebar:
+ order: 5
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+An Agent can schedule tasks to be run in the future by calling `this.schedule(when, callback, data)`, where `when` can be a delay, a `Date`, or a cron string; `callback` the function name to call, and `data` is an object of data to pass to the function.
+
+Scheduled tasks can do anything a request or message from a user can: make requests, query databases, send emails, read+write state: scheduled tasks can invoke any regular method on your Agent.
+
+### Scheduling tasks
+
+You can call `this.schedule` within any method on an Agent, and schedule tens-of-thousands of tasks per individual Agent:
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+
+export class SchedulingAgent extends Agent {
+ async onRequest(request) {
+ // Handle an incoming request
+ // Schedule a task 5 minutes from now
+ // Calls the "checkFlights" method
+ let { taskId } = await this.schedule(600, "checkFlights", { flight: "DL264", date: "2025-02-23" });
+ return Response.json({ taskId });
+ }
+
+ async checkFlights(data) {
+ // Invoked when our scheduled task runs
+ // We can also call this.schedule here to schedule another task
+ }
+}
+```
+
+
+:::caution
+
+Tasks that set a callback for a method that does not exist will throw an exception: ensure that the method named in the `callback` argument of `this.schedule` exists on your `Agent` class.
+
+:::
+
+You can schedule tasks in multiple ways:
+
+
+
+```ts
+// schedule a task to run in 10 seconds
+let task = await this.schedule(10, "someTask", { message: "hello" });
+
+// schedule a task to run at a specific date
+let task = await this.schedule(new Date("2025-01-01"), "someTask", {});
+
+// schedule a task to run every 10 seconds
+let { id } = await this.schedule("*/10 * * * *", "someTask", { message: "hello" });
+
+// schedule a task to run every 10 seconds, but only on Mondays
+let task = await this.schedule("0 0 * * 1", "someTask", { message: "hello" });
+
+// cancel a scheduled task
+this.cancelSchedule(task.id);
+```
+
+
+
+Calling `await this.schedule` returns a `Schedule`, which includes the task's randomly generated `id`. You can use this `id` to retrieve or cancel the task in the future. It also provides a `type` property that indicates the type of schedule, for example, one of `"scheduled" | "delayed" | "cron"`.
+
+:::note[Maximum scheduled tasks]
+
+Each task is mapped to a row in the Agent's underlying [SQLite database](/durable-objects/api/sql-storage/), which means that each task can be up to 2 MB in size. The maximum number of tasks must be `(task_size * tasks) + all_other_state < maximum_database_size` (currently 1GB per Agent).
+
+:::
+
+### Managing scheduled tasks
+
+You can get, cancel and filter across scheduled tasks within an Agent using the scheduling API:
+
+
+
+```ts
+// Get a specific schedule by ID
+// Returns undefined if the task does not exist
+let task = await this.getSchedule(task.id)
+
+// Get all scheduled tasks
+// Returns an array of Schedule objects
+let tasks = this.getSchedules();
+
+// Cancel a task by its ID
+// Returns true if the task was cancelled, false if it did not exist
+await this.cancelSchedule(task.id);
+
+// Filter for specific tasks
+// e.g. all tasks starting in the next hour
+let tasks = this.getSchedules({
+ timeRange: {
+ start: new Date(Date.now()),
+ end: new Date(Date.now() + 60 * 60 * 1000),
+ }
+});
+```
+
+
diff --git a/src/content/docs/agents/examples/using-ai-models.mdx b/src/content/docs/agents/examples/using-ai-models.mdx
new file mode 100644
index 000000000000000..a1a2d4223142f83
--- /dev/null
+++ b/src/content/docs/agents/examples/using-ai-models.mdx
@@ -0,0 +1,263 @@
+---
+title: Using AI Models
+pcx_content_type: concept
+sidebar:
+ order: 7
+
+---
+
+import { AnchorHeading, MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Agents can communicate with AI models hosted on any provider, including [Workers AI](/workers-ai/), OpenAI, Anthropic, and Google's Gemini, and use the model routing features in [AI Gateway](/ai-gateway/) to route across providers, eval responses, and manage AI provider rate limits.
+
+Because Agents are built on top of [Durable Objects](/durable-objects/), each Agent or chat session is associated with a stateful compute instance. Tradtional serverless architectures often present challenges for persistent connections needed in real-time applications like chat.
+
+A user can disconnect during a long-running response from a modern reasoning model (such as `o3-mini` or DeepSeek R1), or lose conversational context when refreshing the browser. Instead of relying on request-response patterns and managing an external database to track & store conversation state, state can be stored directly within the Agent. If a client disconnects, the Agent can write to its own distributed storage, and catch the client up as soon as it reconnects: even if it's hours or days later.
+
+## Calling AI Models
+
+You can call models from any method within an Agent, including from HTTP requests using the [`onRequest`](/agents/api-reference/sdk/) handler, when a [scheduled task](/agents/examples/schedule-tasks/) runs, when handling a WebSocket message in the [`onMessage`](/agents/examples/websockets/) handler, or from any of your own methods.
+
+Importantly, Agents can call AI models on their own — autonomously — and can handle long-running responses that can take minutes (or longer) to respond in full.
+
+### Long-running model requests {/*long-running-model-requests*/}
+
+Modern [reasoning models](https://platform.openai.com/docs/guides/reasoning) or "thinking" model can take some time to both generate a response _and_ stream the response back to the client.
+
+Instead of buffering the entire response, or risking the client disconecting, you can stream the response back to the client by using the [WebSocket API](/agents/examples/websockets/).
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+import { OpenAI } from "openai"
+
+export class MyAgent extends Agent {
+ async onConnect(connection: Connection, ctx: ConnectionContext) {
+ // Omitted for simplicity: authenticating the user
+ connection.accept()
+ }
+
+ async onMessage(connection: Connection, message: WSMessage) {
+ let msg = JSON.parse(message)
+ // This can run as long as it needs to, and return as many messages as it needs to!
+ await queryReasoningModel(connection, msg.prompt)
+ }
+
+ async queryReasoningModel(connection: Connection, userPrompt: string) {
+ const client = new OpenAI({
+ apiKey: this.env.OPENAI_API_KEY,
+ });
+
+ try {
+ const stream = await client.chat.completions.create({
+ model: this.env.MODEL || 'o3-mini',
+ messages: [{ role: 'user', content: userPrompt }],
+ stream: true,
+ });
+
+ // Stream responses back as WebSocket messages
+ for await (const chunk of stream) {
+ const content = chunk.choices[0]?.delta?.content || '';
+ if (content) {
+ connection.send(JSON.stringify({ type: 'chunk', content }));
+ }
+ }
+
+ // Send completion message
+ connection.send(JSON.stringify({ type: 'done' }));
+ } catch (error) {
+ connection.send(JSON.stringify({ type: 'error', error: error }));
+ }
+ }
+}
+```
+
+
+
+You can also persist AI model responses back to [Agent's internal state](/agents/examples/manage-and-sync-state/) by using the `this.setState` method. For example, if you run a [scheduled task](/agents/examples/schedule-tasks/), you can store the output of the task and read it later. Or, if a user disconnects, read the message history back and send it to the user when they reconnect.
+
+### Workers AI
+
+### Hosted models
+
+You can use [any of the models available in Workers AI](/workers-ai/models/) within your Agent by [configuring a binding](/workers-ai/configuration/bindings/).
+
+Workers AI supports streaming responses out-of-the-box by setting `stream: true`, and we strongly recommend using them to avoid buffering and delaying responses, especially for larger models or reasoning models that require more time to generate a response.
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+
+interface Env {
+ AI: Ai;
+}
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request) {
+ const response = await env.AI.run(
+ "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
+ {
+ prompt: "Build me a Cloudflare Worker that returns JSON.",
+ stream: true, // Stream a response and don't block the client!
+ }
+ );
+
+ // Return the stream
+ return new Response(answer, {
+ headers: { "content-type": "text/event-stream" }
+ })
+ }
+}
+```
+
+
+
+Your wrangler configuration will need an `ai` binding added:
+
+
+
+```toml
+[ai]
+binding = "AI"
+```
+
+
+
+### Model routing
+
+You can also use the model routing features in [AI Gateway](/ai-gateway/) directly from an Agent by specifying a [`gateway` configuration](/ai-gateway/providers/workersai/) when calling the AI binding.
+
+:::note
+
+Model routing allows you to route requests to different AI models based on whether they are reachable, rate-limiting your client, and/or if you've exceeded your cost budget for a specific provider.
+
+:::
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+
+interface Env {
+ AI: Ai;
+}
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request) {
+ const response = await env.AI.run(
+ "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
+ {
+ prompt: "Build me a Cloudflare Worker that returns JSON."
+ },
+ {
+ gateway: {
+ id: "{gateway_id}", // Specify your AI Gateway ID here
+ skipCache: false,
+ cacheTtl: 3360,
+ },
+ },
+ );
+
+ return Response.json(response)
+ }
+}
+```
+
+
+
+Your wrangler configuration will need an `ai` binding added. This is shared across both Workers AI and AI Gateway.
+
+
+```toml
+[ai]
+binding = "AI"
+```
+
+
+
+Visit the [AI Gateway documentation](/ai-gateway/) to learn how to configure a gateway and retrieve a gateway ID.
+
+### AI SDK
+
+The [AI SDK](https://sdk.vercel.ai/docs/introduction) provides a unified API for using AI models, including for text generation, tool calling, structured responses, image generation, and more.
+
+To use the AI SDK, install the `ai` package and use it within your Agent. The example below shows how it use it to generate text on request, but you can use it from any method within your Agent, including WebSocket handlers, as part of a scheduled task, or even when the Agent is initialized.
+
+```sh
+npm install ai @ai-sdk/openai
+```
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+import { generateText } from 'ai';
+import { openai } from '@ai-sdk/openai';
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request): Promise {
+ const { text } = await generateText({
+ model: openai("o3-mini"),
+ prompt: "Build me an AI agent on Cloudflare Workers",
+ });
+
+ return Response.json({modelResponse: text})
+ }
+}
+```
+
+
+
+### OpenAI compatible endpoints
+
+Agents can call models across any service, including those that support the OpenAI API. For example, you can use the OpenAI SDK to use one of [Google's Gemini models](https://ai.google.dev/gemini-api/docs/openai#node.js) directly from your Agent.
+
+Agents can stream responses back over HTTP using Server Sent Events (SSE) from within an `onRequest` handler, or by using the native [WebSockets](/agents/examples/websockets/) API in your Agent to responses back to a client, which is especially useful for larger models that can take over 30+ seconds to reply.
+
+
+
+```ts
+import { Agent } from "agents-sdk"
+import { OpenAI } from "openai"
+
+export class MyAgent extends Agent {
+ async onRequest(request: Request): Promise {
+ const openai = new OpenAI({
+ apiKey: this.env.GEMINI_API_KEY,
+ baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/"
+ });
+
+ // Create a TransformStream to handle streaming data
+ let { readable, writable } = new TransformStream();
+ let writer = writable.getWriter();
+ const textEncoder = new TextEncoder();
+
+ // Use ctx.waitUntil to run the async function in the background
+ // so that it doesn't block the streaming response
+ ctx.waitUntil(
+ (async () => {
+ const stream = await openai.chat.completions.create({
+ model: "4o",
+ messages: [{ role: "user", content: "Write me a Cloudflare Worker." }],
+ stream: true,
+ });
+
+ // loop over the data as it is streamed and write to the writeable
+ for await (const part of stream) {
+ writer.write(
+ textEncoder.encode(part.choices[0]?.delta?.content || ""),
+ );
+ }
+ writer.close();
+ })(),
+ );
+
+ // Return the readable stream back to the client
+ return new Response(readable)
+ }
+}
+```
+
+
diff --git a/src/content/docs/agents/examples/websockets.mdx b/src/content/docs/agents/examples/websockets.mdx
new file mode 100644
index 000000000000000..8eccb861b04b4eb
--- /dev/null
+++ b/src/content/docs/agents/examples/websockets.mdx
@@ -0,0 +1,150 @@
+---
+title: Using WebSockets
+pcx_content_type: concept
+sidebar:
+ order: 2
+
+---
+
+import { MetaInfo, Render, Type, TypeScriptExample, WranglerConfig } from "~/components";
+
+Users and clients can connect to an Agent directly over WebSockets, allowing long-running, bi-directional communication with your Agent as it operates.
+
+To enable an Agent to accept WebSockets, define `onConnect` and `onMessage` methods on your Agent.
+
+* `onConnect(connection: Connection, ctx: ConnectionContext)` is called when a client establishes a new WebSocket connection. The original HTTP request, including request headers, cookies, and the URL itself, are available on `ctx.request`.
+* `onMessage(connection: Connection, message: WSMessage)` is called for each incoming WebSocket message. Messages are one of `ArrayBuffer | ArrayBufferView | string`, and you can send messages back to a client using `connection.send()`. You can distinguish between client connections by checking `connection.id`, which is unique for each connected client.
+
+Here's an example of an Agent that echoes back any message it receives:
+
+
+
+```ts
+import { Agent, Connection } from "agents-sdk";
+
+export class ChatAgent extends Agent {
+ async onConnect(connection: Connection, ctx: ConnectionContext) {
+ // Access the request to verify any authentication tokens
+ // provided in headers or cookies
+ let token = ctx.request.headers.get("Authorization");
+ if (!token) {
+ await connection.close(4000, "Unauthorized");
+ return;
+ }
+
+ // Handle auth using your favorite library and/or auth scheme:
+ // try {
+ // await jwt.verify(token, env.JWT_SECRET);
+ // } catch (error) {
+ // connection.close(4000, 'Invalid Authorization header');
+ // return;
+ // }
+
+ // Accept valid connections
+ connection.accept()
+ }
+
+ async onMessage(connection: Connection, message: WSMessage) {
+ // const response = await longRunningAITask(message)
+ await connection.send(message)
+ }
+}
+```
+
+
+
+## Connecting clients
+
+The Agent framework includes a useful helper package for connecting directly to your Agent (or other Agents) from a client application. Import `agents-sdk/client`, create an instance of `AgentClient` and use it to connect to an instance of your Agent:
+
+
+
+```ts
+import { AgentClient } from "agents-sdk/client";
+
+const connection = new AgentClient({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+});
+
+connection.addEventListener("message", (event) => {
+ console.log("Received:", event.data);
+});
+
+connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What patterns do you see?",
+ })
+);
+```
+
+
+
+## React clients
+
+React-based applications can import `agents-sdk/react` and use the `useAgent` hook to connect to an instance of an Agent directly:
+
+
+
+```ts
+import { useAgent } from "agents-sdk/react";
+
+function AgentInterface() {
+ const connection = useAgent({
+ agent: "dialogue-agent",
+ name: "insight-seeker",
+ onMessage: (message) => {
+ console.log("Understanding received:", message.data);
+ },
+ onOpen: () => console.log("Connection established"),
+ onClose: () => console.log("Connection closed"),
+ });
+
+ const inquire = () => {
+ connection.send(
+ JSON.stringify({
+ type: "inquiry",
+ content: "What insights have you gathered?",
+ })
+ );
+ };
+
+ return (
+
+
+
+ );
+}
+
+```
+
+
+The `useAgent` hook automatically handles the lifecycle of the connection, ensuring that it is properly initialized and cleaned up when the component mounts and unmounts. You can also [combine `useAgent` with `useState`](/agents/examples/manage-and-sync-state/) to automatically synchronize state across all clients connected to your Agent.
+
+## Handling WebSocket events
+
+Define `onError` and `onClose` methods on your Agent to explicitly handle WebSocket client errors and close events. Log errors, clean up state, and/or emit metrics:
+
+
+
+```ts
+import { Agent, Connection } from "agents-sdk";
+
+export class ChatAgent extends Agent {
+ // onConnect and onMessage methods
+ // ...
+
+ // WebSocket error and disconnection (close) handling.
+ async onError(connection: Connection, error: unknown): Promise {
+ console.error(`WS error: ${error}`);
+ }
+ async onClose(connection: Connection, code: number, reason: string, wasClean: boolean): Promise {
+ console.log(`WS closed: ${code} - ${reason} - wasClean: ${wasClean}`);
+ connection.close();
+ }
+}
+
+```
+
+
diff --git a/src/content/docs/agents/getting-started/build-a-chat-agent.mdx b/src/content/docs/agents/getting-started/build-a-chat-agent.mdx
new file mode 100644
index 000000000000000..0c6f329b147e1f7
--- /dev/null
+++ b/src/content/docs/agents/getting-started/build-a-chat-agent.mdx
@@ -0,0 +1,9 @@
+---
+pcx_content_type: navigation
+title: Build a Chat Agent
+external_link: https://github.com/cloudflare/agents-starter
+sidebar:
+ order: 3
+head: []
+description: A starter template for building AI-powered chat agents using Cloudflare's Agent platform, powered by agents-sdk. This project provides a foundation for creating interactive chat experiences with AI, complete with a modern UI and tool integration capabilities.
+---
diff --git a/src/content/docs/agents/getting-started/index.mdx b/src/content/docs/agents/getting-started/index.mdx
new file mode 100644
index 000000000000000..d3a068f800f2789
--- /dev/null
+++ b/src/content/docs/agents/getting-started/index.mdx
@@ -0,0 +1,13 @@
+---
+title: Getting started
+pcx_content_type: navigation
+sidebar:
+ order: 1
+ group:
+ hideIndex: true
+
+---
+
+import { DirectoryListing } from "~/components"
+
+
diff --git a/src/content/docs/agents/getting-started/testing-your-agent.mdx b/src/content/docs/agents/getting-started/testing-your-agent.mdx
new file mode 100644
index 000000000000000..d57f42948062a32
--- /dev/null
+++ b/src/content/docs/agents/getting-started/testing-your-agent.mdx
@@ -0,0 +1,144 @@
+---
+title: Testing your Agents
+pcx_content_type: get-started
+sidebar:
+ order: 10
+
+---
+
+import { Render, PackageManagers, WranglerConfig } from "~/components"
+
+Because Agents run on Cloudflare Workers and Durable Objects, they can be tested using the same tools and techniques as Workers and Durable Objects.
+
+## Writing and running tests
+
+### Setup
+
+:::note
+
+The `agents-sdk-starter` template and new Cloudflare Workers projects already include the relevant `vitest` and `@cloudflare/vitest-pool-workers` packages, as well as a valid `vitest.config.js` file.
+
+:::
+
+Before you write your first test, install the necessary packages:
+
+```sh
+npm install vitest@2.1.8 --save-dev --save-exact
+npm install @cloudflare/vitest-pool-workers --save-dev
+```
+
+Ensure that your `vitest.config.js` file is identical to the following:
+
+```js
+import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
+
+export default defineWorkersConfig({
+ test: {
+ poolOptions: {
+ workers: {
+ wrangler: { configPath: "./wrangler.toml" },
+ },
+ },
+ },
+});
+```
+
+### Add the Agent configuration
+
+Add a `durableObjects` configuration to `vitest.config.js` with the name of your Agent class:
+
+```js
+import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
+
+export default defineWorkersConfig({
+ test: {
+ poolOptions: {
+ workers: {
+ main: './src/index.ts',
+ miniflare: {
+ durableObjects: {
+ NAME: 'MyAgent',
+ },
+ },
+ },
+ },
+ },
+});
+```
+
+### Write a test
+
+:::note
+
+Review the [Vitest documentation](https://vitest.dev/) for more information on testing, including the test API reference and advanced testing techniques.
+
+:::
+
+Tests use the `vitest` framework. A basic test suite for your Agent can validate how your Agent responds to requests, but can also unit test your Agent's methods and state.
+
+```ts
+import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test';
+import { describe, it, expect } from 'vitest';
+import worker from '../src';
+import { Env } from '../src';
+
+interface ProvidedEnv extends Env {}
+
+describe('make a request to my Agent', () => {
+ // Unit testing approach
+ it('responds with state', async () => {
+ // Provide a valid URL that your Worker can use to route to your Agent
+ // If you are using routeAgentRequest, this will be /agent/:agent/:name
+ const request = new Request('http://example.com/agent/my-agent/agent-123');
+ const ctx = createExecutionContext();
+ const response = await worker.fetch(request, env, ctx);
+ await waitOnExecutionContext(ctx);
+ expect(await response.text()).toMatchObject({ hello: 'from your agent' });
+ });
+
+ it('also responds with state', async () => {
+ const request = new Request('http://example.com/agent/my-agent/agent-123');
+ const response = await SELF.fetch(request);
+ expect(await response.text()).toMatchObject({ hello: 'from your agent' });
+ });
+});
+```
+
+### Run tests
+
+Running tests is done using the `vitest` CLI:
+
+```sh
+$ npm run test
+# or run vitest directly
+$ npx vitest
+```
+```sh output
+ MyAgent
+ ✓ should return a greeting (1 ms)
+
+Test Files 1 passed (1)
+```
+
+Review the [documentation on testing](/workers/testing/vitest-integration/get-started/write-your-first-test/) for additional examples and test configuration.
+
+## Running Agents locally
+
+You can also run an Agent locally using the `wrangler` CLI:
+
+```sh
+$ npx wrangler dev
+```
+```sh output
+Your Worker and resources are simulated locally via Miniflare. For more information, see: https://developers.cloudflare.com/workers/testing/local-development.
+
+Your worker has access to the following bindings:
+- Durable Objects:
+ - MyAgent: MyAgent
+ Starting local server...
+[wrangler:inf] Ready on http://localhost:53645
+```
+
+This spins up a local development server that runs the same runtime as Cloudflare Workers, and allows you to iterate on your Agent's code and test it locally without deploying it.
+
+Visit the [`wrangler dev`](https://developers.cloudflare.com/workers/wrangler/commands/#dev) docs to review the CLI flags and configuration options.
diff --git a/src/content/docs/agents/guides/anthropic-agent-patterns.mdx b/src/content/docs/agents/guides/anthropic-agent-patterns.mdx
new file mode 100644
index 000000000000000..733b411bda5493e
--- /dev/null
+++ b/src/content/docs/agents/guides/anthropic-agent-patterns.mdx
@@ -0,0 +1,9 @@
+---
+pcx_content_type: navigation
+title: Build a Human-in-the-loop Agent
+external_link: https://github.com/cloudflare/agents/tree/main/guides/human-in-the-loop
+sidebar:
+ order: 2
+head: []
+description: Implement human-in-the-loop functionality using Cloudflare Agents, allowing AI agents to request human approval before executing certain actions
+---
diff --git a/src/content/docs/agents/guides/human-in-the-loop.mdx b/src/content/docs/agents/guides/human-in-the-loop.mdx
new file mode 100644
index 000000000000000..20b69c099ab7b85
--- /dev/null
+++ b/src/content/docs/agents/guides/human-in-the-loop.mdx
@@ -0,0 +1,9 @@
+---
+pcx_content_type: navigation
+title: Implement Effective Agent Patterns
+external_link: https://github.com/cloudflare/agents/tree/main/guides/anthropic-patterns
+sidebar:
+ order: 3
+head: []
+description: Implement common agent patterns using the `agents-sdk` framework.
+---
diff --git a/src/content/docs/agents/guides/index.mdx b/src/content/docs/agents/guides/index.mdx
new file mode 100644
index 000000000000000..ff592a9753bea85
--- /dev/null
+++ b/src/content/docs/agents/guides/index.mdx
@@ -0,0 +1,12 @@
+---
+title: Guides
+pcx_content_type: navigation
+sidebar:
+ order: 4
+ group:
+ hideIndex: true
+---
+
+import { DirectoryListing } from "~/components"
+
+
diff --git a/src/content/docs/agents/index.mdx b/src/content/docs/agents/index.mdx
index 321bead61e59595..701e8f694d16fdd 100644
--- a/src/content/docs/agents/index.mdx
+++ b/src/content/docs/agents/index.mdx
@@ -1,5 +1,5 @@
---
-title: Build agents on Cloudflare
+title: Build Agents on Cloudflare
type: overview
pcx_content_type: overview
sidebar:
@@ -15,6 +15,7 @@ import {
Feature,
LinkButton,
LinkTitleCard,
+ PackageManagers,
Plan,
RelatedProduct,
Render,
@@ -22,482 +23,48 @@ import {
Tabs,
} from "~/components";
-Build AI-powered agents that can autonomously perform tasks, persist state, browse the web, and communicate back to users in real-time over any channel.
+Build and deploy AI-powered Agents on Cloudflare that can autonomously perform tasks, communicate with clients in real time, persist state, execute long-running and repeat tasks on a schedule, send emails, run asynchronous workflows, browse the web, query data from your Postgres database, call AI models, support human-in-the-loop use-cases, and more.
-- **Serverless inference that scales up _and_ down**: run AI directly on Cloudflare, without worrying about pre-provisioning VMs at peak, and worrying about utilization. Call the latest open-source models on [Workers AI](/workers-ai/), and pay just for what you use.
-- **Non I/O bound pricing:** don't pay for long-running processes when your code is not executing. Cloudflare Workers is designed to scale down and [only charge you for CPU time](https://blog.cloudflare.com/workers-pricing-scale-to-zero/), as opposed to wall-clock time.
-- **Designed for durable execution:** [Durable Objects](/durable-objects/) and [Workflows](/workflows) are built for a programming model that enables guaranteed execution for async tasks like long-running deep thinking LLM calls, human-in-the-loop, or unreliable API calls.
-- **Scalable, and reliable, without compromising on performance:** by running on Cloudflare's network, agents can execute tasks close to the user without introducing latency for real-time experiences.
+#### Ship your first Agent
-## Start building
-
-
-
-
-Build agents that can execute complex tasks, progressively save state, and call out to _any_ third party API they need to using [Workflows](/workflows/). Send emails or [text messages](/workflows/examples/twilio/), [browse the web](/browser-rendering/), process and summarize documents, and/or query your database.
-
-```sh
-npm create cloudflare@latest workflows-starter -- --template "cloudflare/workflows-starter"
-cd workflows-starter
-npm i resend
-```
-
-```ts collapse={30-1000}
-import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
-import { Resend } from 'resend';
-
-type Env = {
- MY_WORKFLOW: Workflow;
- RESEND_API_KEY: string;
-};
-
-type Params = {
- email: string;
- metadata: Record;
-};
-
-export class MyWorkflow extends WorkflowEntrypoint {
- async run(event: WorkflowEvent, step: WorkflowStep) {
-
- const files = await step.do('my first step', async () => {
- // Fetch a list of files from $SOME_SERVICE
- return {
- files: [
- 'doc_7392_rev3.pdf',
- 'report_x29_final.pdf',
- 'memo_2024_05_12.pdf',
- 'file_089_update.pdf',
- 'proj_alpha_v2.pdf',
- 'data_analysis_q2.pdf',
- 'notes_meeting_52.pdf',
- 'summary_fy24_draft.pdf',
- ],
- };
- });
-
- const summaries = await step.do('summarize text', async () => {
- const results = {};
- for (const filename of files.files) {
- const fileContent = await this.env.MY_BUCKET.get(filename);
- if (!fileContent) continue;
-
- const text = await fileContent.text();
- const summary = await this.env.WORKERS_AI.run('@cf/meta/llama-3.2-3b-instruct', {
- messages: [{
- role: 'user',
- content: `Please summarize the following text concisely: ${text}`
- }]
- });
- results[filename] = summary.response;
- }
- return results;
- });
-
- await step.sleep('wait on something', '1 minute');
-
- let summaryKey = await step.do(
- 'store summaries in R2',
- async () => {
- const summaryKey = `summaries-${Date.now()}.json`;
- await this.env.MY_BUCKET.put(summaryKey, JSON.stringify(summaries));
- return summaryKey;
- },
- );
-
- await step.do(
- 'email summaries',
- {
- retries: {
- limit: 3,
- delay: '5 second',
- backoff: 'exponential',
- }
- },
- async () => {
- const summaryText = Object.entries(summaries)
- .map(([filename, summary]) => `${filename}:\n${summary}\n\n`)
- .join('');
-
- const resend = new Resend(this.env.RESEND_API_KEY);
-
- await resend.emails.send({
- from: 'notifications@yourdomain.com',
- to: event.payload.email,
- subject: 'Your Document Summaries',
- text: summaryText,
- });
- }
- );
-
- return summaryKey;
- }
-}
-
-export default {
- async fetch(req: Request, env: Env): Promise {
- let id = new URL(req.url).searchParams.get('instanceId');
-
- if (id) {
- let instance = await env.MY_WORKFLOW.get(id);
- return Response.json({
- status: await instance.status(),
- });
- }
-
- let instance = await env.MY_WORKFLOW.create();
- return Response.json({
- id: instance.id,
- details: await instance.status(),
- });
- },
-};
-```
-
-
-
-
-Use [Durable Objects](/durable-objects/) — stateful, serverless, long-running micro-servers — to ship interactive, real-time agents that can connect to the latest AI models.
-
-Stream responses over [WebSockets](/durable-objects/best-practices/websockets/), and don't time out while waiting for the latest chain-of-thought models — including `o1` or `deepseek-r1` — to respond.
-
-```ts
-npm i openai
-```
-
-```ts collapse={30-1000}
-import { DurableObject } from "cloudflare:workers";
-
-export interface Env {
- DURABLE_AGENT: DurableObjectNamespace;
- OPENAI_API_KEY: string;
-}
-
-export default {
- async fetch(request: Request, env: Env, ctx: ExecutionContext) {
- if (request.url.endsWith("/agent/chat")) {
- const upgradeHeader = request.headers.get("Upgrade");
- if (!upgradeHeader || upgradeHeader !== "websocket") {
- return Response.json(
- { error: "Durable Object expected Upgrade: websocket" },
- { status: 426 }
- );
- }
-
- const url = new URL(request.url);
- const agentId = url.searchParams.get("id") || (await crypto.randomUUID());
-
- let id = env.DURABLE_AGENT.idFromName(agentId);
- let agent = env.DURABLE_AGENT.get(id);
-
- return agent.fetch(request);
- }
-
- return Response.json({ message: "Bad Request" }, { status: 400 });
- },
-};
-
-export class DurableAgent extends DurableObject {
- constructor(private state: DurableObjectState, private env: Env) {
- super();
- }
-
- async fetch(request: Request): Promise {
- const webSocketPair = new WebSocketPair();
- const [client, server] = Object.values(webSocketPair);
-
- this.ctx.acceptWebSocket(server);
-
- return new Response(null, {
- status: 101,
- webSocket: client,
- });
- }
-
- async webSocketMessage(ws: WebSocket, message: ArrayBuffer | string) {
- try {
- const openai = new OpenAI({
- apiKey: this.env.OPENAI_API_KEY,
- timeout: 10 * 60 * 1000, // Don't let it think TOO long.
- });
-
- // Stream the response to immediately start sending chunks to the client,
- // rather than buffering the entire response and making the user wait
- const stream = await openai.chat.completions.create({
- model: "o1",
- messages: [{ role: "user", content: message.toString() }],
- stream: true,
- });
-
- for await (const chunk of stream) {
- const content = chunk.choices[0]?.delta?.content;
- if (content) {
- ws.send(content);
- }
- }
- } catch (error) {
- ws.send(
- JSON.stringify({
- error: "OpenAI request failed",
- message: error.message,
- })
- );
- }
- }
-
- async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) {
- ws.close(code, "Durable Object is closing WebSocket");
- }
-}
-```
-
-
-
-
-Use the [Browser Rendering API](/browser-rendering/) to allow your agents to search the web, take screenshots, and directly interact with websites.
-
-```sh
-npm install @cloudflare/puppeteer --save-dev
-```
-
-```ts collapse={30-1000}
-import puppeteer from "@cloudflare/puppeteer";
-
-interface Env {
- MYBROWSER: Fetcher;
- BROWSER_KV_DEMO: KVNamespace;
-}
-
-export default {
- async fetch(request: Request, env: Env): Promise {
- const { searchParams } = new URL(request.url);
- const url = searchParams.get("url");
-
- if (!url) {
- return new Response("Please add an ?url=https://example.com/ parameter");
- }
-
- const normalizedUrl = new URL(url).toString();
- let img = await env.BROWSER_KV_DEMO.get(normalizedUrl, { type: "arrayBuffer" });
-
- if (img === null) {
- const browser = await puppeteer.launch(env.MYBROWSER);
- const page = await browser.newPage();
- await page.goto(normalizedUrl);
- img = await page.screenshot() as Buffer;
-
- await env.BROWSER_KV_DEMO.put(normalizedUrl, img, {
- expirationTtl: 60 * 60 * 24, // 24 hours
- });
-
- await browser.close();
- }
-
- return new Response(img, {
- headers: {
- "content-type": "image/jpeg",
- },
- });
- },
-};
-```
-
-
-
-
-Use [AI Gateway](/ai-gateway/) to cache, log, retry and run [evals](/ai-gateway/evaluations/) (evaluations) for your agents, no matter where they're deployed.
-
-```py collapse={30-1000}
-from anthropic import Anthropic
-
-anthropic = Anthropic(
- api_key="",
- # Route, cache, fallback and log prompt-response pairs between your app
- # and your AI model provider.
- base_url="https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/anthropic"
-)
-
-message = anthropic.messages.create(
- model="claude-3-opus-20240229",
- max_tokens=1000,
- messages=[{
- "role": "user",
- "content": "Generate a Cloudflare Worker that returns a simple JSON payload based on a query param",
- }]
-)
-
-print(message.content)
-```
-
-
-
-
-## Use your favorite AI framework
-
-Build agents using your favorite AI frameworks, and deploy it directly to [Cloudflare Workers](/workers/).
-
-
-
-
-Use [LangChain](https://js.langchain.com/docs/integrations/text_embedding/cloudflare_ai/) to build Retrieval-Augmented Generation (RAG) applications using [Workers AI](/workers-ai/) and [Vectorize](/vectorize/).
-
-Give your agents more context and the ability to search across content, reply to user queries, and expand their domain knowledge.
+Use the agent started template to create your first Agent with the `agents-sdk`:
```sh
-npm i @langchain/cloudflare hono
+# install it
+npm create cloudflare@latest agents-starter -- --template=cloudflare/agents-starter
+# and deploy it
+npx wrangler@latest deploy
```
-```ts collapse={30-1000}
-import {
- CloudflareVectorizeStore,
- CloudflareWorkersAIEmbeddings
-} from "@langchain/cloudflare";
-import { VectorizeIndex } from "@cloudflare/workers-types";
-import { Ai } from "@cloudflare/ai";
-import { Hono } from "hono";
-
-export interface Env {
- VECTORIZE_INDEX: VectorizeIndex;
- AI: Ai;
-}
-
-const app = new Hono<{ Bindings: Env }>();
-
-app.get("/", async (c) => {
- const embeddings = new CloudflareWorkersAIEmbeddings({
- binding: c.env.AI,
- model: "@cf/baai/bge-small-en-v1.5",
- });
-
- const store = new CloudflareVectorizeStore(embeddings, {
- index: c.env.VECTORIZE_INDEX,
- });
-
- const results = await store.similaritySearch("hello", 5);
- return c.json(results);
-});
-
-app.post("/load", async (c) => {
- const embeddings = new CloudflareWorkersAIEmbeddings({
- binding: c.env.AI,
- model: "@cf/baai/bge-small-en-v1.5",
- });
-
- const store = new CloudflareVectorizeStore(embeddings, {
- index: c.env.VECTORIZE_INDEX,
- });
-
- const documents = [
- { pageContent: "hello", metadata: {} },
- { pageContent: "world", metadata: {} },
- { pageContent: "hi", metadata: {} }
- ];
+Head to the guide on [building a chat agent](/agents/getting-started/build-a-chat-agent) to learn how to build and deploy an Agent to prod.
- await store.addDocuments(documents, {
- ids: ["id1", "id2", "id3"]
- });
-
- return c.json({ success: true });
-});
-
-app.delete("/clear", async (c) => {
- const embeddings = new CloudflareWorkersAIEmbeddings({
- binding: c.env.AI,
- model: "@cf/baai/bge-small-en-v1.5",
- });
-
- const store = new CloudflareVectorizeStore(embeddings, {
- index: c.env.VECTORIZE_INDEX,
- });
-
- await store.delete({ ids: ["id1", "id2", "id3"] });
- return c.json({ success: true });
-});
-
-export default app;
-```
-
-
-
-
-Ship faster with the [AI SDK](https://sdk.vercel.ai/docs/introduction): make it easier to generate text, tool call and/or get structured output from your AI models (and then deploy it to [Workers](/workers/)).
-
-```sh
-npm i ai workers-ai-provider
-```
-
-```ts collapse={30-1000}
-import { createWorkersAI } from 'workers-ai-provider';
-import { streamText } from 'ai';
-
-type Env = {
- AI: Ai;
-};
-
-export default {
- async fetch(_: Request, env: Env) {
- const workersai = createWorkersAI({ binding: env.AI });
- const result = streamText({
- model: workersai('@cf/meta/llama-3.2-3b-instruct'),
- prompt: 'Write short essay on why you like Cloudflare Durable Objects.',
- });
-
- return result.toTextStreamResponse({
- headers: {
- 'Content-Type': 'text/x-unknown',
- 'content-encoding': 'identity',
- 'transfer-encoding': 'chunked',
- },
- });
- },
-};
-```
-
-
-
-
-Use any model provider with OpenAI compatible endpoints, including [ChatGPT](https://platform.openai.com/docs/quickstart), [DeepSeek](https://api-docs.deepseek.com/) and [Workers AI](/workers-ai/configuration/open-ai-compatibility/), directly from Cloudflare Workers.
+If you're already building on [Workers](/workers/), you can install the `agents-sdk` package directly into an existing project:
```sh
-npm i openai
+npm i agents-sdk
```
-```ts collapse={30-1000}
-import OpenAI from "openai";
+Dive into the [Agent SDK reference](/agents/api-reference/sdk/) to learn more about how to use the `agents-sdk` package and defining an `Agent`.
-export interface Env {
- OPENAI_API_KEY: string;
-}
+#### Why build agents on Cloudflare?
-export default {
- async fetch(request: Request, env: Env) {
- const url = new URL(request.url);
- const prompt = url.searchParams.get('prompt') || "Make some robot noises";
+We built the `agents-sdk` with a few things in mind:
- const openai = new OpenAI({
- apiKey: env.OPENAI_API_KEY
- });
+- **Batteries (state) included**: Agents come with [built-in state management](/agents/examples/manage-and-sync-state/), with the ability to automatically sync state between an Agent and clients, trigger events on state changes, and read+write to each Agent's SQL database.
+- **Communicative**: You can connect to an Agent via [WebSockets](/agents/examples/websockets/) and stream updates back to client in real-time. Handle a long-running response from a reasoning model, the results of an [asynchronous workflow](/agents/examples/run-workflows/), or build a chat app that builds on the `useAgent` hook included in the `agents-sdk`.
+- **Extensible**: Agents are code. Use the [AI models](/agents/examples/using-ai-models/) you want, bring-your-own headless browser service, pull data from your database hosted in another cloud, add your own methods to your Agent and call them.
- const chatCompletion = await openai.chat.completions.create({
- messages: [{ role: "user", content: prompt }],
- model: "gpt-3.5-turbo",
- });
+Agents built with `agents-sdk` can be deployed directly to Cloudflare and run on top of [Durable Objects](/durable-objects/) — which you can think of as stateful micro-servers that can scale to tens of millions — and are able to run wherever they need to. Run your Agents close to a user for low-latency interactivity, close to your data for throughput, and/or anywhere in between.
- const embeddings = await openai.embeddings.create({
- model: "text-embedding-ada-002",
- input: "Cloudflare Agents documentation",
- });
+***
- return new Response(JSON.stringify({ chatCompletion, embeddings }));
- }
-}
-```
+#### Build on the Cloudflare Platform
-
-
+
-***
+Build serverless applications and deploy instantly across the globe for exceptional performance, reliability, and scale.
-## All the products you need in one platform
+
@@ -517,12 +84,6 @@ Run machine learning models, powered by serverless GPUs, on Cloudflare's global
-
-
-Build real-time serverless video, audio and data applications with WebRTC running on Cloudflare's network.
-
-
-
Build stateful agents that guarantee executions, including automatic retries, persistent state that runs for minutes, hours, days, or weeks.
diff --git a/src/content/docs/agents/build/index.mdx b/src/content/docs/agents/platform/index.mdx
similarity index 77%
rename from src/content/docs/agents/build/index.mdx
rename to src/content/docs/agents/platform/index.mdx
index b75d6ff811c8788..77d033f1bfa25aa 100644
--- a/src/content/docs/agents/build/index.mdx
+++ b/src/content/docs/agents/platform/index.mdx
@@ -1,6 +1,6 @@
---
pcx_content_type: reference
-title: Build
+title: Reference
sidebar:
order: 10
group:
@@ -9,6 +9,6 @@ sidebar:
import { DirectoryListing } from "~/components";
-Build
+Build AI Agents on Cloudflare
diff --git a/src/content/docs/agents/platform/limits.mdx b/src/content/docs/agents/platform/limits.mdx
new file mode 100644
index 000000000000000..be10fa8a1fb9441
--- /dev/null
+++ b/src/content/docs/agents/platform/limits.mdx
@@ -0,0 +1,31 @@
+---
+pcx_content_type: concept
+title: Limits
+sidebar:
+ order: 1
+
+---
+
+import { Render } from "~/components"
+
+Limits that apply to authoring, deploying, and running Agents are detailed below.
+
+Many limits are inherited from those applied to Workers scripts and/or Durable Objects, and are detailed in the [Workers limits](/workers/platform/limits/) documentation.
+
+::: note
+
+| Feature | Limit |
+| ----------------------------------------- | ----------------------- |
+| Max concurrent (running) Agents per account | Tens of millions+ [^1]
+| Max definitions per account | ~250,000+ [^2]
+| Max state stored per unique Agent | 1 GB |
+| Max compute time per Agent | 30 seconds (refreshed per HTTP request / incoming WebSocket message) [^3] |
+| Duration (wall clock) per step [^3] | Unlimited (e.g. waiting on a database call or an LLM response) |
+
+---
+
+[^1]: Yes, really. You can have tens of millions of Agents running concurrently, as each Agent is mapped to a [unique Durable Object](/durable-objects/what-are-durable-objects/) (actor).
+[^2]: You can deploy up to [500 scripts per account](/workers/platform/limits/), but each script (project) can define multiple Agents. Each deployed script can be up to 10 MB on the [Workers Paid Plan](/workers/platform/pricing/#workers)
+[^3]: Compute (CPU) time per Agent is limited to 30 seconds, but this is refreshed when an Agent receives a new HTTP request, runs a [scheduled task](/agents/examples/schedule-tasks/), or an incoming WebSocket message.
+
+
diff --git a/src/content/docs/agents/build/llms.txt.mdx b/src/content/docs/agents/platform/llms.txt.mdx
similarity index 93%
rename from src/content/docs/agents/build/llms.txt.mdx
rename to src/content/docs/agents/platform/llms.txt.mdx
index 2f46b6a2c61acb7..9e4d540f2fd35de 100644
--- a/src/content/docs/agents/build/llms.txt.mdx
+++ b/src/content/docs/agents/platform/llms.txt.mdx
@@ -3,7 +3,7 @@ pcx_content_type: navigation
title: llms.txt
external_link: /llms.txt
sidebar:
- order: 20
+ order: 99
head: []
description: Provide context to your AI models & tools when building on Cloudflare.
---
diff --git a/src/content/docs/agents/platform/prompting.mdx b/src/content/docs/agents/platform/prompting.mdx
new file mode 100644
index 000000000000000..aef72526d8226e4
--- /dev/null
+++ b/src/content/docs/agents/platform/prompting.mdx
@@ -0,0 +1,9 @@
+---
+pcx_content_type: navigation
+title: Prompt Engineering
+external_link: /workers/get-started/prompting/
+sidebar:
+ order: 98
+head: []
+description: Learn how to prompt engineer your AI models & tools when building Agents & Workers on Cloudflare.
+---
diff --git a/src/content/docs/agents/products/ai-gateway.mdx b/src/content/docs/agents/products/ai-gateway.mdx
deleted file mode 100644
index 152bb1879e435b4..000000000000000
--- a/src/content/docs/agents/products/ai-gateway.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: AI Gateway
-external_link: /ai-gateway/
-sidebar:
- order: 3
-head: []
-description: Observe and control your AI applications.
----
-
-
diff --git a/src/content/docs/agents/products/durable-objects.mdx b/src/content/docs/agents/products/durable-objects.mdx
deleted file mode 100644
index 955c94ff44a712d..000000000000000
--- a/src/content/docs/agents/products/durable-objects.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Durable Objects
-external_link: /durable-objects/
-sidebar:
- order: 6
-head: []
-description: Create collaborative applications, real-time chat, multiplayer games and more without needing to coordinate state or manage infrastructure.
----
-
-
diff --git a/src/content/docs/agents/products/email-workers.mdx b/src/content/docs/agents/products/email-workers.mdx
deleted file mode 100644
index f2a3ccc62285ae3..000000000000000
--- a/src/content/docs/agents/products/email-workers.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Email Workers
-external_link: /email-routing/email-workers/
-sidebar:
- order: 4
-head: []
-description: Implement any logic you need to process your emails and create complex rules
----
-
-
diff --git a/src/content/docs/agents/products/index.mdx b/src/content/docs/agents/products/index.mdx
deleted file mode 100644
index 0fce7832787300d..000000000000000
--- a/src/content/docs/agents/products/index.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-pcx_content_type: reference
-title: Products
-sidebar:
- order: 3
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-Example Reference Architectures
-
-
diff --git a/src/content/docs/agents/products/real-time.mdx b/src/content/docs/agents/products/real-time.mdx
deleted file mode 100644
index 3d86980d7cc2e42..000000000000000
--- a/src/content/docs/agents/products/real-time.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Real Time
-external_link: /calls/
-sidebar:
- order: 10
-head: []
-description: Build real-time serverless video, audio and data applications.
----
-
-
diff --git a/src/content/docs/agents/products/workers-ai.mdx b/src/content/docs/agents/products/workers-ai.mdx
deleted file mode 100644
index fb54c8993df2406..000000000000000
--- a/src/content/docs/agents/products/workers-ai.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Workers AI
-external_link: /workers-ai/
-sidebar:
- order: 2
-head: []
-description: Run machine learning models, powered by serverless GPUs, on Cloudflare's global network.
----
-
-
diff --git a/src/content/docs/agents/products/workflows.mdx b/src/content/docs/agents/products/workflows.mdx
deleted file mode 100644
index eeb7aca8b0d2bcf..000000000000000
--- a/src/content/docs/agents/products/workflows.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-pcx_content_type: navigation
-title: Workflows
-external_link: /workflows/
-sidebar:
- order: 5
-head: []
-description: Build durable multi-step applications on Cloudflare Workers with Workflows.
----
-
-
diff --git a/src/content/docs/agents/reference-architectures/index.mdx b/src/content/docs/agents/reference-architectures/index.mdx
deleted file mode 100644
index e1dd3af47f3a47b..000000000000000
--- a/src/content/docs/agents/reference-architectures/index.mdx
+++ /dev/null
@@ -1,14 +0,0 @@
----
-pcx_content_type: reference
-title: Reference Architectures
-sidebar:
- order: 1
- group:
- hideIndex: true
----
-
-import { DirectoryListing } from "~/components";
-
-Example Reference Architectures
-
-
diff --git a/src/content/docs/agents/reference-architectures/rag.mdx b/src/content/docs/agents/reference-architectures/rag.mdx
deleted file mode 100644
index 7154404f2de6591..000000000000000
--- a/src/content/docs/agents/reference-architectures/rag.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-pcx_content_type: navigation
-title: Retrieval Augmented Generation
-external_link: /reference-architecture/diagrams/ai/ai-rag/
-sidebar:
- order: 2
-head: []
-description: Build RAG architectures on Cloudflare
----
\ No newline at end of file
diff --git a/src/content/docs/agents/reference-architectures/text-and-call.mdx b/src/content/docs/agents/reference-architectures/text-and-call.mdx
deleted file mode 100644
index 5471f0d78645de3..000000000000000
--- a/src/content/docs/agents/reference-architectures/text-and-call.mdx
+++ /dev/null
@@ -1,9 +0,0 @@
----
-pcx_content_type: navigation
-title: Send text messages from agents
-external_link: /workflows/examples/twilio/
-sidebar:
- order: 3
-head: []
-description: Send text messages and accept phone calls from your agent
----
\ No newline at end of file