Module 8: Memory and Persistence

Introduction: Why Agents Need Memory

Overview

Your AI Research Assistant is robust. It has retry with exponential backoff, parallel search across multiple sources, merge with deduplication, and graceful degradation when a source fails. You built it in Module 7 and it holds up in the real world.

But it's amnesiac. Every run starts from zero.

Close the terminal, open it again, and your agent has no idea that five minutes ago it was halfway through a research task on RAG. The user comes back tomorrow and asks "can you go deeper on what you found yesterday?" — the agent doesn't know what they're talking about. A research run that spent 5 minutes processing 3 of 5 sources gets interrupted by a system crash — all that work is gone.

This isn't an edge case. It's what happens to any stateless system in production. And it's exactly what separates a demo from a product you can put in front of real users.

Why it matters: Without memory and persistence, Modules 9-12 aren't possible. Human-in-the-loop (M9) requires the agent to save its state so it can pause and wait for approval. Multi-agent (M10) requires agents to share context. Production (M12) requires that nothing gets lost. This module is the technical foundation for everything that follows.


Where are we in the guide?

This is Module 8 of LangChain & LangGraph: From Chains to Agents. It's the first module of Block 3 (Advanced LangGraph).

Block 1: LangChain Core (Modules 1-4)            ✅ Done
Block 2: LangGraph Fundamentals (Modules 5-7)    ✅ Done
Block 3: Advanced LangGraph (Modules 8-10)       ← YOU ARE HERE (Module 8)
Block 4: Production (Modules 11-12)
Your progress:

Block 1 — LangChain Core                    ✅ Done
    │
    │  Module 1: Models and Providers        ✅
    │  Module 2: Tools and Tool Calling      ✅
    │  Module 3: Agents (create_agent)       ✅
    │  Module 4: Middleware and Customization ✅
    │
    ▼
Block 2 — LangGraph Fundamentals            ✅ Done
    │
    │  Module 5: Introduction to LangGraph   ✅
    │  Module 6: Functional API              ✅
    │  Module 7: Advanced Flows              ✅
    │
    ▼
Block 3 — Advanced LangGraph
    │
    │  Module 8: Memory and Persistence      ← YOU ARE HERE
    │  Module 9: Human-in-the-Loop           🔒 Next
    │  Module 10: Multi-Agent Systems        🔒
    │
    ▼
Block 4 — Production                        🔒
    │
    │  Module 11: Deep Agents                🔒
    │  Module 12: LangSmith and Production   🔒

Block 2 gave you the construction tools: StateGraph, Functional API, retry, branching, error handling. Block 3 turns them into enterprise capabilities: persistence (this module), human oversight (M9), and multi-agent collaboration (M10).


The bridge from Module 7

What you already have

Your Research Agent v2 is a solid system:

  • ✅ Retry with exponential backoff when APIs fail
  • ✅ Parallel search across 3 sources at once
  • ✅ Merge with result deduplication
  • ✅ Graceful degradation: if one source fails, the report gets built from the ones that answered
  • ✅ Structured logging for traceability

What it's missing

Run this sequence in your head:

Scenario 1 — Crash mid-process:
  Your agent has spent 5 minutes researching "state of AI in healthcare 2025"
  It processed 3 of 5 sources successfully
  The machine reboots (OS update, crash, accidental close)

  Without persistence: you start from zero. 5 minutes + API costs, gone.
  With persistence: you resume from source 4. 30 seconds.

Scenario 2 — The user comes back tomorrow:
  Today: "Research RAG trends in 2025"
  Tomorrow: "Can you go deeper on what you found yesterday about hybrid search?"

  Without memory: "I have no information about previous research."
  With memory: "Yesterday I found 3 papers on hybrid search. Let me dig into them."

Scenario 3 — Multiple users:
  User A researches AI in healthcare. User B researches AI in finance.
  Both use the same deployed agent.

  Without isolation: the conversations bleed into each other.
  With thread_id: each user gets their own isolated context.

Those three problems — crash recovery, memory across sessions, and multi-user — are exactly what this module solves.


