Deep AgentsDeep Agents 0.7 · Python 3.11+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
28 small wins to finish your pathNext lesson →

Checkpointer: a thread that keeps its files

A checkpointer is the object that saves a deep agent's whole state, its messages and its files, after every step, under a thread_id, so the next call on the same thread continues where the last one stopped.

Last updated: 29 Sep, 2026 · Deep Agents 0.7

The default backend and the checkpointer · from the Complete Deep Agents Course With LangChain · 103:04 to 104:29

One thread, one set of files

In the context engineering section the video creates agents with checkpointer=MemorySaver() and a thread_id in the config. It explains that with the default state backend, what the agent saves lives in the LangGraph state of that thread: it is there for every call on the same thread, and gone when the agent is closed. The file system backend keeps files on disk, and the store backend keeps them for separate sessions; the next part of the course covers both.

MemorySaver is the older name of InMemorySaver; both import from langgraph.checkpoint.memory and are the same class.

The checkpointer syntax

python
from langgraph.checkpoint.memory import InMemorySaver

agent = create_deep_agent(model=model, checkpointer=InMemorySaver())
agent.invoke({"messages": [...]}, config={"configurable": {"thread_id": "paris-trip"}})

The agent with a checkpointer

Start trip.py with search_travel, the catalog tool from Tools: a travel search the agent can call. Everything below goes in the same file, under it.

python
from langchain.tools import tool

CATALOG = {
    "paris": {
        "flight": ["Return flight Delhi to Paris: 42,000 rupees"],
        "hotel": ["Seine Budget Inn, Latin Quarter: 5,200 rupees a night",
                  "Hotel Lumiere, Montmartre: 7,500 rupees a night",
                  "Le Grand Opera Hotel: 16,000 rupees a night"],
        "sight": ["Eiffel Tower summit: 3,100 rupees", "Louvre Museum: 2,000 rupees",
                  "Seine river cruise: 1,500 rupees", "Versailles day trip: 2,600 rupees",
                  "Montmartre walking tour: free"],
        "food": ["Cafe breakfast and bistro dinner: 3,000 rupees a day"],
    },
}


@tool
def search_travel(city: str, kind: str) -> str:
    """Search the travel catalog. kind is "flight", "hotel", "sight" or "food". Prices are in rupees."""
    entries = CATALOG.get(city.lower(), {}).get(kind)
    return "\n".join(entries) if entries else f"The catalog has no {kind} entries for {city}."
python
from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0, max_retries=6)
python
from langgraph.checkpoint.memory import InMemorySaver

agent = create_deep_agent(
    model=model,
    tools=[search_travel],
    checkpointer=InMemorySaver(),     # saves messages and files after every step
    system_prompt="You are a travel planner. Reply in one short sentence.",
)
thread = {"configurable": {"thread_id": "paris-trip"}}

A helper that asks on a thread

ask sends one message on a thread and prints the reply.

python
def ask(text, config):
    result = agent.invoke({"messages": [{"role": "user", "content": text}]}, config=config)
    print("you:  ", text)
    print("agent:", result["messages"][-1].text)
    return result

Editing a plan across two calls, then a new thread

The run edits a file across two calls on the same thread, then asks for it on a new thread and prints every message of that run.

ExampleAPI keytrip.py, continued
ask("Write /trip/plan.md with two lines: Day 1: Eiffel Tower. Day 2: Louvre Museum.", thread)
result = ask("Add the Seine river cruise to day 2 of the plan.", thread)
print(result["files"]["/trip/plan.md"]["content"])
result = agent.invoke({"messages": [{"role": "user", "content": "Read /trip/plan.md and tell me what it says."}]},
                      config={"configurable": {"thread_id": "another-trip"}})
for message in result["messages"]:
    print(f"{message.type:<5}", message.text or [(c["name"], c["args"]) for c in message.tool_calls])

What the three calls show

  • Call one wrote /trip/plan.md. Nothing was passed back by hand.
  • Call two sent only the new message. The checkpointer restored the thread's state, so the agent found the file and updated day 2; the printed file shows the cruise added.
  • The new thread started empty: read_file answered that the file was not found, ls and glob found no files at all, and the agent said the file does not exist. Files in StateBackend belong to one thread.

InMemorySaver vs a saver on disk

CheckpointerSurvives a restartPackage
InMemorySaverNo, it lives in the Python processlanggraph (installed)
SqliteSaverYes, a SQLite filelanggraph-checkpoint-sqlite
PostgresSaverYes, a databaselanggraph-checkpoint-postgres

Integrations: taking the trip planner to production swaps in SqliteSaver and shows a thread surviving a new agent.

Where a checkpointer is needed

  • Any chat: every turn continues the same conversation and files.
  • Pausing for a person, which Human-in-the-loop: approving a booking needs: the paused state has to be saved somewhere.
  • Long tasks you may resume after an error.
Watch out. The thread_id is the key to everything. Reuse one by mistake and a new traveller sees the last traveller's plan; make a fresh one each time and the agent never remembers anything.
Try it yourself
  • Ask a third question on paris-trip: "Move the Louvre to day 1." and print the file.
  • Print agent.get_state(thread).values["files"] to read the thread's files without a model call.
  • Run the new-thread question with thread_id paris-trip instead and compare.

This is what real progress feels like.