What is a tool?
A tool is a Python function that an agent can call. You annotate it with @tool — LangChain reads the docstring and type hints to build the schema the LLM uses to decide when and how to call it.
from langchain_core.tools import tool
@tool
def web_search(query: str) -> str:
"""Search the web for current information about a topic."""
# call your search API here
return f"Results for: {query}"
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression. Input must be a valid Python expression."""
try:
result = eval(expression, {"__builtins__": {}})
return str(result)
except Exception as e:
return f"Error: {e}"
The docstring is critical — it tells the LLM what the tool does and when to use it. Write it as if you are describing the function to a colleague who will decide whether to call it.
The ReAct loop
Most LangChain agents use the ReAct pattern (Reason + Act). The model alternates between reasoning about what to do and taking an action (calling a tool), then observing the result.
Thought
The LLM reasons about the goal and decides which tool to call next.
Thought: I need to find the current price of Bitcoin. I'll use web_search.
Action
The LLM outputs a structured tool call. LangChain executes it.
Action: web_search("Bitcoin price today")
Observation
The tool's return value is fed back to the model.
Observation: "Bitcoin is trading at $67,420..."
Repeat or finish
The model reasons again. If it has enough information, it returns a final answer. Otherwise, it picks another tool.
This Thought → Action → Observation loop repeats until the model decides it has enough information to give a final answer, or a maximum step limit is reached.
Building an agent
LangChain's create_react_agent and AgentExecutor handle the ReAct loop for you.
from langchain import hub
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [web_search, calculator]
# pull the standard ReAct prompt from LangChain Hub
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = executor.invoke({
"input": "What is 15% of the current price of Bitcoin in USD?"
})
print(result["output"])
With verbose=True, you see the full Thought/Action/Observation trace in the console — very useful for debugging agent behaviour.
Example: web search tool
Tavily is the recommended search backend for LangChain agents — it returns clean, LLM-ready results rather than raw HTML.
from langchain_community.tools.tavily_search import TavilySearchResults
import os
os.environ["TAVILY_API_KEY"] = "tvly-..."
search = TavilySearchResults(max_results=3)
# use directly as a standalone tool
results = search.invoke("LangChain latest version")
# or add to an agent's tool list
tools = [search, calculator]
TavilySearchResults is already a LangChain tool — it has the schema and docstring baked in. No @tool decorator needed.
Example: safe calculator tool
LLMs hallucinate arithmetic. A calculator tool grounds the agent in exact numbers. The key is restricting eval() to prevent code injection.
import ast, operator
SAFE_OPS = {
ast.Add: operator.add, ast.Sub: operator.sub,
ast.Mult: operator.mul, ast.Div: operator.truediv,
ast.Pow: operator.pow, ast.USub: operator.neg,
}
def _eval(node):
if isinstance(node, ast.Constant):
return node.value
if isinstance(node, ast.BinOp):
return SAFE_OPS[type(node.op)](_eval(node.left), _eval(node.right))
if isinstance(node, ast.UnaryOp):
return SAFE_OPS[type(node.op)](_eval(node.operand))
raise ValueError(f"Unsupported: {node}")
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression safely. Example: '2 ** 10 + 5'"""
try:
tree = ast.parse(expression, mode="eval")
return str(_eval(tree.body))
except Exception as e:
return f"Error: {e}"
This AST-based approach evaluates only arithmetic operators — no function calls, no imports, no code execution. Safe to expose to an LLM.
Tool calling vs ReAct agents
Tool calling (bind_tools)
Bind tools directly to a chat model using llm.bind_tools(tools). The model emits structured tool calls in one pass. You handle execution and the result loop yourself.
More control, more code. Best for production where you need to inspect and handle each step.
ReAct agent (AgentExecutor)
AgentExecutor runs the full Thought/Action/Observation loop automatically until the model returns a final answer.
Less code, less control. Best for prototyping and exploration.
For production agents with complex control flow, consider LangGraph — it gives you full control over the loop with stateful graph nodes.
Quick summary
- Decorate any Python function with
@toolto make it callable by an LLM - The docstring is the tool's spec — write it clearly so the model knows when to use it
- ReAct loop: Thought → Action → Observation → repeat until done
- Use
TavilySearchResultsfor web search — it returns clean, LLM-ready content - Use AST-based evaluation for a calculator — never expose raw
eval()to an LLM AgentExecutorruns the loop automatically;bind_toolsgives you manual control
LangChain Tools and Agents FAQ
What is a tool in LangChain?
A tool is a Python function an agent can call. You annotate it with the @tool decorator, and LangChain reads the docstring and type hints to build the schema the LLM uses to decide when and how to call it. The docstring is critical because it tells the model what the tool does and when to use it.
What is the ReAct loop in LangChain agents?
ReAct stands for Reason + Act. The agent alternates between a Thought (reasoning about which tool to call), an Action (emitting a structured tool call that LangChain executes), and an Observation (the tool's return value fed back to the model). This loop repeats until the model has enough information to give a final answer or hits a step limit.
What is the difference between tool calling and a ReAct agent?
With tool calling you bind tools to a chat model using llm.bind_tools(tools); the model emits structured calls in one pass and you handle execution yourself — more control, more code. A ReAct agent built with AgentExecutor runs the full Thought/Action/Observation loop automatically — less code, less control, ideal for prototyping.
How do I add web search to a LangChain agent?
Use TavilySearchResults from langchain_community, which is already a LangChain tool with its schema and docstring built in — no @tool decorator needed. Set your TAVILY_API_KEY, create the tool with a max_results limit, and add it to the agent's tool list. Tavily returns clean, LLM-ready results rather than raw HTML.
When should I use LangGraph instead of AgentExecutor?
Use LangGraph for production agents with complex control flow. AgentExecutor is convenient for prototyping but hides the loop, whereas LangGraph gives you full control over each step with stateful graph nodes, branching, and human-in-the-loop checkpoints.