Following a Plan to a Dead End
Ask a large language model (an AI system trained to predict and generate text) a question, and it talks. Give it access to tools—a web browser, a code interpreter, a database—and it can act: look things up, run calculations, fetch records. That's the difference between a chatbot and what's now called an AI "agent": something that doesn't just answer, but does something.
Getting there wasn't as simple as handing the model a list of commands, though. Early attempts had the model draw up a complete plan before doing anything, then carry that plan out step by step. These systems were brittle and failed silently. If one step went wrong—a web link was broken, a search turned up nothing—the model had no way to notice or adjust. It would follow the plan it had already committed to, straight to a dead end.
The fix turned out to be shrinking the loop. Instead of planning everything upfront, the model takes one small step of reasoning (a "thought"), picks a single action to match it, and then stops to look at what happened (an "observation"). That new information feeds the next thought, and the cycle repeats: Thought, then Action, then Observation, then Thought again. This is the core idea behind ReAct, a framework introduced in a 2022 paper, ReAct: Synergizing Reasoning and Acting in Language Models ↗. The pattern lets a model change course mid-task, handle surprises, and work through problems that can't be fully mapped out in advance. It's the logic that turned language models from one-shot answer machines into systems that can actually get things done—and it's quietly running underneath almost every AI agent in use today.

From Monologue to Dialogue
Before agentic loops became standard, two main paradigms dominated how we prompted language models to solve complex tasks: direct prompting and two-stage prompting.
Direct prompting is the simplest approach: you give the model a question and it gives you an answer. For simple factual recall, this works fine. But for anything requiring multiple steps of logic, it often fails. The model tries to generate the final answer in one go, and if its initial path is flawed, the entire answer is wrong. It's a black box that produces a single, take-it-or-leave-it monologue.
The first major improvement was "Chain-of-Thought" (CoT) prompting. This technique encourages the model to "think out loud" by providing examples where the reasoning process is written out step-by-step before the final answer. This dramatically improved performance on arithmetic, commonsense, and symbolic reasoning tasks. The model was no longer just giving an answer; it was showing its work.
However, CoT is still just a monologue. The model's reasoning is entirely internal. It can't access outside information to verify a fact or correct a flawed premise. If it makes a logical error in Step 1, that error will cascade through all subsequent steps. It's reasoning in a vacuum, without any feedback from the real world.
This limitation led to plan-and-execute systems. In this approach, the LLM acts as a planner. It's given a task and a set of available tools. Its job is to generate a complete, step-by-step plan that another system (or the model itself in a second phase) will execute. For example:
Task: What is the combined age of the two actors who played the main protagonists in the movie The Matrix?
Generated Plan:
Find the names of the two main protagonists in The Matrix.
Find the birthdate of the first actor.
Find the birthdate of the second actor.
Calculate their current ages.
Sum the two ages.
Provide the final sum.
This is more robust than simple CoT because it involves actions, but it has a critical weakness: the plan is static. It's created entirely upfront, based on the model's initial understanding of the problem. If step 2 fails because the search query for the actor's birthdate returns an ambiguous result, the entire plan fails. The model has no mechanism to stop, recognize the failure, and generate a new plan. It's a general who draws up a complete battle plan and then goes to sleep, unable to react to the changing realities on the ground.

