Tu agente quema su presupuesto de tokens en una sola llamada a una herramienta, o se olvida de lo que el usuario dijo hace tres turnos. Misma causa raíz: tiene dos tipos de memoria y se mezclaron. Una guarda la conversación; la otra guarda salidas grandes de herramientas, como logs. Necesitan almacenamiento distinto y recuperación distinta, y tratarlas como un solo almacén es lo que hace a los agentes lentos, caros y equivocados.
Este post muestra cómo mantenerlas separadas: el framework ahora descarga los datos grandes por ti (se acabó el código de punteros a mano), y en producción las dos memorias se mapean a dos servicios de AWS. Lo desplegué y medí la diferencia.
Continúa AI Context Window Overflow: Memory Pointer Fix. El código usa Strands Agents; los patrones se trasladan a otros frameworks. Repo: sample-why-agents-fail.
¿Cuáles son los dos tipos de memoria de un agente?
Un agente de IA tiene dos tipos de memoria: la memoria de conversación guarda lo que se dijo (turnos, preferencias, hechos) y se recupera por significado, mientras que la memoria de contexto guarda salidas grandes de herramientas (logs, datasets, documentos) y se recupera por un identificador exacto. Son almacenes distintos con recuperación distinta, y usar uno donde corresponde el otro es la causa raíz tanto de "mi agente se olvida de las cosas" como de "mi agente reventó el presupuesto de tokens".
Antes de cualquier código, deja clara la distinción:
| Memoria de conversación | Memoria de contexto | |
|---|---|---|
| Guarda | Turnos, preferencias, hechos extraídos | Salidas grandes de herramientas (logs, datasets) |
| Se recupera por | Significado (similitud semántica) | Identificador exacto (una referencia) |
| Pregunta que responde | "¿Qué me dijo el usuario antes?" | "Devuélveme ese archivo de log de 5 MB, exacto" |
| Mal encaje para | Un blob de log de 5 MB | "¿Cuál era el nombre del usuario?" |
Esa tabla es todo el artículo. Lo que sigue es solo dónde vive cada fila en el código.
Por qué la memoria de contexto se desborda primero
Las salidas grandes de herramientas desbordan la ventana de contexto porque son indivisibles y se reenvían en cada llamada al modelo. Una herramienta que devuelve 200 KB de logs no cuesta 200 KB una sola vez. Ese payload viaja en la entrada de cada turno posterior hasta que empuja la pregunta original fuera de la ventana.
El primer post lo cuantificó con investigación de IBM (Solving Context Window Overflow in AI Agents, 2025): un flujo de ciencia de materiales que consumía 20.822.181 tokens y fallaba bajó a 1.234 tokens y tuvo éxito una vez que los datos grandes se almacenaron fuera del contexto y se referenciaron con un puntero.
El arreglo, antes y ahora: deja de poner datos en la conversación
El post original almacenaba los datos grandes a mano: una herramienta los escribía en agent.state y devolvía una cadena-puntero corta; la siguiente herramienta los leía de vuelta con esa clave. Funciona, pero la lógica de descarga vivía dentro de cada herramienta.
Strands ahora trae ese mismo patrón como un plugin de primera clase, ContextOffloader, así que tus herramientas vuelven a ser funciones ordinarias:
from strands import Agent
from strands.vended_plugins.context_offloader import ContextOffloader, FileStorage
# Herramientas ordinarias — sin lógica de punteros, sin agent.state dentro
agent = Agent(
model=MODEL,
tools=[fetch_application_logs, count_errors_by_service],
plugins=[ContextOffloader(storage=FileStorage("./artifacts"),
max_result_tokens=800, preview_tokens=200)],
)
agent("Fetch 2 hours of logs for 'api-gateway' and tell me the top error service.")
Cuando el resultado de una herramienta supera max_result_tokens, el plugin lo intercepta, almacena cada bloque en el backend y deja un pequeño preview más una referencia en el contexto. El agente obtiene una herramienta retrieve_offloaded_content(reference) para traer de vuelta los datos completos por referencia exacta cuando realmente los necesita.
¿Qué es el patrón Memory Pointer nativo en Strands?
El patrón Memory Pointer nativo es ContextOffloader, un plugin que intercepta resultados de herramientas demasiado grandes en tiempo de ejecución, almacena cada bloque en un backend de almacenamiento y reemplaza el resultado en contexto con un preview más una referencia. Los datos grandes nunca inundan la ventana de contexto, y tus herramientas nunca tocan lógica de punteros.
Resultados medidos
Corrí la misma consulta con tres estrategias. Misma consulta, gpt-4o-mini, 2 horas de logs:
| Estrategia | Tokens en contexto |
|---|---|
| Sin gestión | ~18.000 a 20.000 |
ContextOffloader (FileStorage) |
~490 |
context_manager="auto" |
~1.000 |
Eso es aproximadamente 97% menos tokens para la misma respuesta. Los números varían por ejecución porque los datos de log son aleatorios; test_native_pointer.py los reproduce.
Una salvedad honesta: el offloader es una red de seguridad, no la victoria completa. El gran ahorro viene de combinarlo con una herramienta selectiva. Mi count_errors_by_service calcula la respuesta del lado del servidor y devuelve un resumen pequeño, así el agente responde desde el resumen y los logs se quedan descargados. Sin una herramienta selectiva, un agente que necesite el dataset completo simplemente llamará a retrieve_offloaded_content y lo traerá todo de vuelta. El offloader garantiza que no desbordes; las herramientas selectivas son las que mantienen bajo el conteo de tokens.
Una línea para la mayoría de los agentes
Para un agente típico de múltiples turnos no configuras la descarga y el resumen por separado:
agent = Agent(model=MODEL, tools=[...], context_manager="auto")
Esto compone un SummarizingConversationManager (resume el historial antiguo con compresión proactiva) y un ContextOffloader (en memoria) con valores por defecto validados por benchmark. Lo que pases explícitamente tiene prioridad.
La misma idea, sobre almacenamiento real en Amazon S3
FileStorage escribe en disco local. Cambia una línea y las salidas grandes de herramientas aterrizan en un bucket S3 real, recuperadas por referencia exacta, nunca en la ventana:
from strands.vended_plugins.context_offloader import ContextOffloader, S3Storage
agent = Agent(
model=MODEL,
tools=[fetch_application_logs, count_errors_by_service],
plugins=[ContextOffloader(S3Storage(bucket=CONTEXT_BUCKET, prefix="log-artifacts/"))],
)
Un dataset de logs de 83 KB se almacenó en S3, ~486 tokens quedaron en contexto, y los datos volvieron byte por byte por su referencia exacta:
📊 Tokens left in LLM context: 486
📦 Objects offloaded to S3: 1
pointer in context: s3://…/log-artifacts/1781569100199_1_call_…_0
storage.retrieve() → 77,050 bytes (text/plain)
verified: 200 log events recovered verbatim — exact data, no loss
Esa es la segunda fila de la tabla, en forma de producción: recuperación por identificador exacto. No quieres "los logs más parecidos a mi consulta". Quieres esos logs, exactos. Eso es almacenamiento de objetos, no búsqueda semántica.
Producción: dos memorias, a propósito
En producción la separación se vuelve arquitectura. Un agente en Amazon Bedrock AgentCore mantiene cada memoria donde corresponde:
-
Conversación → AgentCore Memory. Turnos, preferencias y hechos extraídos, recuperados por similitud semántica (
RetrieveMemoryRecords: embeddings,top_k, score de relevancia), con alcance por usuario medianteactor_id. Conectado mediante elAgentCoreMemorySessionManagerde Strands. -
Memoria de contexto → Amazon S3. El mismo
ContextOffloader, conS3Storageen vez deFileStorage. Recuperada por referencia exacta.
¿Por qué no poner los logs también en AgentCore Memory? Porque AgentCore Memory recupera la memoria semánticamente más parecida, que es exactamente lo equivocado para "devuelve este dataset textual por id". La conversación quiere significado; los datos quieren una clave exacta. Un agente, dos memorias, cada una haciendo lo que sabe hacer bien.
agent = Agent(
model=BedrockModel(region_name=REGION),
tools=[fetch_application_logs, count_errors_by_service],
session_manager=AgentCoreMemorySessionManager(memory_config, REGION), # conversación
plugins=[ContextOffloader(S3Storage(bucket=CONTEXT_BUCKET, prefix="…"))], # datos
)
Observabilidad y evaluación vienen gratis
En AgentCore, la observabilidad completa está integrada. Agregas la librería de instrumentación y obtienes trazas, métricas y logs de cada invocación sin escribir nada de código de monitoreo. El despliegue ya la habilitó: el agente emite trazas y métricas de OpenTelemetry (OTEL) bajo el namespace bedrock-agentcore, y un panel de CloudWatch GenAI Observability muestra vistas de agente, sesión y trazas (latencia, tasa de error, uso de tokens, llamadas a herramientas) listas para usar.
Así diagnostiqué en segundos el error de permisos ListEvents de antes: la traza fallida estaba justo ahí en CloudWatch, sin configuración extra. Ver Visualizar datos de observabilidad de agentes AgentCore.
La misma instrumentación alimenta AgentCore Evaluations: puntuación automatizada con LLM-como-juez de la finalización de tareas y la precisión de las llamadas a herramientas a partir de las mismas trazas, para que midas la calidad del agente de forma continua en lugar de solo en el lanzamiento.
Qué memoria, cuándo
-
¿Solo el problema de datos, local?
ContextOffloader(FileStorage(...)). Herramientas ordinarias, sin código de punteros. -
¿Un agente típico de múltiples turnos?
context_manager="auto". Resumen más descarga en una línea. -
¿Producción? AgentCore Memory para la conversación,
ContextOffloader(S3Storage(...))para los datos. Mantenlas separadas. - En cualquier caso: combina el offloader con herramientas selectivas que devuelvan resúmenes, no blobs crudos. El offloader previene el desbordamiento; las herramientas selectivas mantienen bajo el conteo de tokens.
Pruébalo tú mismo
Necesitas Python 3.11+, uv y una OPENAI_API_KEY (o cambia el modelo por BedrockModel). Los pasos de S3 y AgentCore también requieren credenciales de AWS.
git clone https://github.com/aws-samples/sample-why-agents-fail
cd sample-why-agents-fail/stop-ai-agents-wasting-tokens/01-context-overflow-demo
uv venv && uv pip install -r requirements.txt
uv run python test_native_pointer.py # local, comparación medida de tokens
AWS_PROFILE=you uv run python test_s3_offload_local.py # descarga real a S3
# Despliegue de producción + recorrido de dos memorias: setup_agentcore_s3.ipynb
Notebooks: test_native_pointer.ipynb (local) y setup_agentcore_s3.ipynb (provisionar + desplegar + invocar en AWS).
Puntos clave
- Un agente tiene dos memorias. Conversación (semántica) y datos (referencia exacta). La mayoría de los problemas de contexto son una puesta donde corresponde la otra.
-
Ya no construyes el lado de datos a mano.
ContextOffloaderes el patrón Memory Pointer como plugin; las herramientas siguen siendo funciones ordinarias. - Medí ~97% menos tokens en este demo, y verifiqué un dataset de 83 KB descargado a S3 real y recuperado byte por byte por referencia.
- En producción, mantén las dos memorias separadas. AgentCore Memory para conversación, S3 para datos. Recuperar logs por significado es el diseño equivocado.
- El offloader es una red de seguridad; las herramientas selectivas son la victoria. Devuelve resúmenes, no blobs.
- En AgentCore, la observabilidad y la evaluación son gratis. Agrega la librería y obtén trazas, métricas y puntuación con LLM-como-juez sin código de monitoreo.
Preguntas frecuentes
¿ContextOffloader necesita AWS? No. Con FileStorage o InMemoryStorage corre totalmente en local. Solo necesitas AWS cuando eliges S3Storage o despliegas en AgentCore.
¿Puedo guardar archivos grandes en AgentCore Memory en vez de S3? Puedes, pero no deberías. AgentCore Memory recupera por similitud semántica, así que devuelve la memoria más parecida, no un archivo exacto. Las salidas grandes de herramientas necesitan recuperación por identificador exacto, que es lo que da S3 (mediante ContextOffloader).
¿Necesito Docker para desplegar en AgentCore? No. El starter toolkit construye la imagen en la nube con AWS CodeBuild por defecto. Docker solo se necesita para una compilación local.
¿Cuál es la diferencia entre agent.state y ContextOffloader? agent.state es el patrón Memory Pointer manual: escribes y lees punteros dentro de tus herramientas. ContextOffloader es la misma idea como plugin: las herramientas siguen siendo ordinarias y el framework descarga los resultados grandes por ti.
¿Cuál de mis dos memorias me está costando tokens? La de datos. La memoria de conversación es texto pequeño; las explosiones de tokens vienen de las salidas grandes de herramientas viajando en el contexto. Esa es la memoria que arregla ContextOffloader.
¿Cuál de las dos memorias de tu agente está fugando tokens? Cuéntame en los comentarios.
Referencias
Investigación
- Solving Context Window Overflow in AI Agents — IBM Research, 2025
- Towards Effective GenAI Multi-Agent Collaboration — Amazon, 2024 (referenciación de payloads entre agentes)
Implementación
- Strands · Context Management
- Strands · Conversation Management
- Strands · Agent State
- Amazon Bedrock AgentCore Memory — Get started
- AgentCore Runtime — IAM permissions
- AgentCore — Observability in CloudWatch
- AgentCore — Evaluations
- Código: 01-context-overflow-demo
Gracias!
🇻🇪🇨🇱 Dev.to Linkedin GitHub Twitter Instagram Youtube




Top comments (0)