El Hallazgo de HuggingFace: Los Agentes Reportan Éxito Que No Pueden Verificar
Los investigadores de HuggingFace observaron algo incómodo: los agentes declaran rutinariamente tareas completadas cuando el sistema externo nunca cambió de estado en absoluto. No casi-pero-mal. Realmente, completamente intacto. El agente devuelve una señal de confianza diciendo "hecho" mientras Stripe nunca cobró, el registro del CRM nunca se actualizó, y la inserción de Postgres nunca se confirmó.
Esto no es un problema del modelo. No es un fracaso de ingeniería de prompts o un problema de ajuste RLHF. El agente no tiene un canal de verdad fundamental. Llama a una herramienta, la herramienta devuelve un HTTP 200 (o una señal de éxito sintética), y el agente razona: "la operación tuvo éxito porque obtuve un código de éxito." Ese razonamiento es sólido. La arquitectura está rota.
El modo de fallo de las 2am es peor: ninguna excepción se dispara. Sin registro de errores. Nada gritando que el trabajo necesita ser reintentado. El agente continúa. El operador humano descubre la brecha días después al verificar una muestra de acciones "completadas" contra el sistema de registro y encontrar un 15% silenciosamente fallido.
La Llamada de Herramienta Devolvió 200. Eso No Es Lo Mismo Que Hecho.
HTTP 200 es una mentira. Un endpoint envolvente puede devolver 200 mientras una cola de trabajos descendente tiene contrapresión y aún no ha comenzado a procesar. Una réplica de lectura está retrasada 500ms detrás de la escritura, por lo que tu lectura inmediata para verificación golpea el estado antiguo. Un trabajo SQS asincrónico fue aceptado (202 Reconocido) pero el Lambda detrás falló seis segundos en la ejecución.
Stripe devuelve 200 inmediatamente. El cargo está pendiente. El webhook aún no se ha disparado. Una clave de idempotencia previene cargos duplicados en reintentos—pero el agente malinterpreta la respuesta "Solicitud Duplicada Detectada" como "cargo exitoso, continuando."
La API de Salesforce devuelve 200 cuando el registro fue puesto en cola para indexación. Una búsqueda no lo encontrará durante 15 segundos. Postgres devuelve 200 pero un disparador previo aún se está ejecutando en la misma transacción, y la fila que acabas de intentar leer aún no ha adquirido su estado final.
El patrón es consistente: la herramienta no tiene forma de comunicar "Acepto tu solicitud pero no sé si realmente funcionó." Y el agente no tiene forma de preguntar "¿esto realmente está hecho?" porque ha sido entrenado para confiar en el código de retorno.
La Afirmación del Estado Externo Pertenece Dentro del Bucle del Agente
La solución es arquitectónica. Cada llamada de herramienta que cambia estado debe ser emparejada con una lectura de verificación antes de que se permita al agente declarar éxito y avanzar. No después del hecho. No como observabilidad. Dentro del bucle.
Después de que el agente llama a "create_stripe_charge", debe llamar inmediatamente a "verify_stripe_charge_posted" que golpea la API de Stripe directamente, recupera el objeto de cargo, verifica el campo de estado, y devuelve { actual_state: "succeeded", expected_state: "succeeded", verified_at: "2024-01-15T09:23:14Z" }. Si no coinciden, el agente no procede.
Estructura la verificación como su propia herramienta que el modelo está forzado a llamar. Usa salidas estructuradas (modo JSON de OpenAI, especificación de uso de herramientas de Anthropic) para hacer la verificación no opcional. En flujos de trabajo n8n o Temporal, modela la verificación como un borde requerido en la máquina de estados, no un paso opcional. El agente no consigue reclamar victoria hasta que recibe la señal de verificación.
Sí, esto añade latencia. 200–800ms por verificación, dependiendo del tiempo de respuesta de la API del sistema externo. Eso es negligible comparado con el costo del fallo silencioso: un cargo que nunca se publicó, un cliente potencial nunca asignado, un ajuste de inventario nunca confirmado.
Los Reintentos y DLQs Tienen Que Basarse en el Estado Externo, No en la Confianza del Agente
El disparador para un reintento no es una excepción que el agente notó. Es un desajuste de estado que el paso de verificación detectó. Cuando una lectura de verificación muestra expected_state ≠ actual_state, reintentas toda la secuencia de escritura-luego-verificación con retroceso exponencial.
Las entradas de cola de letras muertas necesitan incluir la diferencia: ¿qué esperabas, qué observaste? Esto es oro de depuración. Te dice si la herramienta falló silenciosamente (la escritura nunca sucedió), la verificación fue demasiado rápida (el estado aún no se ha propagado), o el sistema externo rechazó la operación sin decirlo.
Las claves de idempotencia con alcance por action_id aseguran que los reintentos no carguen dos veces o creen registros duplicados. El seguimiento en vuelo respaldado por Redis previene que el agente intente la misma acción dos veces en paralelo mientras la primera verificación aún está pendiente.
Después de N fallos de verificación (hemos encontrado que 3–5 es sensato), deja de reintentar. Entrega a un humano. Un disyuntor que escala en lugar de hacer bucle vale su peso en credibilidad de producción.
Observabilidad: Registra la Afirmación, No Solo la Llamada
Necesitas tres líneas de registro distintas por acción: tool_called, state_asserted, state_matched. Cada una con un ID de correlación que abarca toda la cadena de llamada-verificación-confirmación. La mayoría de los equipos registran solo la invocación de herramienta y pierden completamente el paso de verificación.
Instrumenta la comparación real. Cuando state_asserted no es igual a state_matched, registra la diferencia. Si el agente esperaba { balance: $500 } y el sistema muestra { balance: $401 }, necesitas esos datos justo ahí en el rastro.
Mide la tasa de éxito de afirmación como una métrica clave. El porcentaje de afirmaciones "task_complete" del agente que pasan la verificación. Este número debe ser >99.5% para trabajo de grado de producción. La mayoría de los equipos descubren que es 82–87%. Ese es el número que te dice que nos llames, o que rediseñes completamente los contratos de herramientas.
Separa tramos de rastro para latencia de afirmación versus latencia de herramienta. La llamada de 200ms del agente a Stripe es independiente de la lectura de 150ms para verificar el cargo. Necesitas ambas en la línea de tiempo. Alerta sobre la tasa de desajuste de afirmación, no la tasa de error de herramienta. Los errores de herramienta son ruidosos. Los desajustes de afirmación son silenciosos.
Cuándo Importa Esto — y Cuándo Estás Sobreconstruyendo
Obligatorio: cualquier cosa que escriba en un sistema de registro. Pagos, registros de CRM, ajustes de inventario, estado visible para el usuario. Si un "hecho" falso cuesta dinero o requiere limpieza humana, afirma.
Opcional: agentes de investigación de solo lectura, generación de borradores, resumen interno, análisis. Si el agente alucina un resumen, el costo es que un humano lo relea. No ideal, pero la economía es diferente.
La prueba de dos preguntas: ¿un "hecho" falso cuesta dinero? ¿Requiere intervención humana para arreglarlo? Si sí a cualquiera, afirma. Si no a ambos, puedes omitirlo.
Las matemáticas de costo son generalmente triviales. Una lectura extra por acción de escritura, ~$0.001 por llamada, <1s latencia. Negligible contra la alternativa. Pero hay un punto donde estás apilando verificación sobre verificación y el agente está gastando más tiempo verificando que trabajando. Cuando estás 4–5 verificaciones de profundidad, escala a humano-en-el-bucle en lugar de añadir otra capa de verificaciones.
Próximo Paso: Audita la Tasa de Desajuste
Audita un agente de producción esta semana comparando sus eventos "task_complete" contra el sistema de registro de los últimos 7 días. Cuenta cuántas acciones completadas nunca cambiaron realmente el estado. La tasa de desajuste te dirá si estás enviando una máquina de alucinación elaborada o algo más cercano a lo real.
Si ese número te hace sentir incómodo, hemos construido sistemas para arreglar exactamente esto. Ponte en contacto.