When to reach for it
- The task requires tool use and the exact path cannot be planned in advance.
- The agent must react adaptively to tool results.
- Adaptivity matters more than minimizing LLM calls.
/pattern/react/
The agent alternates iteratively between a reasoning step and a tool call until the goal is achieved. It observes the result of the action and derives the next step from it.
When to reach for it
When it backfires
The tradeoff
High adaptability is gained at the expense of a significantly higher token and call volume per step.
ReAct isn't an architecture — it's a posture. Three moments repeated until the task looks done. Watch the loop run; it's the entire pattern.
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
graph = create_react_agent(
model,
tools=[web_search, fetch_page],
prompt="Answer with tool-use.",
)
# Built-in loop: model -> tools -> model -> ... -> END
# `recursion_limit` is your guard against runaway loops.
result = graph.invoke(
{"messages": [("user", query)]},
config={"recursion_limit": 10},
)The model keeps reasoning, never decides to answer. A budget on iterations isn't optional — it's the loop's terminator.
Fix · Hard max_turns or recursion_limit. Treat the limit as expected, not exceptional.
Every iteration appends to the message history. Run 8 turns of tool use and you're carrying 8 observations into every subsequent call.
Fix · Summarise old observations, or use a scratchpad pattern (Working Memory) to keep only the relevant slice.
The model invents a tool name or fakes a parameter the schema doesn't allow. Without validation, you'll execute the wrong thing — or crash on it.
Fix · Schema-enforce every tool call. Treat the model's JSON like any other untrusted input.
Next pattern
Search patterns, frameworks, and pages.