n4nAI

LangChain ReAct agents: a hands-on tutorial

Hands-on langchain react agent tutorial: build a ReAct agent with tools, step-by-step code, and expected outputs using LangChain's current API.

n4n Team3 min read623 words

Audio narration

Coming soon — every post will get a voice note here.

This langchain react agent tutorial walks you through building a ReAct-style agent that interleaves reasoning with tool calls using the current LangChain API. We’ll stand up a working agent that searches the web and evaluates arithmetic, then inspect its thought traces to see exactly how the loop operates.

Prerequisites

  • Python 3.10 or newer
  • An OpenAI-compatible API key (OpenAI, or a gateway such as n4n.ai)
  • Basic familiarity with Python environment variables and virtualenvs
  • LangChain 0.3.x installed (instructions below)

ReAct (Reason + Act) is a prompting strategy, not a model feature. The LLM emits a thought, chooses a tool, receives an observation, and repeats until it can answer. LangChain wraps this in AgentExecutor.

Install dependencies

Create a clean environment and install the needed packages.

python -m venv venv
source venv/bin/activate
pip install langchain==0.3.0 langchain-openai==0.1.0 langchain-community==0.3.0 duckduckgo-search==6.1.0

Export your key:

export OPENAI_API_KEY="sk-..."

If you route through n4n.ai, the OpenAI-compatible endpoint covers 240+ models and automatically falls back when a provider is rate-limited or degraded. You’d set base_url="https://api.n4n.ai/v1" on the LLM client; no other code changes are required.

Define your tools

Tools are the actions the agent can take. Each needs a name, a callable, and a description the model uses to decide when to call it. Start with a web search:

from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import Tool

search = DuckDuckGoSearchRun()
tools = [
    Tool(
        name="web_search",
        func=search.run,
        description="Useful for answering questions about current events or facts not in the model's training data."
    )
]

The Tool wrapper gives explicit control over the name and description. The raw DuckDuckGoSearchRun object also works, but wrapping avoids ambiguity when multiple tools are present.

Construct the ReAct prompt

The ReAct format is a strict template. LangChain’s create_react_agent requires placeholders {tools}, {tool_names}, {input}, and {agent_scratchpad}.

from langchain_core.prompts import PromptTemplate

template = """Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}"""

prompt = PromptTemplate.from_template(template)

{agent_scratchpad} is filled by the executor with the accumulating Thought/Action/Observation history. Do not put a space after Thought: in the template’s last line; the model continues directly.

Configure the LLM

Use ChatOpenAI from langchain_openai. Keep temperature=0 for deterministic tool selection.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0,
    # base_url="https://api.n4n.ai/v1",
    # api_key="your-gateway-key",
)

Throughout this langchain react agent tutorial we keep the model swap-ready. Commenting the gateway URL leaves the example provider-agnostic.

Assemble the agent executor

create_react_agent returns a runnable that produces the next action. AgentExecutor drives the loop.

from langchain.agents import create_react_agent, AgentExecutor

agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    handle_parsing_errors=True,
    max_iterations=5,
)

handle_parsing_errors=True converts malformed model output into an observation like “Could not parse LLM output”, giving the model a chance to recover instead of crashing.

Run a query and read the trace

Invoke with a question requiring external data:

response = executor.invoke({
    "input": "What is the height of Mount Everest in meters, and what is that value divided by 10?"
})
print(response["output"])

Expected verbose trace (trimmed):

> Entering new AgentExecutor chain...
Thought: I need the height of Mount Everest. I'll search the web.
Action: web_search
Action Input: height of Mount Everest in meters
Observation: Mount Everest is 8,849 meters above sea level.
Thought: I have the height. Now divide by 10.
Action: calculator
Action Input: 8849 / 10
Observation: 884.9
Thought: I now know the final answer.
Final Answer: Mount Everest is 8,849 meters tall. Divided by 10 that is 884.9.
> Finished chain.

The output key holds the final answer. The trace shows the model reasoning, calling a tool, and consuming the observation.

Add a calculator tool

The trace above references a calculator action we haven’t defined. Add it before assembling the executor:

from langchain_core.tools import tool

@tool
def calculator(expression: str) -> str:
    """Evaluate a basic arithmetic expression. Input like '8849 / 10'."""
    try:
        return str(eval(expression, {"__builtins__": {}}, {}))
    except Exception as e:
        return f"Calculation error: {e}"

tools.append(calculator)

The @tool decorator infers the schema from the function signature. Recreate the executor after appending. The prompt’s {tool_names} expands automatically.

Inspect intermediate steps

For logging or post-processing, access the raw steps:

steps = response["intermediate_steps"]
for action, observation in steps:
    print(f"{action.tool}({action.tool_input!r}) -> {observation}")

This prints each tool call and its result without parsing stdout. Use it to meter token usage or build custom UIs.

Error handling and retries

Tools fail in production. Wrap callables to return a string instead of raising:

@tool
def safe_search(query: str) -> str:
    """Search with graceful failure."""
    try:
        return search.run(query)
    except Exception as e:
        return f"Search failed: {e}"

tools[0] = safe_search

Set max_iterations and max_execution_time to bound cost. If the agent hits the limit, AgentExecutor raises MaxIterationsExceeded; catch it in your service layer.

How the ReAct loop works internally

The executor calls the agent runnable with the current scratchpad. The model returns text. A output parser splits it into Thought and Action/Action Input. If Action is Final Answer, the loop ends. Otherwise the executor looks up the tool, runs it, appends Observation, and repeats. The langchain react agent tutorial code hides this behind AgentExecutor, but understanding the cycle helps when debugging stuck agents.

Production considerations

Verbose mode writes to stdout. Replace it with a CallbackHandler that streams to your log stack. Track intermediate_steps to estimate per-call token spend—scratchpads grow linearly with iterations.

When deploying, externalize the model endpoint. A gateway that honors client routing directives and forwards provider cache-control hints reduces latency and lets you pin providers per request without editing agent code.

Full script

from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.tools import Tool, tool
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor

search = DuckDuckGoSearchRun()
tools = [
    Tool(name="web_search", func=search.run, description="Search the web for current facts."),
]

@tool
def calculator(expression: str) -> str:
    """Evaluate a basic arithmetic expression."""
    try:
        return str(eval(expression, {"__builtins__": {}}, {}))
    except Exception as e:
        return f"Calculation error: {e}"

tools.append(calculator)

template = """Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}"""

prompt = PromptTemplate.from_template(template)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=5)

if __name__ == "__main__":
    out = executor.invoke({"input": "What is the height of Mount Everest in meters, and what is that value divided by 10?"})
    print(out["output"])

Run it. The agent searches, calculates, and returns a single string. That’s the complete loop for this langchain react agent tutorial.

Tagslangchainreact-agentagentstutorial

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All langchain agents & tool calling posts →