CLOUDFLARE AGENTS PATTERNS

Implementing patterns with the Agents SDK

AGENTS SDK 2025.11.29

OVERVIEW

Cloudflare's patterns page demonstrates agent patterns using the AI SDK. Here's how to implement the same patterns using the Cloudflare Agents SDK, which provides additional capabilities like state management, scheduling, and database access.

Agents extend the Agent class, run as Durable Objects, and include built-in state management, scheduling, and database access.

Agent class → Durable Objects
         ↓
    State, scheduling, sql

PATTERN 1: PROMPT CHAINING

Sequential steps where each LLM call processes the previous output. With state persistence.

// Pattern 1: Prompt Chaining with Agents SDK
import { Agent } from "@cloudflare/agents";
import { generateText, generateObject } from "ai";
import { z } from "zod";

export class MarketingCopyAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { input } = await request.json();

    // Check if we have intermediate state from previous run
    const state = await this.getState();
    if (state.currentCopy && state.step === "generated") {
      // Resume from evaluation step
      return this.evaluateAndImprove(state.currentCopy);
    }

    // Step 1: Generate marketing copy
    const { text: copy } = await generateText({
      model: this.env.AI_MODEL,
      prompt: `Write persuasive marketing copy for: ${input}. Focus on benefits and emotional appeal.`,
    });

    // Store intermediate result in database
    await this.sql`
      INSERT INTO agent_state (agent_id, step, data, created_at)
      VALUES (${this.ctx.id.toString()}, 'generated', ${JSON.stringify({ copy })}, ${Date.now()})
    `;

    // Update agent state
    await this.setState({ currentCopy: copy, step: "generated" });

    // Step 2: Evaluate the generated copy
    return this.evaluateAndImprove(copy);
  }

  private async evaluateAndImprove(copy: string): Promise<Response> {
    const { object: qualityMetrics } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        hasCallToAction: z.boolean(),
        emotionalAppeal: z.number().min(1).max(10),
        clarity: z.number().min(1).max(10),
      }),
      prompt: `Evaluate this marketing copy:
      1. Presence of call to action (true/false)
      2. Emotional appeal (1-10)
      3. Clarity (1-10)
      
      Copy: ${copy}`,
    });

    // Step 3: Improve if needed
    if (
      !qualityMetrics.hasCallToAction ||
      qualityMetrics.emotionalAppeal < 7 ||
      qualityMetrics.clarity < 7
    ) {
      const { text: improvedCopy } = await generateText({
        model: this.env.AI_MODEL,
        prompt: `Rewrite this marketing copy with:
        ${!qualityMetrics.hasCallToAction ? "- A clear call to action" : ""}
        ${qualityMetrics.emotionalAppeal < 7 ? "- Stronger emotional appeal" : ""}
        ${qualityMetrics.clarity < 7 ? "- Improved clarity and directness" : ""}
        
        Original: ${copy}`,
      });

      // Store final result
      await this.sql`
        INSERT INTO agent_results (agent_id, result, metrics, created_at)
        VALUES (${this.ctx.id.toString()}, ${improvedCopy}, ${JSON.stringify(qualityMetrics)}, ${Date.now()})
      `;

      await this.setState({ step: "completed" });

      return new Response(
        JSON.stringify({ copy: improvedCopy, qualityMetrics }),
        { headers: { "Content-Type": "application/json" } }
      );
    }

    // Store final result
    await this.sql`
      INSERT INTO agent_results (agent_id, result, metrics, created_at)
      VALUES (${this.ctx.id.toString()}, ${copy}, ${JSON.stringify(qualityMetrics)}, ${Date.now()})
    `;

    await this.setState({ step: "completed" });

    return new Response(
      JSON.stringify({ copy, qualityMetrics }),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

State persists between steps. If agent restarts, it can resume from last step using stored state.

PATTERN 2: ROUTING

Classify input, route to specialized agents. With agent-to-agent communication.

// Pattern 2: Routing with Agent-to-Agent Communication
import { Agent, AgentNamespace } from "@cloudflare/agents";
import { generateObject } from "ai";
import { z } from "zod";

export class RoutingAgent extends Agent {
  private agents: AgentNamespace;

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.agents = env.AGENTS;
  }

  async onRequest(request: Request): Promise<Response> {
    const { query } = await request.json();

    // Step 1: Classify the query
    const { object: classification } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        reasoning: z.string(),
        type: z.enum(["general", "refund", "technical"]),
        complexity: z.enum(["simple", "complex"]),
      }),
      prompt: `Classify this customer query:
      ${query}
      
      Determine:
      1. Query type (general, refund, or technical)
      2. Complexity (simple or complex)
      3. Brief reasoning`,
    });

    // Log routing decision
    await this.sql`
      INSERT INTO routing_log (query, type, complexity, reasoning, timestamp)
      VALUES (${query}, ${classification.type}, ${classification.complexity}, ${classification.reasoning}, ${Date.now()})
    `;

    // Step 2: Route to specialized agent
    const agentId = this.agents.idFromName(`${classification.type}-agent`);
    const specializedAgent = this.agents.get(agentId);

    const response = await specializedAgent.fetch(
      new Request("https://agent/run", {
        method: "POST",
        body: JSON.stringify({ query, classification }),
      })
    );

    return response;
  }
}

