Llega un refactor. Lo entregaron tres agentes —uno leyó el diff y resumió la intención, otro reescribió los call sites, otro ejecutó la suite de tests y la reportó en verde— y los tres registraron éxito. Los logs concuerdan: archivos leídos, modelo llamado, tests invocados, parche aplicado, cero errores. Una semana después producción revienta con un input que ningún test cubría, el rollback es feo y el post mortem no sabe decir cuál de las decisiones de los agentes fue la equivocada. El ingeniero de guardia recorre durante una hora un flujo plano de eventos con timestamp y no aprende nada, porque el fallo no está en ningún evento suelto. Está en el espacio entre ellos.
Ese espacio es exactamente lo que un log no puede enseñarte y un trace sí. A medida que los sistemas de agentes pasan de una llamada a un modelo a una malla coordinada de llamadas, la distancia entre «tenemos logging» y «podemos depurar esto» se vuelve la distancia entre un sistema que operas y un sistema que se cae después de la demo.
Por qué los logs de eventos dejan de servir
El logging por eventos se pensó para un mundo de pasos discretos orquestados desde un centro. Llega una petición, ocurren unas pocas cosas conocidas en un orden conocido, registras cada una y, cuando algo se rompe, lees la línea donde se rompió. Ese modelo aguanta hasta que el control deja de estar centralizado.
Un sistema multiagente no tiene centro. Los agentes se pasan resultados parciales, deciden en función de lo que recibieron, y los fallos interesantes son emergentes: información que era correcta cuando la produjo el agente A ya no está cuando la necesita el agente C; dos agentes se esperan mutuamente; el error de uno envenena en silencio un contexto compartido del que dependen otros tres. Nada de eso produce una línea de error. Cada agente hizo su trabajo y registró éxito. El log es un montón de afirmaciones verdaderas que juntas no explican nada.
La investigación publicada sobre fallos de agentes le pone números duros a esto: traces de ejecución en muchos frameworks muestran tasas de fallo que serían alarmantes en cualquier otro software, y se agrupan en problemas de diseño del sistema, desalineación entre agentes y huecos de verificación. El hilo común es que los fallos viven en la coordinación, y la coordinación es invisible para un logging por componente.
Qué es un trace, exactamente
Un trace no es un log más sofisticado. Es otra estructura de datos: un árbol, no un flujo.
Cada unidad de trabajo es un span —una generación del LLM, una recuperación, una tool call— y cada span lleva su propio ID y el de su padre. Los spans de una misma petición comparten un único trace ID. Ese es todo el truco, y basta para reconstruir la forma causal completa de una petición a través de cada frontera de agente y de tool: qué llamó a qué, en qué orden, con qué inputs y dónde se fueron el tiempo y los tokens. Un log plano te dice que pasaron quince cosas. Un trace te dice que este span provocó esos tres, que alimentaron aquel otro, que es donde el contexto se truncó.
Dos cosas convierten un árbol de trace en un instrumento de depuración y no en un simple diagrama de tiempos:
- Contexto semántico en el span. No solo «ejecutó la recuperación», sino qué recuperó, por qué se eligió esa tool, con cuánta confianza y qué memoria leyó. La secuencia muestra qué pasó; el contexto semántico muestra por qué el agente creyó que debía hacerlo.
- Marcas de versión en el span. Qué versión de prompt, qué config de tool y qué revisión de agente estaban vivas cuando corrió este span. En cuanto tus agentes empiezan a adaptarse a partir del feedback, esto es lo único que te permite conectar un fallo nuevo con el cambio que lo causó. Pero eso merece su propia discusión.
La taxonomía de fallos que estás depurando de verdad
Una vez que ves el árbol, los modos de fallo multiagente que se repiten dejan de ser misterios y pasan a ser formas reconocibles:
- Colapso de contexto — la ventana se llena de output intermedio y un valor crítico anterior (un score de confianza, un flag) se trunca antes de llegar al agente que lo necesitaba.
- Errores en cascada — el output equivocado pero plausible de un agente se convierte en el input confiable de otro, y el error se acumula río abajo.
- Deadlocks de coordinación — dos agentes se esperan mutuamente.
- Tormentas de retries — una mala coordinación dispara reintentos exponenciales que parecen un pico de carga.
- Contaminación de contexto — un agente escribe datos malos en memoria compartida y todos los lectores los heredan.
Un log plano pinta todo esto como «todo tuvo éxito, el output estuvo mal». Un trace pinta cada caso como una imagen concreta y con arreglo conocido.
La observabilidad es parte del build, no un añadido
El error que más vemos es tratar el tracing como algo que atornillas una vez que el agente funciona en la demo. Para entonces ya es tarde y caro: estás metiendo instrumentación a la fuerza en un sistema cuyos fallos ya no puedes ver. La observabilidad es arquitectura. Qué captura un span, dónde están las fronteras del trace y cómo se propaga el contexto entre agentes son decisiones que se toman cuando diseñas, o no se toman bien. Es la misma lección que poner un verificador en el runtime y escribir el runbook de las 3 a.m. antes de que sean las 3 a.m.: el sistema que sobrevive a producción es el que se construyó para poder mirarlo por dentro.
«La IA se equivocó» no es una afirmación que puedas depurar. Es el sonido de un equipo que entregó un sistema multiagente con logs en lugar de traces y que ahora hace arqueología en vez de ingeniería.
Así que construye el tracing desde el primer span. El instrumento que te saltas al diseñar es justo el que vas a buscar durante el incidente, y para entonces ya no hay tiempo de añadirlo.