LangChain Chat History and Memory

LangChain memory is how a chatbot remembers earlier turns. Models are stateless, so "memory" really means storing the chat history and sending the relevant part of it with each new request. This lesson shows a manual approach, how to trim history, and the modern approach with checkpointers.

Manual Chat History

The simplest memory is a list you keep yourself:

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, AIMessage

llm = ChatOllama(model="llama3.2", temperature=0)
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a concise assistant."),
    MessagesPlaceholder("history"),
    ("human", "{input}"),
])
chain = prompt | llm
history = []

def chat(text):
    reply = chain.invoke({"history": history, "input": text})
    history.extend([HumanMessage(text), AIMessage(reply.content)])
    return reply.content

chat("I live in Karachi.")
print(chat("Which country is my city in?"))
Sample output:
Karachi is in Pakistan.

Trimming Long Conversations

Context windows are limited and long histories are slow. trim_messages keeps only the most recent messages that fit a budget:

from langchain_core.messages import trim_messages

trimmer = trim_messages(
    max_tokens=6,           # here: count messages, not tokens
    token_counter=len,
    strategy="last",
    start_on="human",
    include_system=True,
)
short_history = trimmer.invoke(history)
print(len(short_history))
2

With token_counter=len every message counts as one. In real apps pass the model itself (token_counter=llm) or an approximate counter so the limit is in tokens.

Memory with Checkpointers

In LangChain 1.x the recommended memory for agents is a LangGraph checkpointer. It saves the conversation state per thread_id automatically:

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

agent = create_agent(
    "ollama:llama3.2",
    tools=[],
    system_prompt="You are a friendly assistant.",
    checkpointer=InMemorySaver(),
)
cfg = {"configurable": {"thread_id": "user-42"}}
agent.invoke({"messages": [{"role": "user", "content": "Hi, I'm Hina."}]}, cfg)
out = agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, cfg)
print(out["messages"][-1].content)
Sample output:
Your name is Hina.

Each thread_id is a separate conversation. Use a different ID per user or chat session. InMemorySaver loses data when the program stops; for production use a persistent checkpointer such as langgraph-checkpoint-sqlite or langgraph-checkpoint-postgres.

Memory Strategies

  • Full history: simplest, but grows without limit.
  • Sliding window: keep the last N messages or tokens.
  • Summary: periodically ask the model to summarize older turns into one message.
  • Long-term memory: store facts about the user in a database or LangGraph store and retrieve them when relevant.

Frequently asked questions

Is ConversationBufferMemory deprecated?

Yes. The old memory classes were moved to the langchain-classic package. New code should use message lists, RunnableWithMessageHistory or LangGraph checkpointers.

How do I give each user their own chat history?

Use a unique thread_id per user or session in the config when calling an agent with a checkpointer.

How do I save LangChain memory to a database?

Use a persistent checkpointer such as SqliteSaver or PostgresSaver from the LangGraph checkpoint packages instead of InMemorySaver.