export class SupportAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { query } = await request.json();

    const response = await this.generateText({
      model: this.env.AI_MODEL,
      system: "You are an expert customer service agent handling general inquiries.",
      prompt: query,
    });

    return new Response(
      JSON.stringify({ type: "support", response: response.text }),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

export class RefundAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { query } = await request.json();

    const response = await this.generateText({
      model: this.env.AI_MODEL,
      system: "You are a customer service agent specializing in refund requests. Follow company policy and collect necessary information.",
      prompt: query,
    });

    return new Response(
      JSON.stringify({ type: "refund", response: response.text }),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

export class TechnicalAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { query } = await request.json();

    const response = await this.generateText({
      model: this.env.AI_MODEL,
      system: "You are a technical support specialist with deep product knowledge. Focus on clear step-by-step troubleshooting.",
      prompt: query,
    });

    // Store technical queries for analytics
    await this.sql`
      INSERT INTO technical_queries (query, response, timestamp)
      VALUES (${query}, ${response.text}, ${Date.now()})
    `;

    return new Response(
      JSON.stringify({ type: "technical", response: response.text }),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

Agents can call other agents. Each agent is a Durable Object instance. Routing decisions logged to database for analytics.

PATTERN 3: PARALLELIZATION

Run multiple tasks concurrently. With result aggregation and database logging.

// Pattern 3: Parallelization with Result Aggregation
import { Agent } from "@cloudflare/agents";
import { generateObject } from "ai";
import { z } from "zod";

export class CodeReviewAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { code } = await request.json();

    // Track parallel execution state
    await this.setState({ 
      status: "processing",
      startTime: Date.now(),
      tasks: ["security", "performance", "maintainability"]
    });

    // Step 1: Run parallel reviews
    const [securityReview, performanceReview, maintainabilityReview] =
      await Promise.all([
        this.reviewSecurity(code),
        this.reviewPerformance(code),
        this.reviewMaintainability(code),
      ]);

    const reviews = [
      { ...securityReview, type: "security" },
      { ...performanceReview, type: "performance" },
      { ...maintainabilityReview, type: "maintainability" },
    ];

    // Step 2: Store results in database
    await this.sql`
      INSERT INTO code_reviews (code_hash, reviews, created_at)
      VALUES (${this.hashCode(code)}, ${JSON.stringify(reviews)}, ${Date.now()})
    `;

    // Step 3: Aggregate results
    const summary = await this.generateText({
      model: this.env.AI_MODEL,
      system: "You are a technical lead summarizing multiple code reviews.",
      prompt: `Synthesize these code review results into a concise summary with key actions:
      ${JSON.stringify(reviews, null, 2)}`,
    });

    await this.setState({ 
      status: "completed",
      endTime: Date.now(),
      summary: summary.text
    });

    return new Response(
      JSON.stringify({ reviews, summary: summary.text }),
      { headers: { "Content-Type": "application/json" } }
    );
  }

  private async reviewSecurity(code: string) {
    const { object } = await generateObject({
      model: this.env.AI_MODEL,
      system: "You are an expert in code security. Focus on identifying security vulnerabilities, injection risks, and authentication issues.",
      schema: z.object({
        vulnerabilities: z.array(z.string()),
        riskLevel: z.enum(["low", "medium", "high"]),
        suggestions: z.array(z.string()),
      }),
      prompt: `Review this code for security issues:
      ${code}`,
    });
    return object;
  }

  private async reviewPerformance(code: string) {
    const { object } = await generateObject({
      model: this.env.AI_MODEL,
      system: "You are an expert in code performance. Focus on identifying performance bottlenecks, memory leaks, and optimization opportunities.",
      schema: z.object({
        issues: z.array(z.string()),
        impact: z.enum(["low", "medium", "high"]),
        optimizations: z.array(z.string()),
      }),
      prompt: `Review this code for performance issues:
      ${code}`,
    });
    return object;
  }

  private async reviewMaintainability(code: string) {
    const { object } = await generateObject({
      model: this.env.AI_MODEL,
      system: "You are an expert in code quality. Focus on code structure, readability, and adherence to best practices.",
      schema: z.object({
        concerns: z.array(z.string()),
        qualityScore: z.number().min(1).max(10),
        recommendations: z.array(z.string()),
      }),
      prompt: `Review this code for maintainability:
      ${code}`,
    });
    return object;
  }

  private hashCode(code: string): string {
    // Simple hash for tracking
    return Buffer.from(code).toString("base64").slice(0, 16);
  }
}

Results stored in database automatically. Agent state tracks parallel execution. Can resume if partial completion.

PATTERN 4: ORCHESTRATOR-WORKERS

Central agent delegates to worker agents. With proper agent namespace communication.

// Pattern 4: Orchestrator-Workers with Agent Namespace
import { Agent, AgentNamespace } from "@cloudflare/agents";
import { generateObject } from "ai";
import { z } from "zod";

export class OrchestratorAgent extends Agent {
  private agents: AgentNamespace;

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.agents = env.AGENTS;
  }

  async onRequest(request: Request): Promise<Response> {
    const { featureRequest } = await request.json();

    // Track orchestration state
    await this.setState({
      status: "planning",
      featureRequest,
      startTime: Date.now(),
    });

    // Step 1: Plan the implementation
    const { object: implementationPlan } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        files: z.array(
          z.object({
            purpose: z.string(),
            filePath: z.string(),
            changeType: z.enum(["create", "modify", "delete"]),
          })
        ),
        estimatedComplexity: z.enum(["low", "medium", "high"]),
      }),
      system: "You are a senior software architect planning feature implementations.",
      prompt: `Analyze this feature request and create an implementation plan:
      ${featureRequest}`,
    });

    await this.setState({
      status: "executing",
      plan: implementationPlan,
    });

    // Step 2: Delegate to worker agents
    const workerTasks = implementationPlan.files.map((file, index) =>
      this.delegateToWorker(file, featureRequest, index)
    );

    // Step 3: Collect results
    const results = await Promise.allSettled(workerTasks);

    const fileChanges = results.map((result, index) => {
      if (result.status === "fulfilled") {
        return {
          file: implementationPlan.files[index],
          implementation: result.value,
        };
      } else {
        return {
          file: implementationPlan.files[index],
          error: result.reason.message,
        };
      }
    });

    // Step 4: Store orchestration results
    await this.sql`
      INSERT INTO orchestrations (feature_request, plan, results, created_at)
      VALUES (${featureRequest}, ${JSON.stringify(implementationPlan)}, ${JSON.stringify(fileChanges)}, ${Date.now()})
    `;

    await this.setState({
      status: "completed",
      endTime: Date.now(),
    });

    return new Response(
      JSON.stringify({
        plan: implementationPlan,
        changes: fileChanges,
      }),
      { headers: { "Content-Type": "application/json" } }
    );
  }

  private async delegateToWorker(
    file: { purpose: string; filePath: string; changeType: string },
    featureRequest: string,
    index: number
  ) {
    const workerId = this.agents.idFromName(`worker-${index}`);
    const worker = this.agents.get(workerId);

    const workerSystemPrompt = {
      create:
        "You are an expert at implementing new files following best practices and project patterns.",
      modify:
        "You are an expert at modifying existing code while maintaining consistency and avoiding regressions.",
      delete:
        "You are an expert at safely removing code while ensuring no breaking changes.",
    }[file.changeType];

    const response = await worker.fetch(
      new Request("https://agent/run", {
        method: "POST",
        body: JSON.stringify({
          file,
          featureRequest,
          systemPrompt: workerSystemPrompt,
        }),
      })
    );

    return response.json();
  }
}

