Skip to main content

How to Add Long-Term Memory to LangChain Agents

Emily Winks, Data Governance Expert, Atlan
Data Governance Expert
Updated:
|
Published:
26 min read

Key takeaways

  • ConversationBufferMemory was removed at LangChain 1.0 and lives in langchain-classic. Use checkpointers or LangMem.
  • thread-id scopes a single session. user-id scopes across sessions. Confusing them is the most common production mistake.
  • LangMem's documented strength is procedural memory: the agent rewrites its own prompt. Measure retrieval latency yourself.
  • The ZepMemory import error (issue 27356) is fixed by ZepCloudMemory; on 1.x those re-exports live in langchain-classic.

How do you add long-term memory to a LangChain agent?

Use LangGraph checkpointers for within-session memory and LangGraph BaseStore for cross-session long-term memory. For semantic and procedural memory add the LangMem SDK. For temporal knowledge graph memory use ZepCloudMemory. The legacy ConversationBufferMemory class was removed at LangChain 1.0.

Is your data ready for AI agents?

ConversationBufferMemory is gone from the langchain namespace. It was deprecated at v0.3.1 and removed at 1.0, along with the other legacy chains, which now ship in the separate langchain-classic package. The replacement is LangGraph persistence: a checkpointer for in-thread session history and a BaseStore for cross-thread, user-scoped long-term facts. On top of that foundation, you can layer the LangMem SDK for semantic and procedural memory, or connect ZepCloudMemory for temporal reasoning. Atlan’s MCP server is callable from any LangChain agent through LangChain’s built-in MCP support (langchain[mcp]), delivering live governed context: table certifications, column ownership, sensitivity labels, and data lineage that memory stores alone cannot provide.

Plan Your Memory Migration


Give it your current setup and what the agent gets wrong. It returns which memory layer to add, in what order, and what to leave alone. Read the skill.

Paste into a new chat

Use the skill at https://atlan.com/skills/agent-memory-migration-plan.md to plan how to replace our agent's memory. Ask me for whatever it needs.

Run once in a terminal

curl -fsSL --create-dirs \
  -o ~/.agents/skills/agent-memory-migration-plan/SKILL.md \
  https://atlan.com/skills/agent-memory-migration-plan.md

For an agent

curl -fsSL https://atlan.com/skills/agent-memory-migration-plan.md

This guide covers all five approaches with verified import paths:

  • Approach 1 (removed at 1.0): ConversationBufferMemory: what to remove and why
  • Approach 2: LangGraph checkpointer (InMemorySaver, SqliteSaver, PostgresSaver): in-session memory
  • Approach 3: LangGraph BaseStore: cross-session, user-scoped long-term memory
  • Approach 4: LangMem SDK (create_manage_memory_tool): semantic, episodic, and procedural memory
  • Approach 5: ZepCloudMemory: temporal memory, with the fix for GitHub issue #27356

One production caveat up front: LangChain publishes no retrieval-latency figures for LangMem, and memory search sits in the critical path of every turn. Measure it in your own environment before you put it in front of users.

Time to complete: 30-90 minutes per approach | Difficulty: Intermediate | Prerequisites: Python 3.10+, LangChain or LangGraph installed, LLM API key

For the architecture decision that precedes this implementation, see our guide on how to build a memory layer for AI agents.


Why LangChain’s original memory is no longer enough

ConversationBufferMemory and its relatives were built before the modern Chat Model API existed. They cannot work reliably with tool-calling agents, and they store state only in process memory. Restart the Python process and all conversation history is gone.

These classes were deprecated at v0.3.1 and removed from the langchain namespace at 1.0. They now ship in langchain-classic:


  • ConversationBufferMemory
  • ConversationBufferWindowMemory
  • ConversationSummaryMemory
  • ConversationEntityMemory

The root cause of the deprecation was not just in-process storage. These classes were designed for a pre-tool-calling world. When agents started using tool calls, function schemas, and structured outputs, the old memory classes could not reliably intercept and store that flow. They also had no concept of multi-user or multi-thread operation. One ConversationBufferMemory object = one conversation = no persistence, no separation, no production viability.

