Agents·8 min read

How to build a ReAct agent with OpenAI function calling

ReAct (Reason + Act) is the foundational pattern for LLM agents. This guide builds a working ReAct agent from scratch using the OpenAI API and function calling, with a web search and calculator tool.

What is the ReAct pattern

ReAct (Reasoning and Acting) is a prompting and execution pattern where an LLM alternates between thinking through a problem and taking actions (calling tools). Each step produces either a Thought (the model's reasoning), an Action (a tool call), an Observation (the tool result), or a Final Answer.

The pattern works because LLMs are better at single-step reasoning than multi-step planning. By breaking a complex task into small steps — each with an observation before the next step — the model avoids common planning errors and can adapt when tool results are unexpected.

Set up the environment

Install the OpenAI Python SDK: `pip install openai`. Set your API key: `export OPENAI_API_KEY=your_key`. For the web search tool in this example, also install: `pip install duckduckgo-search`.

The agent in this guide will use GPT-4o-mini for cost efficiency — it supports function calling and is fast enough for interactive agent loops.

Define your tools

Create a `tools` list for the API and a corresponding `tool_implementations` dict that maps tool names to Python functions. For this example: `search(query)` calls DuckDuckGo and returns the top 3 results as a formatted string; `calculate(expression)` evaluates a safe math expression using Python's `eval` with a restricted namespace.

The tool descriptions must be precise. Instead of 'searches the web', write 'searches the web and returns the top 3 results as a list of title, URL, and snippet'. The model uses the description to decide which tool to call and how to use the result.

Build the agent loop

Initialise the messages list with a system prompt explaining the agent's role and a user message with the task. Call the OpenAI chat completions endpoint with your tools list. If the response has `tool_calls`, execute each tool, append the results as `tool` role messages, and call the API again.

Add a system prompt that encourages step-by-step reasoning: 'Think through the problem step by step before calling any tools. After each tool result, reflect on whether you have enough information to answer.' This significantly improves the quality of multi-step reasoning.

Handle errors gracefully

Wrap each tool execution in a try-except block. If a tool fails, pass the error message back to the model as the tool result — the model can then choose a different approach or ask for clarification rather than crashing the agent.

Implement a step counter and stop the loop after N iterations with a final message to the user. Most tasks complete in 2–5 steps; if an agent exceeds 10 steps it is usually stuck in a loop.

Test and extend

Test with questions that require multiple steps: 'What is the population of the capital of France divided by 1000?' This requires finding that Paris is the capital, finding its population, and doing the division.

Once the basic loop works, add more tools: a file reader, a code executor with sandboxing, or a database query tool. The architecture scales — each new tool is a Python function and a JSON Schema entry.