wandres.dev
AI GATEWAY · gobernar la IA

Observabilidad: entender de verdad el gasto de IA

El panel de tu proveedor te da un total y ninguna respuesta. El gateway registra cada petición con su modelo, sus tokens de entrada y salida, su coste estimado, su duración y su estado, y te deja etiquetarla con los metadatos de tu negocio para que la pregunta pase de cuánto gastamos a qué función, qué cliente y qué modelo lo gastaron. Vemos qué guarda un log, cómo separar métricas de contenido sensible, y cómo cerrar el círculo midiendo también la calidad.

⏱ 16 min

Preguntar cuánto cuesta tu IA es fácil: el proveedor te da un número a fin de mes. Preguntar por qué cuesta eso es, sin instrumentación, sencillamente imposible, porque la única dimensión que el proveedor conoce de ti es la clave de API con la que llamaste. Toda la estructura que a ti te importa —qué funcionalidad, qué cliente, qué equipo, qué modelo, qué prompt desbocado— se ha perdido en el camino. La observabilidad del gateway existe para recuperar esa estructura, y su idea central es tan vieja como poco practicada: la unidad de análisis no es la petición, es la petición etiquetada.

🎯 Al terminar esta lección sabrás
  • Enumerar qué campos deja cada petición en el log y qué preguntas permite responder cada uno.
  • Separar el registro de métricas del registro de contenido con cf-aig-collect-log y cf-aig-collect-log-payload.
  • Atribuir coste a dimensiones de negocio con metadatos propios en lugar de a una clave de API.
  • Cerrar el círculo uniendo coste, latencia y calidad percibida sobre el mismo identificador de log.

Qué queda escrito de cada petición

El registro está activado por defecto y es sorprendentemente completo. Cada entrada guarda el prompt del usuario y la respuesta del modelo, el proveedor y el modelo concretos, la marca de tiempo, el estado de la petición, los tokens de entrada y de salida, el coste estimado, la duración y el agente de usuario del cliente que la originó. Cuando hay políticas de prevención de fuga de datos activas, las entradas afectadas añaden además la acción tomada y las reglas que coincidieron.

Merece la pena leer esa lista como un conjunto de preguntas más que de campos.

Campo La pregunta que responde
Tokens de entrada y salida Si el gasto viene del contexto que envías o de lo que el modelo escribe
Coste estimado Qué modelos concentran la factura y cuáles son ruido
Duración Dónde se va la latencia percibida por el usuario
Estado y error Qué proveedor te está fallando y con qué patrón
Estado de caché Cuánto ahorro real estás capturando frente al potencial
Agente de usuario Qué SDK, aplicación o Worker genera cada porción del tráfico

La distinción entre tokens de entrada y de salida es la más reveladora de todas y casi nadie la mira. Un gasto dominado por la entrada significa que estás reenviando contexto que podrías recortar, resumir o cachear; un gasto dominado por la salida significa que pides respuestas más largas de las que tu producto usa. Son dos enfermedades distintas con dos tratamientos distintos, y sin esa columna no puedes distinguirlas.

La duración merece una lectura igual de cuidadosa, porque agregarla en una media esconde justo lo que duele. Lo que percibe el usuario no es el tiempo medio sino la cola: el uno por ciento de peticiones que tardan cinco veces más y que se corresponden casi siempre con contextos largos o con reintentos silenciosos. Mirar percentiles altos y cruzarlos con tokens de entrada suele resolver en una tarde un problema de lentitud que llevaba meses atribuyéndose al modelo.

Y el estado de caché es la única columna que valida una decisión anterior. Activaste la caché en la lección previa con una hipótesis sobre tu tasa de repetición; esta es la casilla que dice si acertaste. Sin ella, cachear es un acto de fe con configuración.

⚠️
Registrar el prompt es registrar datos personales

Un log de IA no es un log de acceso: contiene lo que tus usuarios escribieron y lo que el modelo respondió. Eso lo convierte en un almacén de datos potencialmente sensibles con todas las obligaciones que ello implica. La cabecera cf-aig-collect-log-payload puesta a false es la respuesta precisa a este dilema: descarta los cuerpos de petición y respuesta pero conserva la metadata —modelo, proveedor, tokens, coste, duración, estado—. Sigues pudiendo analizar el gasto entero sin persistir ni una frase del usuario. Si necesitas desactivar la entrada completa para una petición, eso es cf-aig-collect-log.

Cada gateway tiene un límite de almacenamiento según tu plan, y cuando se llena los logs nuevos dejan de guardarse. Es un fallo silencioso especialmente cruel, porque el sistema sigue funcionando y solo se rompe tu capacidad de entenderlo. Activar el borrado automático de los más antiguos evita ese escenario. Y usar un gateway autenticado evita el otro: que tráfico ajeno infle tu almacenamiento y ensucie tus datos.