export class WorkerAgent extends Agent {
  async onRequest(request: Request): Promise<Response> {
    const { file, featureRequest, systemPrompt } = await request.json();

    const { object: change } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        explanation: z.string(),
        code: z.string(),
      }),
      system: systemPrompt,
      prompt: `Implement the changes for ${file.filePath} to support:
      ${file.purpose}
      
      Consider the overall feature context:
      ${featureRequest}`,
    });

    // Store worker result
    await this.sql`
      INSERT INTO worker_results (worker_id, file_path, result, created_at)
      VALUES (${this.ctx.id.toString()}, ${file.filePath}, ${JSON.stringify(change)}, ${Date.now()})
    `;

    return new Response(
      JSON.stringify(change),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

Orchestrator uses AgentNamespace to communicate with workers. Each worker is isolated Durable Object. Results aggregated with state tracking.

PATTERN 5: EVALUATOR-OPTIMIZER

Evaluation loop with scheduled optimization. Using built-in scheduling.

// Pattern 5: Evaluator-Optimizer with Scheduling
import { Agent, AgentNamespace } from "@cloudflare/agents";
import { generateText, generateObject } from "ai";
import { z } from "zod";

export class EvaluatorAgent extends Agent {
  async onStart() {
    // Schedule daily evaluation
    await this.schedule({
      name: "daily-evaluation",
      cron: "0 2 * * *", // 2 AM daily
      action: async () => {
        await this.evaluateSystem();
      },
    });
  }

  async onRequest(request: Request): Promise<Response> {
    const { systemOutput, context } = await request.json();

    const evaluation = await this.evaluateOutput(systemOutput, context);

    return new Response(
      JSON.stringify(evaluation),
      { headers: { "Content-Type": "application/json" } }
    );
  }

  private async evaluateSystem() {
    // Get recent system outputs
    const recentOutputs = await this.sql`
      SELECT output, context FROM system_outputs 
      WHERE created_at > ${Date.now() - 24 * 60 * 60 * 1000}
      ORDER BY created_at DESC
      LIMIT 10
    `;

    for (const row of recentOutputs) {
      await this.evaluateOutput(row.output, row.context);
    }
  }

  private async evaluateOutput(
    systemOutput: string,
    context?: Record<string, any>
  ) {
    const { object: evaluation } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        accuracy: z.number().min(0).max(10),
        relevance: z.number().min(0).max(10),
        completeness: z.number().min(0).max(10),
        overallScore: z.number().min(0).max(10),
        strengths: z.array(z.string()),
        weaknesses: z.array(z.string()),
        recommendations: z.array(z.string()),
      }),
      system: "You are an expert in evaluating system performance.",
      prompt: `Evaluate this system output:
      Output: ${systemOutput}
      Context: ${JSON.stringify(context || {})}
      
      Consider:
      1. Overall quality (0-10)
      2. Accuracy (0-10)
      3. Relevance (0-10)
      4. Completeness (0-10)
      5. Strengths and weaknesses
      6. Improvement recommendations`,
    });

    // Store evaluation history
    await this.sql`
      INSERT INTO evaluations (output_hash, overall_score, evaluation_data, created_at)
      VALUES (${this.hashOutput(systemOutput)}, ${evaluation.overallScore}, ${JSON.stringify(evaluation)}, ${Date.now()})
    `;

    // Trigger optimizer if score is low
    if (evaluation.overallScore < 7) {
      await this.triggerOptimizer(evaluation);
    }

    return evaluation;
  }

  private async triggerOptimizer(evaluation: any) {
    const agents = this.env.AGENTS as AgentNamespace;
    const optimizerId = agents.idFromName("optimizer");
    const optimizer = agents.get(optimizerId);

    await optimizer.fetch(
      new Request("https://agent/run", {
        method: "POST",
        body: JSON.stringify({ evaluation }),
      })
    );
  }

  private hashOutput(output: string): string {
    return Buffer.from(output).toString("base64").slice(0, 16);
  }
}

