This guide is for readers who want their first agent built from explicit pieces rather than a black box. It follows the LangGraph quickstart, which builds a small calculator agent in one Python file. By the end you will understand state, nodes, edges and the tool loop, and you will know how to add memory and pauses.
What LangGraph is
LangGraph is a low-level orchestration framework and runtime for long-running, stateful agents. It was built by LangChain Inc, but the README says it can be used without LangChain.[14] Its core model is a graph. A workflow is a shared state, nodes that read the state and return updates, and edges that decide what runs next. Edges are either fixed or conditional.[3] You do not hide the loop inside a prompt. You draw it.
Before you start
Install the packages with pip install -U langgraph langchain langchain-anthropic, or with uv add langgraph langchain langchain-anthropic.[1]
pip install -U langgraph langchain langchain-anthropicThe quickstart needs a model-provider API key. Set it in your shell with export ANTHROPIC_API_KEY="your-api-key", or put ANTHROPIC_API_KEY=your-api-key in a .env file.[1]
Build the agent
The quickstart puts the whole agent in one file, agent.py, in eight steps: define tools and the model; define state; define a model node; define a tool node; define the end logic; build and compile the agent; optionally add LangSmith tracing; and run the agent.[1]
- Define the tools and the model. The model is initialized with
init_chat_model("claude-sonnet-4-6", temperature=0). Tools are functions marked with the@tooldecorator fromlangchain.tools, andmodel.bind_tools(tools)binds them to the model.[1] - Define the state. The state is
MessagesState(TypedDict), withmessages: Annotated[list[AnyMessage], operator.add]andllm_calls: int. TheAnnotatedwrapper withoperator.addappends new messages instead of replacing the list.[1][3] LangGraph also ships a built-in reducer,add_messages, imported fromlanggraph.graph.message. It updates existing messages by ID and accepts shorthand message formats, where plainoperator.addonly appends.[3] - Define the model node. A node is a function that reads the state and returns an update to it. The model node calls the model and returns its reply as new messages.[3]
- Define the tool node. A tool node runs the tool calls the model asked for. The workflow examples write it as a plain function that loops over
tool_calls, invokes each tool, and returns aToolMessagewith the matchingtool_call_idfor each result.[6] The prebuiltToolNodedoes the same job and also handles parallel execution and errors. It is added like this:builder.add_node("tools", ToolNode([search, calculator])).[6] - Define the end logic. The routing function
should_continue(state: MessagesState) -> Literal["tool_node", END]returns"tool_node"when the last message hastool_calls, andENDotherwise.[1] - Build and compile. The quickstart creates
StateGraph(MessagesState), adds the nodes withadd_node, and connects them.add_edge(START, "llm_call")starts the run.add_conditional_edges("llm_call", should_continue, ["tool_node", END])routes after each model call.add_edge("tool_node", "llm_call")sends the tool results back to the model. Thencompile()turns the graph into something runnable.[1] Compilation checks the structure for orphaned nodes, so a node nothing connects to is caught here.[3] - Run it. Start the file with
python agent.py. The agent is invoked withagent.invoke({"messages": messages}), and the quickstart reports the tool result as7.[1]
python agent.pyWhat the graph looks like
The overview's smallest graph has one node and two edges, and it shows the skeleton every LangGraph program shares:[2]
from langgraph.graph import StateGraph, MessagesState, START, END
def mock_llm(state: MessagesState):
return {"messages": [{"role": "ai", "content": "hello world"}]}
graph = StateGraph(MessagesState)
graph.add_node(mock_llm)
graph.add_edge(START, "mock_llm")
graph.add_edge("mock_llm", END)
graph = graph.compile()
graph.invoke({"messages": [{"role": "user", "content": "hi!"}]})START is the virtual entry point and END marks a terminal edge. The entry point is an edge from START.[3] Send and Command are the two patterns the build checklist recommends for more involved cases. Send fans out the same node over items whose count is only known at runtime, and Command lets one step both update state and choose the next node.[13]
Loops need a stop
The calculator agent is a loop: the model calls a tool, the tool result goes back to the model, and the model decides whether to call another tool. A loop needs a stopping condition, and here the conditional edge to END provides it. LangGraph also caps the number of super-steps with a recursion limit. Exceeding it raises GraphRecursionError, which is imported from langgraph.errors, and you set the limit with recursion_limit in the config.[5]
From version 1.1.4 the default recursion limit is 10,007 super-steps, and between 1.0.6 and 1.1.3 it was 10,000. The LANGGRAPH_DEFAULT_RECURSION_LIMIT environment variable or a top-level recursion_limit config key overrides it.[3]
Add memory
The agent in the quickstart forgets everything after a run. LangGraph has two persistence systems for fixing that. Checkpointers save a thread's graph state, which gives you short-term memory within one conversation. Stores hold application-defined data that can be shared across threads, which gives you long-term memory.[8]
To keep state between calls, compile the graph with a checkpointer. The persistence quickstart does it like this:[8]
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
checkpointer = InMemorySaver()
store = InMemoryStore()
graph = builder.compile(checkpointer=checkpointer, store=store)
result = graph.invoke(
{"messages": [{"role": "user", "content": "Hi, my name is Bob."}]},
{"configurable": {"thread_id": "thread-1"}},
)When you invoke a graph with a checkpointer, you must pass a thread_id in the configurable part of the config, for example {"configurable": {"thread_id": "1"}}. The checkpointer uses it as the primary key for storing and loading checkpoints.[9] Multi-turn conversations come from compiling with checkpointer=checkpointer, using InMemorySaver from langgraph.checkpoint.memory.[11]
Persistence also has a durability setting, passed as durability=. "exit" persists only when the graph exits, "async" persists in the background during the next step, and "sync" persists before the next step runs.[9]
Pause for human input
interrupt(), imported from langgraph.types, pauses execution inside a node. It saves state with the checkpointer and waits, with no time limit. Using it requires a checkpointer, a thread_id in the config, and a JSON-serializable payload.[10] You resume by calling with Command(resume=...). The value you pass becomes the return value of the paused interrupt() call, and you must reuse the same thread_id.[10]
When the graph resumes, the node runs again from its beginning. Any work done before interrupt() in that node must therefore be idempotent, meaning it can run twice without harm.[7]
Limits and common problems
In-memory checkpointers are the first trap. MemorySaver and InMemorySaver keep checkpoints in RAM, so all of them are lost when the process restarts. Production setups should use a persistent checkpointer such as PostgresSaver or SqliteSaver.[8]
Interrupts need the checkpointer as well as the thread ID. Without a checkpointer the graph cannot pause and resume.[7]
Loops that never reach END end in GraphRecursionError once they hit the limit. A fix is a clearer stop condition in the routing function, not only a larger limit.[5]
Next steps
The quickstart is the best next read, because it covers the whole calculator agent step by step.[1] For the state and routing rules, read the Graph API overview and the state page.[3][4] For memory and pauses, read the persistence, checkpointers and interrupts pages.[8][9][10] Before you design a larger graph, read the build checklist.[13]
Sources
- 1QuickstartLangChain Docs
- 2LangGraph overviewLangChain Docs
- 3Graph API overviewLangChain Docs
- 4Define and update stateLangChain Docs
- 5Build sequences, branches, and loopsLangChain Docs
- 6Workflows and agentsLangChain Docs
- 7Thinking in the Graph APILangChain Docs
- 8PersistenceLangChain Docs
- 9CheckpointersLangChain Docs
- 10InterruptsLangChain Docs
- 11MemoryLangChain Docs
- 12StreamingLangChain Docs
- 13Build checklist for the Graph APILangChain Docs
- 14LangGraph READMELangChain (GitHub)
Cite this page: LangGraph quickstart: build a tool-calling agent. TellWorld, https://www.tellworld.net/practice/practice-langgraph-quickstart. Updated 2026-11-14