HOW-TO · RAG

How to Implement Tool Calling in LangGraph Agents

intermediate20 minBy Fredoline Eruo
Target environment
Ubuntu 24.04 · Ollama 0.4.x
PREREQUISITES

LangGraph agent set up, tool functions defined, Python 3.10+

What this does

Tool calling in LangGraph agents allows the LLM to request external function execution (API calls, database queries, calculations). The graph routes the LLM's tool requests to a tools node, then feeds results back to the LLM.

Steps

  • Define tool functions. Each tool is a plain Python function with a docstring.
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"The weather in {city} is 22°C and sunny."

def calculate(expression: str) -> str:
    """Evaluate a mathematical expression."""
    return str(eval(expression))
  • Bind tools to the LLM. Convert functions to tool schemas and attach them.
from langchain_ollama import ChatOllama
from langchain.tools import tool

tools = [tool(get_weather), tool(calculate)]
llm = ChatOllama(model="llama3.2", temperature=0)
llm_with_tools = llm.bind_tools(tools)
  • Create the tool execution node. Parse tool calls from the LLM response and execute them.
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage, ToolMessage

class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]

def call_model(state: AgentState) -> dict:
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}

def call_tool(state: AgentState) -> dict:
    last_msg = state["messages"][-1]
    tool_map = {t.name: t for t in tools}
    messages = []
    for tc in last_msg.tool_calls:
        result = tool_map[tc["name"]].invoke(tc["args"])
        messages.append(ToolMessage(content=result, tool_call_id=tc["id"]))
    return {"messages": messages}
  • Add conditional routing. After the LLM, check if tool calls exist.
def should_continue(state: AgentState) -> str:
    last = state["messages"][-1]
    return "continue" if hasattr(last, "tool_calls") and last.tool_calls else "end"
  • Build the graph with tool routing.
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", call_tool)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue, {
    "continue": "tools",
    "end": END
})
workflow.add_edge("tools", "agent")
app = workflow.compile()
  • Invoke with a tool-requiring query.
from langchain_core.messages import HumanMessage
result = app.invoke({"messages": [HumanMessage(content="What's 15 * 7 and the weather in Paris?")]})
print(result["messages"][-1].content)

Verification

python -c "
from langchain.tools import tool
@tool
def add(a: int, b: int) -> int:
    '''Add two numbers.'''
    return a + b
print(add.name, add.invoke({'a': 2, 'b': 3}))
# Expected: add 5
"

Common failures

  • Tool schema mismatch. Function signature parameters must have type hints; otherwise, the binding may fail. Always annotate arguments.
  • Model doesn't support tool calling. Older or smaller models may never produce tool_calls. Use llama3.2:3b or larger.
  • Tool call ID mismatch. The ToolMessage must include the correct tool_call_id from the original call, or the agent ignores it.
  • Version mismatch - The installed package or runtime differs from the command shown; check the version first and rerun the smallest verification command.
  • Local environment drift - Another service, virtual environment, model, or path is being used; print the active binary path and configuration before changing the guide steps.

Related guides

  • How to Set Up Basic LangGraph Agent from Scratch
  • How to Create Conditional Edges in LangGraph