08
septiembre
2026

El evento que no existía: dos días caídos en mi equipo de agentes IA (y el antídoto «Ignorable»)

posted in Programacion, Tecnología 12.48 PM

Infografía del incidente: el veneno, el log de la sesión, el cierre total, el antídoto ignorable y la vacuna
TL;DR: Un plugin nuevo de búsqueda envenenó el log de dos sesiones de mi equipo de agentes. Un solo evento con un tipo que el harness no conocía, y las sesiones enteras se negaron a volver a abrirse. Dos días de cirugía de ficheros con ChatGPT de ayudante, un antídoto llamado «Ignorable», y un puñado de reglas nuevas que ahora son ley en el equipo. Si tocas DSH por dentro, esto te ahorra el mismo viaje.

El plan era perfecto (spoiler: no lo era)

Mi equipo de agentes trabaja sobre DeepSeek Harness (DSH): varias sesiones reales del chat que colaboran como un equipo humano —supervisor, archivista, soporte— coordinándose por un bus de ficheros markdown. Los agentes son efímeros; el equipo vive en los ficheros. Esa frase, que suele ser poesía organizativa, se volvió literal muy pronto.

El encargo parecía de los sencillos: el proveedor de búsqueda DeepSeek se quedó sin saldo («Insufficient Balance»), así que habría que montar la API de Brave Search (2.000 consultas gratis al mes) como paquete nuevo, aditivo y reversible. Mi agente de creación —llamémosle el constructor— hizo un trabajo de manual: verificó los tipos del seam web, el plano de credenciales, los errores, el mapeo de resultados. Desplegó, reinició, buscó: fuentes citables al primer intento.

Diez segundos de gloria. Lo que nadie probó ese día fue otra cosa: volver a abrir la sesión al día siguiente.

El monstruo

Al recargar, la sesión no abría. El error era de los que te hielan la sangre precisamente por lo tranquilizador de su redacción:

SessionFormatUnsupportedError: event type "web/brave-search-request" is unknown
to this harness and not marked ignorable

Traducción: sé leer cada evento de tu historial menos uno, y como no sé qué es, no voy a reconstruir nada. Ni media sesión. Todo el log, rechazado. Y no solo una sesión: cualquier sesión que hubiera usado la búsqueda con el plugin nuevo.

La anatomía del engaño, para quien no conozca las tripas de DSH:

  • El historial de una sesión es un log de eventos tipados: líneas JSONL comprimidas en frames Zstandard. Cada evento lleva un type.
  • El harness mantiene un catálogo interno de tipos que conoce (los que declara su propio código).
  • Al escribirsession.append() acepta cualquier cadena que le eches. Sin validación. Sin avisos.
  • Al leer, se exige que cada tipo esté en el catálogo o lleve una marca de compatibilidad llamada ignorable.

Escribir libre, leer estricto. La asimetría perfecta para una trampa. El plugin —copiando el patrón del proveedor oficial de búsqueda de DeepSeek, que registra sus peticiones como evento de sesión— inventó un tipo nuevo: web/brave-search-request. El oficial puede hacerlo porque su tipo está horneado dentro del catálogo del harness. Un plugin externo, por construcción, jamás estará en esa lista. Un solo evento y la sesión entera, ilegible.

La ironía final: el equipo  verificó que la búsqueda funcionaba. Falló en imaginar que la pregunta correcta no era «¿funciona?» sino «¿funciona mañana?».

Dos días con el meticuloso (y el meticuloso dio en el clavo)

La reparación no era trivial: los logs están comprimidos en frames binarios, y no hay botón de «ignora esto». Ahí entró la pareja improbable: yo, con dos sesiones muertas, y ChatGPT, que es meticulosamente persistente en sus comprobaciones, infatigable como una hormiga arqueóloga.

Entre los dos desenterramos el procedimiento — que ahora está documentado y probado en el KB del equipo:

  1. Localizar el log: ~/.dsh/sessions/<workspace>/<sessionId>/session.jsonl.zstd (múltiples frames Zstandard por fichero).
  2. Descomprimir reutilizando el backend oficial (JsonlSessionPersistence.readRaw(), vía prototipo con sus campos de compresión) o por la vía bruta: partir el fichero por los magic bytes de zstd (28 B5 2F FD) y descomprimir frame a frame.
  3. Encontrar los eventos del tipo conflictivo (un evento en una sesión, cinco en la otra — cada búsqueda con el plugin, un clavo más).
  4. No borrarlos. Añadir "ignorable": true al sobre de cada uno.
  5. Recomprimir en frames con checksum y sustituir el fichero.
  6. Verificar releyendo con el propio backend y abriendo la sesión en DSH.