The two kinds of memory (don't mix them up)

This distinction is fundamental. Confusing short-term and long-term memory is the most common mistake when working with stateful agents. They're completely different concepts with different implementations.

Short-term memory: the current conversation

Short-term memory is the message history within the current session. It's what lets the agent hold context between conversation turns:

Turn 1 — User: "What is RAG?"
Turn 1 — Agent: "RAG is Retrieval-Augmented Generation, a pattern that..."

Turn 2 — User: "Give me an example?"
           ↑ Without short-term memory, the agent doesn't know what "an example" refers to
           ↑ With short-term memory, it knows "an example" means RAG

Characteristics:

  • ✅ Lives inside a single session (one conversation)
  • ✅ Includes: user messages, agent responses, tool calls, tool results
  • ✅ Managed with MessagesState and thread_id
  • ✅ Has a practical ceiling: the model's context window (128K tokens on GPT-4.1)
  • ❌ Lost when the session ends (unless you use checkpointing)

Long-term memory: what survives across sessions

Long-term memory is information that survives across different conversations. It isn't messages — it's structured data the agent accumulates and looks up:

Session 1 (Monday):
  User researches "AI in healthcare"
  Agent stores: preference for academic sources, interest in medical diagnosis

Session 2 (Wednesday):
  User: "Research something new"
  Agent queries long-term memory:
    → "This user prefers academic sources"
    → "Their previous topics include AI in healthcare, especially diagnosis"
    → Adapts the search automatically

Characteristics:

  • ✅ Persists across different sessions (days, weeks)
  • ✅ Includes: user preferences, accumulated knowledge, interest profiles
  • ✅ Managed with InMemoryStore or external databases
  • ✅ Not limited by the context window — it lives outside the model
  • ❌ Requires explicit design: what to store, when to read it, how to update it

Episodic memory: specific past interactions

Episodic memory is a subset of long-term memory that refers to specific interactions the agent can point back to:

User: "Do you remember what you found last time about RAG?"
Agent queries episodic memory:
  → Session from March 3: researched RAG, found 5 papers, main conclusion was...
  → "Yes, last time I found that hybrid search beats dense retrieval by about 15%..."

It isn't a separate system — it's how you use long-term memory to recall past events.

Comparison table

AspectShort-termLong-termEpisodic
ScopeOne sessionEvery sessionSpecific sessions
ContentMessages, tool callsPreferences, profilesPast events
LimitContext windowExternal storageExternal storage
ImplementationMessagesStateInMemoryStore / DBSubset of long-term
Lost if...The session endsThe store gets wipedThe store gets wiped
Example"You just asked about RAG""You prefer academic sources""On Monday you researched healthcare"

Stateless vs stateful: why it matters

Stateless agent (what you have now)

result = agent.invoke({"query": "What is RAG?"})
# The agent answers, and forgets everything immediately.

result = agent.invoke({"query": "Give me more detail"})
# "More detail about what? I have no prior context."

Every invoke() is an island. There's no link between calls. It's like talking to someone with anterograde amnesia: every interaction starts from scratch.

Stateful agent (what you'll build)

config = {"configurable": {"thread_id": "user_123"}}

result = agent.invoke({"query": "What is RAG?"}, config)
# The agent answers AND stores the conversation in thread "user_123"

result = agent.invoke({"query": "Give me more detail"}, config)
# The agent reads the history of thread "user_123"
# It knows "more detail" refers to RAG
# It answers with context

The thread_id is the key. Each thread is an independent conversation with its own history. Same agent, different threads, different contexts. That's how multi-user works.


The persistence stack

LangGraph has a deliberate design for persistence: you start simple for development, and you move to production by changing one line.

MemorySaver: development and testing

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)
  • ✅ Zero config: no database, no Docker
  • ✅ Perfect for development: fast tests, iteration, debugging
  • ❌ In-memory: if the process restarts, everything is gone
  • ❌ Single-process: doesn't work with multiple workers

Use MemorySaver for: prototyping, testing, local development, tutorials.

PostgresSaver: production

from langgraph.checkpoint.postgres import PostgresSaver

checkpointer = PostgresSaver.from_conn_string("postgresql://user:pass@host:5432/db")
graph = builder.compile(checkpointer=checkpointer)
  • ✅ Durable: survives process restarts, crashes, deployments
  • ✅ Multi-process: several workers can read and write
  • ✅ Scalable: PostgreSQL is battle-tested for production
  • ❌ Requires a PostgreSQL instance

Use PostgresSaver for: production, staging, any environment where durability matters.

The migration is trivial

Look at the change:

# Development
checkpointer = MemorySaver()

# Production (the ONLY line that changes)
checkpointer = PostgresSaver.from_conn_string(os.getenv("DATABASE_URL"))

# The rest of the code is IDENTICAL
graph = builder.compile(checkpointer=checkpointer)

One line. The rest of your code — nodes, edges, state, logic — doesn't change at all. That's what a well-designed abstraction looks like: the checkpointer is a swappable backend.


Module map

#CapsuleWhat you'll learnType
01Introduction (this one)Why agents need memory, kinds of memory, the persistence stackIntro
02Short-term memory: conversation historyMessagesState, thread_id, message trimming, summarizationTechnique
03Checkpointing: MemorySaverAutomatic checkpoints, inspecting states, thread managementTechnique
04Durable execution and crash recoverySimulating crashes, resuming from a checkpoint, idempotencyTechnique
05Time-travel debuggingNavigating state history, replay, debugging decisionsTechnique
06Long-term memory with StoreInMemoryStore, namespaces, storing and querying data across sessionsTechnique
07PostgresSaver: persistence in productionPostgreSQL setup, migrating from MemorySaver, multi-workerTechnique
08Project: Research Agent with memoryResearch Agent v3: checkpointing + long-term memory + multi-userProject

The learning flow

You start with short-term memory (capsule 02) — the most immediate concept: how to keep context between conversation turns. Then you learn checkpointing (capsule 03) — the mechanism that saves the agent's state at every step. With checkpointing, you implement crash recovery (capsule 04) — the agent survives interruptions and resumes where it left off. Next, you use those checkpoints for time-travel debugging (capsule 05) — you walk the state history and understand why the agent made each decision. You add long-term memory (capsule 06) — information that persists across sessions. You migrate to PostgresSaver (capsule 07) — real persistence for production. Finally, you put it all together in Research Agent v3 (capsule 08).

The progression is: conversation → checkpoints → durability → debugging → persistent memory → production → project.


Connection with the project

Research Agent v3: the agent that remembers

Your Research Agent takes a big step forward in this module:

v1 (Module 6): Works, but fragile
    ↓
v2 (Module 7): Robust (retry, branching, error handling)
    ↓
v3 (This module): Persistent (checkpointing, memory, multi-user)
    │
    │  + Checkpointing: research gets saved step by step
    │  + Crash recovery: if it's interrupted, it resumes where it stopped
    │  + Long-term memory: remembers preferences across sessions
    │  + Thread management: multi-user with isolated context
    │
    ▼
v4 (Module 9): + Human approvals before expensive actions

The proof is concrete: close the terminal, open a new one, run your agent with the same thread_id — and it remembers the previous research. That moment, when you see the agent "know" what you were researching an hour ago, is when this module clicks.


Connection with Module 9: Human-in-the-Loop

Persistence is a technical prerequisite for human-in-the-loop. The reason is direct:

Without persistence:
  Agent: "I'm about to run an expensive search across 5 APIs"
  System: interrupt() — pause for human approval
  ??? The agent lost its state. It doesn't know where it was.

With persistence:
  Agent: "I'm about to run an expensive search across 5 APIs"
  System: interrupt() — pause for human approval
  State saved in a checkpoint with thread_id
  ... 10 minutes later ...
  Human: "Approved"
  System: resume from the checkpoint — the agent continues exactly where it paused

Without checkpointing, interrupt() makes no sense — the agent can't pause and resume if it has no persistent state. That's why this module comes before M9.


What this module does NOT cover

  • Vector stores and RAG — Storing embeddings for semantic search is a different topic. This module is about the agent's state (conversations, checkpoints, preferences), not external knowledge bases.
  • Knowledge databases — We're not building a retrieval system. Long-term memory here means preferences and profiles, not indexed documents.
  • Redis as a backend — We mention it as an option, but we focus on MemorySaver (dev) and PostgresSaver (prod). Redis is useful for caching and ephemeral sessions.
  • Human-in-the-loop — We mention that persistence enables it, but implementing interrupts and approvals belongs to Module 9.
  • Multi-agent memory sharing — How several agents share memory is covered in Module 10.

Technical setup

Prerequisites

  • Module 7 done — you have a Research Agent v2 with retry, branching, and error handling
  • Python 3.11+ installed
  • ✅ At least one API key from a provider (OpenAI recommended)

Installation

If you finished Module 7, you already have the main dependencies. Add the checkpointing package for PostgreSQL (you'll use it in capsule 07):

pip install langgraph langchain-openai python-dotenv langgraph-checkpoint-postgres

Check that the import works:

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import MessagesState

print("MemorySaver available")
print("MessagesState available")
# Expected output:
# MemorySaver available
# MessagesState available

Environment variables

Your .env from Module 7 still works:

# .env
OPENAI_API_KEY=sk-...

# For capsule 07 (PostgresSaver), you'll add:
# DATABASE_URL=postgresql://user:password@localhost:5432/langgraph_db

Quick check: an agent with memory

Run this script to verify that checkpointing works:

from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.checkpoint.memory import MemorySaver

def echo(state: MessagesState) -> dict:
    last_message = state["messages"][-1].content
    return {"messages": [{"role": "assistant", "content": f"Echo: {last_message}"}]}

builder = StateGraph(MessagesState)
builder.add_node("echo", echo)
builder.add_edge(START, "echo")
builder.add_edge("echo", END)

checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

config = {"configurable": {"thread_id": "test_001"}}

result1 = graph.invoke({"messages": [{"role": "user", "content": "Hello"}]}, config)
print(f"Turn 1: {result1['messages'][-1].content}")

result2 = graph.invoke({"messages": [{"role": "user", "content": "Do you remember what I said?"}]}, config)
print(f"Turn 2: {result2['messages'][-1].content}")
print(f"Messages in history: {len(result2['messages'])}")
# Expected output:
# Turn 1: Echo: Hello
# Turn 2: Echo: Do you remember what I said?
# Messages in history: 4

If you see 4 messages in the history (2 from the user + 2 from the agent), checkpointing is working. The agent accumulates messages across invocations thanks to the thread_id.


Signs of success

By the end of this module, you'll know you got it right if:

  • ✅ Your agent holds a conversation with context: you can refer back to earlier messages and the agent follows
  • ✅ You can close the terminal, open a new one, and the agent remembers the previous conversation (with PostgresSaver)
  • ✅ You simulate a crash mid-research and the agent resumes exactly where it stopped
  • ✅ You can walk the agent's state history and see the state it had at each step (time-travel)
  • ✅ The agent remembers user preferences across different sessions (long-term memory)
  • ✅ Two different users share the same agent with completely isolated contexts (thread_id)

Self-assessment

If you can answer these questions, you're on the right track:

  1. What's the difference between short-term and long-term memory in an agent?
  2. Why isn't MemorySaver suitable for production?
  3. What happens if you don't implement message trimming in a long conversation?
  4. How does checkpointing let an agent pause and resume?
  5. What is a thread_id and why is it necessary for multi-user?

Summary

  • Your Research Agent v2 is robust, but amnesiac: every run starts from zero. Without persistence, crashes destroy work, users get no continuity, and there's no multi-user isolation
  • Short-term memory is the current conversation's history — messages, tool calls, results. Managed with MessagesState and thread_id
  • Long-term memory is information that persists across sessions — preferences, profiles, accumulated knowledge. Managed with InMemoryStore or databases
  • Episodic memory is a subset of long-term: specific past interactions the agent can refer back to
  • MemorySaver = development and testing (in-memory, zero config). PostgresSaver = production (durable, multi-process). The migration is one line of code
  • Persistence is a prerequisite for human-in-the-loop (M9): the agent needs saved state to pause and resume
  • This module turns the Research Agent from a prototype that "works" into a system that remembers — the difference between a demo and a product

Further reading

  1. LangGraph — Persistence — Official docs on LangGraph's persistence system: checkpointers, threads, state
  2. LangGraph — Memory — Short-term and long-term memory concepts in LangGraph
  3. How to add memory to chatbots — Practical guide to adding conversation history
  4. How to add cross-thread memory — Implementing long-term memory that persists across threads
  5. LangGraph Checkpoint PostgreSQL — PostgresSaver setup for production
  6. Designing AI Agents with Memory — LangChain Blog — Article on memory patterns for agents

Module 8 — LangChain & LangGraph: From Chains to Agents

Next capsule: Short-term Memory: Conversation History — you'll learn how MessagesState manages conversation history, how thread_id isolates context per user, and why message trimming is mandatory in production.