Section 11.4.10 : Chatbot — Gestion du contexte et mémoire
🎯 Objectif pédagogique
Implémenter la gestion du contexte et de la mémoire dans un chatbot : historique de conversation, résumé, mémoire long-terme, et stratégies de truncation. Vous serez capable de construire un chatbot qui "se souvient" des échanges passés.
Le problème de la mémoire
Marc discute avec son chatbot depuis 30 minutes. Il a partagé son CV, discuté de 3 offres, et expliqué ses objectifs. Mais les LLMs n'ont pas de mémoire entre les appels API — chaque appel est indépendant. Comment donner au chatbot l'illusion de la mémoire ?
Stratégie 1 : Historique complet (simple mais limité)
class SimpleChatbot:
"""Chatbot avec historique complet en mémoire."""
def __init__(self, system_prompt):
self.system_prompt = system_prompt
self.history = [] # Liste de messages
def chat(self, user_message):
self.history.append(\{"role": "user", "content": user_message\})
messages = [
\{"role": "system", "content": self.system_prompt\}
] + self.history
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
assistant_msg = response.choices[0].message.content
self.history.append(\{"role": "assistant", "content": assistant_msg\})
return assistant_msg
# Problème : après 50 messages, l'historique dépasse la fenêtre de contexte
# et les coûts explosent (on re-envoie TOUT l'historique à chaque appel)
Stratégie 2 : Fenêtre glissante (sliding window)
class SlidingWindowChatbot:
"""Chatbot avec fenêtre de contexte limitée."""
def __init__(self, system_prompt, max_messages=20):
self.system_prompt = system_prompt
self.history = []
self.max_messages = max_messages
def chat(self, user_message):
self.history.append(\{"role": "user", "content": user_message\})
# Ne garder que les N derniers messages
recent_history = self.history[-self.max_messages:]
messages = [
\{"role": "system", "content": self.system_prompt\}
] + recent_history
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
assistant_msg = response.choices[0].message.content
self.history.append(\{"role": "assistant", "content": assistant_msg\})
return assistant_msg
# Mieux ! Mais on perd les premiers messages (ex: le CV partagé au début)
Stratégie 3 : Résumé + récent (la meilleure approche)
class SmartChatbot:
"""Chatbot avec résumé progressif + historique récent."""
def __init__(self, system_prompt, max_recent=10, summary_threshold=15):
self.system_prompt = system_prompt
self.history = []
self.summary = ""
self.max_recent = max_recent
self.summary_threshold = summary_threshold
def _summarize_old_messages(self, messages):
"""Résumer les anciens messages pour économiser des tokens."""
conversation_text = ""
for msg in messages:
role = "Utilisateur" if msg["role"] == "user" else "Assistant"
conversation_text += f"\{role\}: \{msg['content']\}\n"
summary_response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[\{
"role": "user",
"content": f"""Résume cette conversation en 3-5 bullet points.
Garde les infos clés : préférences de l'utilisateur, décisions prises, infos partagées.
Conversation :
\{conversation_text\}
Résumé concis :"""
\}],
max_tokens=300
)
return summary_response.choices[0].message.content
def chat(self, user_message):
self.history.append(\{"role": "user", "content": user_message\})
# Si l'historique dépasse le seuil, résumer les vieux messages
if len(self.history) > self.summary_threshold:
old_messages = self.history[:-self.max_recent]
self.summary = self._summarize_old_messages(old_messages)
self.history = self.history[-self.max_recent:]
# Construire le contexte
context_parts = [self.system_prompt]
if self.summary:
context_parts.append(f"\n## Résumé de la conversation précédente :\n\{self.summary\}")
messages = [
\{"role": "system", "content": "\n".join(context_parts)\}
] + self.history
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
assistant_msg = response.choices[0].message.content
self.history.append(\{"role": "assistant", "content": assistant_msg\})
return assistant_msg
Stratégie 4 : Mémoire long-terme (persistence)
import json
from pathlib import Path
class PersistentMemory:
"""Mémoire long-terme stockée sur disque."""
def __init__(self, user_id, memory_dir="data/memories"):
self.user_id = user_id
self.memory_path = Path(memory_dir) / f"\{user_id\}.json"
self.memory_path.parent.mkdir(parents=True, exist_ok=True)
self.data = self._load()
def _load(self):
if self.memory_path.exists():
return json.loads(self.memory_path.read_text(encoding='utf-8'))
return \{"profile": \{\}, "preferences": \{\}, "history_summaries": [], "facts": []\}
def save(self):
self.memory_path.write_text(
json.dumps(self.data, ensure_ascii=False, indent=2),
encoding='utf-8'
)
def update_profile(self, key, value):
"""Mettre à jour le profil utilisateur."""
self.data["profile"][key] = value
self.save()
def add_fact(self, fact):
"""Ajouter un fait important à retenir."""
self.data["facts"].append(fact)
self.save()
def get_context(self):
"""Retourner le contexte pour le system prompt."""
context = ""
if self.data["profile"]:
context += "## Profil utilisateur :\n"
for k, v in self.data["profile"].items():
context += f"- \{k\}: \{v\}\n"
if self.data["facts"]:
context += "\n## Faits importants :\n"
for fact in self.data["facts"][-10:]: # 10 derniers faits
context += f"- \{fact\}\n"
return context
# Utilisation combinée
memory = PersistentMemory("marc")
memory.update_profile("background", "8 ans finance")
memory.update_profile("objectif", "Data Analyst")
memory.add_fact("Préfère le remote ou hybride")
memory.add_fact("Salaire minimum: 45k€")
# Injecter dans le system prompt
system = SYSTEM_PROMPT + "\n" + memory.get_context()
Comparaison des stratégies
| Stratégie | Coût tokens | Mémoire | Complexité | Best for |
|---|---|---|---|---|
| Historique complet | 📈 Croissant | Tout | Simple | Conversations courtes (moins de 20 msgs) |
| Sliding window | 📊 Constant | Récent seulement | Simple | Conversations moyennes |
| Résumé + récent | 📊 Constant | Résumé + récent | Moyenne | Longues conversations |
| Mémoire persistante | 📊 Constant | Long-terme | Complexe | Assistants personnels |
En production : combinez les stratégies
Les meilleurs chatbots combinent : mémoire persistante (profil, préférences) + résumé de la session + historique récent (10 messages). C'est la stack de mémoire complète : long-terme + moyen-terme + court-terme. Exactement comme la mémoire humaine.
🏋️ Exercice pratique (25 minutes)
- →Implémentez le
SmartChatbotavec résumé progressif - →Testez une conversation de 20+ messages — le résumé se déclenche-t-il ?
- →Implémentez la
PersistentMemoryet vérifiez que le chatbot se souvient entre les sessions - →Comparez la qualité des réponses avec et sans contexte mémoire
Pourquoi un chatbot ne peut-il pas simplement garder tout l'historique en mémoire ?
Quelle est la meilleure stratégie de mémoire pour un assistant personnel utilisé quotidiennement ?