Esta semana estaba leyendo un post-mortem en The New Stack, "The 'silent hallucination' loop: how our autonomous data pipeline poisoned its own vector store" de Emmanuel Akita (julio 2026), y tuve que parar a mitad de lectura. Había llegado por su cuenta a la tesis detrás de cada post de esta serie Resilient Harness: la confiabilidad vive en el harness alrededor del modelo, no en el prompt dentro de él. Su conclusión, en sus propias palabras:
"Probabilistic systems require deterministic boundaries." (Los sistemas probabilísticos requieren fronteras deterministas.)
Esa es toda la idea en siete palabras. Así que déjame contarte qué le pasó a su pipeline RAG, y luego mostrarte dónde reproduje la misma lección desde la otra dirección. Eso incluye el único punto donde su historia parece contradecir mi propio tutorial, y por qué no lo hace.
¿Qué falló dentro del pipeline?
El equipo de Akita construyó un pipeline RAG para un cliente fintech: ingerir miles de PDFs financieros sin estructura, extraer los campos clave, generar embeddings de todo, y alimentar un chatbot interno de preguntas y respuestas. Al principio funcionó sin problemas. Después el chatbot empezó a responder preguntas sobre 2022 citando datos de 2018, y a atribuir ingresos de la competencia a las subsidiarias del cliente.
La parte aterradora, en sus palabras: el retrieval funcionaba correctamente. El dashboard estaba en verde, la latencia por debajo de 100 ms, la búsqueda vectorial devolvía exactamente lo que se le pedía. Los datos que devolvía eran basura. Por eso lo llama silencioso: sin error, sin excepción, sin brecha de seguridad. Un sistema perfectamente sano sirviendo respuestas equivocadas con total confianza.
Esta es la cadena del fallo, y por qué cada eslabón coincide con algo sobre lo que ya escribí.
1. El agente de extracción alucinó, y la alucinación terminó en los embeddings
Su agente de ingesta usaba un LLM frontier para extraer metadata de cada PDF (document_type, fiscal_year, company_entity, un resumen) como JSON, y luego adjuntaba eso a los chunks de texto antes de generar los embeddings. Cuando el LLM se topó con un año fiscal ilegible en un PDF mal escaneado, no lanzó una excepción. Adivinó "2024". Esa adivinanza quedó embebida junto al texto, así que ahora tenían, como dice Akita, "high-speed searches for documents that didn't exist" (búsquedas de alta velocidad de documentos que no existen).
Su diagnóstico de la causa raíz: "treating a probabilistic extraction process as deterministic" (tratar un proceso de extracción probabilístico como determinista).
Este es el fallo silencioso del que escribí en Por Qué los Agentes de IA Fallan en Tareas Multi-Paso: el agente reporta éxito, todos los dashboards se ven sanos, y el resultado está silenciosamente mal. No lo detectas vigilando crashes. Lo detectas verificando el contenido del trabajo contra una fuente de verdad.
2. El validador LLM-as-a-judge le puso el sello a todo
Esta es la parte que me pareció más útil, porque mata un patrón que muchos equipos usan por defecto. El equipo de Akita tenía un segundo LLM, un "Validator Agent", revisando el JSON del extractor contra el texto crudo antes de llegar a la base vectorial. Aun así dejó pasar las alucinaciones. ¿Por qué?
"Using a probabilistic model to police another probabilistic model doesn't give you a firewall; it gives you a confirmation bias loop." (Usar un modelo probabilístico para vigilar a otro modelo probabilístico no te da un firewall; te da un loop de sesgo de confirmación.)
El validador le daba la razón al extractor por pura sycophancy (adulación): no encontraba el año en el texto y racionalizaba "el primer modelo debe haber visto algo que yo me perdí". Dos sistemas probabilísticos revisándose entre sí no suman una garantía determinista. Suman consenso, que no es lo mismo que correctitud.
Ahora bien, yo misma he recomendado LLM-as-a-judge para evaluación. ¿Eso me deja mal parada? No, y la distinción es justo el punto. En mi tutorial de LLM-as-Judge (y en la comparación de 3 frameworks que le siguió) uso un juez LLM offline, en experimentos batch y suites de tests, para puntuar calidad. E incluso ahí nunca trabaja solo: corre con rúbricas explícitas, checks deterministas (Contains, ToolCalled) y evaluación de trayectoria. El error de Akita no fue usar un juez LLM. Fue ponerlo inline en el write path como la puerta de integridad, el componente que decide qué se convierte en verdad confiable en producción. Ahí es exactamente donde un revisor probabilístico no puede ser la última línea. Usa el juez para medir; usa código para bloquear.
| LLM-as-a-judge es la herramienta correcta para | Es la herramienta equivocada para |
|---|---|
| Puntuar calidad subjetiva offline: utilidad, tono, calificación con rúbrica | La puerta de integridad inline en tu write path |
| Evaluación batch en suites de tests, seguir tendencias de calidad entre releases | Cualquier cosa que el código puede verificar exacto ("¿este año aparece en la fuente?") |
| Comparar agentes/prompts donde no existe ground truth | Decisiones de seguridad o integridad de datos donde un solo fallo envenena producción |
3. El prompt engineering lo empeoró
Su primer instinto fue arreglarlo en el prompt: "DO NOT HALLUCINATE", "If you are not sure 100% certain of the metadata, output NULL", "You are a strict financial auditor." El resultado, según su reporte: el validador se volvió excesivamente defensivo, empezó a rechazar datos perfectamente buenos, y los pasos de razonamiento subieron los costos de API ~40%.
Hice casi este mismo argumento en Cómo Detener la Inyección de Prompts en Agentes de IA: no puedes salir de un problema de confianza a punta de prompts, porque la cooperación del modelo es un estado de ánimo, no una garantía. Allá me defendía del texto inyectado por un atacante; Akita se defendía de la alucinación de su propio modelo. Misma conclusión: el prompt es la capa equivocada. El arreglo pertenece al harness.
La solución: una frontera determinista antes de que algo se vuelva estado confiable
El equipo de Akita le quitó toda autoridad de decisión al paso de validación y reemplazó el Validator LLM con Python simple y aburrido. Ese es el principio del harness del que trata toda esta serie: la validación vive en el código alrededor del modelo, no en el prompt dentro de él. El modelo propone; el harness decide. Tres movimientos:
-
Grounding con Pydantic. Que un
fiscal_yearsea un entero plausible no basta: el año tiene que aparecer físicamente en el texto fuente crudo (un check con regex). El "2024" alucinado para un documento de 2018 falla, porque "2024" no está en el texto. -
Cross-referencing determinista. El
company_entityse compara con fuzzy matching contra una tabla SQL fija con las entidades reales del cliente, con un flag de competidor para que un análisis legítimo de la competencia no sea rechazado por error. - Cuarentena por defecto. Nada de la extracción va directo al vector store. Todo pasa primero por una tabla intermedia en PostgreSQL, y solo los payloads que pasan los checks de Pydantic + SQL se convierten en embeddings.
Su resultado reportado: el envenenamiento de datos paró de inmediato, y reemplazar el validador LLM redujo los gastos de API ~50%. (Esos son sus números de un solo post-mortem de producción, no un benchmark reproducible, pero el mecanismo es lo que importa.)
Vuelve a leer ese fix y es la misma forma que Detener Alucinaciones de Agentes de IA: Validar Antes de Escribir en Memoria. La tesis completa de ese post es validar antes de que el agente escriba en memoria: una puerta determinista que decide qué puede convertirse en estado confiable, antes de que se convierta en estado confiable. Akita bloquea escrituras a un vector store con Pydantic; yo bloqueo escrituras a la memoria del agente con un hook BeforeToolCallEvent de Strands. Distinta herramienta, forma idéntica: la frontera vive en el write path, y es código, no un modelo.
Dos caras del mismo bug: auto-envenenamiento vs memory poisoning
El fallo de Akita no tuvo atacante: el sistema se envenenó solo con su propia alucinación. Mi post sobre inyección de prompts y memory poisoning es la imagen en espejo: un atacante planta una instrucción maliciosa en contenido que el agente lee, el agente la guarda como memoria confiable, y una sesión completamente nueva la recarga del disco y actúa según ella. Ambos son "el sistema confía en un almacén persistido, y el almacén está mal".
| El pipeline de Akita (auto-infligido) | El caso espejo (adversarial) | |
|---|---|---|
| ¿Quién metió los datos malos? | La alucinación del propio LLM | La instrucción inyectada por un atacante |
| Almacén confiable | Base de datos vectorial | Memoria del agente (agent.state) |
| Qué falló | Validador LLM-as-a-judge (sycophancy) | El "ignora instrucciones malas" del prompt |
| El fix | Grounding Pydantic + SQL, antes del embedding | Puerta BeforeToolCallEvent, antes de que corra la tool |
| Investigación | Post-mortem independiente; refleja Misevolution | Reproduce Zombie Agents |
El respaldo académico también coincide. Your Agent May Misevolve (Shao et al., sep 2025) es el primer estudio sistemático de agentes que derivan hacia comportamiento inseguro sin atacante externo, que es exactamente el loop de auto-envenenamiento de Akita. Zombie Agents (Yang et al., feb 2026) cubre el lado adversarial: una inyección única guardada en memoria se convierte en un compromiso persistente entre sesiones.
La única regla que cubre todo
Ya sea que los datos malos lleguen por ataque o por alucinación, la defensa es la misma y no vive en el prompt:
Deja que el LLM extraiga, analice y resuma. Pero en el momento en que un dato se escribe en almacenamiento como verdad confiable, debe pasar una barrera más estricta, de ingeniería tradicional.
Esa es la regla de Akita, y es la columna vertebral de todo lo que vengo escribiendo:
- Detener Alucinaciones de Agentes de IA: Validar Antes de Escribir en Memoria — validar antes de que el agente escriba en memoria (su Pydantic-antes-del-embedding, en forma de agente).
- Cómo Detener la Inyección de Prompts en Agentes de IA — bloquear la acción peligrosa en la frontera de la tool, no en el prompt.
- Why AI Agents Fail at Multi-Step Tasks — atrapar el fallo silencioso verificando el trabajo, no confiando en un "listo".
- How to Evaluate AI Agents: LLM-as-Judge Tutorial — usar el juez LLM para medir calidad offline, junto a checks deterministas; nunca como la puerta inline.
Distintos posts, un solo principio: un componente probabilístico puede proponer; solo una frontera determinista debería poder confirmar.
Preguntas que quizás tengas
¿No se supone que un segundo LLM ("LLM-as-a-judge") atrape esto?
Atrapa algunas cosas, pero es probabilístico, así que no puede darte una garantía. Como descubrió Akita, tiende al acuerdo adulador, poniéndole el sello a la salida del primer modelo. Dos modelos de acuerdo es consenso, no correctitud. Usa un juez LLM para medir calidad offline (con rúbricas y checks deterministas, como muestro aquí); para una restricción dura de integridad en el write path ("este año debe aparecer en el texto fuente"), usa código.
¿No puedo arreglar esto con un prompt más estricto?
Esa es la trampa en la que cayó Akita y sobre la que construí mi post de inyección de prompts. Los prompts más estrictos hicieron que su validador rechazara datos buenos y subieron los costos ~40%. El prompt es la capa equivocada: la garantía tiene que vivir donde el estado de ánimo del modelo no alcance.
¿Esto solo aplica a pipelines RAG?
No. El de Akita es un path de ingesta RAG; mis demos son tool calls de agentes. La forma compartida es cualquier punto donde la salida de un componente probabilístico se confirma como estado confiable: memoria, un vector store, una base de datos, una acción externa. Pon una puerta determinista ahí.
¿Dónde va esto en producción?
El mismo allow/deny se mueve a una capa de políticas en la frontera de la tool o del gateway (por ejemplo Amazon Bedrock AgentCore), de modo que la regla queda centralizada y una memoria envenenada (o auto-envenenada) no puede editarla.
Pruébalo tú misma
Construí las versiones agénticas de esto (validar-antes-de-escribir y la puerta en la frontera de la tool) como demos ejecutables. Clona el repo y córrelos:
git clone https://github.com/elizabethfuentes12/resilient-agent-harness-sample-for-aws.git
# Validar antes de que el agente escriba en memoria (el fix de Akita, en forma de agente)
cd resilient-agent-harness-sample-for-aws/01-memory-guardrails
uv venv && source .venv/bin/activate
uv pip install -r requirements.txt
echo "OPENAI_API_KEY=sk-..." > .env
uv run test_memory_guardrails.py
# La imagen espejo adversarial: el veneno sobrevive un restart, una puerta de tool lo bloquea
cd ../02-memory-poisoning-defense
uv pip install -r requirements.txt
cp ../01-memory-guardrails/.env .env
uv run test_memory_poisoning_defense.py
Todo el crédito a Emmanuel Akita por el post-mortem original. Ve a leerlo: es un análisis limpio y honesto de un fallo que la mayoría de los equipos nunca publica.
¿Alguna vez desplegaste un pipeline perfectamente sano y perfectamente equivocado? Cuéntame en los comentarios qué fue lo que finalmente lo detectó.
📬 ¿Construyendo agentes de IA confiables? Escribo sobre memoria de agentes, guardrails, evaluación y patrones multi-agente. Suscríbete a mi newsletter para recibir el próximo.
Gracias!
🇻🇪 Dev.to Linkedin GitHub Twitter Instagram Youtube



Top comments (0)