What Are Agent Patterns?

Agent patterns are reusable architectural blueprints for building AI agent workflows. Just as design patterns in software engineering provide proven solutions to common problems, agent patterns provide structured approaches to common AI agent challenges.

The Problem with Ad-Hoc Agents

When building AI agents from scratch, developers often face:

  1. Inconsistent Results: Single-prompt approaches are unreliable for complex tasks

  2. Hard to Debug: Monolithic prompt chains are difficult to troubleshoot

  3. No Structure: Each agent is built from scratch with unique logic

  4. Limited Reusability: Solutions don’t transfer across projects

  5. Unpredictable Behavior: Without structure, agents behave inconsistently

Example: Ad-Hoc Approach

# Ad-hoc approach - no pattern
def answer_question(question: str) -> str:
    prompt = f"Answer this question: {question}"
    response = llm.invoke(prompt)
    return response

# Problems:
# - No tool use
# - No verification
# - No reasoning structure
# - No error handling
# - No iterative refinement

This works for simple queries but fails for complex tasks requiring:

  • Multiple reasoning steps

  • Tool/API interactions

  • Self-correction

  • Quality refinement

  • Exploration of alternatives

What Are Patterns?

Agent patterns are structured workflows that break complex agent behavior into:

  1. Clear Stages: Distinct phases with specific responsibilities

  2. State Management: Tracking progress and intermediate results

  3. Conditional Logic: Decision points for routing and iteration

  4. Tool Integration: Structured approach to external interactions

  5. Quality Control: Built-in verification and refinement

Example: Pattern-Based Approach

# ReAct Pattern - structured workflow
agent = ReActAgent(
    llm_configs=configs,
    tools={"search": search_tool, "calculator": calc_tool},
    max_iterations=5
)

result = agent.run("What is the population of Tokyo times 2?")

# Workflow:
# 1. Thought: "I need to find Tokyo's population"
# 2. Action: search("Tokyo population")
# 3. Observation: "~14 million"
# 4. Thought: "Now I need to multiply by 2"
# 5. Action: calculator("14000000 * 2")
# 6. Observation: "28000000"
# 7. Final Answer: "28 million"

The pattern provides:

  • Structured reasoning loop

  • Tool selection logic

  • Error recovery

  • Iteration control

  • State tracking

Why Patterns Matter

1. Reliability

Patterns provide consistent, predictable behavior:

# Without pattern: unpredictable
response = llm.invoke("Write a story")
# Quality varies wildly, no guarantee of structure

# With Reflection pattern: consistent quality
agent = ReflectionAgent(llm_configs=configs)
response = agent.run("Write a story")
# Always generates, critiques, and refines

2. Debuggability

Patterns break workflows into inspectable stages:

# Without pattern: black box
result = complex_prompt(input)
# Can't see intermediate steps

# With Plan & Solve pattern: transparent
agent = PlanAndSolveAgent(llm_configs=configs)
result = agent.run(input)
# Can inspect: plan, step results, aggregation

3. Reusability

Patterns transfer across domains:

# Same ReAct pattern, different domains

# Domain 1: Customer Service
customer_agent = ReActAgent(
    tools={"check_order": check_order_status, "cancel_order": cancel},
    llm_configs=configs
)

# Domain 2: Data Analysis
data_agent = ReActAgent(
    tools={"query_db": run_sql, "plot": create_chart},
    llm_configs=configs
)

# Same pattern, different tools!

4. Best Practices

Patterns encode research-backed approaches:

Pattern

Research Paper

Key Insight

ReAct

Yao et al., 2023

Interleaving reasoning and acting improves performance

Reflection

Shinn et al., 2023

Self-reflection improves output quality

Plan & Solve

Wang et al., 2023

Separating planning from execution increases reliability

STORM

Shao et al., 2024

Multi-perspective synthesis improves comprehensiveness

Pattern vs Non-Pattern Approaches

Comparison Table

Aspect

Non-Pattern

Pattern-Based

Structure

Ad-hoc prompts

Defined workflow stages

State

None or implicit

Explicit state tracking

Tools

Manual integration

Structured tool use

Iteration

One-shot

Controlled loops

Debugging

Difficult (black box)

Easy (inspectable stages)

Quality

Inconsistent

Predictable

Reusability

Low

High

Maintenance

Brittle

Modular

Real-World Example

Task: Generate a research report on quantum computing

Non-Pattern Approach:

prompt = """
Write a comprehensive research report on quantum computing.
Include history, current state, applications, and future outlook.
Use authoritative sources and cite them.
"""

report = llm.invoke(prompt)
# Problems:
# - No source verification
# - May hallucinate facts
# - No structured research process
# - Can't trace reasoning
# - Single attempt, no refinement

