8. État de conversation et mémoire
Jusqu'à présent, chaque interaction avec un LLM que nous avons construite a été sans état (stateless). Sans état signifie que chaque requête est indépendante — le modèle n'a aucune mémoire des conversations précédentes. Cela fonctionne bien pour des tâches ponctuelles comme la synthèse de documents ou les questions-réponses simples.
Mais construire un agent conversationnel est une autre histoire. Vous avez besoin que l'agent se souvienne de ce qui a été discuté précédemment, comprenne les pronoms comme "il" ou "cela", et maintienne le contexte tout au long du dialogue. Pour ce faire, vous devez gérer explicitement l'état (state).
(Ici, "état" fait référence aux informations qu'un programme mémorise. Pour les agents conversationnels, l'historique de conversation précédent est l'état.)
Dans ce chapitre, nous couvrirons :
- Pourquoi les LLM ne "se souviennent" pas des conversations
- Comment implémenter la mémoire de conversation en utilisant les outils d'historique de messages de LangChain
- Comment gérer les budgets de tokens pour éviter le débordement de contexte
8.1) Pourquoi les LLM oublient
Les LLM n'ont pas de mémoire
Les LLM ont une caractéristique critique : ils ne se souviennent de rien des conversations précédentes.
Lorsque vous appelez une API LLM, le modèle traite votre entrée et génère une réponse. Mais il ne stocke cet enregistrement nulle part. Il n'y a pas de mémoire à l'intérieur du modèle qui maintient l'état, pas d'historique de conversation. Chaque appel API est complètement indépendant. C'est comme repartir de zéro à chaque fois.
C'est voulu. Les LLM fonctionnent comme des fonctions sans état : vous fournissez une entrée, ils produisent une sortie, et rien n'est conservé. Le modèle qui s'exécute sur les serveurs d'OpenAI en ce moment n'a aucun enregistrement de ce que vous venez de demander.
Pourquoi les LLM semblent se souvenir
Mais attendez — lorsque vous utilisez ChatGPT ou Claude, on a l'impression qu'ils se souviennent de votre conversation. Vous pouvez dire "Parle-moi de Paris", puis enchaîner avec "Quelle est la population ?" et le modèle sait que vous parlez toujours de Paris. Comment cela fonctionne-t-il ?
Voici comment : l'application envoie l'historique de conversation précédent avec chaque nouveau message.
Voici ce qui se passe réellement :
Le LLM ne "se souvient" pas que vous avez demandé des informations sur Paris plus tôt — il le sait uniquement parce que l'application a envoyé l'historique de conversation précédent avec le nouveau message. En fin de compte, c'est l'application qui gère l'état, pas le modèle.
Pourquoi l'"état" doit être géré dans votre application, pas dans le modèle
Pensez à un LLM comme une fonction pure : étant donné une entrée, il produit une sortie. Il n'y a pas d'état interne que le LLM gère au-delà des messages que vous fournissez. C'est voulu.
Pour les agents conversationnels, cela signifie que l'état doit être géré dans votre application.
L'état — l'historique de conversation — vit dans le code de votre application, pas dans le modèle. Cela signifie que vous êtes responsable de :
- Stocker l'historique de conversation
- Envoyer l'historique pertinent avec chaque nouvelle requête
- Gérer la taille de l'historique (couvert dans la section 8.3)
Dans la section 8.2, nous implémenterons cela en utilisant les outils d'historique de messages de LangChain.
Que se passe-t-il si vous ne gérez pas l'état ?
Si vous ne gérez pas l'état, votre agent (votre application) ne peut pas maintenir une conversation cohérente. Voici les échecs les plus courants :
1. Impossible de se souvenir de la conversation précédente
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini")
# Première question
response1 = llm.invoke([HumanMessage(content="Je m'appelle Alice")])
print(response1.content) # Sortie : Ravi de vous rencontrer, Alice !
# Deuxième question (aucun historique envoyé)
response2 = llm.invoke([HumanMessage(content="Quel est mon nom ?")])
print(response2.content) # Sortie : Je ne connais pas votre nom...Le modèle n'a aucune idée que vous avez dit que votre nom était Alice parce que nous n'avons pas envoyé cette information dans le deuxième appel.
2. Impossible de déterminer à quoi les pronoms font référence
# L'utilisateur pose une question sur un sujet
response1 = llm.invoke([HumanMessage(content="Parle-moi de Python")])
print(response1.content)
# Sortie : Python est un langage de programmation de haut niveau connu pour sa lisibilité...
# L'utilisateur fait un suivi avec un pronom
response2 = llm.invoke([HumanMessage(content="Quelles sont ses principales caractéristiques ?")])
print(response2.content)
# Sortie : Je serais ravi de vous aider ! Pourriez-vous préciser de quoi vous parlez ?Sans le message précédent, le modèle ne peut pas déterminer à quoi "ses" fait référence.
Impact dans le monde réel :
Imaginez construire un agent de support client sans gestion d'état :
Utilisateur : "J'ai un problème avec ma commande #12345"
Agent : "Je suis désolé d'apprendre cela. Quel semble être le problème ?"
Utilisateur : "L'adresse de livraison est incorrecte"
Agent : "Je peux vous aider avec cela. Pourriez-vous fournir votre numéro de commande ?"
Utilisateur : "Je viens de vous le dire..."Cette expérience frustre les utilisateurs et perd leur confiance. La gestion d'état n'est pas optionnelle pour les agents conversationnels — elle est essentielle pour créer des interactions cohérentes et utiles.
Dans la section 8.2, nous implémenterons la gestion d'état en utilisant les outils d'historique de messages de LangChain et ajouterons la mémoire de conversation au chat CLI.
8.2) Gestion de l'état de conversation
Maintenant que nous comprenons pourquoi la gestion d'état est critique, apprenons comment l'implémenter. Nous utiliserons les outils d'historique de messages intégrés de LangChain pour gérer l'historique de conversation. À la fin, nous appliquerons ce que nous avons appris pour ajouter la gestion d'état au chat CLI du Chapitre 3.
Comprendre les types de messages : HumanMessage, AIMessage et SystemMessage
Avant d'apprendre à gérer l'état de conversation, vous devez connaître les types de messages utilisés dans l'état de conversation. LangChain utilise trois types de messages pour représenter les conversations : HumanMessage (entrée utilisateur), AIMessage (réponse du modèle) et SystemMessage (instructions). Chaque message a un rôle (type de message) et un contenu (le texte réel).
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
# SystemMessage : Instructions pour le comportement du modèle
system_msg = SystemMessage(content="Vous êtes un assistant utile spécialisé en programmation Python.")
# HumanMessage : Entrée utilisateur
user_msg = HumanMessage(content="Comment lire un fichier en Python ?")
# AIMessage : Réponse du modèle
# (En pratique, LangChain enveloppe la réponse du modèle dans cet objet - montré ici à titre d'illustration)
ai_msg = AIMessage(content="Vous pouvez utiliser la fonction `open()` avec un gestionnaire de contexte...")Pourquoi des types de messages séparés ?
Pour que le modèle comprenne efficacement l'historique de conversation, il doit connaître le but de chaque message et qui l'a dit. Les trois types de messages servent des objectifs distincts :
- SystemMessage : Instructions qui définissent comment le modèle doit se comporter (par exemple, "Soyez concis", "Vous êtes un tuteur Python")
- HumanMessage : Ce que l'utilisateur a dit
- AIMessage : Ce que le modèle a précédemment répondu
Cette structure permet au modèle de distinguer les instructions, les questions des utilisateurs et ses propres réponses passées — ce qui est essentiel pour maintenir des conversations cohérentes sur plusieurs tours.
Construire l'historique de conversation manuellement
Maintenant que nous comprenons les trois types de messages, voyons comment construire manuellement un historique de conversation :
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
llm = ChatOpenAI(model="gpt-4o-mini")
# Construire l'historique de conversation
# SystemMessage définit les instructions (une fois au début)
# Puis : Entrée utilisateur → Réponse IA → Entrée utilisateur (flux de conversation naturel)
messages = [
SystemMessage(content="Vous êtes un tuteur Python concis."),
HumanMessage(content="Qu'est-ce qu'une compréhension de liste ?"),
AIMessage(content="Une compréhension de liste est une façon concise de créer des listes : [x*2 for x in range(5)]"),
HumanMessage(content="Pouvez-vous me montrer un exemple plus complexe ?")
]
# Envoyer l'historique complet avec la nouvelle question
response = llm.invoke(messages)
print(response.content)Sortie :
Bien sûr ! Voici une compréhension de liste qui filtre et transforme :
[x**2 for x in range(10) if x % 2 == 0]
Cela produit :
[0, 4, 16, 36, 64]Le modèle comprend "un exemple plus complexe" fait référence à un exemple de compréhension de liste plus complexe parce que nous avons envoyé l'historique complet de conversation.
Utilisation de InMemoryChatMessageHistory
Construire des listes de messages manuellement devient fastidieux à mesure que les conversations s'allongent. InMemoryChatMessageHistory de LangChain simplifie cela en fournissant des méthodes pour ajouter des messages et récupérer l'historique complet.
Méthodes clés :
add_message(message): Ajoute un seul message (HumanMessage, AIMessage, SystemMessage)add_messages(messages): Ajoute plusieurs messages à la foismessages: Propriété qui retourne la liste complète des messagesclear(): Supprime tous les messages (utile pour repartir de zéro)
Exemple :
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
# Créer un stockage d'historique de messages
history = InMemoryChatMessageHistory()
# Ajouter plusieurs messages à la fois
history.add_messages([
SystemMessage(content="Vous êtes un tuteur Python utile."),
HumanMessage(content="Qu'est-ce qu'un décorateur en Python ?")
])
# Ajouter des messages un par un
history.add_message(AIMessage(content="Un décorateur est une fonction qui modifie le comportement d'une autre fonction..."))
history.add_message(HumanMessage(content="Pouvez-vous montrer un exemple ?"))
# Récupérer tous les messages
messages = history.messagesExemple pratique : Gestion d'état avec InMemoryChatMessageHistory :
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini")
history = InMemoryChatMessageHistory()
# Définir le message système et l'ajouter à l'historique (fait une fois au début)
system_msg = SystemMessage(content="Vous êtes un tuteur Python utile.")
history.add_message(system_msg)
# Fonction de chat
def chat(user_input):
"""Traite l'entrée utilisateur, l'envoie au LLM et gère automatiquement l'historique de conversation"""
# Ajouter l'entrée utilisateur à l'historique
history.add_message(HumanMessage(content=user_input))
# Envoyer l'entrée utilisateur avec l'historique de conversation précédent
response = llm.invoke(history.messages)
# Ajouter la réponse du modèle à l'historique
history.add_message(response)
return response.content
# Simuler une conversation
print(chat("Qu'est-ce qu'une fonction lambda ?"))
print(chat("Montre-moi un exemple")) # Le modèle se souvient du contexte
print(chat("Quelle est la différence avec une fonction régulière ?")) # Se souvient toujoursSortie :
Une fonction lambda est une fonction anonyme définie avec le mot-clé lambda...
Voici un exemple : square = lambda x: x**2
Vous pouvez l'utiliser comme : square(5) # Retourne 25
Les fonctions lambda sont limitées à une seule expression, tandis que les fonctions régulières...La fonction chat() gère automatiquement la gestion d'état : elle envoie chaque requête utilisateur avec l'historique de conversation précédent au LLM, et ajoute à la fois la requête et la réponse à l'historique. Simplement appeler chat() maintient l'état de conversation sans gestion supplémentaire.
Refactorisation du Chapitre 3 : Ajout de mémoire à votre chat CLI
Prenons le chat CLI en streaming du Chapitre 3 et ajoutons la mémoire de conversation. Voici la version sans état originale :
# chapter3_cli.py (original - sans état)
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini")
def chat_loop():
print("Chat démarré. Tapez 'quit' pour quitter.\n")
while True:
user_input = input("Vous : ")
if user_input.lower() == "quit":
break
# Sans état - pas d'historique
response = llm.stream([HumanMessage(content=user_input)])
print("IA : ", end="", flush=True)
for chunk in response:
print(chunk.content, end="", flush=True)
print("\n")
if __name__ == "__main__":
chat_loop()Version refactorisée avec mémoire :
# chapter8_cli.py (avec mémoire)
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
llm = ChatOpenAI(model="gpt-4o-mini")
def chat_loop():
print("Chat démarré. Tapez 'quit' pour quitter.\n")
# Créer l'historique et ajouter le message système
history = InMemoryChatMessageHistory()
history.add_message(SystemMessage(content="Vous êtes un assistant utile."))
while True:
user_input = input("Vous : ")
if user_input.lower() == "quit":
break
# Ajouter le message utilisateur à l'historique
history.add_message(HumanMessage(content=user_input))
# Diffuser la réponse
print("IA : ", end="", flush=True)
full_response = ""
for chunk in llm.stream(history.messages):
print(chunk.content, end="", flush=True)
full_response += chunk.content
print("\n")
# Ajouter la réponse de l'IA à l'historique
history.add_message(AIMessage(content=full_response))
if __name__ == "__main__":
chat_loop()Ce qui a changé :
- Ajout du stockage d'historique : Création d'une instance
InMemoryChatMessageHistory()à l'intérieur dechat_loop() - Message système : Ajouté à l'historique une fois au début
- Envoi de l'entrée utilisateur avec l'historique :
llm.stream(history.messages)inclut la conversation précédente - Suivi de la conversation : Ajout de l'entrée utilisateur et de la réponse du LLM à l'historique
Test du chat refactorisé :
Chat démarré. Tapez 'quit' pour quitter.
Vous : Je m'appelle Alice
IA : Ravi de vous rencontrer, Alice ! Comment puis-je vous aider aujourd'hui ?
Vous : Quel est mon nom ?
IA : Votre nom est Alice.
Vous : Qu'est-ce que je viens de vous demander ?
IA : Vous m'avez demandé quel était votre nom.
Vous : quitLe modèle maintient maintenant le contexte tout au long de la conversation. Il se souvient de votre nom, des questions précédentes, et peut référencer les parties antérieures du dialogue.
Stockage persistant : Au-delà des options en mémoire
InMemoryChatMessageHistory est pratique pour le développement local, mais passer en production nécessite de le remplacer par une solution de stockage qui garantit la persistance.
Limitations techniques de InMemory :
- RAM volatile : Lorsque le processus serveur se termine ou redémarre, tout l'historique de conversation stocké en mémoire est immédiatement supprimé. Les mises à jour ou la récupération d'erreurs entraînent une perte complète du contexte utilisateur.
- Pas de mise à l'échelle horizontale : À mesure que votre service évolue vers plusieurs instances de serveur, chaque serveur maintient sa propre mémoire isolée. Les utilisateurs se connectant à différents serveurs ne peuvent pas partager l'historique de conversation.
- Inefficacité des ressources : Stocker tout l'historique de conversation en RAM est gourmand en mémoire et menace la stabilité du système à mesure que les utilisateurs simultanés augmentent.
Alternatives professionnelles :
PostgresChatMessageHistory(Recommandé) : Le choix le plus robuste et largement adopté. Utilise PostgreSQL pour le stockage permanent et excelle dans les requêtes complexes et l'analyse de données.SQLChatMessageHistory: Exploite les bases de données SQL existantes comme MySQL. Vous permet d'utiliser votre infrastructure actuelle sans changements.RedisChatMessageHistory: Idéal pour les services où la vitesse de réponse est critique. Basé sur la mémoire avec des options de persistance, spécialisé dans la gestion du trafic élevé.
"Le stockage change, le code reste le même"
LangChain fournit une interface unifiée pour tous les backends de stockage. Les mêmes méthodes que vous avez utilisées avec InMemoryChatMessageHistory — comme add_message() et add_messages() — fonctionnent de manière identique avec d'autres options de stockage. Cela signifie que votre logique métier (code de gestion de conversation) ne nécessite aucun changement lors du changement de stockage.
# [Développement] En mémoire local
# from langchain_core.chat_history import InMemoryChatMessageHistory
# history = InMemoryChatMessageHistory()
# [Production] Stockage persistant PostgreSQL
import psycopg
from langchain_postgres import PostgresChatMessageHistory
# Créer une connexion PostgreSQL
sync_connection = psycopg.connect(
"postgresql://user:password@10.1.1.100:5432/agent_db",
autocommit=True,
)
# Spécifier la connexion DB et l'ID de session
# session_id : Clé unique pour identifier les conversations
# - Gestion par utilisateur : session_id = user_id (une conversation par utilisateur)
# - Gestion par session : session_id = uuid (nouvel ID pour chaque conversation)
history = PostgresChatMessageHistory(
table_name="chat_history",
session_id="user_123",
sync_connection=sync_connection
)
# --- Même interface quel que soit le type de stockage ---
history.add_message(HumanMessage(content="Montrez-moi ma commande précédente."))
print(history.messages)Important : Ce guide utilise InMemory pour une progression rapide, mais les déploiements en production doivent passer à un stockage persistant comme PostgresChatMessageHistory.
8.3) Gestion de la longueur de conversation
La fonctionnalité de mémoire de conversation que nous avons implémentée dans la section précédente a un problème important : elle ajoute uniquement des messages à l'historique. Cela signifie que l'historique continue de croître, ce qui crée deux problèmes majeurs :
- Coût : L'historique complet est envoyé avec chaque requête, donc le coût par requête continue d'augmenter à mesure que l'historique grandit
- Limites de fenêtre de contexte : Les modèles ont une taille d'entrée maximale qu'ils peuvent traiter en une seule requête (par exemple, 400K tokens pour GPT-5). Lorsque l'historique de conversation dépasse cette limite, le modèle ne peut pas traiter correctement la requête.
L'une des solutions les plus simples est le modèle de fenêtre glissante (sliding window).
Modèle de fenêtre glissante (Conserver uniquement les N derniers messages)
Le modèle de fenêtre glissante résout les deux problèmes ci-dessus en conservant uniquement les N messages les plus récents dans l'historique de conversation. Il gère la taille de l'historique en supprimant les anciens messages, offrant les avantages suivants :
- Contrôle des coûts : En maintenant l'historique en dessous d'une certaine taille quelle que soit la longueur de la conversation, il empêche les coûts par requête de croître indéfiniment
- Pas de débordement : Maintient la taille d'entrée dans la limite maximale du modèle
Diagramme conceptuel :
Avec une taille de fenêtre de 4, nous conservons uniquement les 4 messages les plus récents (3, 4, 5, 6) et supprimons les messages plus anciens (1, 2). Lorsqu'un nouveau message (7) arrive, la fenêtre se déplace vers le message le plus récent, supprimant le message le plus ancien (3) et conservant les messages 4, 5, 6, 7.
Compromis de la fenêtre glissante :
- Avantages : Limite la taille de l'historique pour maintenir les coûts constants et empêche le débordement de la fenêtre de contexte
- Inconvénients : Les messages au-delà de la taille de fenêtre sont supprimés, donc le modèle ne peut pas les référencer
Ce compromis peut être problématique. La solution consiste à utiliser une fenêtre glissante pour la conversation récente tout en récupérant les informations passées nécessaires d'un stockage séparé lorsque nécessaire. Cela peut être implémenté en utilisant RAG (Retrieval-Augmented Generation), que nous couvrirons au Chapitre 9.
Unités de taille de fenêtre : Nombre de messages vs nombre de tokens
Le diagramme ci-dessus montre un exemple de définition de la taille de fenêtre par nombre de messages. Cependant, dans les environnements de production, le dimensionnement de fenêtre basé sur les tokens est plus couramment utilisé car les tailles de messages varient :
Réduction basée sur le nombre de messages :
Limite la taille de fenêtre par nombre de messages (par exemple, conserver uniquement les 20 derniers messages).
- Caractéristiques : Nombre de messages fixe, mais le nombre total de tokens peut toujours varier
- À utiliser quand : Les tailles de messages sont contrôlées (SMS, chats à caractères limités)
- Risque : Un long message peut toujours dépasser la fenêtre de contexte
Réduction basée sur le nombre de tokens (Recommandé pour la production) :
Limite la taille de fenêtre par nombre de tokens, l'unité d'entrée traitée par les LLM (par exemple, conserver uniquement les 5 000 derniers tokens).
- Caractéristiques : Ne dépasse jamais la fenêtre de contexte quelle que soit la longueur du message
- À utiliser quand : Les tailles de messages varient
Implémentation de la réduction basée sur les tokens : trim_messages()
LangChain fournit un utilitaire trim_messages() qui implémente le modèle de fenêtre glissante basé sur les tokens.
Comment fonctionne trim_messages() :
Cette fonction prend la liste complète de messages et le nombre maximum de tokens, retournant uniquement les messages les plus récents qui tiennent dans la limite de tokens.
Paramètres clés :
messages: Liste de messages à réduiremax_tokens: Nombre maximum de tokens à maintenirtoken_counter: Fonction pour calculer le nombre de tokens pour chaque message (utilise le tokenizer du modèle pour retourner le nombre de tokens du message)include_system: Si le SystemMessage doit toujours être conservé (généralement True)
Configuration du compteur de tokens :
import tiktoken
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
from langchain_core.messages.utils import trim_messages
# Obtenir le tokenizer pour votre modèle (différentes familles de modèles utilisent différents tokenizers)
# Fournir le nom du modèle pour obtenir le tokenizer approprié
# Modèles Claude : Utiliser le tokenizer d'Anthropic (tiktoken est spécifique à OpenAI)
enc = tiktoken.encoding_for_model("gpt-4o")
def token_counter(msg: BaseMessage) -> int:
"""Compte les tokens dans un seul message."""
return len(enc.encode(msg.content or ""))
# Créer l'historique de conversation
messages = [
SystemMessage(content="Vous êtes un assistant utile."),
HumanMessage(content="Salut !"),
AIMessage(content="Bonjour ! Comment puis-je vous aider ?"),
HumanMessage(content="Combien font 2+2 ?"),
AIMessage(content="2+2 égale 4."),
HumanMessage(content="Combien font 3+3 ?"),
AIMessage(content="3+3 égale 6."),
HumanMessage(content="Combien font 4+4 ?"),
]
# Conserver uniquement les messages dans le nombre maximum de tokens
trimmed = trim_messages(
messages,
max_tokens=30,
token_counter=token_counter,
include_system=True,
)
print(f"Original : {len(messages)} messages")
print(f"Réduit : {len(trimmed)} messages")
for m in trimmed:
print(f"{type(m).__name__}: {m.content}")Note : Le nombre de messages conservés dépend de la valeur max_tokens et du nombre réel de tokens de chaque message. Dans l'exemple ci-dessus, max_tokens=30 est une valeur très petite choisie à des fins de test. En production, vous devriez définir une valeur appropriée en considérant la taille moyenne des messages et la plage de conversation souhaitée.
Exemple : Application de la réduction à la fonction de chat
import tiktoken
from langchain_openai import ChatOpenAI
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.messages import SystemMessage, HumanMessage, BaseMessage
from langchain_core.messages.utils import trim_messages
llm = ChatOpenAI(model="gpt-4o-mini")
history = InMemoryChatMessageHistory()
enc = tiktoken.encoding_for_model("gpt-4o-mini")
def token_counter(msg: BaseMessage) -> int:
"""Compte les tokens dans un seul message."""
return len(enc.encode(msg.content or ""))
def chat_with_trimming(user_input: str, max_tokens: int = 1000) -> str:
"""Chat avec réduction automatique de l'historique."""
history.add_message(HumanMessage(content=user_input))
system_msg = SystemMessage(content="Vous êtes un assistant utile.")
all_messages = [system_msg] + history.messages
# Réduire au nombre maximum de tokens
trimmed_messages = trim_messages(
all_messages,
max_tokens=max_tokens,
token_counter=token_counter,
include_system=True,
)
response = llm.invoke(trimmed_messages)
history.add_message(response)
return response.content
# Exemple d'utilisation
print(chat_with_trimming("Je planifie un voyage au Japon"))
print(chat_with_trimming("Que devrais-je visiter à Tokyo ?"))
print(chat_with_trimming("Combien de jours devrais-je y passer ?"))
# Même si l'historique grandit, seule la conversation récente dans le nombre maximum de tokens est envoyée au LLMProchaine étape : Limitations de la gestion d'état de conversation et solutions (Chapitre 9 : RAG)
Fournir l'historique de conversation au modèle aide à maintenir le contexte de conversation. Cependant, cela seul n'est pas suffisant dans certains cas. Par exemple :
- Lorsque vous devez trouver des informations dans des documents d'entreprise ou des manuels
- Lorsque vous devez référencer l'ancien historique de conversation qui a été poussé hors de la fenêtre glissante
C'est là que RAG (Retrieval-Augmented Generation) est nécessaire. RAG fonctionne comme suit :
- Stockage : Stocker les informations dans une base de données vectorielle pour la recherche sémantique
- Récupération : Interroger les informations avec une signification similaire à ce que vous recherchez
Si vous utilisez RAG pour compléter les limitations du modèle de fenêtre glissante :
- Fenêtre glissante : Conserver les 20 derniers messages (contexte récent)
- RAG : Rechercher et récupérer le contenu pertinent des messages poussés hors de la fenêtre
Plutôt que de simplement se souvenir de la conversation récente, RAG vous permet de créer un système de mémoire à long terme.
RAG permet aux agents d'exploiter des connaissances plus larges et un contexte plus long grâce à l'utilisation de connaissances externes et à la récupération de conversations passées.
Le Chapitre 9 couvrira comment implémenter RAG en détail.
Résumé du chapitre :
Dans ce chapitre, vous avez appris :
- Pourquoi les LLM oublient : Les modèles sont sans état — la mémoire est une illusion créée en renvoyant l'historique de conversation
- Types de messages : SystemMessage (instructions), HumanMessage (entrée utilisateur), AIMessage (réponses du modèle)
- Gestion d'état : Gestion de l'historique de conversation avec
InMemoryChatMessageHistory - Gestion de la longueur de conversation : Pourquoi un historique illimité cause des problèmes de coût et de fenêtre de contexte
- Fenêtres glissantes : Un modèle qui conserve uniquement les messages récents pour empêcher l'historique de croître indéfiniment
- Réduction basée sur les tokens : Implémentation de fenêtres glissantes en utilisant
trim_messages()et le comptage de tokens