LangGraph introduced two primitives that fix this cleanly:

  • Checkpointer: manages conversation history within a single thread_id. Conversation turns for the current session. Survives restarts if you use SqliteSaver or PostgresSaver. Does not survive a thread_id change.
  • BaseStore: manages facts that survive across ALL sessions for a user. Namespaced by user identity, not by thread. A returning user with a new thread_id still gets their stored preferences.

If you are hitting ImportError on from langchain.memory import ConversationBufferMemory, you are at exactly the right place. On a pre-1.0 stack the same line raised a DeprecationWarning; on 1.x it fails outright unless you install langchain-classic. See also: why AI agents forget: the stateless LLM problem for the deeper architectural reason this pattern fails.


Prerequisites

Before writing any code, confirm these:

Organizational prerequisites:

  • [ ] Decide on scope: in-session memory only (checkpointer), cross-session memory (store), or both
  • [ ] Choose your backend: dev = InMemorySaver + InMemoryStore (resets on restart); local persistent dev = SqliteSaver; production = PostgresSaver + PostgresStore

Technical prerequisites:

  • [ ] langchain-core>=0.3, langgraph>=0.2, langgraph-prebuilt installed
  • [ ] For Zep: langchain-community>=0.3 and zep-python>=2.0 (older versions cause GitHub issue #27356 ImportError and issue #25791 TypeError)
  • [ ] For LangMem: langmem installed; an embedding model API key (e.g., OpenAI text-embedding-3-small for vector search in InMemoryStore)
  • [ ] For production postgres backends: langgraph-checkpoint-postgres, langgraph-store-postgres, and a running Postgres instance

Time commitment:

  • Setup + dev test: 30-60 minutes per approach
  • Production backend swap: 1-3 hours
  • Full multi-approach evaluation: 1 day

Step 1: Understand what you’re replacing, and why

What you’ll accomplish: Map the deprecated ConversationBufferMemory pattern to its LangGraph equivalent so you know exactly what to change.

Time required: 10 minutes (review your existing code)

Here is the pattern you are replacing:

# REMOVED at LangChain 1.0: do not use in new code
# On 1.x this raises ImportError; the classes moved to the langchain-classic package
from langchain.memory import ConversationBufferMemory   # ImportError on langchain>=1.0
from langchain.chains import ConversationChain          # ImportError on langchain>=1.0

memory = ConversationBufferMemory()
chain = ConversationChain(llm=llm, memory=memory)

Why this breaks: State lives in process RAM. Agent restart = total memory loss. No multi-user support, no multi-thread support. Breaks when tool-calling is in the loop because these classes predate the modern Chat Model API.

The most common architecture mistake


Most developers migrating to LangGraph make this mistake: they use the checkpointer as a drop-in for ConversationBufferMemory and expect it to remember user preferences across sessions. It does not. The checkpointer is in-thread. A returning user with a new thread_id gets a blank slate, because their preferences were stored in the checkpointer (scoped to the old thread_id) instead of the store (scoped to their user ID).

Developers only discover this in production, when a real returning user reports that the agent has forgotten everything about them.

The conceptual split:

  • thread_id scopes one conversation session (checkpointer)
  • user_id scopes all sessions for one person (store namespace)
  • One user has many thread_ids. The store is keyed by user, not by thread.

How to audit your codebase:

  1. Search for from langchain.memory import and list every import
  2. Classify each: does it store conversation turns (replace with checkpointer) or user/entity facts (replace with store)?
  3. Remove all ConversationChain wired to a legacy memory object

Validation checklist:

  • [ ] All from langchain.memory import ... lines identified
  • [ ] Each one classified: conversation history use → checkpointer; persistent facts use → store
  • [ ] No remaining ConversationChain wired to a legacy memory object

Step 2: Add in-thread memory with a LangGraph checkpointer

What you’ll accomplish: Wire a checkpointer to a create_agent call so your agent remembers everything within a single conversation session, across restarts, as long as you pass the same thread_id.

Time required: 15-20 minutes

from langchain.agents import create_agent                   # replaces langgraph.prebuilt.create_react_agent at 1.0
from langgraph.checkpoint.memory import InMemorySaver        # dev / testing only; resets on restart

# For local persistent dev (survives restarts):
from langgraph.checkpoint.sqlite import SqliteSaver

# For production (multi-server, shared state):
# from langgraph.checkpoint.postgres import PostgresSaver

# Dev:
checkpointer = InMemorySaver()

# Local persistent dev (recommended over InMemorySaver for any real testing):
# checkpointer = SqliteSaver.from_conn_string("checkpoints.sqlite")

agent = create_agent(model=llm, tools=tools, checkpointer=checkpointer)

# Every invoke call for the SAME conversation must pass the SAME thread_id.
# Start a new thread_id when a new session begins.
config = {"configurable": {"thread_id": "user-123-session-1"}}
result = agent.invoke(
    {"messages": [{"role": "user", "content": "My name is Alex and I prefer Python examples."}]},
    config=config
)

# Later turn in the same session — agent recalls "Alex prefers Python examples":
result2 = agent.invoke(
    {"messages": [{"role": "user", "content": "What do you remember about my preferences?"}]},
    config=config   # same thread_id = same conversation history
)

The checkpointer stores the full message history for this thread_id. Change the thread_id and the agent starts fresh. Restart the Python process with SqliteSaver and the agent still has the history. Restart with InMemorySaver and it is gone.

Key limitation: Memory is scoped to thread_id only. A new session (new thread_id) = blank slate. Use this for within-session context. Use Step 3 for facts that should survive across sessions.

A known pitfall, tracked as issue #5790: langgraph dev runs against the server’s own injected persistence, so a checkpointer compiled into your graph is bypassed and state resets on restart. Agents appear to persist state, but they do not. Verify persistence with a direct Python invocation, not just langgraph dev.

Validation checklist:

  • [ ] Agent compiled with checkpointer= parameter
  • [ ] thread_id passed consistently in config
  • [ ] SqliteSaver file written to disk (visible after first run)
  • [ ] After process restart, same thread_id recovers full history

Common mistakes:

  • Using InMemorySaver in production (resets on restart; state is gone)
  • Assuming langgraph dev is testing real persistence; always test with direct Python invocation

Still deciding between in-context and external memory? See our architectural comparison of in-context vs external memory for AI agents before committing to a backend.


Step 3: Add cross-thread long-term memory with LangGraph BaseStore

What you’ll accomplish: Wire a BaseStore alongside the checkpointer so your agent can write and retrieve facts that survive across ALL sessions for a given user.

Time required: 20-30 minutes

This is the step most tutorials skip. The checkpointer remembers conversations. The store remembers users. A new thread_id for a returning user should never mean a blank slate; it only does if you stored user preferences in the checkpointer instead of the store.

from langgraph.store.memory import InMemoryStore    # dev / testing only; resets on restart
# For production:
# from langgraph.store.postgres import PostgresStore

from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver

store = InMemoryStore()

# CRITICAL: compile with BOTH checkpointer AND store.
# Passing only one is the single most common LangGraph memory architecture mistake.
agent = create_agent(
    model=llm,
    tools=tools,
    checkpointer=InMemorySaver(),  # in-thread context (current conversation history)
    store=store                    # cross-thread facts (user preferences, learned facts)
)

# Writing to store from a node:
# CRITICAL DISTINCTION:
# thread_id  ->  scopes ONE conversation session  (checkpointer, changes per session)
# user_id    ->  scopes ALL sessions for one person (store namespace root, NEVER changes)
#
# One user has many thread_ids.
# Store is keyed by user_id, not by thread_id.

namespace = ("user-123", "preferences")           # namespace = (user_id, category)
store.put(namespace, "timezone", {"value": "America/New_York"})
store.put(namespace, "language", {"value": "Python"})

# Reading from store in a node (survives thread changes):
memories = store.search(namespace, query="timezone preference")
# OR exact lookup:
timezone = store.get(namespace, "timezone")

# When the same user starts a NEW session (new thread_id), their stored facts are still here:
config_new_session = {"configurable": {"thread_id": "user-123-session-2"}}
# The checkpointer context is fresh, but store.get(("user-123", "preferences"), "timezone")
# still returns "America/New_York"

For production, replace InMemoryStore() with PostgresStore. The API is identical; only the import and constructor change:

# Production store (swap one line):
from langgraph.store.postgres import PostgresStore
store = PostgresStore.from_conn_string("postgresql://user:pass@host/dbname")

For the decision framework on which memory architecture fits your use case, see how to choose an AI agent memory architecture.

Validation checklist:

  • [ ] create_agent called with both checkpointer= and store=
  • [ ] Namespace tuple uses user_id (not thread_id) as root key
  • [ ] Store retains value when agent is re-invoked with a different thread_id for the same user

Common mistakes:

  • Passing only checkpointer when you need cross-session memory (the single most common architecture mistake)
  • Using thread_id as the namespace root in the store (creates per-session silos instead of per-user memory)

Step 4: Add semantic memory with the LangMem SDK

What you’ll accomplish: Layer the LangMem SDK on top of LangGraph’s BaseStore to give your agent three memory types: semantic (user facts and preferences), episodic (past interaction examples), and procedural (self-updated system prompts).

Time required: 20-30 minutes

LangMem is the only option in this stack where the agent can rewrite its own system prompt based on accumulated experience (procedural memory). No equivalent exists in Mem0 or Zep. If your agent needs to adapt its behavior from what it has learned, not just recall stored facts, LangMem adds that capability.

from langmem import create_manage_memory_tool, create_search_memory_tool
from langgraph.store.memory import InMemoryStore
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

# The embedding index is REQUIRED for semantic search.
# Without it, store.search() returns nothing.
store = InMemoryStore(
    index={
        "dims": 1536,
        "embed": "openai:text-embedding-3-small",
    }
)

# Namespace all memories under the user's ID — NOT their thread_id.
manage_memory = create_manage_memory_tool(namespace=("user-123",))
search_memory  = create_search_memory_tool(namespace=("user-123",))

agent = create_agent(
    model=init_chat_model("anthropic/claude-3-5-sonnet-20241022"),
    tools=[manage_memory, search_memory],
    store=store
)

# For production: replace InMemoryStore with PostgresStore (one line change)
# from langgraph.store.postgres import PostgresStore
# store = PostgresStore.from_conn_string("postgresql://user:pass@host/dbname")

The three memory types LangMem manages:

  • Semantic: User preferences and factual knowledge (“user works in Pacific time, prefers Python examples”)
  • Episodic: Past interactions distilled as few-shot examples (“last time the agent solved X by doing Y”)
  • Procedural: Updated system instructions the agent writes to itself (“always confirm before deleting”): unique to LangMem

Production caveat: do not skip this.

Memory search runs on every turn, so its latency is your agent’s latency. LangChain publishes no retrieval-latency figures for LangMem, and neither does any benchmark with a named runner and a published method. Measure it yourself against your own store backend and embedding model before you put it in an interactive path.

  • LangMem: the only one of the three with procedural memory, where the agent rewrites its own prompt from what it has learned. Built on the LangGraph store, MIT, maintained by LangChain.
  • Mem0: Mem0’s own paper (arXiv 2504.19413) reports 0.200s p95 retrieval and 67.13% LOCOMO accuracy. Those are the vendor’s numbers from the vendor’s run, so treat them as a claim rather than an independent score.
  • Zep: built for temporal reasoning over interaction sequences.

None of this surfaces in development testing. It surfaces in production user complaints.

For a full comparison across memory frameworks, see best AI agent memory frameworks 2026.

Validation checklist:

  • [ ] InMemoryStore configured with index dict (dims + embed model)
  • [ ] Both manage and search tools passed to agent
  • [ ] Agent successfully saves and retrieves a test memory across invocations

Common mistakes:

  • Deploying LangMem for interactive agents without measuring actual retrieval latency in your own environment
  • Skipping the embedding index configuration; store.search() returns nothing without it

Step 5: Use Zep for temporal memory (and fix the import error)

What you’ll accomplish: Connect your LangChain agent to ZepCloud with the correct import path and all ZepCloudMemory interface fields documented, plus the fix for GitHub issue #27356.

Time required: 20-30 minutes (plus Zep account setup)

Zep’s differentiator is temporal reasoning. It structures interactions into meaningful sequences and knows not just what a user said, but when, and how that event relates to subsequent events. This makes it architecturally closer to an episodic memory store than flat vector search.

The import error that sends developers to GitHub


The old import paths are broken in current langchain-community versions. Here is the exact error and the exact fix:

# DO NOT USE — raises ImportError: cannot import name 'ZepClient' from 'zep_python'
from langchain.memory.zep_memory import ZepMemory
from langchain_community.memory.zep_memory import ZepMemory  # also broken in recent versions

Error message you will see:

ImportError: cannot import name 'ZepClient' from 'zep_python'

Root cause: The zep_python package v2.0 removed the ZepClient class. The old zep_memory module still imports it, causing the error on any system with zep-python>=2.0.

Fix: Upgrade packages and use the correct module:

pip install "langchain-community>=0.3" "zep-python>=2.0"
# CORRECT import path (requires langchain-community>=0.3 + zep-python>=2.0):
from langchain_community.memory.zep_cloud_memory import ZepCloudMemory

# OR for chat message history:
from langchain_community.chat_message_histories import ZepCloudChatMessageHistory

ZepMemoryInput interface: all fields


This is the interface developers search for while staring at a blank ZepCloudMemory() constructor:

memory = ZepCloudMemory(
    session_id="user-123",          # Required. Stable per-user identifier.
                                     # DO NOT set this to a per-request UUID —
                                     # temporal continuity requires a stable, reused ID.
    api_key="your-zep-api-key",     # Required for ZepCloud.
    url="http://localhost:8000",     # Required ONLY for self-hosted Zep OSS.
                                     # Omit when using ZepCloud.
    memory_key="chat_history",      # Key name for history injected into chain.
                                     # Must exactly match the variable name in your prompt template.
    input_key=None,                  # Key for input variable in chain. None = auto-detect.
    output_key=None,                 # Key for output variable in chain. None = auto-detect.
    return_messages=False,           # True  = returns a list of BaseMessage objects.
                                     # False = returns a formatted string.
                                     # Use True for chat models; False for completion models.
    human_prefix="Human",           # Label for human turns when return_messages=False.
    ai_prefix="AI",                  # Label for AI turns when return_messages=False.
)

Secondary error (GitHub issue #25791): If you see TypeError: ZepCloudMemory() takes no arguments, the cause is a version mismatch between langchain-community and zep-python. Upgrading both packages to the versions above resolves it.

Validation checklist:

  • [ ] langchain-community>=0.3 and zep-python>=2.0 installed
  • [ ] Import from zep_cloud_memory (not zep_memory) resolves without error
  • [ ] session_id set to a stable per-user identifier (not a per-request UUID)
  • [ ] memory_key in ZepCloudMemory matches the variable name in your prompt template

Common mistakes:

  • Using from langchain_community.memory.zep_memory import ZepMemory: this is the wrong module; zep_cloud_memory is correct
  • Setting session_id to a per-request UUID (destroys temporal memory; Zep needs the same ID across sessions)
  • Version mismatch: if ZepCloudMemory() takes no arguments appears, upgrade both packages

Common implementation pitfalls

These are the five mistakes that send developers back to Stack Overflow after they think they have finished implementing memory.

Pitfall 1: Storing user preferences in the checkpointer


Why it happens: The checkpointer works in testing where thread_id never changes. In production, returning users get new thread_ids, and all “remembered” preferences vanish.

How to avoid it: Facts about a user go in the store, namespaced by user ID. Conversation turns go in the checkpointer, scoped by thread ID.

If you encounter it: Migrate preference data from checkpointer snapshots to the store; update agent code to write to the store from day one.

Pitfall 2: Using InMemorySaver in production


Why it happens: Works perfectly in development. Easy to miss the “dev / testing only” annotation.

How to avoid it: Use SqliteSaver for local persistent development. Use PostgresSaver for production from the start.

Pitfall 3: The langgraph dev checkpointer blind spot


Why it happens: In some LangGraph versions, langgraph dev ignores checkpointer configuration and defaults to in-memory storage. Agents appear to persist state, but they do not.

How to avoid it: Always verify persistence with direct Python invocation, not via langgraph dev.

Pitfall 4: Deploying LangMem for interactive agents


Why it happens: LangMem is the “official” LangGraph memory SDK, so teams assume it is production-ready for all use cases.

How to avoid it: measure retrieval latency against your own store and embedding model before it reaches an interactive path. If the numbers do not clear your turn budget, move memory writes to a background job or switch tools.

Pitfall 5: Broken Zep import path


Why it happens: The old zep_memory module existed in older versions and is still referenced in cached search results. Developers import it, hit the ZepClient error, and spend an hour debugging.

How to avoid it: Always import from langchain_community.memory.zep_cloud_memory. Verify langchain-community>=0.3 and zep-python>=2.0 are installed.


Best practices for LangChain long-term memory

Commit these five practices before you deploy:

Practice 1: Always compile with both checkpointer AND store.
Passing only one is the most common architecture mistake. checkpointer handles session continuity; store handles cross-session facts. You need both for a complete memory layer.

Practice 2: Namespace the store by user identity, not by thread.
Namespace tuple root = user_id. Every store operation for a given user hits the same namespace regardless of how many sessions they have had.

Practice 3: Match the memory tool to your latency requirement, and measure it.

  • LangMem → native LangGraph integration and procedural memory; benchmark retrieval before it goes interactive
  • Mem0 → interactive personalization; the 0.200s p95 and 67.13% LOCOMO figures are Mem0’s own
  • Zep → agents requiring temporal reasoning over interaction history

Practice 4: Use stable session IDs for Zep.
A session_id that changes per request destroys temporal continuity. Use a stable per-user identifier that persists across the user’s lifetime in your system.

Practice 5: Swap InMemoryStore for PostgresStore before production.
InMemoryStore resets on restart. The API is identical; one import line changes. Write the swap into your deployment checklist before the first staging push.


When memory isn’t enough: enterprise data agents and Atlan’s context layer

Memory systems (LangMem, Zep, Mem0, checkpointers) solve session continuity and user-fact retrieval across sessions. They do not answer “is this table certified?”, “who owns this column?”, or “has this metric definition changed?” Those are context layer questions, and the distinction is architectural.

After implementing LangMem or Zep, enterprise data agents still return wrong answers in these scenarios:

  • Querying Snowflake tables whose definitions have changed since the agent last saw them
  • Hitting deprecated columns that exist in lineage but not in any memory store
  • Resolving net_revenue conflicts across Looker and dbt, where the agent’s stored definition was correct last week but finance updated the glossary term yesterday

No memory system can tell an agent whether a table is certified, who owns a column, or whether a stored metric definition is still canonical. That requires live, governed metadata, not stored recollection.

This is where Atlan’s context layer fits. Atlan’s MCP server is callable from any LangChain agent through LangChain’s built-in MCP support, installed as langchain[mcp]. The standalone langchain-mcp-adapters package is archived; MCP moved into LangChain itself. Where LangMem remembers what a user said in a past session, Atlan tells the agent what is true about the data right now: certifications, ownership, sensitivity labels, lineage, and governance policies, propagated automatically across Snowflake, Looker, Slack, and every connected system. Agents can query Atlan by DSL, traverse lineage, check data quality, and update asset metadata without a human trigger.

Workday is already using Atlan this way: “All of the work that we did to get to a shared language at Workday can be leveraged by AI via Atlan’s MCP server…as part of Atlan’s AI Labs, we’re co-building the semantic layer that AI needs with new constructs, like context products.”

For enterprise data agents specifically, the architectural question is not which memory tool to choose; it is whether memory alone is sufficient. The line between memory layer and context layer determines that answer.

See how Atlan’s context layer works alongside LangGraph agents: Atlan Context Layer: Enterprise AI Agent Memory.


Real stories: enterprise context layers in production

"AI initiatives require more context than ever. Atlan's metadata lakehouse is configurable, intuitive, and able to scale to hundreds of millions of assets."

— Andrew Reiskind, Chief Data Officer, Mastercard

"Context is the differentiator. Atlan gave our teams the shared vocabulary and lineage to move from reactive data management to proactive AI enablement."

— Kiran Panja, Managing Director, Cloud and Data Engineering, CME Group


Memory or context? What the right architecture actually looks like

The five approaches in this guide give you a complete map of LangChain’s memory landscape. The deprecated path tells you what to remove. The checkpointer gives you session continuity. The BaseStore gives you cross-session user memory. LangMem adds semantic and procedural memory. Zep adds temporal reasoning.

Each one solves a real problem. The decision criteria are clear: benchmark memory retrieval before it lands in an interactive path, use the store for user facts and the checkpointer for conversation turns (never conflate the two), and fix the Zep import path before debugging anything else.

The boundary to watch is the one between memory and context. For teams building agents that query governed data systems (Snowflake, Looker, dbt), memory alone does not answer “what is true about this data right now?” That is the architectural question that takes you from a working memory implementation to a production-grade enterprise agent.

Next steps after implementation:

Once your checkpointer and store are running, measure actual memory retrieval latency in your target deployment environment. Development numbers are not representative; InMemoryStore has no network round-trip cost that PostgresStore does.

For production backend selection: PostgresStore + PostgresSaver is the standard path. If your team is already running Redis, it is a viable alternative; see the Redis + LangGraph integration pattern for setup details.

For the compliance and access-control considerations that follow a memory implementation, see AI agent memory governance. For the architecture path beyond a single-agent deployment, see enterprise AI memory layer: architecture for data leaders.


FAQs about long-term memory in LangChain agents

1. How do I add persistent memory to a LangChain agent across sessions?


Use LangGraph’s BaseStore alongside a checkpointer. The checkpointer (SqliteSaver for local dev, PostgresSaver for production) maintains conversation history within a session. The BaseStore (PostgresStore for production) stores facts that survive across sessions, namespaced by user ID. Both are passed to create_agent at compile time. Without the store, memory resets every time a user starts a new conversation.

2. What replaced ConversationBufferMemory in LangChain?


LangGraph persistence replaced it. ConversationBufferMemory was deprecated at LangChain v0.3.1 and removed at 1.0; it, ConversationChain and the other legacy chains now live in the separate langchain-classic package, so on 1.x from langchain.memory import ConversationBufferMemory raises ImportError rather than a DeprecationWarning. The replacement is a LangGraph checkpointer (InMemorySaver for dev, SqliteSaver or PostgresSaver for production) passed to create_agent. ConversationBufferWindowMemory, ConversationSummaryMemory and ConversationEntityMemory followed the same path.

3. What is the difference between a LangGraph checkpointer and a store?


A checkpointer manages conversation history within a single session, scoped by thread_id. A store manages facts that persist across all sessions, scoped by user or entity namespace. A new thread_id (new session) clears checkpointer context but never touches the store. For an agent that should remember a user’s preferences across multiple conversations, you need both; passing only a checkpointer is the most common architecture mistake.

4. How do I fix the ZepMemory ImportError in LangChain (issue #27356)?


Upgrade to langchain-community>=0.3 and zep-python>=2.0, then change your import to from langchain_community.memory.zep_cloud_memory import ZepCloudMemory. That fix targets a pre-1.0 stack: at LangChain 1.0 the langchain-community re-exports moved to langchain-classic, so on 1.x install that package or use the Zep SDK directly. The old paths (from langchain.memory.zep_memory import ZepMemory and from langchain_community.memory.zep_memory import ZepMemory) raise ImportError: cannot import name 'ZepClient' from 'zep_python' in current versions. A secondary error (ZepCloudMemory() takes no arguments, issue #25791) also resolves with the package version upgrade.

5. What are all the fields in the ZepMemoryInput interface?


ZepCloudMemory accepts: session_id (required, stable per-user identifier), api_key (required for ZepCloud), url (required only for self-hosted Zep OSS), memory_key (key name for history injected into the chain, must match your prompt template), input_key (auto-detect if None), output_key (auto-detect if None), return_messages (True for BaseMessage list, False for formatted string), human_prefix, and ai_prefix.

6. Is LangMem fast enough for production use?


Benchmark it before you decide. LangChain publishes no retrieval-latency figures for LangMem, and memory search runs on every turn, so its latency is your agent’s latency. Measure it against your own store backend and embedding model. If it clears your turn budget, LangMem gives you the tightest LangGraph integration and the only procedural memory in this set. If it does not, move memory writes to a background job or use Mem0 or Zep.

7. What is the difference between InMemorySaver and SqliteSaver?


InMemorySaver (still importable under its older name, MemorySaver) stores checkpointer state in process RAM: fast, zero setup, but resets when the Python process ends. Use it only for development and testing. SqliteSaver writes checkpoints to a local SQLite file that persists across restarts. Use it for local development that needs real persistence. For production multi-server deployments, use PostgresSaver to share state across instances.

8. Should I use LangMem, Mem0, or Zep for my LangChain agent?


It depends on your use case. LangMem integrates natively with LangGraph and uniquely supports procedural memory, where agents rewrite their own system prompts; LangChain publishes no latency figures for it, so benchmark retrieval before it goes interactive. Mem0’s own paper reports 0.200s p95 and 67.13% LOCOMO accuracy, vendor-run figures, and positions it for interactive personalization. Zep adds temporal reasoning over interaction sequences, which matters when the ordering of what a user said, and when, feeds the agent’s reasoning.


Sources

  1. LangChain v1 migration guide, LangChain docs, what moved to langchain-classic at 1.0 and what was deleted
  2. LangGraph v1 migration guide, LangChain docs, create_react_agent to create_agent, and the checkpointer names that still resolve
  3. LangGraph persistence: checkpointers and stores, LangChain docs, InMemorySaver, SqliteSaver, PostgresSaver, and store vector indexing
  4. LangMem SDK for agent long-term memory, LangChain blog, canonical SDK launch post with the memory type definitions
  5. langchain-ai/langmem, GitHub, MIT, 1.7k stars as of 18 September 2026
  6. Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory, arXiv 2504.19413, Mem0’s own benchmark run: 0.200s p95, 67.13% LOCOMO accuracy
  7. LangChain Zep Integration Fails Due to Deprecated Import, GitHub issue #27356, root cause and confirmed fix for the ZepClient ImportError
  8. langgraph dev bypasses a compiled-in checkpointer, GitHub issue #5790, the dev-server persistence blind spot
  9. MCP support in LangChain, LangChain docs, langchain[mcp], which replaced the archived langchain-mcp-adapters
  10. LangGraph & Redis: Build smarter AI agents, Redis blog, Redis as a production store backend option

Share this article

signoff-panel-logo

Atlan is the Context Layer for AI. It translates business knowledge, including data definitions, working procedures, and governance policies, into context AI can actually use. This knowledge lives in a single Enterprise Data Graph that every team and AI agent can reach.

In Atlan's AI Labs benchmark, adding this context improved AI's text-to-SQL accuracy by 38%.

Atlan is recognized as a Leader across multiple Gartner reports and Forrester Waves, and is trusted by over 400 enterprises representing $10T+ in market cap, including Mastercard, Workday, General Motors, CME Group, HubSpot, FOX, Virgin Media O2, and Elastic.

Bridge the context gap.
Ship AI that works.