Building Robust AI Workflows at the Edge: Cloudflare Agents SDK Patterns
Share this article
Cloudflare’s Agents SDK is a lightweight framework that turns Durable Objects into autonomous AI agents. It bundles the power of Cloudflare Workers, the persistence of Durable Objects, and the flexibility of the new AI SDK in a single, developer‑friendly package.
Why Agents Matter
- Stateful LLM workflows – Agents automatically persist state across restarts, so a multi‑step conversation can resume where it left off.
- Built‑in scheduling –
schedule()lets you run daily quality checks or periodic retraining without a separate cron job. - Agent‑to‑Agent communication –
AgentNamespacegives you a typed, safe channel to delegate work to specialized agents. - SQL integration – The
sqltag makes database access feel like a first‑class language feature.
These capabilities make it trivial to implement the five classic AI patterns that Cloudflare’s own patterns page showcases.
Pattern 1: Prompt Chaining with State Persistence
The marketing‑copy agent demonstrates a simple two‑step chain: generate copy, evaluate it, and rewrite if needed. The key is that each step stores its result in the Durable Object’s state and in a D1 table.
export class MarketingCopyAgent extends Agent {
async onRequest(request: Request): Promise<Response> {
const { input } = await request.json();
const state = await this.getState();
if (state.currentCopy && state.step === "generated") {
return this.evaluateAndImprove(state.currentCopy);
}
const { text: copy } = await generateText({
model: this.env.AI_MODEL,
prompt: `Write persuasive marketing copy for: ${input}. Focus on benefits and emotional appeal.`,
});
await this.sql`INSERT INTO agent_state (agent_id, step, data, created_at) VALUES (${this.ctx.id.toString()}, 'generated', ${JSON.stringify({ copy })}, ${Date.now()})`;
await this.setState({ currentCopy: copy, step: "generated" });
return this.evaluateAndImprove(copy);
}
/* ... */
}
If the agent crashes mid‑process, the next request can pick up from the last persisted step, ensuring no duplicated work.
Pattern 2: Routing with Agent‑to‑Agent Communication
Routing agents act as a dispatcher: they classify a user query and hand it off to a specialized agent (support, refund, or technical). The routing decision is logged for analytics.
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();
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}`,
});
await this.sql`INSERT INTO routing_log (query, type, complexity, reasoning, timestamp) VALUES (${query}, ${classification.type}, ${classification.complexity}, ${classification.reasoning}, ${Date.now()})`;
const agentId = this.agents.idFromName(`${classification.type}-agent`);
const specializedAgent = this.agents.get(agentId);
return specializedAgent.fetch(new Request("https://agent/run", { method: "POST", body: JSON.stringify({ query, classification }) }));
}
}
Each downstream agent runs in its own Durable Object, guaranteeing isolation while still sharing the same database.
Pattern 3: Parallelization with Result Aggregation
The code‑review agent launches three independent LLM calls—security, performance, maintainability—using Promise.all. Results are stored together and then summarized by a fourth LLM call.
export class CodeReviewAgent extends Agent {
async onRequest(request: Request): Promise<Response> {
const { code } = await request.json();
const [security, performance, maintainability] = await Promise.all([
this.reviewSecurity(code),
this.reviewPerformance(code),
this.reviewMaintainability(code),
]);
const reviews = [
{ ...security, type: "security" },
{ ...performance, type: "performance" },
{ ...maintainability, type: "maintainability" },
];
await this.sql`INSERT INTO code_reviews (code_hash, reviews, created_at) VALUES (${this.hashCode(code)}, ${JSON.stringify(reviews)}, ${Date.now()})`;
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)}`,
});
return new Response(JSON.stringify({ reviews, summary: summary.text }), { headers: { "Content-Type": "application/json" } });
}
}
Because the agent’s state tracks the status flag, a partial failure can be retried without re‑executing the entire review.
Pattern 4: Orchestrator‑Workers with Agent Namespace
An orchestrator agent plans a feature by generating a file‑change plan, then delegates each file change to a worker agent. Results are aggregated and persisted.
export class OrchestratorAgent extends Agent {
private agents: AgentNamespace;
/* ... */
async onRequest(request: Request): Promise<Response> {
const { featureRequest } = await request.json();
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"]),
}),
prompt: `Analyze this feature request and create an implementation plan: ${featureRequest}`,
});
const workerTasks = implementationPlan.files.map((file, i) => this.delegateToWorker(file, featureRequest, i));
const results = await Promise.allSettled(workerTasks);
/* store and return */
}
}
Each worker is a dedicated Durable Object that can be scaled independently, yet the orchestrator keeps the overall workflow in a single, auditable transaction.
Pattern 5: Evaluator‑Optimizer with Scheduling
The evaluator agent runs a daily cron job that pulls recent system outputs, evaluates them, and triggers an optimizer if the score is low. The optimizer then schedules a follow‑up evaluation every six hours.
export class EvaluatorAgent extends Agent {
async onStart() {
await this.schedule({ name: "daily-evaluation", cron: "0 2 * * *", action: async () => await this.evaluateSystem() });
}
/* evaluation logic */
}
This pattern demonstrates how Agents can be the backbone of a self‑healing AI system.
Wrangling the Deployment
The wrangler.jsonc snippet below shows how to wire all agents, the D1 database, and the AI model binding. Notice the agents field that registers each class with Cloudflare.
{
"name": "agents-patterns",
"main": "src/index.ts",
"agents": {
"binding": "AGENTS",
"agents": [
{ "name": "MarketingCopyAgent", "class": "MarketingCopyAgent" },
{ "name": "RoutingAgent", "class": "RoutingAgent" },
/* ... */
]
},
"d1_databases": [
{ "binding": "DB", "database_name": "agents_db", "database_id": "your-database-id" }
],
"ai": { "binding": "AI_MODEL", "model": "@cf/meta/llama-3-8b-instruct" }
}
Deploying with wrangler publish gives you a fully‑functional AI orchestration stack that scales automatically.
Takeaway
Cloudflare’s Agents SDK turns the edge into a programmable AI factory. By combining Durable Objects, state persistence, scheduling, and inter‑agent messaging, developers can build robust, fault‑tolerant workflows that go beyond simple request‑response patterns. The five patterns above are just the beginning—any complex LLM‑driven pipeline can be expressed as a set of cooperating agents.
Source: https://coey.dev/agents-patterns