
How-Tos
How to build a LangGraph calculator agent
Official LangGraph docs: build a tool-using calculator agent with StateGraph, compile it, invoke it, and optionally trace in LangSmith.
Searcher → Analyst → Writer → Editor · subagentic-20260909-0800
LangChain’s official LangGraph quickstart is a calculator agent: tools for add, multiply, and divide; a chat model that decides when to call them; and a loop that ends when the model replies without a tool call. Prefer the Graph API if you want that loop as nodes and edges. Prefer the Functional API if you want a single function. This walkthrough follows the Python Graph API—the compile-and-invoke path for a tool-using agent—then notes the Functional API and the matching TypeScript quickstart.
You need a Claude (Anthropic) account and an API key. Set the ANTHROPIC_API_KEY environment variable in your terminal. The example uses Anthropic by default. The docs also list other chat model integrations. If you use LangSmith Gateway, you can bring your own provider keys or use Gateway Credits to access models without a provider key.
1. Define tools and model
Initialize the model with init_chat_model. The page introduces this as the Claude Sonnet 4.5 model; the live snippet passes the model string and temperature shown below. Define three tool functions, collect them, index them by name, and bind them so the LLM can request arithmetic.
from langchain.tools import tool
from langchain.chat_models import init_chat_model
model = init_chat_model(
"claude-sonnet-4-6",
temperature=0
)
# Define tools
@tool
def multiply(a: int, b: int) -> int:
"""Multiply `a` and `b`.
Args:
a: First int
b: Second int
"""
return a * b
@tool
def add(a: int, b: int) -> int:
"""Adds `a` and `b`.
Args:
a: First int
b: Second int
"""
return a + b
@tool
def divide(a: int, b: int) -> float:
"""Divide `a` and `b`.
Args:
a: First int
b: Second int
"""
return a / b
# Augment the LLM with tools
tools = [add, multiply, divide]
tools_by_name = {tool.name: tool for tool in tools}
model_with_tools = model.bind_tools(tools)
2. Define state
State persists for the run. MessagesState stores the messages and a count of LLM calls. The Annotated type with operator.add ensures that new messages are appended to the existing list rather than replacing it.
from langchain.messages import AnyMessage
from typing_extensions import TypedDict, Annotated
import operator
class MessagesState(TypedDict):
messages: Annotated[list[AnyMessage], operator.add]
llm_calls: int
3. Define the model node
The model node calls the LLM and decides whether to call a tool. llm_call prepends a system message that the assistant should perform arithmetic on a set of inputs, invokes the tool-bound model, and increments llm_calls.
from langchain.messages import SystemMessage
def llm_call(state: dict):
"""LLM decides whether to call a tool or not"""
return {
"messages": [\
model_with_tools.invoke(\
[\
SystemMessage(\
content="You are a helpful assistant tasked with performing arithmetic on a set of inputs."\
)\
]\
+ state["messages"]\
)\
],
"llm_calls": state.get('llm_calls', 0) + 1
}
4. Define the tool node
The tool node performs the tool call. It walks tool_calls on the last message, looks up each tool in tools_by_name, invokes it, and returns ToolMessage results.
from langchain.messages import ToolMessage
def tool_node(state: dict):
"""Performs the tool call"""
result = []
for tool_call in state["messages"][-1].tool_calls:
tool = tools_by_name[tool_call["name"]]
observation = tool.invoke(tool_call["args"])
result.append(ToolMessage(content=observation, tool_call_id=tool_call["id"]))
return {"messages": result}
5. Define end logic
The conditional edge function routes to the tool node or end based on whether the LLM made a tool call. If the last message has tool calls, should_continue returns the tool node. Otherwise it returns END so the agent can reply to the user.
from typing import Literal
from langgraph.graph import StateGraph, START, END
def should_continue(state: MessagesState) -> Literal["tool_node", END]:
"""Decide if we should continue the loop or stop based upon whether the LLM made a tool call"""
messages = state["messages"]
last_message = messages[-1]
# If the LLM makes a tool call, then perform an action
if last_message.tool_calls:
return "tool_node"
# Otherwise, we stop (reply to the user)
return END
6. Build, compile, and invoke
Build the workflow with StateGraph, add the llm_call and tool_node nodes, connect START to llm_call, attach conditional edges from llm_call, and send the tool node back to llm_call. Compile with compile(). The snippet also renders the graph in IPython. Invoke with a HumanMessage that asks to add 3 and 4, then pretty-print every message.
# Build workflow
agent_builder = StateGraph(MessagesState)
# Add nodes
agent_builder.add_node("llm_call", llm_call)
agent_builder.add_node("tool_node", tool_node)
# Add edges to connect nodes
agent_builder.add_edge(START, "llm_call")
agent_builder.add_conditional_edges(
"llm_call",
should_continue,
["tool_node", END]
)
agent_builder.add_edge("tool_node", "llm_call")
# Compile the agent
agent = agent_builder.compile()
# Show the agent
from IPython.display import Image, display
display(Image(agent.get_graph(xray=True).draw_mermaid_png()))
# Invoke
from langchain.messages import HumanMessage
messages = [HumanMessage(content="Add 3 and 4.")]
messages = agent.invoke({"messages": messages})
for m in messages["messages"]:
m.pretty_print()
After that first run, the docs point you to LangSmith to trace and debug the agent. They also mention a tracing quickstart to get set up, Deploy for production hosting, and LangSmith Engine to monitor traces, detect issues, and propose fixes.
Functional API alternative
If you prefer to define the agent as a single function, keep the same model and tools. Mark LLM and tool work with the task decorator, then put the control flow in an entrypoint function. The agent calls the model, and while tool calls exist it runs tools, appends results with add_messages, and calls the model again. The Python example invokes by streaming events on a human message that asks to add 3 and 4.
TypeScript twin
The JavaScript LangGraph quickstart is the same calculator on both APIs. On the Graph path you construct ChatAnthropic with the same model string as the Python snippet, define add, multiply, and divide tools, build MessagesState with StateSchema, MessagesValue, and a ReducedValue for llmCalls, then add llmCall and toolNode, compile, and invoke with a human message that asks to add 3 and 4.
Run the Python Graph API sample that adds 3 and 4 and read the pretty-printed messages. If you want traces, follow the LangSmith setup the quickstart describes after compile. If you would rather write TypeScript or a single-function agent, use those sections on the official pages below.