GoF Patterns in AI Agents: The Complete Map
GoF patterns don't disappear in agentic systems—they transform. Command=tool call, Mediator=orchestrator, Adapter=MCP. The complete map with code.
Contributors: Esther Aznar
GoF patterns have existed for thirty years: Factory, Observer, Command, Singleton. They are proven ways to organize code.
In AI agents, these patterns reappear with new names and a clearer purpose.
Before you continue: this article is technical, but you don’t need to be an expert. It’s helpful to know some basic concepts, but we’ll explain it step by step with easy-to-understand examples.
GoF Patterns in Thirty Seconds
In 1994, a very famous book came out with 23 solutions to problems every programmer faces. They’re not lines of code, but proven ways to organize your program so it’s easier to change and understand.
Imagine they’re like construction blueprints: instead of inventing how to build each house, you use a blueprint that already worked.
Patterns are divided into groups by what they solve:
-
Creational: how to create things (like telling a machine to fabricate an object)
-
Structural: how to connect things together (like assembling pieces)
-
Behavioral: who does what and in what order (how actions are coordinated)
-
Enterprise: how to organize very large systems
In AI agent systems (machines that think and act on their own), almost all these patterns reappear with new names. The following table shows how old patterns serve modern agents. Don’t worry if you don’t understand the full table now—we’ll explain the most important ones step by step.
The Complete Map
| Classic Pattern | Family | Agentic Equivalent |
|---|---|---|
| Factory | Creational | Creates the right subagent based on task type |
| Builder | Creational | Incrementally builds agent context |
| Prototype | Creational | Reusable and cloneable prompt templates |
| Singleton | Creational | Centralized tool registry |
| Adapter | Structural | MCP: adapts any API to the format the model expects |
| Decorator | Structural | Guardrails: wrap the agent without touching its logic |
| Proxy | Structural | Model router: redirects by cost or capacity |
| Composite | Structural | Agent-of-agents: one agent coordinating other agents |
| Command | Behavioral | Tool call: encapsulated action the model can invoke |
| Observer | Behavioral | Hooks: react to agent events |
| Strategy | Behavioral | Model selection based on task |
| Mediator | Behavioral | Orchestrator: coordinates agents without direct communication |
| Chain of Responsibility | Behavioral | Pipeline of chained agents |
| Template Method | Behavioral | System prompt: defines expected response structure |
| Repository | PoEAA | RAG: abstracts external knowledge retrieval |
| DTO | PoEAA | Structured outputs: schema-defined objects between agents |
| Service Layer | PoEAA | Layer of orchestrator agents with clear responsibility |
| Saga | EIP | Long-running agentic transactions with compensation |
| Pub-Sub | EIP | Agents reacting to async events |
| Scatter-Gather | EIP | Multi-agent debate and best-answer synthesis |
| Circuit Breaker | Resilience | Maximum iteration limit in the tool loop |
| Bulkhead | Resilience | Budget and context isolation per agent |
| Fallback | Resilience | Chain of alternative models if primary fails |
The table gives you the map. The following sections explain the patterns that appear most in early real-world agentic systems.
Creational Patterns: How an Agent is Born
Factory is like a worker who picks the right specialist for each task.
If you need code written, call the programming expert. If you need to search the web, call the search expert. If you need to analyze a document, call the analysis expert. Factory automatically picks the right specialist without you having to tell it manually which one to use.
// Factory: returns the correct agent based on task type
function createAgent(taskType: string): Agent {
switch (taskType) {
case "document-analysis":
return new DocumentAnalysisAgent({
model: "claude-opus-4-6",
tools: [readFile, extractText, summarizeContent],
systemPrompt: "You are an expert in analyzing complex documents..."
});
case "code-generation":
return new CodeGenerationAgent({
model: "claude-opus-4-6",
tools: [writeFile, runTests, linter],
systemPrompt: "You are a senior software engineer..."
});
case "web-research":
return new ResearchAgent({
model: "claude-haiku-4-5-20251001",
tools: [webSearch, fetchArticle],
systemPrompt: "You are a researcher gathering up-to-date information..."
});
default:
throw new Error(`Unsupported agent type: ${taskType}`);
}
}
// Usage: the system automatically picks the best agent for the task
const agent = createAgent("document-analysis");
await agent.run(newTask);
Builder is useful when you need to prepare an agent step by step, like putting together a puzzle.
First you give it basic instructions (“you’re an analysis expert”). Then you add the history of previous conversations. Then the documents it needs. Instead of giving it everything at once, you do it in steps, and each step is a piece that fits into place.
Prototype is when you have a model or template you reuse.
For example: you have a “prompt” (instruction) that works well for analyzing code. Instead of writing new instructions for each project, you copy this template and adapt it slightly for each case. It’s like having a form you fill with different data, but the structure is always the same.
Singleton solves a specific problem: the tool registry. A single point from which the system knows all available tools. Each tool registers once; agents invoke it by name. Without this centralized registry, you end up with tools defined in multiple places with mismatched schemas. Small, but it fixes an error that appears early.
Structural Patterns: How Everything Connects
Imagine your AI agent is like a person in an office. These four patterns are the “infrastructures” that make everything work together:
Adapter: it’s like a translator between languages
Suppose your agent needs to use different external tools: Google, a database, a private API from your company. The problem is each one “speaks” in a different format. Adapter is a translator that converts each format to one your agent understands. That way, your agent doesn’t need to learn 10 different languages. The translator handles the conversion automatically.
Decorator: it’s like a bodyguard who reviews your actions (without getting into your head)
Your agent wants to execute an action: delete files, change a setting, send money. Before it does, you wrap it with a control layer that checks: “Are you sure you want to do this?” or “Does this action follow safety rules?” The agent doesn’t know there’s a bodyguard reviewing. It keeps thinking normally. The Decorator just acts as an external filter.
Proxy: it’s like an intelligent receptionist who routes requests
When someone enters the office, the receptionist decides: “This simple question goes to a Junior. This complex question goes to the Senior.” Proxy is like that: it receives the request, evaluates how complicated it is, and routes to the right model (Haiku for simple tasks, Opus for complex tasks). Your code stays the same; the receptionist makes the decision.
Composite: it’s like a project director who coordinates specialists
Imagine your main agent is a director who doesn’t do all the work alone. It’s smarter: it has specialists under its command (an analysis agent, another for search, another for writing). From the outside, it looks like one person is working on the project. Inside, it’s a coordinated team. The director (main agent) decides who does what, but the effort is collective.
Behavioral Patterns: How the Agent Acts
Imagine your AI agent is like an assistant that needs to do things. These patterns explain how that assistant works, what actions it can take, and how all those actions are coordinated.
Command is like giving a to-do list written on a note
Your agent has a list of things it can do: search the web, write a document, analyze an image, etc. Each task is written clearly on a note with:
- What it’s called (for example: “search the web”)
- What it does (for example: “find up-to-date information”)
- What information it needs (for example: “text to search”)
When the agent needs to do something, you give it a note with that specific task. The agent reads the note, understands what it needs to do, and executes it. You control which tasks are available in the list.
Observer is like having alarms that go off at specific moments
Imagine you have alarms that go off when certain things happen:
- An alarm that goes off when the agent finishes a search
- An alarm that goes off when the agent starts writing
- An alarm that goes off when it finishes completely
When each alarm goes off, you execute an action (for example: save the result, send a notification, update a database). The agent doesn’t know there are alarms. It just does its work, and the alarms respond to what it does.
Strategy is like having different paths to the same destination
You have several ways to solve a problem:
- Ask model A (fast but less accurate)
- Ask model B (slow but more exact)
- Ask model C (very precise but very expensive)
Depending on the situation, you pick a path: if the task is urgent and not important, use the fast model. If you need precision, use the exact model. The agent’s logic stays the same; you only change which model it uses based on what you need at that moment.
Mediator is like having an orchestra conductor
Imagine you have several specialists:
- One searches for information
- Another analyzes documents
- Another writes reports
Without a conductor, each specialist would need to know how to communicate with the others, who needs what information, etc. It’s chaos.
With a conductor (Mediator), everyone talks only to the conductor:
- The searcher says: “I found this information”
- The conductor receives it and sends it to the analyst
- The analyst says: “I’ve analyzed this”
- The conductor sends it to the writer
- The writer delivers the final report
The conductor is the only one who knows how all the parts work. The specialists only need to know the conductor.
Enterprise Patterns: PoEAA and EIP
They’re ways to organize large, complex systems. We’ll explain with simple examples:
Repository: it’s like a “gateway to data”
Your agent needs to search for information. Instead of the agent knowing where the data is (Is it in a database? In files? On the internet?), there’s a single gateway: ask for the information and someone finds it wherever it is. RAG (pattern for agents to access external knowledge) works like this: your agent just asks, without caring where the answer comes from.
DTO (sending structured information): it’s like an envelope with clearly defined fields
When two agents need to communicate, they don’t send loose text. They send an “envelope” with clear structure: this is a name, this is a number, this is a date. Less confusion, fewer errors.
Scatter-Gather: ask several at once and gather the answers
You have a difficult problem. Instead of asking one model (who might be wrong), you ask three models in parallel. They all solve the same problem from their perspectives. Then you gather the answers and reach the best conclusion. It’s like asking advice from several friends instead of one.
Saga: it’s a plan with “Plan B” if something goes wrong
Your agent needs to do five steps in sequence (step 1, step 2, step 3, step 4, step 5). If step 4 fails, what happens to steps 1, 2, and 3? Saga is a plan that says: if it fails here, undo this; if it fails there, undo that. That way the system keeps working without getting stuck halfway.
Resilience: When the Agent Fails or Gets Stuck
Imagine your agent is like a person working on a task. Sometimes it gets stuck: it keeps trying the same thing over and over without making progress, wasting time and money without results.
Circuit Breaker: it’s like an emergency button
The agent tries to do something up to 10 times (or whatever number you set). If after those 10 attempts it can’t succeed, the system stops. Without this limit, the stuck agent would keep trying forever, wasting your money on tokens without ever finishing the task.
// Emergency button: stop if it tries more than 10 times
if (attemptsAttempted > 10) {
stopNow();
}
Bulkhead: it’s like giving each agent its own budget
If you have several agents working at once, each gets its budget limit (tokens). If one goes overboard and uses up its limit, the others keep working with their budget. One doesn’t ruin everyone.
Fallback chains: it’s like having a Plan B
If the agent tries the Claude model (the most powerful but expensive) and fails, it automatically tries the Sonnet model (cheaper). If that also fails, it tries Haiku (the fastest). The system always has an alternative option.
What Has No Classic Equivalent
These are genuinely new agentic patterns. They’re not in GoF, not in Fowler, not in Hohpe.
Context engineering: actively managing what information enters the context window (the memory space the model has during a conversation) at each moment. It’s not caching or lazy loading. It’s an explicit decision about what the model knows and doesn’t know, made step by step.
Eval harness: infrastructure to measure whether the agent works well. Classic testing doesn’t apply because model outputs are probabilistic, not deterministic. An eval harness defines test cases, runs the agent multiple times, and aggregates metrics to detect degradation.
LLM-as-Judge: use one model to evaluate the quality of another model’s response. There’s no classic equivalent because in traditional software tests are binary. Here the “judge” reasons about quality, coherence, or semantic correctness.
Sandbox execution: run code generated by the agent in an isolated environment before it touches production. The agent writes. The sandbox verifies. Only if it passes does it deploy. Without this pattern, every agent run is a gamble.
Cost/latency-aware routing: route requests to different models not just by capability, but with explicit constraints on cost and latency. Strategy solves the “how to choose”. This pattern adds “how much can you spend and how long can you wait” as first-class citizens in the decision.
The difference between these five and GoF patterns is they require reasoning about uncertainty. GoF assumes code does what you tell it to. Agentic patterns assume the model can surprise you.
Common Mistakes When Mapping These Patterns
Confusing Decorator with Proxy
They’re structurally similar, but the intent is different. Decorator adds behavior to the agent’s output (verify, filter, enrich). Proxy controls access to the agent or model (redirect, limit, authenticate). If you’re checking output, it’s Decorator. If you’re redirecting requests, it’s Proxy. Mixing them leads to guardrails that do too much, or routers that also validate, and neither can change without breaking the other.
Applying Factory When You Only Have One Agent Type
Factory makes sense when you have two or more variants with different logic. If you only have one agent with one configuration, Factory is pure over-engineering. Start with a direct function. Add Factory when the second agent type actually appears.
Ignoring Circuit Breaker Until the Problem Happens
I’ve seen this in early agentic projects I worked on in production: the tool loop gets stuck in a loop, the agent keeps calling the same tool expecting a different result, and token spending multiplies without anyone noticing until the bill arrives. The iteration limit isn’t an optimization. It’s the first line of defense.
Using Saga Without Defining Compensations First
Saga without compensations is just a sequence of steps that doesn’t know how to recover if something fails. Before implementing Saga, write on paper what the system does if step 3 of 5 fails. If you can’t answer that question, you’re not ready for Saga yet.
Implementation Checklist
-
Subagents are created with Factory based on task type, not scattered conditional logic
-
Each tool call is modeled as a Command object with explicit schema and clear description
-
Guardrails are implemented as Decorator, external to agent logic
-
The orchestrator centralizes communication between agents (Mediator), agents don’t call each other
-
The tool loop has a Circuit Breaker with iteration limit defined before production deployment
-
RAG abstracts access to external knowledge (Repository), the agent doesn’t know the data source
-
Outputs between agents use structured outputs with typed schema (DTO)
-
The system has at least a basic eval harness before deployment
-
Tools that execute user code run in an isolated sandbox before touching production
Frequently Asked Questions
Do I need to know GoF patterns to start with agents?
No. You can build functional agents without having read the 1994 book. But if you already know them, this map gives you an advantage: instead of learning agentic concepts from scratch, you recognize familiar structures with new roles. You already know how to use Factory. That it now creates subagents instead of business objects is a small step.
What pattern should I learn first if I’m starting from zero?
Command=tool call. It’s the most fundamental of all. If you don’t understand that each tool you give the model is a Command object with name, description, and schema, the rest of the patterns have no solid foundation. Everything else builds on that concept.
What’s the difference between Decorator and Proxy in an agentic system?
Decorator adds behavior: the guardrails that verify or filter agent output. Proxy controls access: the router that decides which model to send the request to. The code structure is almost identical, but the intent is different. In practice: if you’re modifying or checking output, it’s Decorator. If you’re redirecting requests, it’s Proxy.
Does the Singleton pattern serve any purpose in agents?
Yes, for the tool registry: a centralized registry of all available tools, accessible from anywhere in the system. Each tool registers once. Agents invoke it by name. Without this registry, you end up with tools defined in multiple places with inconsistent schemas.
Are resilience patterns (Circuit Breaker, Bulkhead) for advanced systems?
Circuit Breaker is not. It’s the first you should implement, before even any orchestration pattern. An agent without an iteration limit is one that can cost you money unpredictably. Bulkhead is more relevant when you have multiple agents running in parallel and need one’s failure not to affect the others.