Building the ReAct loop

Now let's put it all together into a working ReAct loop. The agent will: think about the question, choose a tool, see the result, think again, and repeat until it reaches a Final Answer.

Two things: a max_steps safety limit, and the "Final Answer:" keyword. When the LLM decides it has enough information, it outputs "Final Answer:" instead of another Action. Our code detects that and exits the loop.

02-react-pattern.ipynb
python
def run_react_agent(question, max_steps=5):
    messages = [
        {"role": "system", "content": REACT_SYSTEM_PROMPT},
        {"role": "user", "content": f"Question: {question}"}
    ]

    for i in range(max_steps):
        response = completion(
            model=DEFAULT_MODEL,
            messages=messages,
            temperature=0.0,
            stop=["Observation:"]
        )
        text = response.choices[0].message.content

        if "Final Answer:" in text:
            return text.split("Final Answer:")[1].strip()

        # Parse action from text
        action_match = re.search(r"Action: (\w+)", text)
        input_match = re.search(r"Action Input: (.*)", text)

        if action_match and input_match:
            tool_name = action_match.group(1)
            tool_input = input_match.group(1).strip()
            observation = tools[tool_name](tool_input)

            messages.append({"role": "assistant", "content": text})
            messages.append({
                "role": "user",
                "content": f"Observation: {observation}"
            })

    return "Max steps reached without final answer."

The complete ReAct loop: iterate up to max_steps, parse actions with regex, execute tools, and feed results back.

With the loop built, let us test it on a multi-step question that requires chaining two tools together.

02-react-pattern.ipynb
python
# Multi-step: get weather, then multiply
result = run_react_agent(
    "What is the temperature in Tokyo multiplied by 2?"
)
print(result)
# "The temperature in Tokyo is 25°C. Multiplied by 2, that's 50."

The agent uses two tools in sequence: get_weather for the temperature, then calculate to multiply.

Execution trace:"Temperature in Tokyo * 2"

The agent reasons through two tool calls to arrive at the final answer.

Fill in the blanks: Complete the action parsing

Loading practice…

Matching exercise: Match ReAct components to their roles

Loading practice…

That is the biggest weakness of manual ReAct! If the LLM writes "Tool: calculate" instead of "Action: calculate", or adds extra whitespace, the regex fails silently. This fragility is why the industry moved to native function calling, which we'll implement up next.

Hints: Hints

Loading practice…

Notice we use stop=["Observation:"] in the completion call. This tells the LLM to stop generating text before writing the Observation itself. Our code fills in the real Observation from the tool result. Without this, the LLM might hallucinate a fake observation.