Chapter 11 β Agentic Design Patterns
βThe difference between a demo agent and a production agent is design patterns.β
Why Patterns Matter
Building a single agent is easy. Building a reliable, maintainable, cost-effective agent system is hard. Design patterns are proven solutions to common agent architecture problems. Master these and you can design any agentic system.
The Six Core Patterns
Pattern 1: Router
A router classifies the input and directs it to the right specialist. Itβs the simplest pattern and the foundation of customer support, coding assistants, and knowledge systems.
def router(user_input: str) -> str:
"""Classify intent and route to the right agent."""
classification = llm.invoke(f"""
Classify this user request into one category:
- technical: coding, debugging, architecture questions
- creative: writing, brainstorming, content creation
- factual: research, data, current events
Request: {user_input}
Respond with just the category name.
""")
agents = {
"technical": coding_agent,
"creative": writing_agent,
"factual": research_agent,
}
return agents[classification.content.strip()](user_input)
When to use: Multiple distinct capabilities, high request volume, different SLAs per category.
Pattern 2: Orchestrator-Worker
A central orchestrator breaks down complex tasks and delegates sub-tasks to specialized workers.
When to use: Complex tasks with multiple sub-skills, where each sub-task benefits from a focused specialist.
Pattern 3: Evaluator-Optimizer
Generate a draft β evaluate its quality β improve it. Repeat until quality threshold is met. This is how coding agents fix bugs and how writing agents polish prose.
def evaluator_optimizer(task: str, max_rounds: int = 3, threshold: float = 8.0):
output = generator.invoke(task)
for round in range(max_rounds):
# Evaluate
evaluation = evaluator.invoke(f"""
Task: {task}
Output: {output}
Rate the quality from 0-10 and provide specific feedback for improvement.
Format: SCORE: X\nFEEDBACK: ...
""")
score = parse_score(evaluation)
if score >= threshold:
return output
feedback = parse_feedback(evaluation)
# Optimize
output = generator.invoke(f"""
Original task: {task}
Previous output: {output}
Feedback: {feedback}
Improve the output based on the feedback.
""")
return output
When to use: Code generation (write β test β fix), content creation, data analysis reports.
Pattern 4: Parallelization
Run independent tasks simultaneously to reduce latency. Two flavors:
- One task, split into independent parts
- "Research 3 companies" β 3 parallel searches
- Results are combined at the end
- Linear speedup with # of sections
- Same task, run multiple times
- "Is this code secure?" β 3 agents vote
- Majority wins or disagreements flagged
- Higher reliability, higher cost
import asyncio
async def parallel_research(topics: list[str]):
"""Research multiple topics in parallel."""
tasks = [research_agent.ainvoke(topic) for topic in topics]
results = await asyncio.gather(*tasks)
return combine_results(results)
# Sectioning: 3x faster than sequential
results = await parallel_research([
"Apple Q4 2025 earnings",
"Google Q4 2025 earnings",
"Microsoft Q4 2025 earnings",
])
Pattern 5: Human-in-the-Loop
Pause agent execution at critical checkpoints for human approval. Essential for actions with real consequences.
# LangGraph human-in-the-loop
from langgraph.graph import StateGraph
def should_continue(state):
"""Check if the next action requires human approval."""
last_action = state["pending_action"]
REQUIRES_APPROVAL = ["send_email", "delete_data", "deploy", "purchase"]
if last_action["tool"] in REQUIRES_APPROVAL:
return "human_review"
return "execute"
graph.add_conditional_edges("plan", should_continue, {
"human_review": "wait_for_approval",
"execute": "execute_action",
})
When to use: Any action that costs money, sends communications, modifies production data, or is irreversible.
Pattern 6: Guardrails
Constrain what the agent can do β before it acts, not after.
# OpenAI Agents SDK guardrails
from agents import Agent, GuardrailFunctionOutput, input_guardrail
@input_guardrail
async def block_off_topic(ctx, agent, input):
"""Reject requests that aren't related to research."""
result = await llm.invoke(f"""
Is this request related to research, analysis, or information gathering?
Request: {input}
Respond with YES or NO.
""")
if "NO" in result.content:
return GuardrailFunctionOutput(
output_info="I can only help with research-related questions.",
tripwire_triggered=True,
)
agent = Agent(
name="Researcher",
instructions="...",
input_guardrails=[block_off_topic],
)
Pattern Combinations
Real systems combine multiple patterns:
| System | Patterns Used |
|---|---|
| Customer support bot | Router + Swarm (handoffs) + Human-in-the-loop |
| Coding assistant | Evaluator-Optimizer (write β test β fix) + Guardrails |
| Research pipeline | Orchestrator-Worker + Parallelization + Evaluator |
| Content platform | Router + Parallelization + Human-in-the-loop |
Decision Framework
Whatβs Next
Patterns tell you how to build. But how do you make agents reliable and observable in production? Thatβs deployment.
Next: Chapter 12 β Deployment & Production β
β Previous: Chapter 10 β Build Your First Agent Β· Next: Chapter 12 β Deployment & Production β
Last updated: April 2026