export class OptimizerAgent extends Agent {
  private agents: AgentNamespace;

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.agents = env.AGENTS;
  }

  async onRequest(request: Request): Promise<Response> {
    const { evaluation } = await request.json();

    // Step 1: Determine optimization strategy
    const { object: strategy } = await generateObject({
      model: this.env.AI_MODEL,
      schema: z.object({
        priority: z.enum(["high", "medium", "low"]),
        strategies: z.array(z.string()),
        expectedImprovement: z.number().min(0).max(10),
        implementationSteps: z.array(z.string()),
      }),
      system: "You are an expert in system optimization.",
      prompt: `Based on this evaluation, suggest specific optimization strategies:
      Evaluation: ${JSON.stringify(evaluation)}
      
      Provide:
      1. Priority level (high/medium/low)
      2. Specific strategies
      3. Expected improvement (0-10)
      4. Implementation steps`,
    });

    // Step 2: Apply optimization if priority is high
    if (strategy.priority === "high") {
      const { text: implementation } = await generateText({
        model: this.env.AI_MODEL,
        system: "You are an expert at implementing optimizations.",
        prompt: `Implement the following optimization strategies:
        ${strategy.strategies.join("\n")}
        
        Provide a detailed implementation plan.`,
      });

      // Store optimization history
      await this.sql`
        INSERT INTO optimizations (evaluation_id, strategy, result, created_at)
        VALUES (${evaluation.overallScore}, ${JSON.stringify(strategy)}, ${implementation}, ${Date.now()})
      `;

      // Schedule follow-up evaluation
      await this.schedule({
        name: "post-optimization-evaluation",
        cron: "0 */6 * * *", // Every 6 hours
        action: async () => {
          const evaluatorId = this.agents.idFromName("evaluator");
          const evaluator = this.agents.get(evaluatorId);

          await evaluator.fetch(
            new Request("https://agent/run", {
              method: "POST",
              body: JSON.stringify({
                systemOutput: implementation,
                context: { optimized: true },
              }),
            })
          );
        },
      });

      return new Response(
        JSON.stringify({
          optimized: true,
          strategy,
          implementation,
        }),
        { headers: { "Content-Type": "application/json" } }
      );
    }

    return new Response(
      JSON.stringify({
        optimized: false,
        strategy,
        reason: "Priority not high enough for immediate optimization",
      }),
      { headers: { "Content-Type": "application/json" } }
    );
  }
}

