# Agent Design Patterns: Tool Loop, ReAct, Planning, Reflection, Routing
Ask five engineers to build "an agent that answers customer refund questions," and you will get five different designs. One wires the model to a database and lets it loop. One makes it think out loud before every action. One has it write a plan first. One has it critique its own draft. One sends easy questions to a cheap model and hard ones to an expert. Same task, five architectures, and only some of them actually work reliably.
An agent is just a language model that can take actions in the world (call tools, search, run code) and decide what to do next based on the results. The design pattern you pick is the difference between an agent that quietly does its job and one that loops forever, hallucinates, or burns money.
Let's walk through the five core patterns, each with a concrete job.
This is the foundation everything else builds on. A tool is any function the model can call: search the web, look up an order, send an email, query a database.
The loop is simple:
1. Give the model a goal and a list of tools.
2. The model either answers directly or asks to call a tool.
3. You run the tool, hand back the result.
4. Repeat until the model gives a final answer.
Concrete job: "What's the status of order #4471?" The model calls get_order(4471)
shippedHere is the loop in vendor-neutral pseudocode. Every provider's SDK wraps this same shape.
messages = [{"role": "user", "content": "Status of order #4471?"}]
while True:
response = model.generate(messages, tools=[get_order, cancel_order])
if response.tool_calls:
for call in response.tool_calls:
result = run_tool(call.name, call.args) # you execute it
messages.append({"role": "tool", "content": result})
else:
print(response.text) # final answer, exit loop
breakThe tool-use loop is enough for a huge number of real tasks. ReachReachThe number of unique people exposed to your message in a given period. Unlike impressions, reach counts each person once, no matter how often they see it.View full definition → for the fancier patterns only when this one starts failing.
ReAct stands for "Reasoning and Acting." The model writes a short thought before each action, then acts, then reads the result, then thinks again. It interleaves thinking and doing.
Concrete job: "Which of our three warehouses can ship a standing desk to Denver fastest?"
A plain tool loop might grab one warehouse and stop. ReAct reasons through it:
> Thought: I need stock and shipping time from all three warehouses.
> Action: check_stock("desk", "Salt Lake") → in stock, 2 days
> Thought: Good, but Phoenix might be closer. Check it.
> Action: check_stock("desk", "Phoenix") → in stock, 1 day
> Thought: Phoenix is faster. Check Dallas to be sure.
> Action: check_stock("desk", "Dallas") → out of stock
> Thought: Phoenix wins. Answer the user.
The visible reasoning keeps the model honest and makes it easy to debug: when it goes wrong, you can see exactly which thought led it astray. ReAct is the workhorse pattern for tasks that need several dependent lookups.
The original paper is a clear, readable classic: ReAct: Synergizing Reasoning and Acting in Language Models.
Sometimes you want the agent to think about the *whole* job before touching anything. A planner produces an ordered list of steps up front, then executes them one by one.
Concrete job: "Prepare a competitive brief on three rival products."
ReAct might wander. A planner commits first:
> Plan:
> 1. Identify the three top competitors.
> 2. Pull pricing for each.
> 3. Pull top features for each.
> 4. Find recent customer complaints for each.
> 5. Write a one-page comparison.
Then it works the list. The advantage: for long, multi-step tasks the agent doesn't lose the thread halfway through. You can also show the plan to a human for approval before anything runs, which matters when steps cost money or send emails.
A common upgrade is plan-and-execute with re-planning: run a few steps, check whether reality matches the plan, and rewrite the remaining steps if not. Real work is messy, and rigid plans break.
Rule of thumb: short, exploratory tasks favor ReAct. Long tasks with many known sub-steps favor a planner.
Models produce better work when they review it. A reflection loop has the agent generate a draft, then switch roles and critique that draft, then revise.
Concrete job: "Write a SQLSQLSales Qualified Lead: a prospect the sales team has validated as ready for direct outreach and a proposal, having passed clear qualification criteria.View full definition → query to find customers who churned last quarter."
1. Draft: the agent writes the query.
2. Reflect: "Does this handle customers with no orders? Does 'last quarter' use the right dates? Any joins that could duplicate rows?"
3. Revise: it fixes the issues it just found.
You can even run the draft and feed the *actual* error message back in, which turns reflection into a tight self-correcting loop. This pattern shines for code, structured writing, and anything with a clear notion of "correct."
A minimal reflection loop:
draft = model.generate("Write a SQL query for churned customers last quarter.")
for _ in range(2): # cap the iterations
critique = model.generate(f"Find bugs or gaps in this query:\n{draft}")
if "no issues" in critique.lower():
break
draft = model.generate(f"Fix these issues:\n{critique}\n\nQuery:\n{draft}")One caution: always cap the loop. Without a limit, reflection can spin in circles "improving" forever and running up your bill.
Knowledge check
1. According to the lesson, what fundamentally distinguishes an 'agent' from a plain language model?
2. Why does the lesson emphasize that 'only some' of the five architectures for the same refund task actually work reliably?
3. In the tool-use loop pseudocode, what condition causes the loop to terminate?
4. Select ALL correct answers about how the tool-use loop operates as described in the lesson.
Select all the correct answers.
5. Select ALL correct answers about why the tool-use loop is called the foundation the other patterns build on.
Select all the correct answers.
A router looks at an incoming request and sends it to the right handler: a specific model, a specific tool set, or a specific sub-agent. Think of it as a smart receptionist.
Concrete job: a support inbox that gets three kinds of messages.
Routing does two things well. It keeps each specialist simple, because a billing agent that only handles billing is easier to get right than one agent trying to do everything. And it saves money: send easy questions to a small cheap model and reserve the expensive model for hard ones.
A router is often just one model call that returns a category:
route = model.generate(
f"Classify this message as one of: billing, technical, howto.\n\n{message}"
)
handler = {"billing": billing_agent,
"technical": tech_agent,
"howto": docs_agent}[route.strip()]
handler.run(message)Routing is how most serious production agents are built in 2026: not one giant brain, but a dispatcher in front of several focused specialists.
These are not rival teams. Real systems stack them.
A production support agent might: route a message to the billing specialist, which runs a ReAct loop over its refund tools, and if it drafts a customer email, run one reflection pass before sending. A research agent might plan the report, then use a tool loop for each section.
Start with the simplest pattern that could work (usually the plain tool loop) and add complexity only when you see a specific failure. Over-engineering an agent is as real a risk as under-engineering it.
Each major provider ships an SDK that gives you these patterns as building blocks: the OpenAI Agents SDK, the Claude Agent SDK, and Google's Agent Development Kit (ADK). The patterns here transfer across all of them; the provider deep-dive blocks cover the tool-specific syntax. For a broad, vendor-neutral mapmapUsing software to automate repetitive marketing tasks and campaigns, enabling personalisation at scale across channels like email, web, and social.View full definition → of the space, Anthropic's Building Effective Agents is a great free read.