Pattern Approach (STORM):

agent = STORMAgent(
    llm_configs=configs,
    retrieval_tools={"search": wikipedia_search, "scholar": academic_search}
)

report = agent.run("quantum computing")

# Workflow:
# 1. Generate outline (intro, history, applications, future, conclusion)
# 2. Generate perspectives (physicist, engineer, business analyst)
# 3. Generate questions per perspective per section
# 4. Retrieve information for each question
# 5. Synthesize each section from retrieved info
# 6. Compile final report with citations

# Benefits:
# - Systematic research process
# - Multiple viewpoints
# - Source-backed claims
# - Transparent methodology
# - Structured output

Common Pattern Categories

1. Tool-Using Patterns

Agents that interact with external systems:

  • ReAct: Reason about what to do, act with tools

  • LLM Compiler: Plan and execute tools in parallel

  • REWOO: Separate planning from tool execution for efficiency

Use When: Need to search, calculate, query APIs, or access external data

2. Refinement Patterns

Agents that improve output through iteration:

  • Reflection: Generate, critique, refine

  • Reflexion: Learn from failures across trials

  • LATS: Explore multiple solution paths

Use When: Output quality is critical (writing, coding, design)

3. Planning Patterns

Agents that decompose complex tasks:

  • Plan & Solve: Create plan, execute sequentially

  • Self-Discovery: Dynamically select reasoning strategies

Use When: Task has multiple logical steps or requires strategic thinking

4. Multi-Perspective Patterns

Agents that synthesize diverse viewpoints:

  • STORM: Multi-perspective research and synthesis

Use When: Need comprehensive, well-rounded analysis

When to Use Patterns

Use Patterns When:

  1. Task Complexity: Requires multiple steps or reasoning stages

  2. Quality Matters: Output must be reliable and consistent

  3. Need Tools: Must interact with external systems/data

  4. Refinement Required: Initial output needs improvement

  5. Reusability: Will apply same workflow to different inputs

  6. Debugging: Need to understand agent behavior

  7. Collaboration: Others will work on or extend the agent

Use Simple Prompts When:

  1. Task is Trivial: Single, straightforward question

  2. Speed Critical: Can’t afford multi-step workflow

  3. Prototyping: Exploring what’s possible

  4. Budget Constrained: Multiple LLM calls are too expensive

Example Decision:

# Simple task - direct prompt is fine
answer = llm.invoke("What is the capital of France?")

# Complex task - use pattern
agent = ReActAgent(
    tools={"search": search, "calculate": calc},
    llm_configs=configs
)
answer = agent.run(
    "Find the GDP of France and Germany, "
    "calculate the ratio, and explain the economic factors"
)

The Agent Patterns Library

This library provides 9 battle-tested patterns:

Pattern

Category

Best For

ReAct

Tool-Using

Q&A with external data

Reflection

Refinement

High-quality content

Plan & Solve

Planning

Multi-step tasks

Reflexion

Refinement

Learning from failures

LLM Compiler

Tool-Using

Parallel tool execution

REWOO

Tool-Using

Cost-efficient workflows

LATS

Refinement

Exploring solutions

Self-Discovery

Planning

Adaptive reasoning

STORM

Multi-Perspective

Comprehensive research

Each pattern is:

  • Fully Implemented: Ready to use out of the box

  • Customizable: Override prompts and behavior

  • Type-Safe: Full type hints for IDE support

  • Well-Tested: Comprehensive test coverage

  • Documented: Clear API and examples

Design Philosophy

Agent Patterns follows these principles:

1. Synchronous by Design

# Synchronous - simple and debuggable
result = agent.run(input)

# NOT async - complexity removed
# result = await agent.run(input)

Why: Async adds complexity, makes debugging harder, and isn’t needed for most agent workflows.

2. Externalized Configuration

# Prompts in files, not code
agent = ReActAgent(prompt_dir="my_prompts")

# Easy to customize without touching code

Why: Separates prompt engineering from software engineering, enables non-coders to customize.

3. Explicit State

# State is explicit and inspectable
state = {
    "input": "query",
    "thought": "...",
    "action": {...},
    "observation": "...",
}

Why: Makes debugging and monitoring straightforward.

4. Composable Tools

# Tools are simple functions
def my_tool(input: str) -> str:
    return result

# Easy to test, mock, and reuse

Why: Keep tools simple, focused, and testable.

Next Steps

Key Takeaways

  1. Patterns provide structure for complex agent workflows

  2. They improve reliability, debuggability, and reusability

  3. Use patterns for complex tasks, simple prompts for trivial queries

  4. Each pattern encodes research-backed best practices

  5. The library provides 9 production-ready patterns

  6. All patterns share a consistent architecture making them easy to learn and use