> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lab Walkthrough Build a ReAct Agent

> Guide to implementing ReAct agents in Python to expose chain-of-thought while calling tools like calendar and contact search for transparent multi-step reasoning and debugging.

Time to implement the ReAct pattern so your agent exposes its internal reasoning while calling tools. The ReAct loop makes the agent produce a "Thought" before acting and an "Observation" after each tool result, letting you follow its chain-of-thought: Reason → Act → Observe → Repeat.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/WUKBeXdogksN49S5/images/AI-Agents-for-Beginners-OpenClaw-Case-Study/Building-AI-Agents/Lab-Walkthrough-Build-a-ReAct-Agent/retro-pixel-build-react-agent-splash.jpg?fit=max&auto=format&n=WUKBeXdogksN49S5&q=85&s=d988a86882bf6e41eebd455125b1e1b6" alt="A retro, pixel-art style splash screen that says &#x22;BUILD A REACT AGENT&#x22; with the subtitle &#x22;You built an agent that calls tools.&#x22; A small &#x22;HANDS-ON LAB&#x22; label and buttons including &#x22;But you can't see its reasoning&#x22; and &#x22;REACT PATTERN&#x22; are shown below." width="1920" height="1080" data-path="images/AI-Agents-for-Beginners-OpenClaw-Case-Study/Building-AI-Agents/Lab-Walkthrough-Build-a-ReAct-Agent/retro-pixel-build-react-agent-splash.jpg" />
</Frame>

This guide walks through a minimal Python ReAct agent. The environment (Python, virtualenv, OpenAI SDK) is already set up and your working directory is `/root/code`. We'll:

* Start with a compact agent implementation that calls a single `check_calendar` tool.
* Modify the system prompt to require Thought/Observation lines (the ReAct style).
* Add a second tool (`search_contacts`) and show a multi-step interaction.
* Summarize best practices and include a tool reference table.

Why this matters: ReAct helps debug and audit multi-step agent decisions by making intermediate reasoning visible.

## 1) Minimal agent that calls a single tool

Create `react_agent.py` with this simple agent loop that supports one tool: `check_calendar(day)`. The model may call the tool, and the agent dispatches to the local function and appends the tool result back into the conversation. At this stage, the model does not reveal its chain-of-thought.

```python theme={null}
# react_agent.py
import json
from openai import OpenAI

client = OpenAI()

def check_calendar(day):
    events = {
        "monday": "Standup 9am",
        "thursday": "1pm lunch, 3pm review"
    }
    return events.get(day.lower(), "No events")

# Tools describes the functions the model may call
tools = [
    {
        "type": "function",
        "function": {
            "name": "check_calendar",
            "parameters": {
                "type": "object",
                "properties": {
                    "day": {"type": "string"}
                },
                "required": ["day"]
            }
        }
    }
]

messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Thursday plans? Is 2pm free?"}
]

while True:
    resp = client.chat.completions.create(
        model="openai/gpt-4.1-mini",
        messages=messages,
        tools=tools
    )

    msg = resp.choices[0].message
    messages.append(msg)

    # If the model requested a tool call, execute it
    if getattr(msg, "tool_calls", None):
        for tc in msg.tool_calls:
            # tc is expected to contain 'name' and 'arguments' (JSON string)
            args = json.loads(tc["arguments"])
            if tc["name"] == "check_calendar":
                result = check_calendar(**args)
            else:
                result = "Unknown tool"

            # Append the tool's result back into the conversation
            messages.append({
                "role": "tool",
                "name": tc["name"],
                "content": result
            })
    else:
        # No tool calls: final answer from the assistant
        print(msg.content)
        break
```

Run the script. The model will call `check_calendar` and return a final answer, but its internal reasoning will be hidden.

Example console output (chain-of-thought not visible):

```text theme={null}
Standup 9am
```

## 2) Make the model reveal its reasoning: add a ReAct system prompt

To force the model to expose its internal loop, replace the simple system prompt with explicit ReAct instructions that require the model to write a `Thought:` line before each tool call and an `Observation:` line after every tool result.

Update the `messages` list as shown:

```python theme={null}
messages = [
    {
        "role": "system",
        "content": (
            "You are a helpful personal assistant.\n"
            "Before every tool call, write:\n"
            "Thought: [your reasoning]\n"
            "After every tool result, write:\n"
            "Observation: [what you learned]\n"
            "Then decide your next step."
        )
    },
    {"role": "user", "content": "Thursday plans? Is 2pm free?"}
]
```

