AI Agent Patterns
Architectural patterns for building agents on top of LLMs β from simple ReAct to multi-agent systems with code.
π·πΊ Russian version: patterns.ru.md
β AI agents Β· Architecture Β· Frameworks β
Contents
- ReAct (Reason + Act)
- Plan-and-Execute
- Reflection / Self-Correction
- Tool Use (Function Calling)
- Multi-Agent
- How to choose a pattern
- Whats next
1. ReAct (Reason + Act)
The most popular pattern. The agent alternates between reasoning (Reason) and action (Act) in a loop.
How it works
Thought: The user is asking about weather. I need to find information.
Action: call search_web(query="weather Tokyo")
Observation: "In Tokyo +22C, clear"
Thought: I have the information. I can answer.
Answer: Its currently +22C and clear in Tokyo.
Each cycle: model thinks what to do does (calls a tool or responds) receives result thinks again.
When ReAct
Tasks that need information search
Questions requiring calculations
Simple action chains (find β analyze β answer)
Long-plan tasks (use Plan-and-Execute instead)
Implementation
import requests
import json
OLLAMA = "http://localhost:11434/api/chat"
def react_agent(task: str, tools: list, max_steps: int = 5):
"""
ReAct agent: alternates reasoning and actions.
"""
messages = [
{"role": "system", "content": (
"You are a ReAct agent. Answer user queries. "
"If you need information use tools. "
"Only answer when you have enough data."
)},
{"role": "user", "content": task}
]
for step in range(max_steps):
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": messages,
"tools": tools,
"stream": False
})
msg = response.json()["message"]
messages.append(msg)
if msg.get("tool_calls"):
for tc in msg["tool_calls"]:
name = tc["function"]["name"]
args = tc["function"]["arguments"]
print(f" [Step {step+1}] {name}({json.dumps(args)})")
result = {"result": f"executed {name} with {args}"}
messages.append({
"role": "tool",
"name": name,
"content": json.dumps(result)
})
else:
return msg["content"]
return "Failed to complete the task in the given steps"
Pros and Cons
| Pros | Cons |
|---|---|
| Simple to understand and implement | Can loop infinitely |
| Works with any model | Each step consumes tokens |
| Transparent you see each decision | No forward planning |
ReAct fits executor agents: analyst finds data, developer reads docs, tester checks results. Each works in its own loop.
2. Plan-and-Execute
Separates planning from execution in two phases.
How it works
STEP 1 Planning:
Plan:
1. Find the project repository on GitHub
2. Read the README
3. Collect dependencies
4. Generate report
STEP 2 Execution:
Executing step 1: search_web("repo awesome-ai-handbook")
Result: found github.com/bestdeejay-design/awesome-ai-handbook
Executing step 2: read_file("https://github.com/.../README.md")
...
When Plan-and-Execute
Complex tasks with 3+ steps
Code migration, refactoring
Research tasks
Simple questions (ReAct is faster)
Implementation
def plan_and_execute(task: str, tools: list):
"""Plan-and-Execute: plan first then execute."""
# Phase 1: create plan
plan_prompt = (
f"Create a detailed plan for: {task}\n"
"Return the plan as a list of steps in JSON format.\n"
"Each step: {\"step\": \"description\", \"tool\": \"tool_name\"}"
)
plan_response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [{"role": "user", "content": plan_prompt}],
"format": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"step": {"type": "string"},
"tool": {"type": "string"}
}
}
}
}
},
"stream": False
})
plan = json.loads(plan_response.json()["message"]["content"])
print(f"Plan: {len(plan['steps'])} steps")
# Phase 2: execute steps
results = []
for i, step in enumerate(plan["steps"]):
print(f" Step {i+1}: {step['step']}")
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": (
f"You are executing step {i+1} of the plan. "
f"Context: {step['step']}. "
f"Previous results: {json.dumps(results)}"
)},
{"role": "user", "content": step['step']}
],
"stream": False
})
result = response.json()["message"]["content"]
results.append({"step": i+1, "result": result})
return results
Pros and Cons
| Pros | Cons |
|---|---|
| Agent doesnt forget the goal | Bad plan = bad result |
| Clear progress tracking | Cant adapt mid-execution |
| Plan can be reused | Uses tokens on planning |
Plan-and-Execute is ideal for project manager agents: create plan, distribute tasks, track progress.
3. Reflection / Self-Correction
The agent generates a response, critiques it, and improves it.
How it works
PASS 1 Generation:
Answer: Python was created in 1991 by Guido van Rossum
PASS 2 Reflection:
Critique: Answer is correct but could add version and context
PASS 3 Improvement:
Final: Python was created by Guido van Rossum in 1991.
Version 0.9.0 was released on February 20, 1991. Today Python
is one of the most popular programming languages.
When Reflection
Writing text (articles, docs)
Code that needs review before use
Complex reasoning
Simple answers (overkill)
Implementation
def reflection_agent(task: str, iterations: int = 2):
"""Reflection agent: generate critique improve."""
messages = [
{"role": "system", "content": "You are an expert. Answer questions."},
{"role": "user", "content": task}
]
# Initial generation
response = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": messages,
"stream": False
})
answer = response.json()["message"]["content"]
for i in range(iterations):
# Critique
critique = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": (
"You are a strict critic. Find errors inaccuracies "
"and missing details. Be picky."
)},
{"role": "user", "content": f"Task: {task}\n\nAnswer: {answer}"}
],
"stream": False
})
feedback = critique.json()["message"]["content"]
# Improvement
improved = requests.post(OLLAMA, json={
"model": "qwen3.5:4b",
"messages": [
{"role": "system", "content": (
"Improve the answer based on the critique. Fix all "
"errors add details. Return only the final version."
)},
{"role": "user", "content": (
f"Original answer: {answer}\n\n"
f"Critique: {feedback}\n\n"
f"Improved answer:"
)}
],
"stream": False
})
answer = improved.json()["message"]["content"]
return answer
Pros and Cons
| Pros | Cons |
|---|---|
| Significantly better quality | Uses 23x more tokens |
| Catches errors the model missed | Can over-improve and break |
| Useful for important answers | Not needed for simple tasks |
Reflection is a pattern for reviewer agents. They check code, text, and decisions before they go further. In your team: developer writes code β QA tests β reviewer evaluates quality.
4. Tool Use (Function Calling)
A single tool invocation without the ReAct thinking loop.
Tool Use vs ReAct
| Tool Use | ReAct |
|---|---|
| One tool call | Loop of multiple calls |
| No reasoning between calls | Thought between Action |
| Simpler fewer tokens | More complex but flexible |
When Tool Use
Extracting data from text
Classification (choose a category)
JSON conversion
When guaranteed output format is needed
Implementation
def tool_call(model: str, user_input: str, tools: list):
"""Single tool call without loop."""
response = requests.post(OLLAMA, json={
"model": model,
"messages": [{"role": "user", "content": user_input}],
"tools": tools,
"stream": False
})
return response.json()["message"]
Detailed API guide in ollama-for-agents.md.
5. Multi-Agent
Multiple agents working together on a single task.
Architectures
Supervisor (hierarchical):
ββββββββββββββ
β Supervisor β β manages
βββββ¬ββββ¬βββββ
β β
βββββ βββββ
βΌ βΌ
ββββββββ ββββββββ
βAgent β βAgent β
β A β β B β
ββββββββ ββββββββ
Peer-to-Peer (horizontal):
ββββββββ ββββββββ
βAgent ββββββΊβAgent β
β A β β B β
ββββββββ ββββββββ
β² β²
βββββββ¬ββββββ
βΌ
ββββββββββ
β Shared β
β task β
ββββββββββ
Swarm:
Agents dynamically hand off tasks
Agent A β (can't) β Agent B β (done) β Agent C β ...
When Multi-Agent
Tasks requiring different expertise (coding + design + DevOps)
Large projects needing decomposition
When review and balance is needed (one writes, another checks)
Simple tasks (single agent is faster)
Implementation
class Agent:
"""Simple agent with a role."""
def __init__(self, name: str, role: str, model: str = "qwen3.5:4b"):
self.name = name
self.role = role
self.model = model
def run(self, task: str, context: str = "") -> str:
response = requests.post(OLLAMA, json={
"model": self.model,
"messages": [
{"role": "system", "content": (
f"You are {self.name}. Your role: {self.role}."
)},
{"role": "user", "content": f"{context}\n\nTask: {task}"}
],
"stream": False
})
return response.json()["message"]["content"]
class Supervisor:
"""Manages a team of agents."""
def __init__(self, agents: list[Agent]):
self.agents = agents
def run(self, project_task: str):
context = ""
for agent in self.agents:
print(f"\n {agent.name} working...")
result = agent.run(project_task, context)
print(f" Result: {result[:100]}...")
context += f"\n{agent.name}: {result}"
return context
# Example: project analysis team
pm = Agent("PM", "Project manager. Set tasks and check deadlines.")
analyst = Agent("Analyst", "Analyze requirements and write specs.")
dev = Agent("Developer", "Write code per specification.")
team = Supervisor([pm, analyst, dev])
result = team.run("Create a REST API for task management")
Detailed guide in multi-agent.md and 02-agent-team tutorial.
6. How to choose a pattern
def choose_pattern(task: str):
"""Heuristic: which pattern fits the task."""
if len(task.split()) < 10:
return "Tool Use"
elif "check" in task.lower() or "improve" in task.lower():
return "Reflection"
elif any(w in task.lower() for w in ["find", "search", "how many", "what"]):
return "ReAct"
elif any(w in task.lower() for w in ["plan", "make project", "develop"]):
return "Plan-and-Execute"
elif any(w in task.lower() for w in ["team", "agents", "distribute"]):
return "Multi-Agent"
else:
return "ReAct"
| Situation | Pattern |
|---|---|
| βWhat is 2 + 2?β | Tool Use (calculator) |
| βFind information aboutβ¦β | ReAct (search answer) |
| βWrite an article aboutβ¦β | Reflection (draft review) |
| βRefactor this moduleβ | Plan-and-Execute (plan steps) |
| βStart a project from scratchβ | Multi-Agent (team) |
7. Whats next
| If you want | Go to | |ββββ-|ββ-| | Choose a framework for the pattern | frameworks.md | | Connect local model (tool calling API) | ollama-for-agents.md | | Understand agent architecture deeper | architecture.md | | Build an agent team | multi-agent.md | | Write your first agent | tutorials/01-first-agent.md | | Back to navigation | README.md | β
In section: architecture Β· evaluation Β· frameworks Β· memory Β· multi-agent Β· ollama-for-agents Β· orchestrators Β· patterns Β· prompting Β· ready-made Β· safety Β· skills
Related sections: Zero Level Β· Local Models Β· Use Cases Β· Resources
Navigation: β AI Agents Β· β Back to main Β· π·πΊ Π ΡΡΡΠΊΠΈΠΉ