Home / Articles / Five LangChain Memory Strategies, From Buffer to Entity Memory

This article is published in English.

Five LangChain Memory Strategies, From Buffer to Entity Memory

How chat models forget between calls and the five memory patterns for LangChain and LangGraph: buffer, window, summary, summary buffer and entity, with trade-offs.

1667 words

Plenty of people assume a chat model keeps track of what you told it. It doesn't. Call GPT-5, GPT-4o, Claude or Gemini and the result is the same: each request starts from zero, and unless you send earlier turns along again, the model has no idea they happened. Here's the smallest demonstration:

from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")llm.invoke("Hi, my name is Asha.")
llm.invoke("What's my name?")

The second call knows nothing about the first. Asha's name was never sent, so the model can't produce it.

What memory means in LangChain

There's nothing magical in it. Memory is a policy for one question: which parts of earlier conversation get sent back with the next request? Your answer shapes how smart, how scalable and how cheap the application turns out. Below are five policies used with LangChain and LangGraph, what each one is good and bad at, and where it fits.

The underlying tension

Say a user introduces herself as Asha, gets a friendly reply, and later asks what her name is. For a correct answer, that first message has to ride along in the new prompt.

Trouble starts as chats grow to 10, 100 or 1,000 messages. Resending all of it every time pushes up token usage, API bills, latency and pressure on the context window. Each choice trades one thing for another:

  • Remember more, and cost goes up.
  • Remember less, and cost drops but accuracy suffers.
  • Send more context, and answers slow down.
  • Send less context, and answers speed up but lose information.

Strategy 1: buffer memory

Buffer memory keeps the full transcript and sends all of it on every call. Picture a chat app that never deletes a thing. The code is short:

from langchain_core.messages import HumanMessage
history = []def chat(user_input):
    history.append(HumanMessage(user_input))    response = llm.invoke(history)    history.append(response)    return response.content

An example conversation:

User: I live in Chennai.
Assistant: That's great.

Later on:

User: Which city do I live in?
Assistant: Chennai

The model gets Chennai right because the earlier line is still in the history it receives.

What's good: recall is perfect, it's trivial to build, and debugging is pleasant because you can see everything. What's bad: tokens grow without limit, costs climb, replies slow down over time, and eventually you hit the context ceiling. It suits short sessions, internal tools, development environments and testing.

Strategy 2: window memory

Here you keep only the last K messages. Anything older is deleted outright, a bit like a whiteboard where every new note erases the oldest one.

from langchain_core.messages import trim_messages
trimmed = trim_messages(
    history,
    strategy="last",
    max_tokens=10,
    token_counter=len
)

Try this exchange:

User: My name is Asha.
Assistant: Nice to meet you.
User: Message 1
User: Message 2
User: Message 3
User: Message 4User: What's my name?

If the name message has already scrolled out of the window, the model has to admit defeat:

Assistant: I don't know your name.

That fact is gone for good. On the upside, token use is predictable, replies are quick and the bill stays low. The downside is that old information vanishes, so long-running conversations can't work. Think FAQ bots, casual chatbots, customer-service helpers and any low-budget app.

Strategy 3: summary memory

Suppose you don't want to hoard everything but you also don't want to forget everything. Summary memory has the model write a compact recap of older turns every so often, and the recap replaces the raw text.

People do this naturally. After a two-hour meeting you don't recall each sentence. You recall that the budget was discussed, the proposal approved and a progress review set for next week.

After a long chat, the stored state might read:

Summary:
User is building a shipment tracking application.
The discussion focused on Azure deployment and performance optimization.

Rather than sending 100 messages, you send that. A rough implementation starts with an empty running summary:

running_summary = ""

Then, once the history passes a threshold, older messages get condensed:

if len(history) > 6:
    old_messages = history[:-2]    running_summary = llm.invoke(
        f"Summarize this conversation:\n{old_messages}"
    ).content    history[:] = history[-2:]

Strengths: token growth flattens out, long conversations work, and costs fall. Weaknesses: exact wording disappears, important details can get dropped, and the result is only as good as the model writing the summary. It fits lengthy support sessions, virtual assistants, coaching apps and multi-hour chats.

Strategy 4: summary buffer memory

If you open a real production chatbot's code, you'll usually find some version of this one. It blends the previous two: recent messages stay word for word and older ones get summarized. You get sharp recent context and affordable long conversations.

A good executive assistant works the same way. They remember yesterday's request exactly:

You asked for the deployment status yesterday.

And they carry the background in compressed form:

