LangChain Persistent Chat History

Memory stored in a Python variable disappears when the program stops. A user who closes the browser and returns tomorrow expects the assistant to remember yesterday's talk. Persistent chat history saves conversations in a database so that sessions survive restarts, crashes, and server moves.

The Notebook Analogy

A waiter who memorizes orders forgets everything at the end of the shift. A waiter who writes orders in a notebook can pick up any table's story the next morning. A database acts as that notebook for your assistant.

 User A (session "a1")  ─┐
 User B (session "b7")  ─┼→  Chat app  ⇄  Database
 User C (session "c3")  ─┘              (every message saved by session id)

Session IDs Keep Chats Separate

A session id is a label that groups messages of one conversation. Two users with different ids never see each other's messages. Pick ids that are hard to guess, such as a random UUID, and tie them to a signed-in user when your app has accounts.

Storing Messages in SQLite

SQLite is a small database that lives in a single file, which makes it perfect for learning and small apps.

from langchain_community.chat_message_histories import SQLChatMessageHistory

history = SQLChatMessageHistory(
    session_id="user-42",
    connection="sqlite:///chat_history.db",
)

history.add_user_message("My favorite color is green.")
history.add_ai_message("Nice choice! I will remember green.")

print(history.messages)

Run the program twice. The second run still prints both messages, because the file keeps them.

Connecting History to a Chain

The wrapper RunnableWithMessageHistory loads the saved messages before each call and saves the new ones afterward. You write one function that returns the right history for a session id.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    MessagesPlaceholder("history"),
    ("human", "{question}"),
])

chain = prompt | model

def get_history(session_id):
    return SQLChatMessageHistory(
        session_id=session_id,
        connection="sqlite:///chat_history.db",
    )

chat = RunnableWithMessageHistory(
    chain,
    get_history,
    input_messages_key="question",
    history_messages_key="history",
)

Talking Across Sessions

config = {"configurable": {"session_id": "user-42"}}

chat.invoke({"question": "My name is Meera."}, config=config)
reply = chat.invoke({"question": "What is my name?"}, config=config)
print(reply.content)   # Your name is Meera.

Another session id starts with a blank page.

other = {"configurable": {"session_id": "user-99"}}
chat.invoke({"question": "What is my name?"}, config=other)   # unknown

The Full Loop

 New question + session id
          |
          v
 Load saved messages for that id
          |
          v
 Prompt = rules + history + question
          |
          v
 Model replies
          |
          v
 Save question and reply to the database

Choosing a Database

  • SQLite fits prototypes and single-server apps.
  • PostgreSQL fits production apps with many users and servers.
  • Redis fits fast, short-lived sessions.
  • MongoDB fits teams that already store documents there.

Switching often takes a one-line change to the connection string, because LangChain offers a matching history class for each database.

Combining with Trimming

A saved history grows forever. Add a trimmer in the chain so the model sees only recent messages while the database keeps the full record for audits and support.

Privacy and Safety

  • Tell users that the app stores conversations.
  • Offer a delete option and honor deletion requests.
  • Avoid saving passwords, card numbers, or other secrets.
  • Protect the database with strong credentials and encryption.

Real-World Scenario

A homework helper app serves hundreds of students. Each student signs in, and the app uses the student id as the session id. A student who studies fractions on Monday returns on Thursday and picks up the lesson without repeating the background. The database file stores every turn, and the support team can review a conversation when a parent asks a question. A delete button removes a student's history on request, and the app respects that choice immediately.

Practice Tasks

  1. Save three messages with SQLChatMessageHistory and restart the script to confirm they remain.
  2. Create two session ids and verify that their histories stay separate.
  3. Connect RunnableWithMessageHistory to a simple chain and ask the bot to recall your name.
  4. Write a function that deletes all messages for one session id.

Key Points

  • Persistent history stores messages in a database by session id.
  • SQLChatMessageHistory saves and loads messages with a connection string.
  • RunnableWithMessageHistory automates loading and saving around a chain.
  • Different session ids keep users separate.
  • Trimming and privacy rules keep stored history healthy.