Luis Juarros

Nuestro sistema dijo que habíamos agotado todos los proveedores. Era mentira.

Nuestro sistema dijo que habíamos agotado todos los proveedores. Era mentira.

Durante semanas estuvimos optimizando el proveedor equivocado.

No por falta de datos. Al contrario: teníamos un mensaje de error clarísimo, repetido cientos de veces en los logs, que decía exactamente lo que estaba pasando. ALL_FALLBACKS_EXHAUSTED. Habíamos probado todos los proveedores de generación de vídeo y ninguno había devuelto nada utilizable.

Era mentira. No habíamos probado ni la mitad.

La explicación llega más abajo, porque el camino hasta ella es lo interesante. Pero el resumen es este: el mensaje de error no describía la realidad, describía una suposición. Y nosotros la creímos, porque venía de nuestro propio sistema y sonaba a diagnóstico.

Si estás construyendo un producto encima de modelos de IA, esto te va a pasar. No es una cuestión de si tu equipo es bueno. Es una propiedad estructural de los sistemas hechos de muchas etapas que no se pueden verificar entre sí.

Qué hay realmente dentro de «generar un vídeo con IA»

Desde fuera, generar un vídeo parece una acción. El usuario sube fotos de un inmueble, pulsa un botón y unos minutos después tiene un vídeo de marketing con voz en off, música y subtítulos.

Por dentro no es una acción. Es una cadena:

  1. Input — fotos, datos del inmueble, preferencias.
  2. Análisis — qué se ve en cada imagen, qué estancia es, qué merece protagonismo.
  3. Guion y voz — redacción del texto comercial y síntesis de audio.
  4. Generación de clips — cada foto se convierte en movimiento.
  5. Evaluación — ¿lo generado es utilizable?
  6. Montaje — clips, voz, música y transiciones se unen en un archivo.
  7. Checks finales — subtítulos, marca de agua, duración, formato de entrega.

Siete estaciones. Varios modelos distintos, de varios proveedores distintos, cada uno con su propia latencia, su propio formato de error y su propia idea de lo que significa «correcto». Ninguno sabe nada de los demás.

Y esa es la parte que rompe la intuición que traemos del software tradicional.

Los errores no se quedan quietos

En un sistema clásico, un error es un evento local. La consulta falla, devuelves un 500, el usuario reintenta. Molesto, pero contenido.

En una cadena de modelos generativos hay tres clases de fallo que no se comportan así.

Clase 1: el fallo que no falla

Una etapa devuelve algo perfectamente válido a nivel técnico y perfectamente inútil a nivel de producto. Un clip con un artefacto en el encuadre. Un guion doscientos milisegundos más largo de lo que debería, que hace que la voz no encaje con la imagen. Un plano que técnicamente contiene la cocina, pero enseña sobre todo la puerta del horno.

La etapa siguiente lo hereda sin protestar. La siguiente también. Al final del proceso hay un archivo correcto, entregable y malo. El sistema lo marca como éxito. Nadie se entera hasta que lo ve un cliente.

Un status 200 no es una medida de calidad. Es una medida de que la conexión HTTP funcionó.

Clase 2: el fallo que no hace ruido

Peor todavía. Tuvimos una etapa de post-proceso —los subtítulos— que en cierta condición de fallo no devolvía error ni resultado. Simplemente no devolvía nada.

El proyecto se quedaba esperando. Para siempre.

Sin excepción que capturar, sin alerta que disparar, sin cola de descarte donde aparecer. Desde el panel de control, un proyecto «en proceso» indefinidamente. Desde el punto de vista del cliente, un producto que no funciona y que tampoco tiene la decencia de decírselo.

Un error es información. Un cuelgue silencioso es ausencia de información, y es mucho más caro de detectar.

Clase 3: el fallo que miente

Y llegamos al mensaje del principio.

Nuestra generación de clips tiene una cascada de proveedores: si el primero falla o tarda demasiado, se prueba el segundo, luego el tercero. Es un patrón sensato. Cada intento tiene su propio timeout, y la suma de todos daba un presupuesto total de unos 1.500 segundos.

El contenedor que ejecutaba esa cascada tenía un timeout de 600 segundos.

El proceso moría a mitad de camino. Y por cómo estaba escrito el manejo de errores, lo que quedaba registrado era el estado final del bucle: «no quedan proveedores por probar». Literalmente cierto desde dentro de la función, y completamente falso respecto a lo que había pasado.

Dos capas de configuración. Cada una razonable por separado. Nadie las había mirado juntas. Resultado: semanas de trabajo dirigidas a un problema que no existía, guiadas por nuestro propio sistema.

Otros dos que duelen

El frame congelado que le achacamos al modelo

Durante meses asumimos que el último segundo congelado de algunos clips era una limitación del modelo de vídeo. Lo dábamos por hecho. Lo comentábamos en reuniones como si fuera el clima. No lo era: nunca le enviábamos el parámetro de duración, así que el modelo aplicaba su valor por defecto y nosotros rellenábamos el resto. Un campo. En nuestro código.

Cuando no tienes forma de verificar una etapa, cualquier explicación externa suena razonable.

La ausencia de checks no se siente como ignorancia. Se siente como conocimiento del dominio.

El flag que recortó el vídeo a la música

