Skip to main content
Entre bastidores
Entre bastidoresEngineeringDecisions

Cuando nuestro proveedor de IA retiró el modelo

Un cambio de modelo, respuestas vacías sin ningún error y una factura que de repente se multiplicó. Qué se rompió y por qué la causa era tan difícil de ver.

Publicado el por AlphaCloud5 min de lectura

En julio de 2026 varias funciones dejaron de funcionar a la vez: el chat IA, las respuestas de IA en los tickets, los resúmenes de tickets, el constructor de servidores, además de la moderación y la traducción de ideas y reportes de bugs.

La causa no era nuestro código. Nuestro proveedor de IA había retirado el modelo que usábamos.

Primera parte: el modelo ya no está

Esa es la parte obvia, y se arregló rápido. Un proveedor deja de ofrecer un modelo, pones el sucesor y listo.

El incidente de verdad empezó después.

Segunda parte: respuestas vacías, sin errores

Tras el cambio llegaban respuestas. Solo que a veces estaban vacías. No cortadas, no malformadas, sino una respuesta completamente vacía sin ningún error.

La causa está en una diferencia que el nombre del modelo no revela: el sucesor es un modelo de razonamiento. Antes de la respuesta propiamente dicha, primero escribe una cadena de pensamiento interna en un campo aparte. Ambos tiran del mismo presupuesto de tokens.

Si el pensamiento agota el presupuesto, vuelve una respuesta que formalmente está bien: el proveedor informa «longitud alcanzada» y el campo de contenido está vacío. Ningún error, ninguna pista, nada que pudiera ver un try/catch.

Lo reprodujimos con una prueba real: un presupuesto de cinco tokens ya basta para producir una respuesta vacía en cada petición.

Tercera parte: la factura

En la llamada más cara, el constructor de servidores IA, habíamos puesto un presupuesto generoso porque allí se generan respuestas largas. Así que el pensamiento no chocaba con ningún límite, simplemente seguía.

Las consecuencias llegaron por dos lados a la vez. Las peticiones tardaban tanto que se topaban con timeouts. Y costaban varias veces más, porque cada vez se pagaba una cadena de pensamiento completa e invisible. Al final llegó un mensaje de error que no teníamos previsto: saldo insuficiente.

La solución es una línea. El pensamiento se puede desactivar en el cuerpo de la petición, y entonces el modelo vuelve a comportarse como el antiguo. Desde entonces esa línea va en cada llamada, y en el repositorio hay más de un puñado.

Cuarta parte: lo que encontramos al limpiar

Si de todas formas estás tocando cada llamada a la IA, las miras. Eso sacó a la luz un segundo tema, sin relación con el incidente pero igual de caro.

Los proveedores cobran bastante menos por un prompt cuyo comienzo no cambia y se puede servir desde una caché. La diferencia entre un acierto y un fallo es de aproximadamente treinta veces.

Ese descuento no se consigue con un flag. Lo probamos expresamente: un campo del cuerpo de la petición pensado para eso tuvo un efecto nulo; la caché se aplica de todos modos automáticamente. La única palanca es el orden dentro del prompt, de lo estable a lo variable.

Cuatro sitios en los que habíamos hecho justo lo contrario:

Un valor variable dentro del bloque fijo de instrucciones. La comprobación de duplicados de los reportes de bugs tenía el nombre del módulo en el texto de instrucciones. Eso le daba a un bloque por lo demás idéntico su propia línea de caché por módulo. El valor pertenece a la parte variable, donde cambia de todos modos.

Lo nuevo antes de lo estable. La misma comprobación enviaba el nuevo reporte de bug antes de los hasta cincuenta candidatos de comparación. Eso convertía el prompt más grande de la API en un fallo total permanente.

Datos por usuario en la parte fija. El chat IA público tenía las notas sobre el usuario concreto en el bloque de instrucciones, es decir, antes del historial compartido del canal. Eso daba a cada usuario del mismo canal su propio comienzo, y el historial compartido se pagaba por separado para cada usuario.

Una ventana deslizante sobre el historial. Enviar los últimos N mensajes con cada mensaje desplaza el comienzo con cada mensaje. Entonces el historial completo es un fallo permanente. Lo correcto es recortar por bloques, o sea, rara vez y de forma sustancial.

Y un detalle que parece quisquilloso y no lo es: una lista dentro de un prompt ordenada por un solo criterio puede cambiar de orden en caso de empate. Una sola línea intercambiada en mitad del bloque de instrucciones cuesta el acierto de todo lo que viene detrás. Las ordenaciones dentro de los prompts necesitan un criterio de desempate inequívoco.

Lo que nos llevamos

Un cambio de modelo no es un cambio de configuración. El nombre parece un valor en una variable. Detrás había un comportamiento distinto, y ese comportamiento rompió nuestro código, no la interfaz.

El fallo peligroso fue el silencioso. Que el modelo antiguo desapareciera se vio al instante y se arregló en minutos. Las respuestas vacías volvían como respuestas formalmente válidas y podrían haber seguido así indefinidamente.

La medición pertenece a cada llamada. Ahora registramos la tasa de aciertos de caché en cada una. Una llamada bien construida se estabiliza en una tasa alta tras la primera petición; una mal construida se queda en cero. Eso lo ves en una línea de log y en ningún otro sitio, porque las dos funcionan.

Y la conclusión incómoda: usar el modelo de otro significa que parte de tu producto depende de una decisión que se toma en otra parte. Por eso, desde agosto, cualquier servidor puede apuntar las funciones de IA a su propio endpoint en lugar de depender del nuestro.

Más artículos