Memory and persistence

LangChain · Advanced · 14 min

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:

  • InMemorySaver for development and tests.
  • SqliteSaver, PostgresSaver (packages langgraph-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 way

Remember

  • 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.

Check yourself

  1. Two users talk to the same agent with checkpointer=InMemorySaver(). How do you keep their conversations separate?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.langchain.com/oss/python/langgraph/persistence