LangChain agents can call external tools, but the real power comes when you define your own custom tools.

Let’s see an agent use a tool to check if a given string is a palindrome.

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

# Define a custom tool
@tool
def is_palindrome(text: str) -> bool:
    """Checks if a given string is a palindrome."""
    return text == text[::-1]

# Define a list of tools the agent can use
tools = [is_palindrome]

# Define the LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# Define the prompt
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 question
"""
prompt = PromptTemplate.from_template(template)

# Create the agent
agent = create_react_agent(llm, tools, prompt)

# Create the agent executor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Run the agent
response = agent_executor.invoke({"input": "Is 'racecar' a palindrome?"})
print(response)

response = agent_executor.invoke({"input": "Is 'hello' a palindrome?"})
print(response)

The agent, powered by a ReAct (Reasoning and Acting) prompt, orchestrates the use of tools. It first reasons about the user’s request, then decides which tool to use and what input to provide it. After the tool executes, the agent observes the result and continues reasoning until it can provide a final answer.

The core components are:

  1. Tools: These are Python functions decorated with @tool. They encapsulate specific capabilities (like checking for palindromes, searching the web, or querying a database). The decorator automatically serializes the function’s signature and docstring into a format the LLM can understand.
  2. LLM: The language model that generates the reasoning and decides which tool to call.
  3. Prompt: This is crucial. It guides the LLM on how to use the tools, defining the expected input/output format for tool calls and the overall conversational flow. The {tools} placeholder in the prompt is dynamically populated with a description of available tools, and {tool_names} lists their names.
  4. Agent: This is the logic that orchestrates the LLM and tools. create_react_agent is a common way to build this, setting up the ReAct loop.
  5. AgentExecutor: This class runs the agent. It takes the user’s input, passes it to the agent, executes the chosen tool, feeds the observation back to the agent, and repeats until the agent determines it has the final answer.

When you define a tool, LangChain doesn’t just pass the function object. It serializes the function’s name, its docstring (which acts as a description for the LLM), and its parameters’ names and types. The LLM then uses this metadata to decide if a tool is relevant and how to call it. For example, is_palindrome(text: str) tells the LLM that there’s a tool named is_palindrome that takes a single string argument named text.

The Action Input in the ReAct trace is a JSON string representing the arguments to the tool function. The AgentExecutor parses this JSON, calls the actual Python function with the provided arguments, and then passes the function’s return value as the Observation back to the LLM for the next step.

The verbose=True flag on the AgentExecutor is invaluable. It prints the entire ReAct loop—the agent’s thoughts, the tool it chooses, the input it provides, and the tool’s output—giving you a clear view of the agent’s decision-making process.

The real magic happens when you combine multiple tools. An agent can then decide to use one tool, get an observation, and based on that observation, decide to use another tool, or even call the first tool again with different input. This allows for complex, multi-step reasoning and task execution.

The LLM doesn’t execute the Python code directly; it generates a structured output (the Action and Action Input) that the AgentExecutor interprets and uses to call your Python functions. The LLM only sees the result of your function call as an Observation.

The next step is often building more complex agents that can use multiple tools in sequence, or handling cases where tool execution fails.

Want structured learning?

Take the full Langchain course →