ReAct: Interleaving Thought, Action, and Observation
The ReAct framework solves the static-plan problem by merging the reasoning of Chain-of-Thought with the ability to take actions. Instead of a long internal monologue or a rigid upfront plan, the model engages in a dialogue with its environment, one step at a time.
This process generates a trajectory—the sequence of thoughts, actions, and observations that documents the model's path to a solution. This makes the agent's behavior transparent and debuggable, a critical feature for any production system.
The loop looks like this:
Thought: The model is given a task. It analyzes its current state and the overall goal, then formulates a thought about what it needs to do next. This is an internal monologue, a piece of scratchpad reasoning. For example: "I need to find out what company developed the popular open-source library, TensorFlow."
Action: Based on its thought, the model chooses an action to take. This usually involves calling an external tool. The action is a concrete, executable command, like
search("who developed TensorFlow")orpython("import pandas as pd; ...").Observation: The model executes the action and receives an observation—the output from the tool. This could be a snippet of text from a search result, the output of a piece of code, or the contents of a database record. For example: "TensorFlow was originally developed by the Google Brain team."
Repeat: The model takes this new observation and loops back to the "Thought" step. The observation becomes part of the context for the next turn. The new thought might be, "Okay, I have the developer. Now I can formulate the final answer." or "The search results were ambiguous. I need to try a more specific query."
This cycle continues until the model concludes that it has enough information to give a final answer.
A Worked Example: Navigating a Tricky Question
Let's walk through how a ReAct-style agent would tackle a multi-hop question designed to be slightly misleading.
Task: Who was the president of the United States when the director of Jaws was born?
A human would quickly break this down, but for an LLM, it involves several dependent steps.
Trajectory 1:
Thought: I need to solve this step-by-step. First, I need to find out who directed the movie Jaws.
Action:
search("director of Jaws")Observation: Steven Spielberg directed the movie Jaws.
Trajectory 2:
Thought: Now that I know the director is Steven Spielberg, I need to find his birthdate.
Action:
search("Steven Spielberg birthdate")Observation: Steven Spielberg was born on December 18, 1946.
Trajectory 3:
Thought: I have the birthdate: December 18, 1946. Now I need to determine who was the U.S. President on that date.
Action:
search("US president December 1946")Observation: Harry S. Truman was the President of the United States in December 1946. He served from 1945 to 1953.
Trajectory 4:
Thought: I have all the pieces of information required. The director of Jaws is Steven Spielberg, who was born on December 18, 1946. The president at that time was Harry S. Truman. I can now form the final answer.
Action:
finish("Harry S. Truman")
This example illustrates the power of the loop. If any action had failed—for example, if the search for "Steven Spielberg birthdate" returned multiple people with that name—the model could have used its next thought to formulate a recovery plan, such as refining the search to "Steven Spielberg director birthdate". A plan-and-execute system would likely have failed.

Why Interleaving Beats Pre-Planning
The ReAct pattern isn't just an architectural curiosity; it provides tangible benefits that have made it the default for building capable agents. By weaving reasoning and acting together, it creates systems that are more robust, adaptable, and trustworthy.
Dynamic Course Correction
The most significant advantage is the ability to react to the real world. The world is messy. APIs change, web pages go down, search results are noisy, and code has bugs. A static plan that assumes perfection is doomed to fail.
The ReAct loop is fundamentally adaptive. Every observation provides feedback that informs the next thought.
If a tool returns an error, the model can think, "That API call failed with a 404 error. The endpoint might be wrong. I will try searching for the correct API documentation."
If a search query returns irrelevant results, the model can think, "These results are about a different topic. I will rephrase my query to be more specific."
If a code execution fails, the model can read the traceback and attempt to debug its own code in the next turn.
This constant feedback loop allows the agent to navigate unforeseen obstacles, making it far more reliable for tasks that interact with external, unpredictable systems.
Grounding Reasoning in Fact
Chain-of-Thought reasoning, while powerful, is prone to hallucination. A model might "recall" a fact incorrectly and build a perfectly logical argument on top of that faulty premise. The final answer will be wrong, but the reasoning will look plausible.
ReAct mitigates this by forcing the model to ground its thoughts in external observations. Instead of just reasoning internally, the model must actively seek out and incorporate information from trusted tools. When it needs to know a fact, it doesn't just guess; it performs a search. When it needs to perform a calculation, it doesn't just approximate; it calls a calculator or a code interpreter.
This makes the agent's reasoning process less about "remembering" and more about "discovering." The final answer is built upon a foundation of verifiable information retrieved from the outside world, not just the model's internal knowledge.
Debuggability and Trust
When a simple LLM call produces a wrong answer, it's often impossible to know why—the reasoning is opaque. When a ReAct agent fails, its trajectory provides a complete audit trail. You can see every thought, every action it took, and every observation it received.
This transparency is invaluable for developers. Debugging is no longer a matter of tweaking the prompt and hoping for the best. Instead, you can pinpoint the exact step where the agent went wrong:
Did it misinterpret the initial prompt? (Check the first
Thought).Did it use the wrong tool? (Check the
Action).Did it misinterpret the tool's output? (Check the
Observationand the subsequentThought).Did it get stuck in a repetitive loop? (Look for repeating
Thought/Actionpairs).
By examining the trajectory, you can understand the agent's "mind" and fix the underlying problem, whether it's by improving the prompt, refining the tool descriptions, or adding examples of how to handle specific edge cases.

