Looking for a practical llamaindex react agent tutorial? This guide builds a ReAct agent in LlamaIndex that reasons over a user query, calls custom Python tools, and synthesizes the answer—all against an OpenAI-compatible endpoint. You’ll get runnable code, expected traces, and notes on production concerns like fallback.
Prerequisites
- Python 3.10 or newer
llama-indexcore plus the OpenAI LLM adapter- An API key for any OpenAI-compatible gateway (this example uses n4n.ai’s endpoint)
python-dotenvto load secrets from.env
Install the dependencies:
pip install llama-index llama-index-llms-openai python-dotenv
Create a .env file:
N4N_API_KEY=sk-your-key-here
Configure the LLM
LlamaIndex’s OpenAI class speaks the standard /v1/chat/completions protocol. Point api_base at your gateway. The n4n.ai endpoint exposes 240+ models behind one OpenAI-compatible URL, with automatic fallback when a provider is rate-limited and per-token usage metering.
import os
from dotenv import load_dotenv
from llama_index.llms.openai import OpenAI
load_dotenv()
llm = OpenAI(
model="gpt-4o-mini",
api_key=os.environ["N4N_API_KEY"],
api_base="https://api.n4n.ai/v1",
temperature=0,
)
Keep temperature=0 for deterministic tool-call parsing. ReAct prompts are brittle with randomness.
Define tools
LlamaIndex wraps any typed Python function as a FunctionTool. The docstring becomes the tool description the model sees—write it like an API contract.
from llama_index.core.tools import FunctionTool
def multiply(a: int, b: int) -> int:
"""Multiply two integers and return the product."""
return a * b
multiply_tool = FunctionTool.from_defaults(fn=multiply)
def get_weather(city: str) -> str:
"""Return current weather summary for a given city name."""
# Stub: replace with a real API call in production
return f"Sunny in {city}, 22C"
weather_tool = FunctionTool.from_defaults(fn=get_weather)
In this llamaindex react agent tutorial we keep the tool surface small so the reasoning trace stays readable. Each tool must have a precise signature; the agent serializes arguments as JSON.
Build the ReAct agent
ReActAgent implements the thought-action-observation loop. It prompts the LLM to emit a Thought, then an Action with Action Input, executes the tool, feeds the Observation back, and repeats until an Answer token appears.
from llama_index.agent.react import ReActAgent
agent = ReActAgent.from_tools(
tools=[multiply_tool, weather_tool],
llm=llm,
verbose=True,
max_iterations=5,
)
Set max_iterations to bound cost. Without it a confused model will loop until context overflow.
Run the agent and read the trace
Fire a query that forces two tool calls:
response = agent.chat("What is 7 times 8 and what's the weather in Berlin?")
print(str(response))
With verbose=True you get the raw loop on stdout. Expected shape:
Thought: I need to multiply 7 and 8, and check weather in Berlin.
Action: multiply
Action Input: {"a": 7, "b": 8}
Observation: 56
Action: get_weather
Action Input: {"city": "Berlin"}
Observation: Sunny in Berlin, 22C
Thought: I have both results.
Answer: 7 times 8 is 56, and Berlin is sunny at 22C.
The final response object carries the answer string. The intermediate steps are logged, not returned—capture them with a callback if you need audit trails.
Add a failing tool to test recovery
Real tools throw. Wrap logic so the agent sees a clean observation instead of a stack trace:
def divide(a: int, b: int) -> str:
"""Divide a by b, returning a string error if b is zero."""
if b == 0:
return "Error: division by zero"
return str(a / b)
divide_tool = FunctionTool.from_defaults(fn=divide)
Rebuild the agent with the new tool. If the model tries b=0, it reads the error string and can retry with a corrected call or explain the failure. In this llamaindex react agent tutorial we prefer returning errors as strings; raising exceptions breaks the ReAct parser in older LlamaIndex versions.
Production notes
ReAct is token-hungry. Every iteration re-sends the full system prompt, tool schemas, and prior observations. Three levers matter:
- Model choice – a smaller instruction-tuned model often suffices for the loop; reserve large models for synthesis.
- Tool count – more tools inflate the prompt and confuse action selection. Group related ops.
- Gateway routing – the gateway can pin a provider via client headers and forwards cache-control hints, so repeated identical tool schemas hit provider prompt caches.
Because the gateway meters per token, you can attribute cost to each agent run from the response headers or usage endpoint. Automatic fallback means a provider 429 doesn’t kill the agent mid-loop; the request silently reroutes.
Wrapping up
You now have a working ReAct agent that calls Python functions through LlamaIndex. The pattern extends to REST APIs, database queries, or RAG retrievers—any function with a clear docstring works. For the full code from this llamaindex react agent tutorial, drop the snippets into a single agent.py and run it.
Keep tool descriptions honest, cap iterations, and watch token spend. That’s the difference between a demo and a system you can ship.