Multi-turn assistants, resumable workflows and human review all depend on persisted state.
A checkpointer saves the graph's state after every step. Pass one when compiling a graph or to create_agent(..., checkpointer=...), then call with a thread ID:
config = {"configurable": {"thread_id": "user-42"}}
agent.invoke({"messages": [{"role": "user", "content": "I'm Ana"}]}, config)
agent.invoke({"messages": [{"role": "user", "content": "My name?"}]}, config)The second call loads the thread's saved state, so the agent sees the earlier messages: this is short-term memory. Different thread IDs are independent conversations.
Checkpointers:
InMemorySaverfor development and tests.SqliteSaver,PostgresSaver(packageslanggraph-checkpoint-sqlite,langgraph-checkpoint-postgres) and others for production; call.setup()once to create tables.
Checkpoints also enable graph.get_state(config) to inspect the latest state, get_state_history for time travel (replaying or forking from an earlier step), fault tolerance (resume after a crash), and interrupts (next lessons).
Long histories grow without bound; trim or summarize messages (e.g. SummarizationMiddleware in agents).
Long-term memory that spans threads (user preferences, facts) uses a store (InMemoryStore, PostgresStore) passed as store=; tools and nodes read and write namespaced items, optionally with semantic search.
Example
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import START, MessagesState, StateGraph
model = GenericFakeChatModel(messages=iter(["Nice to meet you, Ana!", "Your name is Ana."]))
def chatbot(state: MessagesState):
return {"messages": [model.invoke(state["messages"])]}
builder = StateGraph(MessagesState)
builder.add_node("chatbot", chatbot)
builder.add_edge(START, "chatbot")
graph = builder.compile(checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "user-42"}}
graph.invoke({"messages": [("user", "Hi, I'm Ana")]}, cfg)
out = graph.invoke({"messages": [("user", "What's my name?")]}, cfg)
print(len(out["messages"])) # 4: history was restored from the checkpoint
other = graph.get_state({"configurable": {"thread_id": "user-99"}})
print(other.values) # {}: a new thread starts empty
# create_agent(model, tools, checkpointer=InMemorySaver()) works the same wayRemember
- Checkpointers save state per step; the thread_id selects the conversation
- Use InMemorySaver in dev, SQLite/Postgres savers in production
- Stores provide long-term memory across threads
Watch out for
- Compiling with a checkpointer but forgetting thread_id in the config, which raises an error about a missing thread_id.