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) # unknownThe 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
- Save three messages with SQLChatMessageHistory and restart the script to confirm they remain.
- Create two session ids and verify that their histories stay separate.
- Connect RunnableWithMessageHistory to a simple chain and ask the bot to recall your name.
- 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.
