Saltar al contenido principal

Memoria de agentes

Definición

La memoria de agentes se refiere a los mecanismos mediante los cuales un agente de IA almacena, indexa y recupera información durante su operación. Sin memoria, cada interacción comienza desde cero — el agente no puede aprender de conversaciones pasadas, acumular hechos ni rastrear el estado de una tarea de larga duración. La memoria transforma una llamada LLM sin estado en un sistema persistente y orientado a objetivos.

En las ciencias cognitivas, la memoria se divide en varios tipos: memoria de trabajo (información activa que se mantiene en mente en ese momento), memoria a corto plazo (eventos recientes que se retienen durante un período limitado) y memoria a largo plazo (conocimiento duradero que persiste indefinidamente). Los agentes de IA reflejan de cerca esta taxonomía. La ventana de contexto del LLM actúa como memoria de trabajo; un buffer deslizante de mensajes recientes sirve como memoria a corto plazo; y un almacén externo — a menudo una base de datos vectorial — sirve como memoria a largo plazo.

La memoria habilita el razonamiento de múltiples pasos. Cuando un agente necesita responder una pregunta de seguimiento, ejecutar un plan a través de varios pasos o recordar las preferencias de un usuario de una sesión anterior, recurre a una o más de estas capas de memoria. El diseño correcto de la memoria determina si un agente se siente como un asistente con conocimiento o un chatbot amnésico.

Cómo funciona

Memoria de trabajo y la ventana de contexto

La ventana de contexto es la forma más inmediata de memoria disponible para cualquier agente impulsado por LLM. Todos los mensajes, resultados de herramientas y pensamientos intermedios dentro de una única llamada de inferencia residen en la memoria de trabajo. Las ventanas de contexto típicas van de 8K a 200K tokens y establecen un límite estricto de sobre qué puede razonar activamente el agente. Cuando se alcanza este límite, la información más antigua debe resumirse, comprimirse o eliminarse para hacer espacio. La memoria de trabajo es rápida y de latencia cero, pero completamente volátil — desaparece cuando termina la llamada.

Buffer de memoria a corto plazo

La memoria a corto plazo se implementa como un buffer rotativo que mantiene las últimas N rondas de conversación. Cuando llega una nueva ronda, la más antigua se descarta cuando el buffer está lleno. Este enfoque es simple, económico y suficiente para la continuidad conversacional dentro de una sola sesión. El buffer normalmente se serializa y se pasa de vuelta a la ventana de contexto al comienzo de cada nueva llamada de inferencia. Su principal limitación es que no escala para sesiones largas ni para recuperación entre sesiones.

Almacén semántico a largo plazo

La memoria a largo plazo utiliza un almacén persistente externo — típicamente una base de datos vectorial — para mantener incrustaciones de eventos pasados, hechos y resúmenes. Cuando el agente necesita recordar algo, incrusta la consulta actual y realiza una búsqueda de vecinos aproximados para recuperar los recuerdos semánticamente más relevantes. Los fragmentos recuperados se inyectan en la ventana de contexto antes de la inferencia. Este patrón escala a millones de hechos almacenados y admite recuperación entre sesiones, pero añade latencia de recuperación y requiere un modelo de incrustación.

Memoria episódica vs. semántica

La memoria episódica almacena eventos pasados específicos con su contexto: "En la sesión 23, el usuario preguntó sobre la política de devoluciones y estaba frustrado." La memoria semántica almacena conocimiento general del mundo o hechos acumulados: "La ventana de devoluciones es de 30 días." Ambos tipos pueden coexistir en el mismo almacén vectorial, distinguidos por metadatos. La memoria episódica es valiosa para la personalización; la semántica es valiosa para anclar al agente en el conocimiento del dominio.

Bucle de recuperación

El bucle de recuperación conecta todas las capas. En cada ronda, el agente consulta la memoria a largo plazo para obtener contexto relevante, lo fusiona con el buffer a corto plazo y alimenta el contexto combinado en la memoria de trabajo del LLM. Después de la generación, los hechos importantes de la nueva ronda pueden escribirse de vuelta en el almacén a largo plazo, cerrando el bucle.

Cuándo usar / Cuándo NO usar

Usar cuandoEvitar cuando
El agente necesita recuperar información de sesiones o rondas anterioresLa tarea es completamente autocontenida en un único prompt y no tiene preguntas de seguimiento
Los usuarios esperan personalización basada en interacciones pasadasLos costos de almacenamiento o la latencia son inaceptables para el caso de uso
El agente rastrea tareas de larga duración con muchos resultados intermediosLa ventana de contexto es lo suficientemente grande como para contener toda la información relevante
El conocimiento del dominio supera lo que cabe en una sola ventana de contextoLos requisitos de privacidad prohíben almacenar conversaciones de usuarios
Se necesita comportamiento consistente a través de múltiples invocaciones del agenteLa complejidad añadida supera el beneficio marginal de la persistencia

Ventajas y desventajas

