💻 Todo el código de esta serie está en un solo repo: resilient-agent-harness-sample-for-aws. Este post es la demo de Memory Guardrails (
01-memory-guardrails). Clona el repo y sigue el paso a paso.
Un modelo de lenguaje alucina una vez y lo corriges. Un agente alucina una vez, escribe el dato malo en su memoria, y luego re-lee ese dato como contexto confiable en cada sesión que sigue. Un error se vuelve permanente.
Esa es la trampa de la que nadie te advierte: la memoria de tu agente es su contexto. Lo que sea que llegue al store se recarga en el prompt la próxima vez. Así que el día que el modelo inventa un valor que nadie definió y lo guarda, el agente no solo se equivoca en una respuesta, recarga esa basura como verdad en cada conversación futura, y paga tokens para re-leerla cada vez. Un mejor prompt no te salva aquí, porque el dato malo ya está dentro del store en el que el agente confía. Tienes que detenerlo en el momento de la escritura.
Para hacerlo concreto, construí un pequeño agente de viajes e intenté romper su memoria a propósito. La demo completa, ejecutable de principio a fin, está en el repo resilient-agent-harness.
El diagrama de abajo es toda la idea: el modelo puede alucinar un dato durante la extracción, un hook determinista BeforeToolCallEvent valida esa escritura contra un schema, y una escritura inválida se cancela antes de que llegue a agent.state, así que solo datos validados persisten en la siguiente sesión.
¿Cuál es la demo?
El agente está construido con Strands Agents y tiene dos herramientas:
-
book_flightbusca una tarifa real en el sandbox de Duffel y guarda la reserva en la memoria del agente. -
recall_bookingslee lo que el agente tiene almacenado.
La memoria es el agent.state nativo del agente, y se persiste a disco con un FileSessionManager. Ahí es donde Strands se gana su lugar primero: nunca escribí una capa de storage. Construyo un nuevo Agent con el mismo session_id y auto-restaura el estado previo y el historial de mensajes desde disco. Eso significa que "una sesión posterior" en esta demo es un reinicio real, no una variable que reseteo para simular uno.
¿Qué es un memory guardrail?
Un memory guardrail es un check determinista que se ejecuta antes de que un agente de IA actúe y escriba en memoria: valida los datos contra un schema y cancela la llamada si no encajan, para que la herramienta nunca se ejecute con input malo y solo datos limpios se almacenen. Un dato alucinado nunca se convierte en memoria permanente, porque nunca se escribe en primer lugar.
La palabra clave es determinista. No le estamos preguntando a un segundo modelo "¿esto se ve bien?", lo cual solo agrega otra cosa que puede alucinar. Ejecutamos validación en Python puro que devuelve el mismo veredicto para el mismo input, siempre.
¿Cómo funciona el guardrail?
En Strands, el lugar nativo para esto es un hook BeforeToolCallEvent. Se ejecuta antes de que la herramienta de escritura en memoria se ejecute, y puede cancelar la llamada:
# guardrail.py — el hook corre ANTES de la herramienta de booking y cancela escrituras inválidas.
from strands.hooks import BeforeToolCallEvent, HookProvider, HookRegistry
class MemoryGuardrailHook(HookProvider):
def register_hooks(self, registry: HookRegistry, **kwargs) -> None:
registry.add_callback(BeforeToolCallEvent, self._gate)
def _gate(self, event: BeforeToolCallEvent) -> None:
if event.tool_use["name"] not in self.write_tool_names:
return # solo intercepta la herramienta de booking/escritura
data = event.tool_use.get("input", {}) # los datos que el modelo quiere escribir
valid, errors = validate_entry(data, self._current_schema())
if not valid:
event.cancel_tool = f"REJECTED: {'; '.join(errors)}" # la herramienta nunca se ejecuta
validate_entry es Python puro. El hook es un adaptador delgado sobre ella. El schema (FLIGHT_SCHEMA en la demo) es la definición de realidad del agente: los campos requeridos deben estar presentes, los números deben ser numéricos, las fechas deben verse como YYYY-MM-DD, la clase de cabina debe venir de un conjunto permitido, y campos desconocidos se rechazan. Aquí es donde Strands brilla por segunda vez: un hook se registra una vez y gobierna cada herramienta de escritura en memoria, incluyendo herramientas que tú no escribiste, sin tocar el código de la herramienta. El modelo puede alucinar todo lo que quiera en la extracción; el gate decide qué se convierte en memoria.
¿Por qué un hook en vez de un mejor prompt?
Una instrucción en el system prompt es una solicitud que el modelo puede ignorar, y bajo presión lo hará. El hook es aplicación forzosa: si cancela la escritura, la herramienta no se ejecuta, sin importar lo que el modelo decidió. La decisión del guardrail es determinista; si el modelo emite datos malos en una ejecución dada, no lo es. Esa es exactamente la razón por la que el hook, no un prompt, es lo que se lleva a producción.
Antes y después: dos agentes, una línea de diferencia
Ejecuto el mismo escenario de dos formas, como dos agentes separados. La única diferencia que el lector ve es hooks=[guardrail]: mismo modelo, mismas dos herramientas, mismo prompt, misma sesión.
El viajero pide reservar una clase de cabina "ultra", que no existe (el conjunto permitido es economy, premium_economy, business, first).
Agente #1, sin el guardrail, simplemente llama a book_flight. Gasta una llamada real a la API de Duffel en una solicitud que nunca fue válida, guarda la reserva "ultra" mala en agent.state, y ese dato sobrevive al reinicio: un agente completamente nuevo en el mismo session_id lo recarga directo desde disco. Al hacer recall, el agente lee la reserva inválida como verdad y te cobra por ella.
Agente #2, con el guardrail (hooks=[guardrail]), cancela el book_flight inválido antes de que se ejecute. No se gasta llamada a la API, nada malo se guarda. El agente le dice al viajero que la clase de cabina es inválida y pide una real; el viajero la corrige a economy, y solo esa reserva válida se guarda. Después del mismo reinicio, la memoria tiene una sola reserva limpia.
El notebook mide tokens reales de la API de métricas de Strands en cada ejecución. Esto es lo que produjo mi ejecución (tus números variarán por ejecución y por modelo, que es el punto de ejecutarlo tú mismo):
| SIN hook | CON hook | |
|---|---|---|
| reservas después del reinicio | 2 (una es la "ultra" mala) | 1 (solo la válida) |
| tokens de recall (por recall) | 1,871 | 1,213 |
El agente con guardrail hace recall con ~35% menos tokens y devuelve las reservas correctas, porque el dato malo nunca entró en memoria para ser re-leído. El agente sin guardrail paga más para recargar una reserva que nunca debió existir. Ejecútalo con tu propio modelo e inputs de viajero y observa cómo se mantiene la misma forma.
Lo que un schema guardrail no puede atrapar
Un schema detiene errores de estructura: tipo incorrecto, una opción que no existe, un precio fuera de cualquier rango sensato, campos que nadie definió. No puede atrapar un valor plausible-pero-incorrecto, como una tarifa que es un número perfectamente válido pero simplemente incorrecto para la ruta. Ese es un límite real, y la demo lo dice en vez de sobre-prometer. Para ese caso el sample agrega una segunda capa opcional, un cross-check de ground truth contra la tarifa real capturada, pero un schema solo no va a atrapar semántica mala.
Preguntas frecuentes
¿Esto detiene todas las alucinaciones?
No. Detiene que un dato alucinado sea almacenado y re-leído como contexto confiable, que es el fallo que se multiplica. El modelo todavía puede alucinar en una respuesta individual; el guardrail evita que ese error se convierta en memoria permanente.
¿Por qué no validar con un segundo modelo?
Porque eso agrega otro componente no-determinista que también puede equivocarse. Un check de schema es determinista, el mismo input da el mismo veredicto siempre, y es barato: Python puro.
¿Esto solo funciona con OpenAI, o solo en AWS?
Ninguno de los dos. Strands es agnóstico al modelo: los proveedores son intercambiables a través de una interfaz de modelo unificada, así que el mismo código corre en Amazon Bedrock (el default del SDK), Anthropic, OpenAI, o un modelo local a través de Ollama. Esta demo usa OpenAI gpt-4o-mini por defecto porque solo necesita una API key para probar, pero eso sigue siendo una llamada a la nube, no un modelo en tu máquina. Para producción, el mismo hook se coloca sin cambios frente a un store durable como Amazon Bedrock AgentCore Memory.
Ejecútalo tú mismo
La demo completa, los dos agentes con y sin el guardrail, el reinicio real de sesión, y la comparación de tokens, es un solo notebook ejecutable. Clona el repo y ejecútalo:
git clone https://github.com/elizabethfuentes12/resilient-agent-harness-sample-for-aws.git
cd resilient-agent-harness-sample-for-aws/01-memory-guardrails
uv venv && source .venv/bin/activate
uv pip install -r requirements.txt
# Default: OpenAI gpt-4o-mini (solo necesitas una API key para probar)
echo "OPENAI_API_KEY=sk-..." > .env
echo "DUFFEL_API_KEY=duffel_test_..." >> .env # token sandbox gratuito de app.duffel.com
uv run test_memory_guardrails.py
¿Prefieres notebooks? Abre test_memory_guardrails.ipynb y ejecútalo de arriba a abajo.
El patrón sigue a Governed Memory (Taheri, Mar 2026). Las cifras de benchmark y la lectura completa están en el README del repo. Lo que esta demo reproduce es el mecanismo: validar en la frontera de herramientas antes de la escritura.
¿Cuál es la alucinación que te ha mordido en producción: un campo inventado, un enum incorrecto, un valor que se veía bien pero no lo era? Cuéntame en los comentarios.
📬 ¿Construyes agentes de IA confiables? Escribo sobre memoria de agentes, guardrails, evaluación y patrones multi-agente. Suscríbete a mi newsletter para recibir el siguiente.
¡Gracias!
🇻🇪 Dev.to Linkedin GitHub Twitter Instagram Youtube

Top comments (0)