LangChain Memory Types: Legacy Classes and Replacements
This guide compares legacy LangChain memory classes with 1.x checkpointers, stores, trimming, and summarization for short- and long-term memory.
The memory bug shows up in one of two ways. Either the bot forgets the order number the user gave it one message ago, because each request starts from a blank prompt, or memory is on and prompt tokens per turn climb until a long conversation dies with a context-length error. Both are the same design decision made badly. This is LangChain memory types explained in terms of what ships in LangChain 1.x: which classes are deprecated, what replaced them, and which one keeps token growth flat.
ConversationBufferMemory is deprecated: what replaces it
ConversationBufferMemory is deprecated since version 0.3.1, as recorded in the legacy class source. For LangChain 1.x agents, move conversation history into agent state and pass a LangGraph checkpointer to create_agent. Supply a thread_id on each invocation so the next turn reads the same conversation. The short-term memory guide documents this pattern.
The replacement depends on what the old memory object was doing:
| Legacy need | LangChain 1.x mapping |
|---|---|
| Keep the conversation transcript | Message state plus a checkpointer, scoped to a thread |
| Keep only recent turns or a token budget | Trim messages before the model call, or remove messages from state |
| Summarize older conversation turns | SummarizationMiddleware with a recent-message tail |
| Recall facts across separate conversations | A LangGraph store, passed through store= to create_agent |
| Retrieve saved facts by similarity | A store configured with an embedding index and an explicit search step |
The long-term memory guide covers namespaces, keys and reading or writing stored facts through tools. A checkpointer does not automatically extract cross-thread facts, and a store does not automatically put them in the prompt. Add the read and write logic for the behavior you need. Persistence alone also does not trim the transcript; choose a context-management policy separately.
The legacy classes and what each one did
A tutorial that imports from langchain.memory describes the pre-1.0 API. The v0.3 migration guide explains the move from legacy memory implementations to LangGraph persistence. Treat the old class names below as descriptions of behavior to preserve during migration, and use the current memory guides for the replacement APIs.
Each one still names a strategy you will re-implement:
- ConversationBufferMemory: the whole transcript, verbatim, every turn. Token cost grows linearly with conversation length.
- ConversationBufferWindowMemory: the last
nturns only. Flat cost, hard amnesia beyond the window. - ConversationTokenBufferMemory: the most recent messages that fit inside a token budget. Same idea, budgeted in tokens instead of turns.
- ConversationSummaryMemory: a running summary written by the model, replacing the transcript.
- ConversationSummaryBufferMemory: recent messages verbatim plus a summary of everything older, under one token limit.
- VectorStoreRetrieverMemory: past exchanges embedded and retrieved by similarity to the current input.
- ConversationEntityMemory: structured facts about named entities extracted as the conversation runs.
One exception from the guide: “If you have been using RunnableWithMessageHistory or BaseChatMessageHistory, you do not need to make any changes.”
The two memory types that matter now
Current LangChain collapses the list above into two scopes, defined in the LangGraph memory concepts page.
Short-term memory is thread-scoped. It is the message history of one conversation, held in the agent’s state and persisted by a checkpointer after every step, keyed by a thread_id. Passing the same thread_id resumes the conversation. This is what ConversationBufferMemory did, except that persistence also buys fault tolerance, human-in-the-loop pauses and time travel over checkpoints.
Long-term memory is cross-thread. It is a store of JSON documents organized by namespace and key, where the docs describe a namespace as working “like a folder” and a key “like a file name”. Namespaces usually carry a user or org ID, so a fact learned on Monday’s thread is available on Friday’s. The docs borrow three categories from psychology: semantic (facts about a user), episodic (past agent actions), and procedural (the instructions the agent runs under, such as its system prompt).
The Generative Agents paper describes recording experiences, synthesizing reflections and retrieving memories to plan behavior. It provides background for separating stored experiences from the context selected for a particular response.
Wiring up short-term memory
From the short-term memory guide, the minimum is a checkpointer on the agent and a thread_id in the config:
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[],
checkpointer=InMemorySaver(),
)
config = {"configurable": {"thread_id": "support-4471"}}
agent.invoke({"messages": [{"role": "user", "content": "My order is 88213."}]}, config)
agent.invoke({"messages": [{"role": "user", "content": "Where is it?"}]}, config)
InMemorySaver is for development. The docs are direct about production: “use a checkpointer backed by a database”, with PostgresSaver from langgraph-checkpoint-postgres as the reference option and SQLite, Redis, MongoDB and Oracle packages listed on the add-memory page. Each needs checkpointer.setup() once before first use.
Keeping the thread from eating the context window
A checkpointer alone gives you ConversationBufferMemory semantics: everything, forever. The metric to watch is prompt tokens per turn against turn number. With a plain buffer that line is a diagonal, and it only ends one way. Three tools flatten it, mapping onto the old window, token-buffer and summary classes.
trim_messages from langchain_core is the window and token buffer in one function. Its signature takes a max_tokens budget, a strategy of "first" or "last", a token_counter (a model, a callable, or "approximate"), and boundary controls so a tool result is never cut away from the call that produced it:
from langchain_core.messages import trim_messages
trimmed = trim_messages(
state["messages"],
max_tokens=3000,
strategy="last",
token_counter=model,
start_on="human",
include_system=True,
allow_partial=False,
)
RemoveMessage deletes messages from state permanently, and RemoveMessage(id=REMOVE_ALL_MESSAGES) clears a thread. Trimming is per-call; removal changes what the checkpointer stores.
SummarizationMiddleware is ConversationSummaryBufferMemory reborn. It fires when the history crosses a trigger and folds older messages into a summary while keeping a recent tail verbatim:
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="anthropic:claude-sonnet-4-6",
tools=[],
checkpointer=checkpointer,
middleware=[
SummarizationMiddleware(
model="openai:gpt-5.4-mini",
trigger=("tokens", 4000),
keep=("messages", 20),
)
],
)
The docs state the trade-off in one line: trimming or removing messages means “you may lose information”. Summarization loses less, at the price of a model call each time it fires.
Wiring up long-term memory
The long-term memory guide exposes the store to tools through runtime.store, with get and put on a namespace. Add an embedding index and search becomes the replacement for VectorStoreRetrieverMemory:
from langchain.embeddings import init_embeddings
from langgraph.store.memory import InMemoryStore
store = InMemoryStore(
index={"embed": init_embeddings("openai:text-embedding-3-small"), "dims": 1536}
)
namespace = ("user-1042", "memories")
store.put(namespace, "pref-units", {"data": "Prefers metric units and short answers."})
hits = store.search(namespace, query="how should measurements be formatted?", limit=3)
Swap InMemoryStore for PostgresStore in production. The remaining choice is when to write. The concepts page distinguishes writing “in the hot path”, where the agent saves memories during the turn and the user pays the latency, from writing “in the background”, where a separate job extracts memories later and must be triggered carefully so it does not run on a half-finished thread.
The persistence layer is shared by create_agent and custom graphs; LangChain vs LangGraph explains when to configure an agent or define the graph directly.
What you’ll see
Plot prompt tokens per turn by thread and the memory type is legible from the shape. Buffer: a diagonal that ends in a context-length exception. Window or trim: a ramp that goes flat at the budget, plus tickets about the bot “forgetting” something from twelve turns back. Summary: a sawtooth that drops each time the middleware fires, with the risk that a summary silently rewrites a fact. Instrument at the span level rather than guessing from the bill; the OpenTelemetry GenAI conventions define per-call input and output token attributes to plot.
Caveats
thread_idmust be unique per conversation, not per user. Reusing a user ID as the thread ID merges every session that user ever had into one state.InMemorySaverandInMemoryStorevanish on restart: fine on a laptop, silent data loss in a rescheduled container.- The store is an injection surface. Anything a tool writes into a namespace is replayed into prompts on later threads, so a poisoned document retrieved once can persist as a “fact”. The patterns in indirect prompt injection in RAG pipelines apply to memory writes too.
- Summaries drift. Each pass is a lossy rewrite of the last one, and numbers, names and negations go first.
- Embedding
dimsmust match the model you pass toindex; a mismatch fails at write time, not at configuration time. - None of this is a single “memory”. The MemGPT paper frames the context window as fast main memory and external storage as a separate tier. In a LangChain application, decide separately what to persist and what to include in the next model call.
For how memory sits alongside tools and control flow, see LangChain building blocks: chains, tools and agent control.
If history is present but the agent repeats a step or mishandles a tool response, follow the diagnostic guide to why LangChain agents loop, skip tools, or fail to parse.
Related across the network
- LlamaIndex vs LangChain: Which to Use for RAG — llamaindexhub.com
- CVE Roundup: AI/ML Infrastructure Vulnerabilities — Q1 2026 — ai-alert.org
- Agent Tool-Use Exfiltration: When Indirect Injection Does Damage — aisec.blog
- Measuring Prompt-Injection Robustness in Tool-Using Agents — aisecbench.com
- AI Sec Weekly: Friday, May 15, 2026 — aisecweekly.com
Sources
- LangChain documentation: Short-term memory
- LangChain documentation: Long-term memory
- LangGraph documentation: Memory (concepts)
- LangGraph documentation: Add and manage memory
- LangChain v0.3 source: ConversationBufferMemory deprecation
- LangChain v0.3 docs: How to migrate to LangGraph memory
- LangChain API reference: trim_messages
- MemGPT: Towards LLMs as Operating Systems (arXiv 2310.08560)
- Generative Agents: Interactive Simulacra of Human Behavior (arXiv 2304.03442)
Related
LangChain vs LangGraph: Agents, State and Control Flow
Compare LangChain vs LangGraph for agent loops, state, checkpoints and human approval, with guidance on when to use create_agent or a custom graph.
LangChain Chunk Size: Configure Splitters for RAG
Configure LangChain chunk_size and chunk_overlap, choose character or token units, preserve source metadata, and compare splitter settings for RAG.
LangChain with Ollama: Local Setup, Tools, and RAG
This guide explains how LangChain connects to Ollama for local chat, streaming, tool calling, structured output, embeddings, and RAG.