Mémoire tampon et mémoire à fenêtre
Implémentez ConversationBufferMemory et ConversationBufferWindowMemory pour conserver les N derniers tours dans le contexte, puis mesurez l’effet de la taille de la fenêtre sur la cohérence et le coût.
Mémoire tampon et mémoire à fenêtre est une leçon AI Engineering Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.
Mémoire tampon : tout conserver
La mémoire tampon est la stratégie la plus simple : stocker chaque message de chaque échange dans une liste et inclure l'historique complet dans chaque appel d'API. Elle préserve un contexte parfait — le modèle peut faire référence à tout ce qui a été dit, à n'importe quel moment. Son inconvénient est une croissance linéaire du contexte, sans limite. Pour les sessions courtes, comme l'accomplissement d'une tâche unique, la mémoire tampon est parfaitement adaptée et la plus facile à implémenter.
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
return_messages=True, # return Message objects, not a string
memory_key='history' # key to inject into prompt template
)
# Add messages manually
memory.chat_memory.add_user_message('What is a neural network?')
memory.chat_memory.add_ai_message('A neural network is a system of layers...')
# Load what will be injected
print(memory.load_memory_variables({}))Intégrer la mémoire tampon à une chaîne
Pour utiliser la mémoire tampon avec LCEL, reliez-la à RunnableWithMessageHistory ou utilisez ConversationChain pour l'approche historique. L'objet mémoire contient l'historique, et la chaîne utilise un MessagesPlaceholder dans le modèle d'invite pour l'y injecter. Après chaque invocation, la mémoire ajoute automatiquement le nouvel échange.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
store = {}
def get_history(session_id: str):
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
chain = (
ChatPromptTemplate.from_messages([
('system', 'You are helpful.'),
MessagesPlaceholder('history'),
('human', '{input}')
])
| ChatOpenAI(model='gpt-4o-mini')
| StrOutputParser()
)
with_memory = RunnableWithMessageHistory(
chain, get_history,
input_messages_key='input',
history_messages_key='history'
)Le problème du tampon illimité
La mémoire tampon fonctionne jusqu'à ce que la conversation devienne trop longue et dépasse la fenêtre de contexte du modèle. Avec le contexte de 128K de GPT-4o-mini, une conversation standard peut durer 200 à 400 échanges avant de déborder. Plus concrètement, même avec 50 échanges, vous envoyez plus de 50K jetons par requête — un coût important. Vous devez adopter une stratégie pour limiter la taille de l'historique.
import tiktoken
enc = tiktoken.encoding_for_model('gpt-4o-mini')
def count_history_tokens(messages: list) -> int:
total = 0
for msg in messages:
total += len(enc.encode(msg.content))
total += 4 # per-message overhead
return total
# Check how big the history has grown
history = store.get('session-1')
if history:
token_count = count_history_tokens(history.messages)
print(f'History size: {len(history.messages)} messages, {token_count} tokens')Mémoire par fenêtre : conserver les N derniers échanges
La mémoire par fenêtre ne conserve que les K derniers échanges de la conversation et supprime les messages plus anciens. Elle limite le contexte à K * avg_tokens_per_turn, quelle que soit la durée de la conversation. En contrepartie, le contexte des débuts est perdu — le modèle peut oublier des éléments mentionnés par l'utilisateur de nombreux échanges auparavant. Pour la plupart des agents conversationnels généralistes, une fenêtre de 5 à 10 échanges offre une bonne cohérence à un coût acceptable.
from langchain.memory import ConversationBufferWindowMemory
# Keep last 5 turns (10 messages: 5 user + 5 assistant)
memory = ConversationBufferWindowMemory(
k=5, # number of TURNS to keep (each turn = user + AI)
return_messages=True,
memory_key='history'
)
# After 10 turns, only turns 6-10 will be in context
# Turns 1-5 are silently droppedImplémenter une mémoire par fenêtre avec InMemoryChatMessageHistory
Le InMemoryChatMessageHistory de LangChain conserve tous les messages, mais vous pouvez réduire l'historique avant de l'injecter dans l'invite. Une approche courante consiste à conserver l'historique complet pour la journalisation, mais à ne transmettre au LLM que les N derniers messages. Utilisez la notation de tranche de Python sur history.messages pour obtenir la fenêtre la plus récente.
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables import RunnableLambda
WINDOW_SIZE = 10 # last 10 messages (5 turns)
def get_windowed_history(session_id: str):
full_history = store.get(session_id, InMemoryChatMessageHistory())
store[session_id] = full_history
return full_history
# In the chain, trim before injecting
def trim_history(messages):
return messages[-WINDOW_SIZE:] if len(messages) > WINDOW_SIZE else messages
# Use in prompt with trimming
from langchain_core.messages import trim_messages
trimmer = trim_messages(
max_tokens=2000,
strategy='last',
token_counter=ChatOpenAI(model='gpt-4o-mini'),
include_system=True
)Fenêtre fondée sur les jetons ou sur les échanges
La taille de la fenêtre peut être définie en échanges (les K dernières paires humain/IA) ou en jetons (les T derniers jetons de l'historique). La fenêtre fondée sur les jetons est plus fiable, car la longueur des échanges varie — un échange contenant du code est 10 fois plus long qu'un échange contenant « oui ». L'utilitaire trim_messages() de LangChain prend en charge les deux stratégies et peut préserver l'invite système tout en réduisant l'historique.
from langchain_core.messages import trim_messages, SystemMessage, HumanMessage, AIMessage
# Token-based trimming — keep last 1000 tokens of conversation
trimmer = trim_messages(
max_tokens=1000,
strategy='last', # keep most recent messages
token_counter=len, # approximate: count characters / 4
include_system=True, # always include the system prompt
allow_partial=False, # don't split a message in half
start_on='human' # start window on a human message
)
trimmed = trimmer.invoke(all_messages)
print(f'Trimmed to {len(trimmed)} messages')Mesurer la cohérence en fonction de la taille de la fenêtre
Choisir la bonne taille de fenêtre nécessite de mesurer la dégradation de la cohérence de la conversation à mesure que la fenêtre rétrécit. Exécutez une série de conversations de test dans lesquelles l'échange N fait référence à des informations des échanges N-5, N-10 et N-20. Vérifiez si le modèle peut encore répondre correctement avec des fenêtres de 5, 10 et 20 échanges. Le résultat vous donne la taille minimale de fenêtre nécessaire pour votre cas d'utilisation précis.
def test_reference_at_distance(chain_with_memory, distances=[5, 10, 20]):
results = {}
for distance in distances:
session_id = f'test-dist-{distance}'
# Fill with filler turns
for i in range(distance):
chain_with_memory.invoke(
{'input': f'Turn {i}: filler message'},
config={'configurable': {'session_id': session_id}}
)
# Ask about something said at the start
first_msg = 'Recall that the user said the magic word is AZURE.'
response = chain_with_memory.invoke(
{'input': 'What was the magic word?'},
config={'configurable': {'session_id': session_id}}
)
results[distance] = 'AZURE' in response.upper()
return resultsCombiner l'invite système avec la mémoire par fenêtre
Lorsque vous utilisez une mémoire par fenêtre, préservez toujours l'invite système — elle définit la persona et les règles de l'IA. Sans elle, le modèle peut perdre sa persona une fois que la fenêtre a dépassé le premier échange. Utilisez l'option include_system=True dans les fonctions de réduction, ou insérez toujours le message système avant l'historique réduit dans votre modèle d'invite.
# Always put system prompt BEFORE the windowed history
prompt = ChatPromptTemplate.from_messages([
('system', 'You are a Python tutor. Always explain with code examples.'),
MessagesPlaceholder('history'), # windowed history injected here
('human', '{input}'),
])
# The system prompt is never trimmed — only the history window is managed
# This ensures the model's persona is always present regardless of windowGuide de choix entre mémoire tampon et mémoire par fenêtre
Utilisez la mémoire tampon lorsque les conversations sont courtes et limitées (tâche ponctuelle, assistant de formulaire), que le contexte complet est essentiel (analyse juridique, révision de code) et que le budget de jetons n'est pas un problème. Utilisez la mémoire par fenêtre lorsque les conversations peuvent être arbitrairement longues (assistance clientèle, assistants généralistes), que le contexte récent est plus important que le contexte éloigné et que vous avez besoin de coûts en jetons prévisibles et limités.
# Decision matrix in code
def choose_memory_strategy(
expected_turns: int,
max_context_tokens: int = 128000,
avg_tokens_per_turn: int = 200
) -> str:
buffer_tokens = expected_turns * avg_tokens_per_turn
if buffer_tokens < max_context_tokens * 0.5:
return 'buffer' # Safe to keep everything
elif expected_turns <= 20:
return 'window_10' # Keep last 10 turns
else:
return 'summary' # Need summarization for long convos
print(choose_memory_strategy(5)) # 'buffer'
print(choose_memory_strategy(50)) # 'window_10'
print(choose_memory_strategy(200)) # 'summary'Conserver la fenêtre dans Redis
Pour une utilisation en production, stockez l'historique complet de la conversation dans Redis (pour une récupération rapide), puis réduisez-le à la taille de la fenêtre lors de la lecture. Redis, associé à un TTL, garantit l'expiration automatique des anciennes sessions. Utilisez un ensemble trié ou une liste avec LRANGE pour récupérer efficacement les N derniers messages sans charger tout l'historique.
from langchain_community.chat_message_histories import RedisChatMessageHistory
def get_windowed_redis_history(session_id: str, window: int = 10):
# RedisChatMessageHistory stores all messages
history = RedisChatMessageHistory(
session_id=session_id,
url='redis://localhost:6379',
ttl=3600 # 1 hour TTL
)
# Trim to window size in-memory before use
all_msgs = history.messages
if len(all_msgs) > window * 2: # window turns = window*2 messages
history.messages = all_msgs[-(window * 2):]
return historySurveiller l'état de la mémoire en production
Suivez ces métriques en production pour détecter les problèmes liés à la mémoire : nombre moyen de jetons d'historique par requête pour repérer les sessions qui deviennent trop volumineuses, utilisation de la fenêtre de contexte (émettez un avertissement au-delà de 80 %), nombre de sessions dans le stockage pour détecter les fuites de mémoire dans le stockage des sessions, et taux de succès du cache pour les sessions rechargées depuis Redis. Déclenchez une alerte lorsque le nombre moyen de jetons d'historique dépasse un seuil configurable.
import time
from langchain_core.callbacks import BaseCallbackHandler
class MemoryMonitorCallback(BaseCallbackHandler):
def on_chain_start(self, serialized, inputs, **kwargs):
history = inputs.get('history', [])
token_estimate = sum(len(m.content.split()) * 1.3 for m in history)
if token_estimate > 50000:
print(f'WARNING: Large history {token_estimate:.0f} estimated tokens')
def on_chain_end(self, outputs, **kwargs):
# Log usage for dashboards
passVérification rapide
Vérifiez votre compréhension des stratégies de mémoire tampon et de mémoire par fenêtre.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris que : la mémoire tampon préserve l'historique complet, mais croît sans limite, ce qui la réserve aux conversations courtes et limitées ; la mémoire par fenêtre ne conserve que les K derniers échanges afin d'obtenir un coût prévisible et limité, au prix de la perte du contexte éloigné ; et la réduction fondée sur les jetons avec trim_messages() est plus fiable que les fenêtres fondées sur les échanges, car la longueur des messages varie. Nous allons maintenant étudier la mémoire par résumé pour les conversations longues et ouvertes.
Questions Fréquemment Posées
La leçon « Mémoire tampon et mémoire à fenêtre » est-elle gratuite ?
Oui — le texte complet de « Mémoire tampon et mémoire à fenêtre » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Mémoire tampon et mémoire à fenêtre » ?
Implémentez ConversationBufferMemory et ConversationBufferWindowMemory pour conserver les N derniers tours dans le contexte, puis mesurez l’effet de la taille de la fenêtre sur la cohérence et le coû… Tu pratiques AI Engineering Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Engineering Academy ?
Aucune expérience préalable n'est requise. AI Engineering Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Mémoire tampon et mémoire à fenêtre » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Engineering Academy ?
Oui. Chaque leçon AI Engineering Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Pourquoi les LLM sans état ont besoin d’une mémoire externe
- Mémoire tampon et mémoire à fenêtre
- Mémoire par résumé et troncature tenant compte des tokens
- Conserver l’historique des conversations dans Redis et PostgreSQL