Gestion de la mémoire conversationnelle avec LangChain : Du chatbot basique au système multi-utilisateurs
Un chatbot sans mémoire, c’est comme parler à quelqu’un qui a l’amnésie immédiate. Chaque réponse ignore complètement ce qui a été dit avant. La mémoire conversationnelle transforme un simple générateur de texte en un véritable assistant interactif. Dans cet article, nous explorons comment LangChain gère la mémoire, du cas simple au système multi-utilisateurs en production.

Le problème sans mémoire
Chatbot sans mémoire
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
# Conversation
print(llm.invoke("Bonjour, je m'appelle Alice"))
# → "Bonjour Alice ! Comment puis-je vous aider ?"
print(llm.invoke("Quel est mon nom ?"))
# → "Je n'ai pas accès à votre nom." ❌
Chaque appel est isolé, le LLM ne se souvient de rien.
Chatbot avec mémoire
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
llm = ChatOpenAI()
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm=llm,
memory=memory
)
print(conversation.predict(input="Bonjour, je m'appelle Alice"))
# → "Bonjour Alice ! Comment puis-je vous aider ?"
print(conversation.predict(input="Quel est mon nom ?"))
# → "Votre nom est Alice." ✅
La mémoire conserve le contexte entre les échanges.
Types de mémoire dans LangChain
ConversationBufferMemory
Description : Stocke tous les messages de la conversation.
Avantages : Contexte complet, aucune perte d’information Inconvénients : Croissance illimitée, peut dépasser les limites de tokens
from langchain.memory import ConversationBufferMemory
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
# Création de la mémoire
memory = ConversationBufferMemory()
# Chain avec mémoire
llm = ChatOpenAI(model="gpt-4o-mini")
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True # Affiche ce qui est envoyé au LLM
)
# Conversation
conversation.predict(input="Bonjour, je m'appelle Sophie")
conversation.predict(input="J'aime le jazz")
conversation.predict(input="Ma couleur préférée est le bleu")
# Le LLM se souvient de tout
response = conversation.predict(input="Résume ce que tu sais de moi")
# → "Tu t'appelles Sophie, tu aimes le jazz et ta couleur préférée est le bleu."
# Voir l'historique complet
print(memory.load_memory_variables({}))
Quand l’utiliser :
- ✅ Conversations courtes (< 10 échanges)
- ✅ Besoin de contexte complet
- ✅ Prototypage rapide
Éviter si :
- ❌ Conversations longues (> 50 messages)
- ❌ Budget tokens limité
ConversationBufferWindowMemory
Description : Garde seulement les N derniers messages.
Avantages : Taille contrôlée, tokens limités Inconvénients : Perd le contexte ancien
from langchain.memory import ConversationBufferWindowMemory
# Garde seulement les 3 derniers échanges
memory = ConversationBufferWindowMemory(k=3)
conversation = ConversationChain(llm=llm, memory=memory)
# Messages 1-5
conversation.predict(input="Je m'appelle Alice")
conversation.predict(input="J'ai 30 ans")
conversation.predict(input="J'habite à Paris")
conversation.predict(input="Je travaille dans l'informatique")
conversation.predict(input="J'aime la photographie")
# Ne se souvient que des 3 derniers échanges
response = conversation.predict(input="Quel est mon nom ?")
# → "Je ne sais pas" (message trop ancien, oublié)
response = conversation.predict(input="Qu'est-ce que j'aime ?")
# → "Tu aimes la photographie" ✅ (message récent)
Configuration optimale :
# Pour un chatbot de support
memory = ConversationBufferWindowMemory(k=5) # ~10 messages
# Pour une conversation casual
memory = ConversationBufferWindowMemory(k=10) # ~20 messages
# Pour un assistant technique
memory = ConversationBufferWindowMemory(k=3) # Focus sur problème actuel
Quand l’utiliser :
- ✅ Conversations moyennes (10-50 échanges)
- ✅ Context récent plus important que l’ancien
- ✅ Optimisation tokens/coûts
ConversationSummaryMemory
Description : Résume l’historique au lieu de tout stocker.
Avantages : Scalable à l’infini, conserve l’essentiel Inconvénients : Perd les détails, coût de résumé
from langchain.memory import ConversationSummaryMemory
# Création avec résumé automatique
memory = ConversationSummaryMemory(llm=llm)
conversation = ConversationChain(llm=llm, memory=memory)
# Longue conversation
conversation.predict(input="Je m'appelle Marc")
conversation.predict(input="Je suis développeur Python depuis 5 ans")
conversation.predict(input="Je travaille sur des projets d'IA")
conversation.predict(input="J'utilise LangChain pour créer des chatbots")
conversation.predict(input="Mon dernier projet est un assistant RAG")
# Le résumé est automatiquement créé
print(memory.load_memory_variables({}))
# → {
# 'history': "L'utilisateur s'appelle Marc, il est développeur Python
# depuis 5 ans spécialisé en IA. Il utilise LangChain pour créer des
# chatbots, notamment un assistant RAG récemment."
# }
# Le LLM utilise le résumé
response = conversation.predict(input="Parle-moi de mon expérience")
# → Réponse basée sur le résumé (contexte préservé)
Personnalisation du résumé :
from langchain.prompts import PromptTemplate
# Template personnalisé pour le résumé
summary_template = PromptTemplate(
input_variables=["summary", "new_lines"],
template="""
Résume progressivement l'historique en conservant les informations clés.
Résumé actuel:
{summary}
Nouveaux messages:
{new_lines}
Nouveau résumé (concis et factuel):
"""
)
memory = ConversationSummaryMemory(
llm=llm,
prompt=summary_template
)
Quand l’utiliser :
- ✅ Conversations très longues (> 100 messages)
- ✅ Support client sur plusieurs jours
- ✅ Assistants personnels long-terme
ConversationSummaryBufferMemory
Description : Hybride - garde les messages récents + résumé des anciens.
Avantages : Meilleur des deux mondes Inconvénients : Complexité légèrement supérieure
from langchain.memory import ConversationSummaryBufferMemory
# Garde les derniers messages + résumé du reste
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=500 # Limite de tokens pour partie "buffer"
)
conversation = ConversationChain(llm=llm, memory=memory)
# Longue conversation
for i in range(20):
conversation.predict(input=f"Information numéro {i}")
# Structure interne :
# - Résumé : messages 0-15 (condensés)
# - Buffer : messages 16-19 (complets)
print(memory.load_memory_variables({}))
# → {
# 'history': "Résumé: L'utilisateur a partagé des informations
# numérotées de 0 à 15.\n\nMessages récents:\nHuman: Information
# numéro 16\nAI: ...\nHuman: Information numéro 17\n..."
# }
Configuration optimale :
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=1000, # Ajuster selon le modèle
return_messages=True # Retourne des objets Message
)
Quand l’utiliser :
- ✅ Cas par défaut en production (meilleur compromis)
- ✅ Conversations de durée variable
- ✅ Besoin de contexte complet ET récent
ConversationEntityMemory
Description : Extrait et stocke des entités (personnes, lieux, dates).
Avantages : Focus sur informations structurées Inconvénients : Extraction peut rater des infos
from langchain.memory import ConversationEntityMemory
# Mémoire avec extraction d'entités
memory = ConversationEntityMemory(llm=llm)
conversation = ConversationChain(llm=llm, memory=memory)
# Conversation avec entités
conversation.predict(input="Je m'appelle Sophie Martin")
conversation.predict(input="Je travaille chez Microsoft à Paris")
conversation.predict(input="Mon manager s'appelle Jean Dupont")
conversation.predict(input="J'ai rencontré Alice hier au café")
# Entités extraites automatiquement
print(memory.entity_store.store)
# → {
# "Sophie Martin": "L'utilisateur, travaille chez Microsoft à Paris",
# "Microsoft": "Entreprise où travaille l'utilisateur, située à Paris",
# "Jean Dupont": "Manager de l'utilisateur",
# "Alice": "Personne rencontrée par l'utilisateur hier au café",
# "Paris": "Ville où travaille l'utilisateur"
# }
# Utilisation intelligente des entités
response = conversation.predict(input="Parle-moi de Jean")
# → Utilise les informations stockées sur "Jean Dupont"
Quand l’utiliser :
- ✅ CRM et support client (tracking d’informations)
- ✅ Assistants personnels (rappels, contacts)
- ✅ Contextes avec beaucoup de noms/lieux
ConversationKGMemory (Knowledge Graph)
Description : Construit un graphe de connaissances relationnel.
Avantages : Relations complexes, raisonnement Inconvénients : Complexe, coût élevé
from langchain.memory import ConversationKGMemory
# Mémoire avec graphe de connaissances
memory = ConversationKGMemory(llm=llm)
conversation = ConversationChain(llm=llm, memory=memory)
# Conversation avec relations
conversation.predict(input="Alice est la manager de Bob")
conversation.predict(input="Bob travaille sur le projet Phoenix")
conversation.predict(input="Le projet Phoenix utilise Python")
# Graphe construit automatiquement
# Alice --[manager_de]--> Bob
# Bob --[travaille_sur]--> Projet Phoenix
# Projet Phoenix --[utilise]--> Python
# Raisonnement via le graphe
response = conversation.predict(input="Quel langage Alice manage-t-elle ?")
# → "Python (via Bob et le projet Phoenix)"
Quand l’utiliser :
- ✅ Domaines complexes (organisations, projets)
- ✅ Besoin de raisonnement relationnel
- ✅ Budgets élevés (extraction coûteuse)
Comparaison des types de mémoire
| Type | Tokens | Coût | Précision | Scalabilité | Complexité | Cas d’usage |
|---|---|---|---|---|---|---|
| Buffer | ⬆️ Élevé | 💰💰 | ⭐⭐⭐⭐⭐ | ⬇️ Faible | ⭐ Simple | Conversations courtes |
| Window | ➡️ Moyen | 💰 | ⭐⭐⭐⭐ | ➡️ Moyen | ⭐ Simple | Support client |
| Summary | ⬇️ Faible | 💰 | ⭐⭐⭐ | ⬆️ Excellent | ⭐⭐ Moyen | Conversations longues |
| SummaryBuffer | ➡️ Moyen | 💰 | ⭐⭐⭐⭐⭐ | ⬆️ Excellent | ⭐⭐ Moyen | Production (défaut) |
| Entity | ⬇️ Faible | 💰💰 | ⭐⭐⭐⭐ | ⬆️ Excellent | ⭐⭐⭐ Complexe | CRM, assistants |
| KG | ⬇️ Très faible | 💰💰💰 | ⭐⭐⭐⭐⭐ | ⬆️ Excellent | ⭐⭐⭐⭐ Complexe | Graphes relationnels |
Recommandation : ConversationSummaryBufferMemory pour 80% des cas.
Mémoire persistante
Par défaut, la mémoire est en RAM et disparaît au redémarrage. Pour la production, il faut persister.
Avec Redis
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
# Configuration Redis
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id="user-123" # ID unique par utilisateur
)
# Mémoire persistante
memory = ConversationBufferMemory(
chat_memory=message_history,
return_messages=True
)
conversation = ConversationChain(llm=llm, memory=memory)
# Les messages sont automatiquement sauvegardés dans Redis
conversation.predict(input="Bonjour")
conversation.predict(input="Je m'appelle Alice")
# Même après redémarrage, la mémoire est récupérée
# (tant que session_id est le même)
Installation Redis :
# Docker
docker run -d -p 6379:6379 redis
# Ou local
brew install redis # macOS
sudo apt install redis # Linux
Configuration avancée :
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}",
ttl=86400 # Expiration après 24h
)
Avec PostgreSQL
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import PostgresChatMessageHistory
# Configuration PostgreSQL
message_history = PostgresChatMessageHistory(
connection_string="postgresql://user:password@localhost/chatbot",
session_id="user-123"
)
memory = ConversationBufferMemory(chat_memory=message_history)
conversation = ConversationChain(llm=llm, memory=memory)
Créer la table :
CREATE TABLE message_store (
id SERIAL PRIMARY KEY,
session_id TEXT NOT NULL,
message JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_session_id ON message_store(session_id);
Avec MongoDB
from langchain.memory.chat_message_histories import MongoDBChatMessageHistory
message_history = MongoDBChatMessageHistory(
connection_string="mongodb://localhost:27017/",
database_name="chatbot",
collection_name="conversations",
session_id="user-123"
)
memory = ConversationBufferMemory(chat_memory=message_history)
Avec SQLite (simple)
from langchain.memory.chat_message_histories import SQLChatMessageHistory
message_history = SQLChatMessageHistory(
session_id="user-123",
connection_string="sqlite:///chatbot.db"
)
memory = ConversationBufferMemory(chat_memory=message_history)
Avantages SQLite :
- ✅ Aucune dépendance externe
- ✅ Fichier local
- ✅ Parfait pour développement/prototypes
Multi-utilisateurs
Gérer plusieurs utilisateurs simultanément avec mémoire isolée.
Session ID par utilisateur
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
from langchain.chains import ConversationChain
def create_conversation_for_user(user_id: str):
"""Crée une conversation avec mémoire par utilisateur"""
# Mémoire isolée par user_id
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}"
)
memory = ConversationBufferMemory(
chat_memory=message_history,
return_messages=True
)
conversation = ConversationChain(
llm=ChatOpenAI(model="gpt-4o-mini"),
memory=memory
)
return conversation
# Utilisation
alice_chat = create_conversation_for_user("alice-123")
bob_chat = create_conversation_for_user("bob-456")
# Conversations isolées
alice_chat.predict(input="Je m'appelle Alice")
bob_chat.predict(input="Je m'appelle Bob")
# Chaque utilisateur a sa propre mémoire
print(alice_chat.predict(input="Quel est mon nom ?")) # → "Alice"
print(bob_chat.predict(input="Quel est mon nom ?")) # → "Bob"
Cache de conversations
from typing import Dict
from langchain.chains import ConversationChain
class ConversationManager:
"""Gestionnaire de conversations multi-utilisateurs"""
def __init__(self):
self.conversations: Dict[str, ConversationChain] = {}
self.llm = ChatOpenAI(model="gpt-4o-mini")
def get_conversation(self, user_id: str) -> ConversationChain:
"""Récupère ou crée une conversation pour un utilisateur"""
if user_id not in self.conversations:
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}"
)
memory = ConversationBufferMemory(
chat_memory=message_history,
return_messages=True
)
self.conversations[user_id] = ConversationChain(
llm=self.llm,
memory=memory
)
return self.conversations[user_id]
def chat(self, user_id: str, message: str) -> str:
"""Envoie un message et retourne la réponse"""
conversation = self.get_conversation(user_id)
return conversation.predict(input=message)
def clear_conversation(self, user_id: str):
"""Efface l'historique d'un utilisateur"""
if user_id in self.conversations:
self.conversations[user_id].memory.clear()
del self.conversations[user_id]
# Utilisation
manager = ConversationManager()
# Conversations simultanées
response1 = manager.chat("alice", "Bonjour, je m'appelle Alice")
response2 = manager.chat("bob", "Salut, je suis Bob")
response3 = manager.chat("alice", "Quel est mon nom ?") # → "Alice"
response4 = manager.chat("bob", "Comment je m'appelle ?") # → "Bob"
# Nettoyage
manager.clear_conversation("alice")
API REST avec mémoire
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from langserve import add_routes
app = FastAPI()
manager = ConversationManager()
class ChatRequest(BaseModel):
user_id: str
message: str
class ChatResponse(BaseModel):
response: str
user_id: str
@app.post("/chat", response_model=ChatResponse)
async def chat_endpoint(request: ChatRequest):
"""Endpoint de chat avec mémoire par utilisateur"""
try:
response = manager.chat(request.user_id, request.message)
return ChatResponse(
response=response,
user_id=request.user_id
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/chat/{user_id}")
async def clear_chat(user_id: str):
"""Efface l'historique d'un utilisateur"""
manager.clear_conversation(user_id)
return {"status": "cleared", "user_id": user_id}
# Lancer : uvicorn server:app --reload
Client :
import requests
# Conversation Alice
requests.post("http://localhost:8000/chat", json={
"user_id": "alice",
"message": "Bonjour, je m'appelle Alice"
})
# Conversation Bob
requests.post("http://localhost:8000/chat", json={
"user_id": "bob",
"message": "Salut, je suis Bob"
})
# Alice demande son nom
response = requests.post("http://localhost:8000/chat", json={
"user_id": "alice",
"message": "Quel est mon nom ?"
})
print(response.json()) # → {"response": "Alice", ...}
Mémoire avec RAG
Combiner mémoire conversationnelle et récupération de documents.
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
# Setup RAG
vectorstore = FAISS.load_local("./vectorstore", OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
# Mémoire conversationnelle
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
output_key="answer" # Important pour RAG
)
# Chain RAG conversationnel
llm = ChatOpenAI(model="gpt-4")
qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
return_source_documents=True
)
# Conversation avec RAG + mémoire
response1 = qa_chain({"question": "Qu'est-ce que LoRA ?"})
print(response1["answer"])
# Utilise le contexte précédent
response2 = qa_chain({"question": "Quels sont ses avantages ?"})
# → "Les avantages de LoRA sont..." (comprend que "ses" = LoRA)
print(response2["answer"])
# Reformulation grâce à la mémoire
response3 = qa_chain({"question": "Et QLoRA, c'est quoi ?"})
# → Comprend le contexte (fine-tuning, LoRA)
Optimisation pour conversations longues
Résumé automatique
from langchain.memory import ConversationSummaryBufferMemory
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=1000,
moving_summary_buffer="", # Résumé initial vide
)
# Après 100+ messages, le résumé se met à jour automatiquement
for i in range(100):
conversation.predict(input=f"Message {i}")
# Le résumé est maintenu à jour sans intervention
Compression de contexte
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
# Compresseur de contexte
compressor = LLMChainExtractor.from_llm(llm)
# Appliqué à l'historique
def compress_memory(memory: ConversationBufferMemory) -> str:
"""Compresse l'historique de mémoire"""
history = memory.load_memory_variables({})["history"]
compressed = compressor.compress_documents(
documents=[Document(page_content=history)],
query="Résume les points clés de cette conversation"
)
return compressed[0].page_content
# Utilisation périodique
if len(memory.buffer) > 50:
compressed_history = compress_memory(memory)
memory.clear()
memory.save_context(
{"input": "Résumé précédent"},
{"output": compressed_history}
)
Archivage intelligent
import json
from datetime import datetime
class ArchivingMemory:
"""Mémoire avec archivage automatique"""
def __init__(self, llm, archive_threshold=100):
self.memory = ConversationBufferMemory()
self.llm = llm
self.archive_threshold = archive_threshold
self.archives = []
def add_message(self, input_msg: str, output_msg: str):
"""Ajoute un message et archive si nécessaire"""
self.memory.save_context(
{"input": input_msg},
{"output": output_msg}
)
# Archivage automatique
if len(self.memory.buffer) >= self.archive_threshold:
self._archive_and_clear()
def _archive_and_clear(self):
"""Archive l'historique et réinitialise"""
# Créer résumé
summary_prompt = f"""Résume cette conversation en gardant
les informations clés:\n\n{self.memory.buffer}"""
summary = self.llm.invoke(summary_prompt).content
# Archiver
self.archives.append({
"timestamp": datetime.now().isoformat(),
"summary": summary,
"message_count": len(self.memory.buffer)
})
# Sauvegarder
with open("archives.json", "w") as f:
json.dump(self.archives, f, indent=2)
# Réinitialiser avec résumé
self.memory.clear()
self.memory.save_context(
{"input": "Résumé des échanges précédents"},
{"output": summary}
)
Patterns de production
Expiration automatique
from langchain.memory.chat_message_histories import RedisChatMessageHistory
# Mémoire avec expiration après 24h
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}",
ttl=86400 # 24 heures en secondes
)
memory = ConversationBufferMemory(chat_memory=message_history)
# Après 24h, l'historique est automatiquement supprimé
Limite de messages
from langchain.memory import ConversationBufferWindowMemory
class LimitedMemory:
"""Mémoire avec limite stricte de messages"""
def __init__(self, max_messages=50):
self.memory = ConversationBufferWindowMemory(k=max_messages)
self.message_count = 0
def add_message(self, input_msg: str, output_msg: str):
self.memory.save_context(
{"input": input_msg},
{"output": output_msg}
)
self.message_count += 1
# Alerte si limite atteinte
if self.message_count >= 50:
print("⚠️ Limite de messages atteinte. Considérez un résumé.")
Métadonnées enrichies
from langchain.schema import HumanMessage, AIMessage
from datetime import datetime
class EnrichedMemory:
"""Mémoire avec métadonnées"""
def __init__(self):
self.messages = []
def add_message(self, role: str, content: str, metadata: dict = None):
"""Ajoute un message avec métadonnées"""
message_data = {
"role": role,
"content": content,
"timestamp": datetime.now().isoformat(),
"metadata": metadata or {}
}
self.messages.append(message_data)
def get_context(self, user_id: str = None) -> str:
"""Récupère le contexte pour un utilisateur"""
relevant_messages = self.messages
if user_id:
relevant_messages = [
m for m in self.messages
if m["metadata"].get("user_id") == user_id
]
return "\n".join([
f"{m['role']}: {m['content']}"
for m in relevant_messages
])
# Utilisation
memory = EnrichedMemory()
memory.add_message(
role="human",
content="Bonjour",
metadata={"user_id": "alice", "platform": "web"}
)
Cas d’usage pratiques
Support client avec historique
from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryBufferMemory
from langchain.memory.chat_message_histories import PostgresChatMessageHistory
def create_support_chat(ticket_id: str):
"""Chat de support avec historique persistant"""
# Mémoire persistante par ticket
message_history = PostgresChatMessageHistory(
connection_string="postgresql://localhost/support",
session_id=f"ticket-{ticket_id}"
)
memory = ConversationSummaryBufferMemory(
llm=ChatOpenAI(model="gpt-4o-mini"),
chat_memory=message_history,
max_token_limit=1000,
return_messages=True
)
# Prompt spécialisé support
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", """Tu es un agent de support technique.
Historique: {history}
Règles:
- Sois courtois et professionnel
- Propose des solutions concrètes
- Demande des clarifications si besoin
- Escalade si problème complexe"""),
("human", "{input}")
])
conversation = ConversationChain(
llm=ChatOpenAI(model="gpt-4"),
memory=memory,
prompt=prompt
)
return conversation
# Utilisation
support = create_support_chat("TICKET-12345")
# Jour 1
support.predict(input="Mon application ne démarre pas")
support.predict(input="J'ai Windows 11")
# Jour 2 (même ticket, mémoire préservée)
support.predict(input="J'ai essayé de réinstaller mais ça ne marche pas")
# → Le chatbot se souvient du contexte (Windows 11, problème de démarrage)
Assistant personnel
from langchain.memory import ConversationEntityMemory
def create_personal_assistant(user_id: str):
"""Assistant personnel avec extraction d'entités"""
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"assistant-{user_id}",
ttl=604800 # 7 jours
)
memory = ConversationEntityMemory(
llm=ChatOpenAI(model="gpt-4o-mini"),
chat_memory=message_history
)
conversation = ConversationChain(
llm=ChatOpenAI(model="gpt-4"),
memory=memory
)
return conversation
# Utilisation
assistant = create_personal_assistant("alice")
# Apprentissage progressif
assistant.predict(input="Mon anniversaire est le 15 mars")
assistant.predict(input="Ma sœur s'appelle Sophie")
assistant.predict(input="J'ai RDV chez le dentiste jeudi prochain")
# Rappels intelligents
response = assistant.predict(input="Rappelle-moi mes événements importants")
# → Utilise les entités extraites (dates, personnes)
Tuteur éducatif
from langchain.memory import ConversationSummaryMemory
def create_tutor(student_id: str, subject: str):
"""Tuteur avec historique d'apprentissage"""
message_history = PostgresChatMessageHistory(
connection_string="postgresql://localhost/education",
session_id=f"student-{student_id}-{subject}"
)
memory = ConversationSummaryMemory(
llm=ChatOpenAI(model="gpt-4o-mini"),
chat_memory=message_history
)
# Prompt pédagogique
system_prompt = f"""Tu es un tuteur expert en {subject}.
Historique d'apprentissage: {{history}}
Règles:
- Adapte ton niveau selon les réponses de l'étudiant
- Encourage et sois patient
- Utilise des exemples concrets
- Vérifie la compréhension régulièrement
- Rappelle les concepts déjà vus"""
conversation = ConversationChain(
llm=ChatOpenAI(model="gpt-4"),
memory=memory
)
return conversation
# Utilisation sur plusieurs sessions
tutor = create_tutor("student-123", "mathématiques")
# Session 1
tutor.predict(input="Je ne comprends pas les fractions")
# Session 2 (quelques jours plus tard)
tutor.predict(input="Peux-tu m'expliquer les pourcentages ?")
# → Le tuteur se souvient des difficultés précédentes et adapte
Bonnes pratiques
Choisir le bon type de mémoire
# Matrice de décision
def choose_memory(conversation_length: str, budget: str, precision_needed: str):
"""Guide de choix de mémoire"""
if conversation_length == "courte" and precision_needed == "haute":
return ConversationBufferMemory()
if conversation_length == "moyenne" and budget == "limité":
return ConversationBufferWindowMemory(k=5)
if conversation_length == "longue":
return ConversationSummaryBufferMemory(
llm=ChatOpenAI(model="gpt-4o-mini"),
max_token_limit=1000
)
# Par défaut
return ConversationSummaryBufferMemory(
llm=ChatOpenAI(model="gpt-4o-mini"),
max_token_limit=1000
)
Toujours persister en production
# ❌ MAL - Mémoire volatile
memory = ConversationBufferMemory()
# ✅ BIEN - Mémoire persistante
message_history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}"
)
memory = ConversationBufferMemory(chat_memory=message_history)
Isoler par utilisateur
# ❌ MAL - Mémoire partagée
global_memory = ConversationBufferMemory()
# ✅ BIEN - Mémoire par utilisateur
def get_user_memory(user_id: str):
return ConversationBufferMemory(
chat_memory=RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id=f"user-{user_id}"
)
)
Nettoyer périodiquement
from celery import Celery
from datetime import datetime, timedelta
app = Celery('tasks')
@app.task
def cleanup_old_conversations():
"""Tâche périodique de nettoyage"""
cutoff_date = datetime.now() - timedelta(days=30)
# Exemple 1 : Redis
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Scanner toutes les sessions
for key in r.scan_iter("conversation:*"):
last_access = r.hget(key, "last_access")
if last_access:
last_date = datetime.fromisoformat(last_access)
if last_date < cutoff_date:
r.delete(key)
print(f"Supprimé: {key}")
# Exemple 2 : PostgreSQL
# from sqlalchemy import create_engine, text
# engine = create_engine(os.getenv("DATABASE_URL"))
# with engine.connect() as conn:
# result = conn.execute(text("""
# DELETE FROM message_store
# WHERE updated_at < :cutoff_date
# """), {"cutoff_date": cutoff_date})
# print(f"Supprimé {result.rowcount} conversations")
# Exemple 3 : MongoDB
# from pymongo import MongoClient
# client = MongoClient(os.getenv("MONGODB_URI"))
# db = client.langchain
# result = db.conversations.delete_many({
# "updated_at": {"$lt": cutoff_date}
# })
# print(f"Supprimé {result.deleted_count} conversations")
# Lancer tous les jours à 2h du matin
app.conf.beat_schedule = {
'cleanup-daily': {
'task': 'tasks.cleanup_old_conversations',
'schedule': crontab(hour=2, minute=0)
}
}
Monitoring de la mémoire
import logging
class MonitoredMemory:
"""Wrapper avec monitoring"""
def __init__(self, memory):
self.memory = memory
self.logger = logging.getLogger(__name__)
def save_context(self, inputs, outputs):
self.memory.save_context(inputs, outputs)
# Log metrics
buffer_size = len(str(self.memory.buffer))
self.logger.info(f"Memory size: {buffer_size} chars")
if buffer_size > 10000:
self.logger.warning("Memory size exceeds 10K chars")
def load_memory_variables(self, inputs):
return self.memory.load_memory_variables(inputs)
Conclusion
La gestion de la mémoire conversationnelle est fondamentale pour créer des chatbots et assistants IA professionnels. LangChain offre une palette complète d’options, du simple buffer au graphe de connaissances complexe.
Recommandations par cas d’usage
| Cas d’usage | Type de mémoire | Persistance | Notes |
|---|---|---|---|
| Prototype rapide | Buffer | Non | Simplicité maximale |
| Chatbot casual | Window (k=10) | Redis | Bon compromis |
| Support client | SummaryBuffer | PostgreSQL | Traçabilité complète |
| Assistant personnel | Entity | Redis | Extraction d’informations |
| Tuteur éducatif | Summary | PostgreSQL | Historique long-terme |
| CRM/Sales | KG | Neo4j | Relations complexes |
Checklist de production
- Mémoire persistante (Redis/PostgreSQL)
- Isolation par utilisateur (session_id unique)
- Expiration automatique (TTL)
- Limite de taille (max tokens/messages)
- Monitoring (taille mémoire, erreurs)
- Nettoyage périodique (tâches cron)
- Tests de charge (plusieurs utilisateurs)
- Backup des conversations importantes
Pour aller plus loin
- Introduction à LangChain - Concepts fondamentaux
- RAG avec LangChain - Récupération augmentée
- Agents LangChain - Agents autonomes
- LangGraph - Workflows avancés
- LangSmith - Monitoring production
- LangServe - Déploiement APIs
- Guide Pratique - 40+ recettes
La mémoire transforme un générateur de texte en un véritable partenaire conversationnel. Choisissez le bon type, persistez correctement, et vos utilisateurs auront l’impression de parler à quelqu’un qui les comprend vraiment. 🧠
Navigation
← Retour à la Formation LangChain | Module suivant : Testing & Évaluation →