This project started in January and focuses on Azure migration.

Fresh details stay crisp while old ones turn into condensed knowledge. A minimal version sets a limit on how many recent messages to keep:

RECENT_LIMIT = 4
running_summary = ""def chat(user_input):
    global running_summary    history.append(HumanMessage(user_input))    if len(history) > RECENT_LIMIT:        older_messages = history[:-RECENT_LIMIT]        running_summary = llm.invoke(
            f"""
            Update this summary:
            {running_summary}            Using:
            {older_messages}
            """
        ).content        history[:] = history[-RECENT_LIMIT:]    context = [
        SystemMessage(
            content=f"Summary: {running_summary}"
        )
    ] + history    response = llm.invoke(context)    history.append(response)    return response.content

Strengths: a strong balance of cost and accuracy, support for long chats, recent details preserved, and good production behavior. Weaknesses: a little more complexity, plus periodic summarization calls. Typical uses are copilots, enterprise chatbots, support platforms and production-grade assistants.

Strategy 5: entity memory

Sometimes the transcript isn't what you need to keep. The facts are. Consider statements like these:

User: My sister's birthday is in March.
User: I work at Contoso.
User: My manager is Priya.

Months later you still want those facts. Entity memory keeps structured records on the people, places, projects and companies that come up, so the original chat can vanish while the useful bits remain. An entity store might hold:

{
    "Priya": [
        "User's manager"
    ],
    "Contoso": [
        "User works here"
    ]
}

Extraction begins with an empty store:

entity_store = {}

and a function asks the model to pull out entities and facts:

def extract_entities(text):    prompt = f"""
    Extract entities and facts:
    {text}
    """    return llm.invoke(prompt).content

Strengths: great for long-term memory, very cheap in tokens, and the data is structured. Weaknesses: the architecture is heavier, you need extraction logic, and it can't replay an exact conversation. It works for personal assistants, CRM systems, employee helpers and relationship-management tools.

The LangGraph way

Older tutorials use classes you'll recognize:

ConversationBufferMemory
ConversationSummaryMemory
ConversationBufferWindowMemory

Those are legacy patterns now, and current LangChain work leans on LangGraph. The old style created a memory object:

memory = ConversationBufferMemory()

The newer style uses a checkpointer such as

MemorySaver()

or

PostgresSaver()

to store conversation state. Trimming, summarizing and processing that state is then up to you. A graph setup begins with these imports:

from langgraph.graph import (
    StateGraph,
    MessagesState,
    START
)

and the full wiring, including trimming, looks like this:

from langgraph.checkpoint.memory import (
    MemorySaver
)from langchain_core.messages import (
    trim_messages
)def call_model(state: MessagesState):    trimmed = trim_messages(
        state["messages"],
        strategy="last",
        max_tokens=10,
        token_counter=len
    )    response = llm.invoke(trimmed)    return {
        "messages": [response]
    }graph = StateGraph(MessagesState)graph.add_node("model", call_model)graph.add_edge(START, "model")app = graph.compile(
    checkpointer=MemorySaver()
)

One point trips people up: the checkpointer doesn't decide what to remember. It only decides where the memory lives, such as in-process, in a database or in another store. Choosing between buffer, window, summary, summary buffer or entity is still your job.

Can Azure Blob Storage hold memory?

Yes. It can keep chat history, summaries, entity data and session checkpoints. For real-time conversation memory, though, it's usually a poor fit, since reads are slower than with Redis, it isn't tuned for frequent reads and writes, and you'd need extra indexing logic. A common enterprise layout instead looks like this:

Redis
    ↓
PostgreSQL
    ↓
Azure Blob Storage

In that arrangement, Redis holds active conversations, PostgreSQL holds session history and Blob Storage archives long-term data.

Choosing a strategy

Match the policy to the product. Short-lived or internal chats can use buffer memory. Cheap, casual bots can use windows. Long sessions where wording matters little suit summaries. Anything customer-facing and long-running generally wants the summary buffer, and anything that must recall durable facts about people or organizations wants entity memory.

Closing thoughts

Memory design ranks among the biggest decisions in an AI product. A bot with no memory feels forgetful, and a bot with unlimited memory turns expensive and slow. The aim isn't to remember everything but to remember the right things.

For most production apps right now, the best balance of scale, cost and user experience comes from summary buffer plus entity memory plus LangGraph checkpointing. If you're building assistants on LangChain or LangGraph, getting memory right will improve quality and performance more than nearly any prompt trick. The smartest systems aren't the ones that remember it all. They're the ones that know what to keep.