LangChainGuide
A dark isometric diagram shows one glowing pink central node linked by thin lines to eight surrounding cube and cylinder nodes, evoking connected memory stores in an AI system.
agents

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.

By LangChainGuide Editorial · ·Updated · 12 min read

LangChain memory is what lets an agent remember earlier messages and facts. In LangChain 1.x it comes in two types: short-term memory, the thread’s message history saved by a checkpointer and keyed by thread_id, and long-term memory, JSON documents in a LangGraph store shared across threads. The old langchain.memory classes such as ConversationBufferMemory are deprecated.

A memory bug usually shows up one of two ways. In the first, the bot forgets the order number the user gave it one message ago, because each request starts from a blank prompt. In the second, memory is on and prompt tokens per turn keep climbing until a long conversation dies with a context-length error. Both come from the same design decision made badly. The sections below cover which classes are deprecated, what replaced them, how an agent reads and writes its memory, and which policy 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 class was not deleted at 1.0. The v0.3 decorator set removal="1.0.0", but the class moved with other legacy code into the separate langchain-classic package. In the current langchain-classic source, the decorator keeps since="0.3.1", pushes removal to "2.0.0" and names langchain.agents.create_agent as the alternative. The package’s memory module still exports the window, token-buffer, summary-buffer, entity and vector-store memory classes too. PyPI listed langchain-classic 1.0.8, released June 10, 2026, requiring Python 3.10 or later (checked October 2026).

The replacement depends on what the old memory object was doing:

Legacy needLangChain 1.x mapping
Keep the conversation transcriptMessage state plus a checkpointer, scoped to a thread
Keep only recent turns or a token budgetTrim messages before the model call, or remove messages from state
Summarize older conversation turnsSummarizationMiddleware with a recent-message tail
Recall facts across separate conversationsA LangGraph store, passed through store= to create_agent
Retrieve saved facts by similarityA 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.

A migration off the deprecated class usually runs in this order:

  1. Confirm the environment. The v1 migration guide states that all LangChain packages now require Python 3.10 or higher.
  2. If legacy code must keep running in the meantime, pip install langchain-classic and change imports from langchain.memory to langchain_classic.memory. The same guide lists legacy chains such as ConversationChain among the things that moved there.
  3. Replace the chain-plus-memory pair with create_agent(..., checkpointer=...) and pass a thread_id in config on every call.
  4. Map the old class’s strategy onto trimming, summarization or a store using the table above.
  5. Swap InMemorySaver for a database-backed saver and run its one-time setup() before the first request.

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 n turns 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 MongoDB, Redis and Oracle savers listed on the add-memory page.

Initialization is backend-specific. For PostgresSaver, install langgraph-checkpoint-postgres with "psycopg[binary]", build the saver with PostgresSaver.from_conn_string(DB_URI) and call checkpointer.setup() before first use, as shown in the short-term memory guide. The add-memory page shows the same first-run checkpointer.setup() call for the Redis and Oracle savers; for any other backend, follow that package’s own initialization instructions.

BackendCheckpointer (short-term)Store (long-term)Package
In-processInMemorySaverInMemoryStoreships with langgraph
PostgresPostgresSaver, AsyncPostgresSaverPostgresStore, AsyncPostgresStorelanggraph-checkpoint-postgres
RedisRedisSaver, AsyncRedisSaverRedisStore, AsyncRedisStorelanggraph-checkpoint-redis
MongoDBMongoDBSaver, AsyncMongoDBSaverno store documentedlanggraph-checkpoint-mongodb
OracleOracleSaver, AsyncOracleSaverOracleStore, AsyncOracleStorelanggraph-oracledb

The table follows the add-memory page; the async variants use asetup() for the same one-time migration.

LangChain agent memory: what the agent can read and write

Agent memory in LangChain 1.x is the agent’s state rather than a separate memory object. create_agent keeps messages in an AgentState, the checkpointer saves that state per thread, and tools reach it through a ToolRuntime parameter that the model never sees. Extend the state when the agent must remember more than the transcript.

The short-term memory guide subclasses AgentState and passes it as state_schema:

from langchain.agents import create_agent, AgentState
from langchain.tools import tool, ToolRuntime
from langgraph.checkpoint.memory import InMemorySaver

class CustomState(AgentState):
    user_id: str

@tool
def get_user_info(runtime: ToolRuntime) -> str:
    """Look up user info."""
    user_id = runtime.state["user_id"]
    return "Known customer" if user_id == "user_123" else "Unknown user"

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[get_user_info],
    state_schema=CustomState,
    checkpointer=InMemorySaver(),
)

