Memoria de buffer y de ventana
Implementará ConversationBufferMemory y ConversationBufferWindowMemory para mantener en el contexto los últimos N turnos, y medirá cómo afecta el tamaño de la ventana a la coherencia y el coste.
Memoria de buffer y de ventana es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.
Memoria de búfer: conservarlo todo
La memoria de búfer es la estrategia más sencilla: almacena en una lista todos los mensajes de cada turno e incluye el historial completo en cada llamada a la API. Conserva el contexto íntegro: el modelo puede hacer referencia a cualquier cosa que se haya dicho en cualquier momento. La desventaja es que el contexto crece linealmente y no tiene un límite. Para sesiones cortas, como completar una única tarea, la memoria de búfer es perfectamente adecuada y la más fácil de implementar.
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({}))Integrar la memoria de búfer con una cadena
Para usar la memoria de búfer con LCEL, conéctela mediante RunnableWithMessageHistory o use ConversationChain como enfoque heredado. El objeto de memoria contiene el historial y la cadena utiliza un MessagesPlaceholder en la plantilla del prompt para inyectarlo. Después de cada invocación, la memoria añade automáticamente el nuevo turno.
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'
)El problema de un búfer ilimitado
La memoria de búfer funciona hasta que la conversación crece demasiado y supera la ventana de contexto del modelo. Con el contexto de 128K de GPT-4o-mini, un chat típico podría durar entre 200 y 400 turnos antes de desbordarse. De forma más práctica, incluso con 50 turnos, estaría enviando más de 50K tokens por solicitud, lo que supone un coste considerable. Necesita una estrategia para limitar el tamaño del historial.
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 de ventana: conservar los últimos N turnos
La memoria de ventana conserva únicamente los últimos K turnos de la conversación y descarta los mensajes anteriores. Esto limita el contexto a K * avg_tokens_per_turn, independientemente de la duración de la conversación. La contrapartida es que se pierde el contexto más antiguo: el modelo puede olvidar cosas que el usuario dijo muchos turnos atrás. Para la mayoría de los chatbots de propósito general, una ventana de entre 5 y 10 turnos proporciona una buena coherencia con un coste aceptable.
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 droppedImplementar la memoria de ventana con InMemoryChatMessageHistory
InMemoryChatMessageHistory de LangChain conserva todos los mensajes, pero puede recortar el historial antes de inyectarlo en el prompt. Un patrón habitual consiste en almacenar el historial completo para el registro, pero pasar al LLM únicamente los últimos N mensajes. Use la notación de porciones de Python sobre history.messages para obtener la ventana más reciente.
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
)Ventana basada en tokens frente a ventana basada en turnos
El tamaño de la ventana puede especificarse en turnos (los últimos K pares de mensajes humano/IA) o en tokens (los últimos T tokens del historial). La ventana basada en tokens es más fiable porque la duración de los turnos varía: un turno con código es 10 veces más largo que uno con «sí». La utilidad trim_messages() de LangChain admite ambas estrategias y puede conservar el prompt del sistema mientras recorta el historial.
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')Medir la coherencia frente al tamaño de la ventana
Para elegir el tamaño adecuado de la ventana, debe medir cómo se degrada la coherencia de la conversación a medida que se reduce dicho tamaño. Ejecute varias conversaciones de prueba en las que el turno N haga referencia a información de los turnos N-5, N-10 y N-20. Compruebe si el modelo todavía puede responder correctamente con ventanas de 5, 10 y 20 turnos. El resultado le proporcionará el tamaño mínimo de ventana necesario para su caso de uso específico.
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 resultsCombinar el prompt del sistema con la memoria de ventana
Al usar memoria de ventana, conserve siempre el prompt del sistema, ya que define la personalidad y las reglas de la IA. Sin él, el modelo puede perder su personalidad cuando la ventana deje atrás el primer turno. Use la opción include_system=True en las funciones de recorte o inserte siempre el mensaje del sistema antes del historial de ventana en la plantilla del 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 windowGuía para decidir entre memoria de búfer y de ventana
Use la memoria de búfer cuando las conversaciones sean cortas y acotadas (una tarea puntual o un asistente para completar formularios), el contexto completo sea esencial (análisis jurídico o revisión de código) y el presupuesto de tokens no sea una preocupación. Use la memoria de ventana cuando las conversaciones puedan alargarse indefinidamente (atención al cliente o asistentes generales), el contexto reciente sea más importante que el distante y necesite costes de tokens previsibles y limitados.
# 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'Persistir la ventana en Redis
Para uso en producción, almacene el historial completo de la conversación en Redis (para recuperarlo rápidamente) y recórtelo al tamaño de la ventana en el momento de la lectura. Redis, junto con un TTL, garantiza que las sesiones antiguas caduquen automáticamente. Use un conjunto ordenado o una lista con LRANGE para recuperar eficazmente solo los últimos N mensajes sin cargar todo el historial.
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 historySupervisar la salud de la memoria en producción
Realice un seguimiento de estas métricas en producción para detectar problemas relacionados con la memoria: promedio de tokens del historial por solicitud, para detectar sesiones que crecen demasiado; utilización de la ventana de contexto (emita una advertencia si supera el 80 %); número de sesiones almacenadas, para detectar fugas de memoria en el almacén de sesiones; y tasa de aciertos de la caché para las sesiones recargadas desde Redis. Genere una alerta cuando el promedio de tokens del historial supere un umbral 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
passComprobación rápida
Compruebe que entiende las estrategias de memoria de búfer y de ventana.
Resumen de la lección
En esta lección ha aprendido lo siguiente: la memoria de búfer conserva el historial completo, pero crece sin límite, por lo que solo resulta adecuada para conversaciones cortas y acotadas; la memoria de ventana conserva únicamente los últimos K turnos para ofrecer un coste previsible y limitado, a cambio de perder el contexto distante; y el recorte basado en tokens con trim_messages() es más fiable que las ventanas basadas en turnos porque la longitud de los mensajes varía. A continuación analizaremos la memoria de resumen para conversaciones largas y abiertas.
Preguntas frecuentes
¿La lección «Memoria de buffer y de ventana» es gratis?
Sí — el texto completo de «Memoria de buffer y de ventana» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Memoria de buffer y de ventana»?
Implementará ConversationBufferMemory y ConversationBufferWindowMemory para mantener en el contexto los últimos N turnos, y medirá cómo afecta el tamaño de la ventana a la coherencia y el coste. Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Engineering Academy?
No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Memoria de buffer y de ventana»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?
Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Por qué los LLM sin estado necesitan memoria externa
- Memoria de buffer y de ventana
- Memoria de resumen y truncamiento consciente de los tokens
- Persistencia del historial de chat en Redis y PostgreSQL