Run the agent again with the same `check_calendar` tool. The model will produce a visible Thought line, a tool call, and an Observation line before the final answer. Example illustrative output:

```text theme={null}
Thought: I need to check Thursday's calendar to see what events exist.
[tool_call] check_calendar(day="thursday")
Observation: Thursday has 1pm lunch with Alex and 3pm product review. 2pm is free.
Final Answer: 2pm is free on Thursday.
```

Now you can follow the agent's reasoning and tool interaction.

## 3) Add a second tool for multi-step tasks

Next, implement `search_contacts(name)` and extend the `tools` list and dispatch logic so the agent can perform multi-step workflows that require both tools (e.g., check calendar then look up a contact).

Add the contact-search function and extend tools:

```python theme={null}
# Add the new tool implementation
def search_contacts(name):
    contacts = {
        "sarah": "sarah@example.com",
        "alex": "alex@example.com"
    }
    return contacts.get(name.lower(), "No contact found")

# Extend the tools list with the new function
tools.append({
    "type": "function",
    "function": {
        "name": "search_contacts",
        "parameters": {
            "type": "object",
            "properties": {
                "name": {"type": "string"}
            },
            "required": ["name"]
        }
    }
})
```

Update the dispatch logic inside the main `while True` loop to handle both function names:

```python theme={null}
    if getattr(msg, "tool_calls", None):
        for tc in msg.tool_calls:
            args = json.loads(tc["arguments"])
            if tc["name"] == "check_calendar":
                result = check_calendar(**args)
            elif tc["name"] == "search_contacts":
                result = search_contacts(**args)
            else:
                result = "Unknown tool"

            messages.append({
                "role": "tool",
                "name": tc["name"],
                "content": result
            })
```

Change the user prompt to a multi-step request:

```python theme={null}
messages[-1] = {"role": "user", "content": "Do I have anything with Sarah on Thursday, and if not, what's Sarah's email?"}
```

When you run the updated script with the ReAct system prompt, the model will produce a step-by-step loop: it will write Thoughts, call tools, show Observations, and then provide a combined final answer.

Illustrative output:

```text theme={null}
Thought: I'll check Thursday's calendar for events with Sarah.
[tool_call] check_calendar(day="thursday")
Observation: Thursday has 1pm lunch with Alex and 3pm product review. No events with Sarah.
Thought: Since Sarah is not on the calendar, I'll look up her contact information.
[tool_call] search_contacts(name="sarah")
Observation: Sarah's email is sarah@example.com
Final Answer: You don't have anything scheduled with Sarah on Thursday. Sarah's email is sarah@example.com.
```

## 4) Tool reference (quick lookup)

| Tool name | Purpose | Example call |
| - | - | - |
| `check_calendar` | Look up events on a given day | `check_calendar(day="thursday")` |
| `search_contacts` | Return contact info for a given name | `search_contacts(name="sarah")` |

Note: When documenting parameter examples or small JSON objects in tables, keep them inline code as shown above so MDX doesn't mis-parse them.

## 5) Best practices and tips

* Always include clear tool descriptions (name, parameters, required fields) so the model understands available actions.
* Append tool results as `role: "tool"` messages so the model can observe outcomes and continue reasoning.
* Use the ReAct system prompt pattern for transparent multi-step reasoning:
  * Thought: state your reasoning
  * \[tool\_call] run the tool
  * Observation: report the tool’s output
  * Repeat until confident to answer
* Use ReAct for debugging and audits: it makes decision steps explicit and easier to verify.

## Summary

* ReAct exposes model reasoning in the agent loop using Thought → Act → Observation → (repeat).
* Add function/tool descriptions to the agent (so the model knows what it can call).
* Append tool results as `role: "tool"` conversation messages so the model can observe and continue reasoning.
* The ReAct pattern is especially helpful for debugging multi-step agent behavior.

References and further reading:

* ReAct paper: [https://arxiv.org/abs/2210.03629](https://arxiv.org/abs/2210.03629)
* OpenAI function calling & agents guide: [https://platform.openai.com/docs/guides/gpt/function-calling](https://platform.openai.com/docs/guides/gpt/function-calling)

<Callout icon="lightbulb" color="#1CB2FE">
  Use the ReAct system prompt whenever you want the model to make its intermediate reasoning and tool usage explicit. This is especially helpful for debugging multi-step agent behavior.
</Callout>

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/ai-agents-for-beginner-openclaw-case-study/module/d77598d4-d1d3-4768-97da-03ead60bf984/lesson/12c618e7-f5d7-4389-91e9-bb9a788f45a0" />
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.