En el montaje usábamos un flag de FFmpeg que corta la salida a la duración de la pista más corta. Con voz en off, la pista más corta era el vídeo y todo iba bien. Sin voz en off, la pista más corta pasaba a ser la música: treinta segundos. El vídeo entero se recortaba a treinta segundos.

Seis estaciones de trabajo perfecto, un flag en la séptima, y el producto final destruido. La cadena vale lo que vale su última estación.

El cuello de botella no estaba donde mirábamos

Un último dato, porque cambió cómo pensamos el sistema entero.

Nuestras colas estaban configuradas para despachar hasta 1.000 trabajos en paralelo. Sonaba a capacidad. En realidad, la etapa de montaje —la que junta todo y hace el trabajo pesado de vídeo— soportaba alrededor de 10 en paralelo antes de degradarse.

La capacidad real del sistema era 10. Todo lo que despachábamos por encima de eso no producía más vídeos: producía más cola, más trabajos a medias ocupando memoria y más timeouts aguas arriba que a su vez generaban reintentos, que a su vez generaban más cola.

Cualquier persona que haya pisado una fábrica reconoce esto al instante. Una línea de producción funciona a la velocidad de su estación más lenta. Meter más material por la entrada no aumenta la producción: aumenta el inventario en curso, y el inventario en curso es un coste, no un logro.

Nosotros tardamos en verlo porque estábamos pensando en términos de software: «escala horizontalmente». Pero un pipeline con estaciones de capacidad desigual no escala horizontalmente. Se atasca.

Lo que hacemos ahora: quality gates

Nada de lo anterior se arregla con un modelo mejor. Se arregla con control de calidad entre estaciones. Esto es lo que hemos ido incorporando:

1. Un contrato por etapa, verificado. Cada estación declara qué produce y qué garantiza: duración, dimensiones, presencia de pista de audio, encuadre mínimo. Nada pasa a la siguiente sin cumplirlo. Es aburrido. Es lo que más fallos ha capturado.

2. Evaluadores, no códigos de estado. «La llamada funcionó» y «el resultado sirve» son dos afirmaciones distintas y hay que medirlas por separado. Los checks deterministas son baratos: ponlos todos. Los evaluadores basados en modelos, donde compensen.

3. Presupuestos que caben dentro de su contenedor. Toda cadena de reintentos tiene un presupuesto total, y tiene que ser demostrablemente menor que el timeout del proceso que la ejecuta. Si no lo es, tu sistema no reporta lo que pasó: reporta dónde se quedó.

4. Trazabilidad de la producción. Qué modelo, qué proveedor y qué parámetros generaron cada pieza, en base de datos, no solo en logs. Lo aprendimos tarde: cuando quisimos comparar calidad por proveedor, la evidencia ya había rotado. Si no lo persistes, no puedes atribuir ni calidad ni coste, y sin atribución no puedes decidir nada.

5. Un estado terminal obligatorio. Ninguna etapa puede quedarse en «en proceso» indefinidamente. Todo trabajo acaba en éxito o en fallo explícito, con watchdog si hace falta. Un fallo ruidoso es infinitamente más barato que un cuelgue silencioso.

6. Contrapresión desde el cuello de botella. La capacidad de entrada se dimensiona según la estación más lenta, no según lo que aguanta la cola.

La analogía industrial va en serio

No es una metáfora bonita para un post. Las fábricas llevan un siglo resolviendo exactamente este problema, y su vocabulario es directamente aplicable:

  • Estación — cada etapa del pipeline, con su capacidad y su tiempo de ciclo.
  • Control de calidad en proceso — se inspecciona entre estaciones, no solo al final, porque una pieza defectuosa que avanza consume trabajo que ya no se recupera.
  • First-pass yield — qué porcentaje sale bien a la primera, sin reintentos. Es la métrica que de verdad te dice cómo está el sistema, y casi nadie la mide en pipelines de IA.
  • Inventario en curso — trabajos a medias. Cuesta dinero y esconde problemas.
  • Andon — cualquier estación puede parar la línea. Un cuelgue silencioso es exactamente lo contrario: la línea sigue y nadie tira del cable.

Un pipeline de IA es una planta de producción con estaciones no deterministas. Eso hace el control de calidad más necesario, no menos: en una fábrica clásica, si la estación 4 funcionó ayer, probablemente funcione hoy. Con modelos generativos, esa garantía no existe.

Por dónde empezar mañana

Si tienes un producto de IA en producción, tres preguntas que se responden en una tarde y que suelen doler:

  1. ¿Cuál es tu first-pass yield? De cada 100 trabajos, ¿cuántos salen bien a la primera, sin reintento ni intervención? Si no sabes medirlo, ya tienes el primer proyecto.
  2. ¿Tu presupuesto total de reintentos cabe dentro del timeout del proceso que los ejecuta? Suma los timeouts. Compáralo. Es una multiplicación.
  3. ¿Puede algún trabajo quedarse sin estado terminal? Busca el trabajo más antiguo que siga «en proceso» en tu base de datos. La respuesta suele estar ahí.

Una llamada a una API se comprueba con un try/catch.

Una cadena industrial se comprueba con control de calidad en cada estación.

Un sistema de IA en producción es lo segundo, y a nosotros nos costó bastante más de lo que me gustaría admitir entenderlo.