Los filtros del panel merecen un recorrido tranquilo alguna vez, porque revelan qué preguntas se anticipó que querrías hacer: por estado y tipo de error, por si la respuesta vino de caché, por proveedor y por modelo, por umbral de coste o de tokens, por duración, por valoración recibida, por clave o valor de metadato y por agente de usuario. Cada uno de ellos es una investigación distinta ya montada. El de coste con umbral, en particular, es el atajo más rápido a la pregunta que casi siempre importa: enséñame las cien peticiones más caras de ayer.

Metadatos: de la clave de API a la unidad de negocio

Aquí ocurre el salto cualitativo. El gateway no sabe nada de tus usuarios, tus equipos ni tus funcionalidades, y no puede inventárselo. Se lo tienes que decir tú, adjuntando hasta cinco pares de clave y valor a cada petición. Admite cadenas, números y booleanos; no admite objetos anidados.

Es un ejemplo de libro de un principio que reaparece en toda la observabilidad: el contexto solo puede añadirlo quien lo tiene, y hay que añadirlo en el momento en que existe. Cuando la petición ya salió, la información de a quién servía y para qué se ha perdido para siempre y ninguna herramienta posterior puede reconstruirla. Etiquetar es barato en el instante de llamar e imposible cinco minutos después.

const salida = await env.AI.run(
  "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
  { prompt: texto },
  {
    gateway: {
      id: "mi-gateway",
      metadata: {
        userId: usuario.id,
        equipo: usuario.equipo,
        funcion: "resumen-de-ticket",
        entorno: "produccion",
      },
    },
  },
);

Esas cuatro etiquetas convierten un total anónimo en un modelo analítico. Ahora puedes filtrar el gasto por función y descubrir que el resumen de tickets consume dos tercios de la factura; segmentar por cliente y detectar al que cuesta más de lo que paga; separar producción de pruebas y dejar de contar como negocio lo que era un experimento. Y como los límites de gasto se pueden acotar por esas mismas dimensiones, la etiqueta que hoy usas para entender es la que mañana usarás para poner un techo.

El límite de cinco entradas no es una molestia, es una disciplina útil. Obliga a elegir dimensiones estables y de baja cardinalidad, que son precisamente las que sirven para agregar: una función tiene diez valores posibles, un entorno tiene tres, un plan tiene cuatro. Meter ahí un identificador de sesión o una marca de tiempo produce grupos de un solo elemento sobre los que no se puede razonar. La regla es preguntarse si querrás sumar por esa etiqueta; si la respuesta es no, no es una dimensión, es un detalle del payload.

🧾

Etiqueta por función

La dimensión que más rápido paga: sin ella nunca sabrás qué parte de tu producto justifica la factura ni cuál habría que rediseñar.

👤

Etiqueta por cliente

Imprescindible en cualquier producto multi inquilino: es la única forma de calcular margen por cuenta en lugar de margen global.

🧪

Separa entornos

Sin distinguir pruebas de producción, cualquier análisis de tendencia está contaminado por el trabajo de tu propio equipo.

🏷️

Cinco y no más

Solo se guardan las cinco primeras entradas. Elige dimensiones estables y con poca cardinalidad antes que un cajón de sastre.

Cerrar el círculo: coste, latencia y calidad

Hay una tercera magnitud que el gateway registra pero que solo tú puedes rellenar: si la respuesta fue buena. Cada llamada desde un Worker deja su identificador accesible en env.AI.aiGatewayLogId, y con él puedes volver más tarde sobre esa misma entrada para adjuntar una puntuación, una valoración del usuario o metadatos que no conocías en el momento de llamar.

const salida = await env.AI.run(modelo, entrada, { gateway: { id: "mi-gateway" } });
const idLog = env.AI.aiGatewayLogId;

// mas tarde, cuando el usuario valora la respuesta
const gateway = env.AI.gateway("mi-gateway");
await gateway.patchLog(idLog, { feedback: 1, score: 90 });

Con eso el log deja de medir solo lo que gastaste y empieza a medir lo que obtuviste. La pregunta interesante ya no es qué modelo es más barato, sino cuánto cuesta una respuesta buena en cada modelo, que es una métrica completamente distinta y a veces invierte el ranking: un modelo el triple de caro que acierta a la primera puede salir más barato que uno económico al que hay que repreguntar tres veces.

La señal no tiene por qué ser un pulgar del usuario, que llega poco y sesgado. Sirve igual cualquier indicio implícito que ya produzca tu producto: si el usuario copió la respuesta, si reformuló la pregunta acto seguido, si el ticket se cerró sin intervención humana, si el JSON generado validó contra el esquema a la primera. Cualquiera de esas señales, adjuntada al log correcto, convierte una intuición de pasillo en una serie temporal.