The Failures We Still Haven't Solved
Despite its power, the ReAct pattern is not a silver bullet. The original paper, "ReAct: Synergizing Reasoning and Acting in Language Models", hinted at several failure modes, and years later, developers building agents still wrestle with the very same challenges. These aren't obscure edge cases; they are the common, frustrating ways that agents break in production.
Failure Mode 1: Repetitive Loops
The most common failure is the agent getting stuck in a loop. It repeats the same thought and action, perhaps with minor variations, without making any progress.
For example, an agent tasked with finding a specific piece of information might issue a search query, find the results unsatisfactory, and then issue the exact same query again. The thought process might look like this:
Thought: I need to find the quarterly revenue for Acme Corp. The search results were not helpful. I should try searching for Acme Corp's quarterly revenue.
Action:
search("Acme Corp quarterly revenue")Observation: [List of general news articles, none with specific numbers.]
Thought: The search results did not contain the specific quarterly revenue. I need to find the quarterly revenue for Acme Corp. I will search for it.
Action:
search("Acme Corp quarterly revenue")
This can happen for several reasons. The model might not have the sophistication to formulate alternative search strategies, or it might be so focused on the immediate goal that it fails to recognize it's making no progress. Breaking these loops often requires meta-cognition—a "thought about the thoughts"—which is still a frontier challenge.
Failure Mode 2: Tool Output Misinterpretation
Sometimes the tool works perfectly, but the model misunderstands the result. An API might return a JSON object with a subtle error message nested inside a successful 200 OK response. The model sees the 200 OK and assumes the call succeeded, ignoring the actual error payload.
Similarly, a search result might contain nuanced or sarcastic language that the model takes literally, sending its reasoning in a completely wrong direction. The agent is only as good as its ability to comprehend the data it receives, and even today's advanced models can be surprisingly naive readers. This is particularly dangerous when dealing with structured data like JSON or XML, where a misplaced comma or a subtle schema difference can be completely missed by the LLM.
Failure Mode 3: Inefficient or Reckless Task Decomposition
The art of using a ReAct agent lies in breaking a large problem into appropriately sized steps. Models often struggle with this.
Sometimes they are too granular, taking dozens of tiny, inefficient steps for a task that could be done in two or three. This adds significant latency and cost, as each turn in the loop involves a full LLM inference. An agent might make a separate search call for a person's birth day, birth month, and birth year, rather than just asking for their full birthdate.
Other times, they are too reckless, trying to accomplish too much in a single, complex action. The model might write a 100-line Python script with multiple dependencies and try to execute it all at once. When the script inevitably fails, the error message is long and complex, and the model struggles to debug its own monolithic creation. The ideal path is a series of small, verifiable code executions, but teaching a model that discipline remains a challenge.
ReAct gives a model the ability to act, but not, by itself, the judgment to act efficiently or safely. Closing that gap is most of the day-to-day work of building an agent: writing prompts that discourage repeated dead-end queries, giving tools clear and unambiguous outputs so the model can't mistake a "200 OK" wrapper for a successful result, and nudging models toward small, checkable steps instead of monolithic scripts that fail all at once. ReAct supplies the loop; the judgment that fills each turn of that loop is still something developers have to build in by hand, one failure mode at a time.