Y aquí está la lección que da nombre al post: «Ignorable».

El antídoto «Ignorable»

Cuando el harness se encuentra un tipo de evento que no conoce pero que lleva "ignorable": true, hace lo correcto: lo salta y sigue leyendo el resto del historial. Es, literalmente, la válvula de compatibilidad hacia adelante que el sistema trae de fábrica para estos casos. Nosotros no la inventamos; descubrimos que existía, tarde y a fuego.

¿Por qué marcar y no borrar? Porque borrar el historial de un agente es editar su pasado: pierdes secuencias, rompes continuidad, y encima mientes al respecto. Marcar como ignorable conserva todo: la historia intacta, las secuencias coherentes, y el log vuelve a ser legible. El evento envenenado se convierte en una cicatriz documentada en lugar de una amputación.

Así que sí: «Ignorable» es el antídoto universal para resucitar cualquier sesión que una de nuestras criaturas haya envenenado con tipos que no existen.

Pero atención, que aquí está la mitad de la verdad: el antídoto no es la vacuna. Si tu plugin sigue escribiendo tipos desconocidos, volverás a necesitar cirugía la próxima vez. La vacuna es otra y es de sentido común una vez la has visto:

Un plugin externo no inventa tipos de eventos de sesión. Nunca. Verifica el vocabulario en la fuente antes del primer dispatch.

Lo que cambió en el equipo (para que esto no vuelva a pasar)

  • El plugin se corrigió por construcción: la versión 1.1.0 no appenda nada al log de sesión (para un buscador, el evento era redundante: la query ya queda registrada en el tool/call de la herramienta). El histórico de eventos envenenados sigue ahí, marcado, y no molesta a nadie.
  • Test de regresión rojo/verde: hay un ensayo automatizado donde la versión vieja, ejecutada contra un contexto simulado, demuestra que appenda el tipo prohibido, y la nueva demuestra cero appends. El modo de fallo queda cazado para siempre, no en la memoria de nadie.
  • Sesión canario: nació Test Lab Creator, una sesión sacrificable donde se estrena todo lo nuevo. Si muere, se borra y se recrea en un minuto. Las sesiones buenas ya no prueban primero.
  • El ritual del doble rebote: probar algo no es usarlo y ver que funciona; es reiniciar, reabrir y comprobar que sigue vivo. El fallo no estaba en la búsqueda: estaba en el día siguiente.

Las reglas, para quien esté construyendo lo suyo

  1. Verifica los contratos en la fuente, no en la plantilla que estás copiando. Que el código oficial lo haga no significa que puedas hacerlo tú: primero puede lo first-party.
  2. Prueba la recarga, no solo la ejecución. El error más caro es el que aparece mañana.
  3. Canario antes que producción. Una sesión desechable cuesta un minuto; una sesión de trabajo envenenada, dos días.
  4. Si un log se envenena: «Ignorable», no borrón. Marca los eventos desconocidos, conserva la historia.
  5. El rollback tiene que ser de una línea. Todo lo que despliegues, que se pueda desmontar quitando dos filas de un YAML.

Epílogo, sin buscar culpables

La cadena del fallo tuvo tres eslabones: un diseño que pedía el evento, una plantilla oficial que lo normalizaba, y un constructor que verificó todo menos el catálogo de vocabulario. Falló el sistema, no solo el último eslabón — y por eso la respuesta fue institucional, no un castigo: el antídoto documentado, la vacuna convertida en regla, el canario en plantilla.

Al final, la frase de fundación del equipo se cumplió con crueldad de lema: los agentes son efímeros; el equipo vive en los ficheros. Aquel fin de semana los agentes no solo no murieron: quedaron mejor de lo que estaban. Con una cicatriz, un antídoto y un laboratorio donde muerden las próximas criaturas.

El equipo vive en los ficheros. La cicatriz también.

Deja un comentario