flowchart LR
REQ[Peticion etiquetada] --> LOG[Entrada de log]
LOG --> M1[Tokens y coste]
LOG --> M2[Duracion y estado]
LOG --> M3[Feedback y score]
M1 --> DEC[Coste por respuesta util]
M2 --> DEC
M3 --> DEC
style DEC fill:#a6e3a1,color:#11111b
style LOG fill:#89b4fa,color:#11111b

Sacar los datos de donde nacen

Un panel es un buen sitio para investigar un incidente y un mal sitio para vigilar un negocio. Los datos del gateway acaban siendo útiles de verdad cuando salen de su interfaz y se juntan con el resto de tu telemetría, y para eso hay dos caminos complementarios.

El primero es la exportación continua de logs hacia tu propio destino, que te libera del límite de almacenamiento del gateway y te deja conservar el histórico tanto tiempo como tu análisis necesite. El segundo es la integración con OpenTelemetry, que emite las trazas de las llamadas de IA con el mismo vocabulario que ya usa el resto de tu plataforma: una petición de usuario deja de ser dos historias separadas —lo que hizo tu aplicación por un lado y lo que costó su IA por otro— y pasa a ser una sola traza donde la llamada al modelo es un tramo más, con su duración y su coste al lado del resto.

Queda un ajuste fino que marca la diferencia entre un panel orientativo y uno creíble: si negociaste tarifas propias con un proveedor, la configuración de costes a medida sustituye el precio de catálogo por el tuyo. Sin ese paso, todos tus análisis comparan modelos con precios que no son los que pagas, y las conclusiones se desvían justo donde más dinero hay en juego.

Con esas tres piezas colocadas —exportación, trazas y precios reales— el gasto de IA deja de ser un apartado especial que solo mira una persona y pasa a vivir donde ya viven tus otras magnitudes operativas. Ese es el objetivo final y también la prueba de que lo has hecho bien: cuando alguien de tu equipo puede preguntar cuánto costó en IA la última campaña, y la respuesta sale del mismo sitio del que salen la latencia y los errores, has terminado.

💡
Alertar sobre la derivada, no sobre el total

Una alerta sobre el gasto acumulado llega tarde por construcción: cuando salta, ya gastaste. La señal útil es el cambio de ritmo —coste por hora, tokens de entrada por petición, proporción de aciertos de caché— porque un despliegue que duplica el contexto se detecta en minutos ahí y en semanas en el total. Vigila pendientes y deja los techos para los límites de gasto, que sí saben frenar.

Sin atribución no hay ingeniería de costes, solo susto a fin de mes

La historia de la ingeniería de sistemas puede contarse como una sucesión de magnitudes que primero se sufrieron a ciegas y luego se instrumentaron. Nadie optimizó la latencia en serio hasta que hubo percentiles por endpoint; nadie arregló el consumo de memoria hasta que hubo perfiles por asignación. El coste de la inferencia está exactamente en esa fase previa en la mayoría de los equipos de 2026: se sufre, se comenta en reuniones, se intenta reducir con medidas de brocha gorda —cambiemos todos al modelo pequeño— y no se mide. La razón no es pereza, es que el proveedor no puede darte la dimensión que necesitas: él ve una clave de API llamando a un modelo, y tú necesitas ver una funcionalidad sirviendo a un cliente. Esa traducción solo puede hacerla quien conoce ambos mundos, y el único punto del sistema que los toca a la vez es tu propio punto de paso. De ahí que la observabilidad no sea una función más del gateway sino la que da sentido a todas las otras: sin ella, la caché es fe —crees que ahorra pero no sabes cuánto—, el límite de gasto es un número inventado, y elegir modelo es una discusión de opiniones. Con ella, cada decisión de este nivel se vuelve empírica y reversible: activas la caché y ves subir la proporción de aciertos, bajas el modelo de una función concreta y ves si el score se desploma o no se entera, subes un techo y ves a quién estabas frenando. La observabilidad convierte el gasto de IA de un hecho consumado en una variable de diseño. Y una vez que es una variable, ya no la sufres: la eliges.

⚔️ Instrumenta antes de optimizar
  1. Etiqueta todas tus llamadas con función, cliente y entorno, y resiste la tentación de añadir una sexta dimensión.
  2. Compara tokens de entrada frente a salida en tu función más cara y diagnostica cuál de las dos enfermedades tienes.
  3. Calcula tu proporción real de aciertos de caché y contrástala con la tasa de repetición que estimaste en la lección anterior.
  4. Decide si tu producto puede permitirse guardar prompts, y aplica cf-aig-collect-log-payload en consecuencia sin perder métricas.
  5. Adjunta una puntuación a las respuestas de una funcionalidad durante una semana y calcula el coste por respuesta útil de dos modelos distintos: elige con ese número, no con el precio por token.