Build a Durable AI Agent·续

This is especially important for:

  • LLM calls: Expensive and slow, should not repeat unnecessarily
  • External APIs: May have rate limits or side effects
  • Idempotency: Some tools (like sending emails) should not run twice

</details>

4. Write your Agent

The Agent handles HTTP requests, WebSocket connections, and Workflow lifecycle events. It triggers a workflow instance runWorkflow() and receives progress updates via callbacks.

Create src/agent.ts:

src/agent.tsts

import { Agent } from "agents";

type State = {
    currentWorkflow?: string;
    status?: string;
};

export class ResearchAgent extends Agent<Env, State> {
    initialState: State = {};

// Start a research task - called via HTTP or WebSocket
    async startResearch(task: string) {
        const instanceId = await this.runWorkflow("RESEARCH_WORKFLOW", { task });
        this.setState({
            ...this.state,
            currentWorkflow: instanceId,
            status: "running",
        });
        return { instanceId };
    }

// Get status of a workflow
    async getResearchStatus(instanceId: string) {
        return this.getWorkflow(instanceId);
    }

// Called when workflow reports progress
    async onWorkflowProgress(
        workflowName: string,
        instanceId: string,
        progress: unknown,
    ) {
        // Broadcast to all connected WebSocket clients
        this.broadcast(JSON.stringify({ type: "progress", instanceId, progress }));
    }

// Called when workflow completes
    async onWorkflowComplete(
        workflowName: string,
        instanceId: string,
        result?: unknown,
    ) {
        this.setState({ ...this.state, status: "complete" });
        this.broadcast(JSON.stringify({ type: "complete", instanceId, result }));
    }

// Called when workflow errors
    async onWorkflowError(
        workflowName: string,
        instanceId: string,
        error: string,
    ) {
        this.setState({ ...this.state, status: "error" });
        this.broadcast(JSON.stringify({ type: "error", instanceId, error }));
    }
}

5. Configure your project

  1. Open wrangler.jsonc and add the Agent and Workflow configuration:
   {
    "$schema": "node_modules/wrangler/config-schema.json",
    "name": "durable-ai-agent",
    "main": "src/index.ts",
       "upload_source_maps": true,
    // Set this to today's date
    "compatibility_date": "2026-10-27",
    "observability": {
        "enabled": true
    },
    "durable_objects": {
        "bindings": [
            {
                "name": "ResearchAgent",
                "class_name": "ResearchAgent"
            }
        ]
    },
    "workflows": [
        {
            "name": "research-workflow",
            "binding": "RESEARCH_WORKFLOW",
            "class_name": "ResearchWorkflow"
        }
    ],
    "migrations": [
        {
            "tag": "v1",
            "new_sqlite_classes": ["ResearchAgent"]
        }
    ]
   }
   ```

```toml
   "$schema" = "node_modules/wrangler/config-schema.json"
   name = "durable-ai-agent"
   main = "src/index.ts"
   upload_source_maps = true
   # Set this to today's date
   compatibility_date = "2026-10-27"

[observability]
   enabled = true

[[durable_objects.bindings]]
   name = "ResearchAgent"
   class_name = "ResearchAgent"

[[workflows]]
   name = "research-workflow"
   binding = "RESEARCH_WORKFLOW"
   class_name = "ResearchWorkflow"

[[migrations]]
   tag = "v1"
   new_sqlite_classes = [ "ResearchAgent" ]
   ```

2. Generate types for your bindings:

```sh
   npx wrangler types
   ```

This creates a `worker-configuration.d.ts` file with the `Env` type that includes your bindings.

## 6. Write your API

The Worker routes requests to the Agent, which manages workflow lifecycle. Use `routeAgentRequest()` for WebSocket connections and `getAgentByName()` for server-side RPC calls.

Replace `src/index.ts`:

*src/index.tsts*

```ts
import { getAgentByName, routeAgentRequest } from "agents";

export { ResearchAgent } from "./agent";
export { ResearchWorkflow } from "./workflow";

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);

// Route WebSocket connections to /agents/research-agent/{name}
        const agentResponse = await routeAgentRequest(request, env);
        if (agentResponse) return agentResponse;

// HTTP API for starting research tasks
        if (request.method === "POST" && url.pathname === "/research") {
            const { task, agentId } = await request.json<{
                task: string;
                agentId?: string;
            }>();

// Get agent instance by name (creates if doesn't exist)
            const agent = await getAgentByName(
                env.ResearchAgent,
                agentId ?? "default",
            );

// Start the research workflow via RPC
            const result = await agent.startResearch(task);
            return Response.json(result);
        }