VentajasDesventajas
Permite continuidad de múltiples pasos y entre sesionesLos almacenes a largo plazo añaden latencia de recuperación
Admite personalización y contexto específico del usuarioLas bases de datos vectoriales introducen complejidad de infraestructura
Escala más allá de los límites de la ventana de contextoLa calidad de recuperación depende de la precisión del modelo de incrustación
La memoria episódica mejora significativamente la experiencia del usuarioEl envejecimiento de la memoria requiere estrategias de desalojo o actualización
La memoria semántica ancla al agente en el conocimiento del dominioLas políticas de privacidad y retención de datos deben gestionarse explícitamente

Ejemplos de código

"""
Simple agent memory implementation combining a list-based short-term buffer
with a vector-based long-term store using sentence-transformers and numpy.
"""
from __future__ import annotations

import json
import numpy as np
from dataclasses import dataclass, field
from typing import Optional
from sentence_transformers import SentenceTransformer # pip install sentence-transformers


# ---------------------------------------------------------------------------
# Short-term buffer memory (last N turns)
# ---------------------------------------------------------------------------

@dataclass
class ShortTermMemory:
"""Keeps the most recent `max_turns` conversation turns in a list."""
max_turns: int = 10
turns: list[dict] = field(default_factory=list)

def add(self, role: str, content: str) -> None:
self.turns.append({"role": role, "content": content})
# Evict oldest turn when capacity is exceeded
if len(self.turns) > self.max_turns:
self.turns.pop(0)

def get_history(self) -> list[dict]:
"""Return all buffered turns for injection into the context window."""
return list(self.turns)


# ---------------------------------------------------------------------------
# Long-term vector memory (semantic retrieval)
# ---------------------------------------------------------------------------

@dataclass
class LongTermMemory:
"""
Simple in-memory vector store backed by numpy.
In production, replace with Chroma, Pinecone, or pgvector.
"""
model_name: str = "all-MiniLM-L6-v2"
_model: Optional[SentenceTransformer] = field(default=None, init=False, repr=False)
_texts: list[str] = field(default_factory=list, init=False)
_embeddings: Optional[np.ndarray] = field(default=None, init=False)

@property
def model(self) -> SentenceTransformer:
if self._model is None:
self._model = SentenceTransformer(self.model_name)
return self._model

def store(self, text: str) -> None:
"""Embed and store a piece of text in long-term memory."""
embedding = self.model.encode([text]) # shape: (1, dim)
self._texts.append(text)
if self._embeddings is None:
self._embeddings = embedding
else:
self._embeddings = np.vstack([self._embeddings, embedding])

def retrieve(self, query: str, top_k: int = 3) -> list[str]:
"""Return the top_k most semantically similar stored memories."""
if not self._texts:
return []
query_emb = self.model.encode([query]) # shape: (1, dim)
# Cosine similarity
norms = np.linalg.norm(self._embeddings, axis=1, keepdims=True)
normed = self._embeddings / (norms + 1e-9)
query_norm = query_emb / (np.linalg.norm(query_emb) + 1e-9)
scores = (normed @ query_norm.T).flatten()
top_indices = np.argsort(scores)[::-1][:top_k]
return [self._texts[i] for i in top_indices]


# ---------------------------------------------------------------------------
# Agent with combined memory
# ---------------------------------------------------------------------------

class MemoryAgent:
"""
A simple agent that combines short-term buffer and long-term vector memory.
Uses a mock LLM call for illustration; replace with openai.chat.completions.create.
"""

def __init__(self, max_short_term_turns: int = 6):
self.short_term = ShortTermMemory(max_turns=max_short_term_turns)
self.long_term = LongTermMemory()
self.system_prompt = "You are a helpful assistant with access to past context."

def _build_context(self, user_message: str) -> list[dict]:
"""Combine long-term retrieval + short-term buffer into a message list."""
# Retrieve relevant memories from long-term store
memories = self.long_term.retrieve(user_message, top_k=3)
memory_block = "\n".join(f"- {m}" for m in memories) if memories else "None"

messages = [
{"role": "system", "content": self.system_prompt},
{"role": "system", "content": f"Relevant past context:\n{memory_block}"},
]
# Append recent conversation history
messages.extend(self.short_term.get_history())
# Append the current user message
messages.append({"role": "user", "content": user_message})
return messages

def chat(self, user_message: str) -> str:
"""Process a user message and return the agent's response."""
messages = self._build_context(user_message)

# --- Replace this mock with a real LLM call ---
# import openai
# response = openai.chat.completions.create(model="gpt-4o", messages=messages)
# reply = response.choices[0].message.content
reply = f"[Mock LLM reply to: {user_message!r} with {len(messages)} context messages]"
# ----------------------------------------------

# Update short-term buffer
self.short_term.add("user", user_message)
self.short_term.add("assistant", reply)

# Write important facts to long-term memory (in production, use LLM to decide)
self.long_term.store(f"User said: {user_message}")
self.long_term.store(f"Assistant replied: {reply}")

return reply


# ---------------------------------------------------------------------------
# Example usage
# ---------------------------------------------------------------------------
if __name__ == "__main__":
agent = MemoryAgent(max_short_term_turns=4)

turns = [
"My name is Alice and I prefer concise answers.",
"What is the capital of France?",
"What did I say my name was?",
]
for turn in turns:
print(f"User: {turn}")
print(f"Agent: {agent.chat(turn)}\n")

Recursos prácticos

Ver también