Custom fields such as user_id can be supplied in the invoke input alongside messages, and the checkpointer persists them with the rest of the thread. The agent has four ways to touch memory:

  • Read thread state in a tool: runtime.state["..."], as above.
  • Write thread state from a tool: return a Command with an update, which can include a ToolMessage for the conversation.
  • Rewrite state before each model call: a function decorated with @before_model from langchain.agents.middleware returns a state update. The guide’s example keeps the first message plus the most recent few.
  • Read and write cross-thread memory: the long-term memory guide passes a store as store= and a Context dataclass as context_schema=. A tool then calls runtime.store.get(("users",), runtime.context.user_id) or runtime.store.put(...) with the same namespace and key.

Keep the identity used for namespaces in context, not in something the model writes. A tool that takes the user ID as a model-generated argument lets one user’s thread read or overwrite another user’s memories.

LangChain memory management: keeping the thread inside 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.

PolicyAPIWhat changes in storageExtra cost
Trim per calltrim_messagesNothing; the model sees a subsetNone
Rewrite history@before_model returning RemoveMessage(id=REMOVE_ALL_MESSAGES) plus kept messagesStored messages replacedNone
Delete specific messagesRemoveMessage(id=...)Those messages gone for goodNone
SummarizeSummarizationMiddleware(trigger=..., keep=...)Older messages folded into a summaryOne model call per firing

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. Removal relies on a state key with the add_messages reducer, which the default AgentState provides, and the guide warns that what remains must still be a valid history for your provider: some require it to start with a user message, and every tool call needs its matching tool result.

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. Point the summarizer at a cheaper model than the agent’s; how to reduce LangChain token costs covers the other levers on the same bill.

Managing memory also means inspecting and deleting what is stored. The add-memory page documents get_state(config) for a thread’s latest snapshot (add a checkpoint_id to the config for an older one), get_state_history(config) for every snapshot newest first, and checkpointer.delete_thread(thread_id) to drop all checkpoints for a thread. That last call is the one to wire into account deletion and retention jobs.

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_id must 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.
  • InMemorySaver and InMemoryStore vanish 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 dims must match the model you pass to index; 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.

FAQ

can i still use conversationbuffermemory in langchain 1.x?

Yes, if you install langchain-classic and import it from langchain_classic.memory. The class still raises a deprecation warning, and that package schedules its removal for version 2.0.0 with create_agent named as the alternative. create_agent takes a checkpointer, not a memory object, so treat the class as a bridge for legacy chains during migration.

what is the difference between a checkpointer and a store in langchain?

A checkpointer saves one thread’s state after every step, keyed by thread_id, so the same conversation can resume. A store holds JSON documents under namespaces and keys that any thread can read, which is how a preference learned in one conversation reaches the next. An agent can use both: pass them as checkpointer= and store= to create_agent.

does langchain memory persist after a restart?

Only with a database-backed saver. InMemorySaver and InMemoryStore keep everything in process memory, so a restart or a rescheduled container wipes every thread. For persistence, use PostgresSaver and PostgresStore from langgraph-checkpoint-postgres, or the Redis or Oracle equivalents, and run the one-time setup() call before the first request reaches the agent.

how do i clear conversation memory in a langchain agent?

Return RemoveMessage(id=REMOVE_ALL_MESSAGES) from a node or middleware to wipe the messages in a thread’s state, or call checkpointer.delete_thread(thread_id) to drop every checkpoint stored for that thread. Starting a new thread_id also gives a clean history without deleting anything, which keeps the old conversation available for audit or time travel.

is langchain memory the same as langgraph memory?

In 1.x, yes. create_agent runs on LangGraph, so its short-term memory is a LangGraph checkpointer and its long-term memory is a LangGraph store. The same savers and stores work in a hand-written StateGraph. Only the pre-1.0 langchain.memory classes are a separate system, and they now ship in the langchain-classic package.

Sources

  1. LangChain documentation: Short-term memory
  2. LangChain documentation: Long-term memory
  3. LangGraph documentation: Memory (concepts)
  4. LangGraph documentation: Add and manage memory
  5. LangChain v0.3 source: ConversationBufferMemory deprecation
  6. LangChain v0.3 docs: How to migrate to LangGraph memory
  7. LangChain API reference: trim_messages
  8. MemGPT: Towards LLMs as Operating Systems (arXiv 2310.08560)
  9. Generative Agents: Interactive Simulacra of Human Behavior (arXiv 2304.03442)
  10. langchain-classic source: ConversationBufferMemory deprecation (master)
  11. langchain-classic source: memory module exports
  12. PyPI: langchain-classic
  13. LangChain documentation: LangChain v1 migration guide
#langchain #langgraph #memory#agents#context-window#checkpointing

Related