The ReAct pattern LLM agents adopt combines explicit reasoning steps with external tool calls in a tight iterative loop. Instead of generating a final answer in one shot, the model emits a thought, chooses an action, observes the result, and repeats until it can respond with confidence. The approach was formalized by Yao et al. in 2022 and remains a baseline for agentic systems.
What the ReAct pattern actually is
The ReAct pattern LLM agents implement is a control flow convention, not a library or a hosted service. It fuses two previously separate ideas: chain-of-thought prompting (let the model reason in text) and action-execution (let the model invoke external tools). The fusion matters because reasoning without action hallucinates, and action without reasoning blindly triggers APIs based on unstated assumptions.
A classic text-only ReAct trace looks like this:
Thought: The user wants the population of the city with the tallest building.
Action: search("tallest building in the world")
Observation: Burj Khalifa in Dubai.
Thought: I need Dubai's population.
Action: search("population of Dubai")
Observation: 3.6 million.
Thought: I have the answer.
Action: finish("Dubai has about 3.6 million people.")
The model alternates between free-text reasoning and structured action requests. The environment executes the action and returns an observation that is appended to the context. This loop continues until a termination action (or a direct answer) appears.
How the loop works
The mechanics are straightforward:
- Seed the context with the task and a description of available tools.
- Generate a model completion. Instruct it to output
Thought:andAction:lines. - Parse the action. If it is a finish/answer action, stop.
- Execute the action against the real tool (API, DB, calculator).
- Append the observation as
Observation: <result>. - Repeat from step 2.
In practice, modern models support native function calling, which removes the need for fragile string parsing. The ReAct pattern LLM agents use with function calling is identical in spirit: the model emits a tool call (action) and the system returns the tool result (observation). The “thought” becomes the model’s internal rationale or an optional textual aside.
Prompt skeleton for text-only ReAct
You are an agent that solves tasks by interleaving reasoning and actions.
Available tools: search(query), calculator(expr)
Format:
Thought: <your reasoning>
Action: <tool>(<arg>)
Observation: <result>
...
Task: {user_query}
A parser can split on Action: with a regex. Keep the full transcript; do not truncate observations or you break the chain.
Observation formatting
Observations should be compact and unambiguous. Return JSON when the tool produces structured data, but strip unnecessary fields. If a tool fails, return a clear error string—Observation: ERROR 429 from search API—so the next thought can adapt. Never let an exception bubble out of the loop without being captured as an observation; the model can only react to what it sees in context.
Why it matters for engineering
Engineers reach for the ReAct pattern LLM agents need because it solves concrete failures in single-shot prompting.
- Grounding. Each action fetches real data. The model cannot invent a search result if the observation comes from a live API.
- Debuggability. The thought lines are a built-in trace. When an agent fails, you read the thoughts to see where reasoning diverged.
- Composability. Tools are just functions. You can swap a search API for a SQL query without changing the loop.
- Error recovery. If a tool returns an error observation, the next thought can say “that failed, try another approach.” Pure completion cannot self-correct as gracefully.
- Model portability. The loop is model-agnostic. Any instruction-tuned model that can follow the format works, from open weights to frontier APIs.
Concrete implementation
Below is a minimal Python loop using the OpenAI Chat Completions API with native tools. The same code runs against any OpenAI-compatible endpoint.
from openai import OpenAI
# Point this at any OpenAI-compatible gateway; the ReAct loop is unchanged.
client = OpenAI(base_url="https://api.openai.com/v1")
tools = [{
"type": "function",
"function": {
"name": "search",
"description": "Web search returning a snippet",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}
}]
messages = [{"role": "user", "content": "What is the height of the tallest mountain?"}]
for _ in range(5): # bound the loop
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = resp.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
# Stub execution; replace with real search.
obs = f"Search results for {call.function.arguments}: Everest 8849m"
messages.append({"role": "tool", "tool_call_id": call.id, "content": obs})
else:
print("Answer:", msg.content)
break
When you route this through an OpenAI-compatible gateway such as n4n.ai, the client code stays identical while you gain access to 240+ models and automatic fallback if a provider is rate-limited or degraded. The ReAct loop does not care which backend produced the tokens.
Streaming and latency
ReAct loops multiply round-trips. Each action adds at least one inference call. Batch tool calls when the model supports parallel calls, and set a hard iteration cap to avoid runaway cost. If you stream tokens, buffer the thought text for logging but do not act on partial actions.
Common misconceptions
ReAct requires a framework
False. The pattern is a prompt and control-flow convention. LangChain, LlamaIndex, and others provide helpers, but a 30-line script suffices. Dependency weight is not a feature.
ReAct guarantees correct answers
No. The model can still reason poorly or call the wrong tool. The observation grounds facts but does not validate logic. You still need offline evaluation and guardrails on tool side effects.
It is only for search or retrieval
Early papers used Wikipedia APIs, but the action space is arbitrary: database reads, code execution, payment APIs. The ReAct pattern LLM agents use applies wherever a tool extends context beyond the model weights.
More steps always improve quality
Longer traces increase token cost and risk drift. Set a max step count. Often a single tool call with a well-designed schema beats a 10-step meander that loses the original intent.
ReAct replaces planning
ReAct is reactive, not deliberative. For tasks needing upfront decomposition, pair it with a planner that emits a plan, then execute each step with ReAct. Treating ReAct as a full planner leads to aimless loops.
When to skip it
If the task is a single retrieval or a classification, a direct call is faster and cheaper. ReAct shines when the number of steps is unknown at author time, or when intermediate results change the path. Don’t wrap a get_user(id) call in a thought loop—call it directly.
Production considerations
Long ReAct transcripts inflate prompt size. Use a sliding window or summarize old observations after N steps. Honor provider cache-control hints to avoid re-billing the system prompt on every iteration. Per-token metering is essential: log each step’s token count to attribute cost to a user or workflow.
Gateways that forward provider cache-control hints and honor client routing directives, like n4n.ai, let you pin a specific model per step or fall back transparently without rewriting the agent code. That keeps the ReAct pattern LLM agents rely on portable across infrastructure changes.
Bound the loop, log the traces, and treat the model as a junior engineer who thinks out loud and asks for tools—not as an oracle.