// Check workflow status
        if (url.pathname === "/status") {
            const instanceId = url.searchParams.get("instanceId");
            const agentId = url.searchParams.get("agentId") ?? "default";

if (!instanceId) {
                return Response.json({ error: "instanceId required" }, { status: 400 });
            }

const agent = await getAgentByName(env.ResearchAgent, agentId);
            const status = await agent.getResearchStatus(instanceId);

return Response.json(status);
        }

return new Response("POST /research with { task } to start", {
            status: 400,
        });
    },
} satisfies ExportedHandler<Env>;

7. Develop locally

  1. Create a .env file for local development:

.envsh

   ANTHROPIC_API_KEY=your-api-key-here
   ```

2. Start the dev server:

```sh
   npx wrangler dev
   ```

3. Start a research task:

```sh
   curl -X POST http://localhost:8787/research \
     -H "Content-Type: application/json" \
     -d '{"task": "Compare open-source LLM projects"}'
   ```

```json
   { "instanceId": "abc-123-def" }
   ```

4. Check progress (may take a few seconds to complete):

```sh
   curl "http://localhost:8787/status?instanceId=abc-123-def"
   ```

The agent will search for repositories, fetch details, and return a comparison. Progress updates are broadcast to any connected WebSocket clients.

## 8. Deploy

1. Deploy the Worker:

```sh
   npx wrangler deploy
   ```

2. Add your API key as a secret:

```sh
   npx wrangler secret put ANTHROPIC_API_KEY
   ```

3. Start a research task on your deployed Worker:

```sh
   curl -X POST https://durable-ai-agent.<your-subdomain>.workers.dev/research \
     -H "Content-Type: application/json" \
     -d '{"task": "Compare open-source LLM projects"}'
   ```

4. Inspect workflow runs with the CLI:

```sh
   npx wrangler workflows instances describe research-workflow latest
   ```

This shows every step the agent took, including LLM calls, tool executions, timing, and any retries.

You can also view this in the Cloudflare dashboard under **research-workflow**. [Go to **Workflows** ↗](https://dash.cloudflare.com/?to=/:account/workers/workflows)

## Real-time client integration

Connect to your Agent via WebSocket to receive real-time progress updates. The `useAgent` hook connects to `/agents/{agent-name}/{instance-name}`:

```plaintext
/agents/research-agent/default  → ResearchAgent instance "default"
/agents/research-agent/user-123 → ResearchAgent instance "user-123"
import { useState } from "react";
import { useAgent } from "agents/react";

function ResearchUI({ agentId = "default" }) {
    const [progress, setProgress] = useState(null);

const { state } = useAgent({
        agent: "research-agent", // Maps to ResearchAgent class
        name: agentId, // Instance name
        onMessage: (message) => {
            const data = JSON.parse(message.data);
            if (data.type === "progress") {
                setProgress(data.progress);
            }
        },
    });

return (
        <div>
            {progress && (
                <p>
                    {progress.message} ({Math.round(progress.percent * 100)}%)
                </p>
            )}
        </div>
    );
}

Agent class names are automatically converted to kebab-case for URLs (ResearchAgent → research-agent).

Learn more

Agents SDK Workflows

Complete API reference for AgentWorkflow, lifecycle callbacks, and bidirectional communication.

Events and parameters

Pass data to Workflows and pause for external events with waitForEvent.

Sleeping and retrying

Configure retry behavior and sleep patterns.

Workers API

Explore the full Workflows API for programmatic control.

Agents SDK

For interactive agents with real-time chat and WebSocket connections.

Was this helpful?

YesNo

On this page

Build a Durable AI Agent·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/workflows/get-started/durable-agents/#page","headline":"Build a Durable AI Agent","description":"Build a durable AI agent using Cloudflare Workflows that researches GitHub repositories with automatic retries.","url":"https://developers.cloudflare.com/workflows/get-started/durable-agents/","inLanguage":"en","image":"https://developers.cloudflare.com/workflows/get-started/durable-agents/og.png?v=174bc75a15bd8094","dateModified":"2026-10-12","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Create and deploy your first Cloudflare Workflow with durable, multi-step execution on the Workers platform. title: Build your first Workflow image: https://developers.cloudflare.com/workflows/get-started/guide/og.png?v=96c1b93bf642b79e


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/workflows/llms.txt
Use this file to discover all available pages before exploring further.