Uses schedule() for recurring evaluations. Optimization history in database. Can trigger other agents on conditions.

WRANGLER CONFIG

// wrangler.jsonc
{
  "name": "agents-patterns",
  "main": "src/index.ts",
  "compatibility_date": "2024-01-01",
  
  "agents": {
    "binding": "AGENTS",
    "agents": [
      { "name": "MarketingCopyAgent", "class": "MarketingCopyAgent" },
      { "name": "RoutingAgent", "class": "RoutingAgent" },
      { "name": "SupportAgent", "class": "SupportAgent" },
      { "name": "RefundAgent", "class": "RefundAgent" },
      { "name": "TechnicalAgent", "class": "TechnicalAgent" },
      { "name": "CodeReviewAgent", "class": "CodeReviewAgent" },
      { "name": "OrchestratorAgent", "class": "OrchestratorAgent" },
      { "name": "WorkerAgent", "class": "WorkerAgent" },
      { "name": "EvaluatorAgent", "class": "EvaluatorAgent" },
      { "name": "OptimizerAgent", "class": "OptimizerAgent" }
    ]
  },
  
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "agents_db",
      "database_id": "your-database-id"
    }
  ],
  
  "ai": {
    "binding": "AI_MODEL",
    "model": "@cf/meta/llama-3-8b-instruct"
  }
}

AGENTS SDK FEATURES

  • State persistence: Agents survive restarts with state management.
  • Database access: Built-in sql template tag for database operations.
  • Scheduling: schedule() for recurring tasks and workflows.
  • Agent communication: AgentNamespace for agent-to-agent calls with proper isolation.
  • Durable Objects: Each agent runs as a Durable Object instance with automatic scaling and persistence.

AGENTS SDK CAPABILITIES

  • • Agent class extends DurableObject
  • • Built-in state management with setState() and getState()
  • • schedule() for recurring tasks and workflows
  • • AgentNamespace for agent-to-agent communication
  • • sql template tag for database operations
  • • State persistence across restarts
  • • Automatic scaling with Durable Objects

REFERENCE

Original patterns: Cloudflare Agents Patterns

Agents SDK docs: Cloudflare Agents Documentation