Memoria a buffer e a finestra
Implementerà ConversationBufferMemory e ConversationBufferWindowMemory per mantenere nel contesto gli ultimi N turni e misurerà l'effetto della dimensione della finestra su coerenza e costo.
Memoria a buffer e a finestra è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.
Memoria a buffer: conservare tutto
La memoria a buffer è la strategia più semplice: memorizza in un elenco ogni messaggio di ogni scambio e include la cronologia completa in ogni chiamata API. Conserva il contesto completo: il modello può fare riferimento a qualsiasi elemento pronunciato in qualunque momento. Lo svantaggio è una crescita lineare e senza limiti del contesto. Per sessioni brevi, come il completamento di una singola attività, la memoria a buffer è perfettamente adatta e costituisce l'implementazione più semplice.
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({}))Integrazione della memoria a buffer con una catena
Per utilizzare la memoria a buffer con LCEL, la colleghi tramite RunnableWithMessageHistory oppure utilizzi ConversationChain per l'approccio precedente. L'oggetto di memoria contiene la cronologia e la catena utilizza un MessagesPlaceholder nel modello di prompt per inserirla. Dopo ogni invocazione, la memoria aggiunge automaticamente il nuovo scambio.
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'
)Il problema del buffer illimitato
La memoria a buffer funziona finché la conversazione non diventa troppo lunga e supera la finestra di contesto del modello. Con la finestra di contesto da 128K di GPT-4o-mini, una chat tipica potrebbe durare 200–400 scambi prima di traboccare. Più concretamente, già con 50 scambi si inviano oltre 50K token per richiesta: un costo significativo. È quindi necessario limitare le dimensioni della cronologia.
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')Memoria a finestra: conservare gli ultimi N scambi
La memoria a finestra conserva solo gli ultimi K scambi della conversazione e scarta i messaggi precedenti. In questo modo il contesto è limitato a K * avg_tokens_per_turn, indipendentemente dalla durata della conversazione. Lo svantaggio è la perdita del contesto iniziale: il modello potrebbe dimenticare informazioni comunicate dall'utente molti scambi prima. Per la maggior parte dei chatbot generici, una finestra di 5–10 scambi offre una buona coerenza a un costo accettabile.
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 droppedImplementazione della memoria a finestra con InMemoryChatMessageHistory
InMemoryChatMessageHistory di LangChain conserva tutti i messaggi, ma può ridurre la cronologia prima di inserirla nel prompt. Un approccio comune consiste nel conservare la cronologia completa per la registrazione, passando però all'LLM solo gli ultimi N messaggi. Utilizzi la notazione delle sezioni di Python su history.messages per ottenere la finestra più recente.
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
)Finestra basata sui token o sugli scambi
La dimensione della finestra può essere specificata in scambi (gli ultimi K abbinamenti umano/IA) oppure in token (gli ultimi T token della cronologia). La finestra basata sui token è più affidabile, perché la lunghezza degli scambi varia: uno scambio con del codice è 10 volte più lungo di uno con «sì». L'utility trim_messages() di LangChain supporta entrambe le strategie e può preservare il prompt di sistema durante la riduzione della cronologia.
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')Misurazione della coerenza in base alla dimensione della finestra
Per scegliere la dimensione corretta della finestra, è necessario misurare come la coerenza della conversazione peggiora quando la finestra si riduce. Esegua una serie di conversazioni di test in cui lo scambio N fa riferimento a informazioni degli scambi N-5, N-10 e N-20. Verifichi se il modello riesce ancora a rispondere correttamente con finestre di 5, 10 e 20 scambi. Il risultato indica la dimensione minima della finestra necessaria per il caso d'uso specifico.
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 resultsCombinazione del prompt di sistema con la memoria a finestra
Quando utilizza la memoria a finestra, preservi sempre il prompt di sistema: definisce la personalità e le regole dell'IA. Senza di esso, il modello potrebbe perdere la propria personalità quando la finestra supera il primo scambio. Utilizzi l'opzione include_system=True nelle funzioni di riduzione oppure inserisca sempre il messaggio di sistema prima della cronologia sottoposta a finestra nel modello di prompt.
# 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 windowGuida alla scelta tra memoria a buffer e a finestra
Utilizzi la memoria a buffer quando: le conversazioni sono brevi e delimitate (un'attività una tantum, una procedura guidata per moduli), il contesto completo è fondamentale (analisi legale, revisione del codice) e il budget dei token non è un problema. Utilizzi la memoria a finestra quando: le conversazioni possono avere una durata arbitraria (assistenza clienti, assistenti generici), il contesto recente è più importante di quello lontano e sono necessari costi in token prevedibili e limitati.
# 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'Persistenza della finestra in Redis
In produzione, memorizzi la cronologia completa della conversazione in Redis (per recuperarla rapidamente) e la riduca alla dimensione della finestra al momento della lettura. Redis, insieme a un TTL, garantisce la scadenza automatica delle sessioni obsolete. Utilizzi un insieme ordinato oppure un elenco con LRANGE per recuperare in modo efficiente solo gli ultimi N messaggi senza caricare l'intera cronologia.
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 historyMonitoraggio dello stato della memoria in produzione
In produzione, monitori queste metriche per individuare i problemi legati alla memoria: media dei token della cronologia per richiesta, per rilevare sessioni che diventano troppo grandi; utilizzo della finestra di contesto (emetta un avviso oltre l'80%); numero di sessioni nell'archivio, per rilevare perdite di memoria nell'archivio delle sessioni; e percentuale di riscontri nella cache per le sessioni ricaricate da Redis. Generi un avviso quando la media dei token della cronologia supera una soglia configurabile.
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
passVerifica rapida
Verifichi di aver compreso le strategie di memoria a buffer e a finestra.
Riepilogo della lezione
In questa lezione ha imparato che: la memoria a buffer conserva la cronologia completa, ma cresce senza limiti, quindi è adatta solo a conversazioni brevi e delimitate; la memoria a finestra conserva solo gli ultimi K scambi, offrendo costi prevedibili e limitati a scapito del contesto lontano; e la riduzione basata sui token con trim_messages() è più affidabile delle finestre basate sugli scambi, perché la lunghezza dei messaggi varia. Ora analizzeremo la memoria con riepilogo per conversazioni lunghe e aperte.
Domande Frequenti
La lezione «Memoria a buffer e a finestra» è gratuita?
Sì — il testo completo di «Memoria a buffer e a finestra» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.
Cosa imparerò in «Memoria a buffer e a finestra»?
Implementerà ConversationBufferMemory e ConversationBufferWindowMemory per mantenere nel contesto gli ultimi N turni e misurerà l'effetto della dimensione della finestra su coerenza e costo. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Engineering Academy?
Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Memoria a buffer e a finestra»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?
Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Perché gli LLM stateless hanno bisogno di memoria esterna
- Memoria a buffer e a finestra
- Memoria basata sui riepiloghi e troncamento consapevole dei token
- Persistenza della cronologia chat in Redis e PostgreSQL