Todos los recursos

La mayoría de las facturas de LLM están infladas por costumbre, no por necesidad real. Estas son las cuatro palancas que de verdad mueven la aguja (caché, enrutamiento, recorte de contexto y lotes), ordenadas por esfuerzo contra beneficio, con los comandos para aplicarlas y las fallas que, sin que te des cuenta, terminan costándote más de lo que ahorraste.

Controla los costos de tus apps de LLM sin sacrificar calidad

En resumen

  • Mide antes de optimizar. Registra los tokens de entrada y salida por request, agrupados por funcionalidad: casi siempre hay una funcionalidad descontrolada que se lleva la mayor parte de la factura.
  • La caché de prompts es la palanca que más rinde con menos esfuerzo: un cache read cuesta ~0.1x del input base, así que un prompt grande que se repite baja ~90% a partir de la primera llamada.
  • Enruta por dificultad. Opus 4.8 cuesta $5/$25 por 1M de tokens; Sonnet 4.6, $3/$15; Haiku 4.5, $1/$5. Manda lo fácil a un nivel más bajo y reserva el modelo fuerte para lo difícil.
  • Recorta la entrada, no la salida. Bajar max_tokens para ahorrar en la salida sale peor: el modelo corta la respuesta a la mitad y tienes que repetir toda la llamada.
  • Manda el trabajo que no corre prisa (reportes nocturnos, clasificación masiva) por la Batches API y paga la mitad.

La mayoría de las facturas de LLM están infladas por costumbre, no por necesidad real. Estás reenviando el mismo system prompt de 8K tokens en cada llamada, pasando todo por tu modelo más caro y metiendo documentos enteros en un prompt del que solo se usan tres párrafos. Nada de eso te da más calidad: solo se nota en la factura. Esta guía recorre las cuatro palancas que de verdad mueven la aguja, más o menos en orden de beneficio por esfuerzo, con los comandos para aplicarlas y las fallas que, sin que te enteres, terminan costándote más de lo que ahorraste.

00 · Requisitos previos

No puedes optimizar lo que no puedes ver. Antes de tocar cualquiera de las cuatro palancas, deja esto montado: es una tarde de setup, no un proyecto entero.

  • Una forma de leer el usage de cada response. El SDK de Anthropic devuelve response.usage con input_tokens, output_tokens, cache_read_input_tokens y cache_creation_input_tokens. Ese objeto es tu fuente de verdad; sin él, optimizas a ciegas.
  • Una etiqueta por funcionalidad en cada llamada. Da igual si es una columna en una tabla, una línea de log estructurado o un label de métricas: necesitas agrupar el gasto por para qué fue la llamada, no solo por día.
  • La tabla de precios al día, porque las palancas de abajo solo tienen sentido frente a números reales: Opus 4.8 cuesta $5 de entrada / $25 de salida por millón de tokens; Sonnet 4.6, $3 / $15; Haiku 4.5, $1 / $5. Un cache read sale a alrededor de 0.1x del precio del input base; un cache write de 5 minutos cuesta cerca de 1.25x.

Importante

Si de toda esta guía vas a hacer una sola cosa, que sea medir. Una y otra vez he visto cómo una sola funcionalidad mal acotada (un retry que reenvía el contexto completo, una ruta de debug que quedó registrando todo a máxima verbosidad) se lleva más de la mitad de la factura del mes. Eso no lo descubres mirando el total. Agrupa por funcionalidad y el culpable suele saltar a la vista en cinco minutos.

Con una línea de log mínima ya tienes para arrancar:

log({
  feature: "support-reply",
  model: res.model,
  inputTokens: res.usage.input_tokens,
  outputTokens: res.usage.output_tokens,
  cacheRead: res.usage.cache_read_input_tokens ?? 0,
})

Déjalo correr unos días, suma inputTokens y outputTokens por funcionalidad, multiplica por la tabla de precios y vas a saber exactamente hacia dónde apuntar.

01 · Caché de prompts: el 90% gratis

Si mandas el mismo prefijo largo en cada llamada (un system prompt congelado, un manual de producto, un bloque de few-shot, un set de documentos recuperados) estás pagando el precio completo del input por releer tokens idénticos. La caché corta eso de raíz. El modelo mental cabe en una frase: la caché es un match por prefijo, y basta con que cambie un solo byte en cualquier parte del prefijo para invalidar todo lo que viene después.

La API arma tu request en un orden fijo (tools, luego system, luego messages) y la cache key son los bytes exactos hasta cada breakpoint. De ahí sale una regla simple: lo estable primero, lo volátil al final. Tus instrucciones congeladas y los documentos recuperados van adelante; la pregunta real del usuario, la fecha de hoy y cualquier request ID van al final.

La economía

Un cache read cuesta cerca de 0.1x del precio del input base. Un cache write de 5 minutos cuesta cerca de 1.25x. Es decir, la primera llamada paga un pequeño extra por escribir y cada llamada posterior paga más o menos la décima parte. Con la ventana por defecto de 5 minutos, con dos reúsos ya sales ganando; en un endpoint con tráfico, donde el mismo prefijo se golpea decenas de veces por minuto, la parte cacheada sale prácticamente gratis.

Cómo hacerlo

Con el SDK de TypeScript, la forma más simple es el auto-caching de nivel superior, que marca por ti el último bloque cacheable:

const res = await client.messages.create({
  model: "claude-opus-4-8",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" }, // cachea el último bloque cacheable
  system: FROZEN_SYSTEM_PROMPT,          // estable: va en el prefijo cacheado
  messages: [{ role: "user", content: userQuestion }], // volátil: después del breakpoint
})

Atención

Hay dos trampas que se comen el hit rate sin hacer ruido. La primera: el prefijo mínimo cacheable depende del modelo (cerca de 4096 tokens en Opus 4.8 y Haiku 4.5, 2048 en Sonnet 4.6) y por debajo de eso la caché no hace nada y sin lanzar ningún error. La segunda: cualquier cosa no determinista en el prefijo (un timestamp, un UUID, un JSON.stringify sin keys ordenadas) cambia los bytes en cada request y escribes una entrada nueva cada vez. Verifícalo con usage.cache_read_input_tokens: si da cero entre requests idénticos, tienes un invalidador silencioso.

02 · Enrutamiento de modelos: deja de pagar de más por lo fácil

Tu modelo más fuerte es un exceso para casi todo lo que le mandas. Clasificar, reescribir algo corto, extraer un campo, enrutar un ticket: nada de eso necesita el nivel más alto, y la diferencia de precio es enorme. Opus 4.8 cuesta $25 por millón de tokens de salida; Haiku 4.5, $5. Eso es 5x de diferencia en la salida para trabajo donde el modelo barato rinde de sobra.

El patrón es poner un paso barato de triaje delante del caro. Manda todo primero a un nivel bajo y escala solo las llamadas que de verdad necesitan profundidad.

  • Arranca abajo y escala hacia arriba. Pon Sonnet 4.6 o Haiku 4.5 como tu modelo por defecto y sube a Opus 4.8 solo cuando haya señales: confianza baja, un flag de alto riesgo, una clasificación explícita de "esto es complejo".
  • Divide por subtarea, no por funcionalidad. Una misma funcionalidad suele mezclar pasos triviales con pasos difíciles. Corre la extracción en Haiku y el razonamiento en Opus, en lugar de mandar toda la funcionalidad a un solo modelo.
  • No cambies de modelo a media conversación si estás cacheando. Las cachés están atadas al modelo: cambiar de modelo dentro de una conversación cacheada descarta el prefijo cacheado. Elige el modelo al inicio de la sesión, o levanta un subagente aparte más barato para la tarea secundaria en vez de cambiarle el modelo al loop principal.

Un ejemplo concreto: una bandeja de soporte donde el 70% de los tickets son consultas simples de FAQ y el 30% requiere razonar de verdad sobre el historial de la cuenta. Mandar el 70% a Haiku y dejar el 30% en Opus baja el gasto de modelo casi a la mitad frente a correr todo en Opus, y sin perder calidad en los tickets difíciles, porque esos siguen recibiendo el modelo fuerte.

03 · Recorte de contexto: manda lo que el modelo realmente usa

Casi con seguridad metes en el prompt más de lo que el modelo lee. El instinto de "dale todo por si acaso" es justo lo que infla los tokens de entrada, y en Opus eso son $5 por millón que quizá no hace falta gastar. Dos jugadas resuelven casi todo.

Recupera, no vuelques

Si tienes una base de conocimiento, no pegues el documento entero en el prompt. Recupera los pocos fragmentos relevantes y manda solo esos. Un contexto enfocado de 2K tokens normalmente le gana a un volcado de 40K tanto en costo como en calidad de la respuesta: el modelo no se distrae con veinte páginas que no vienen al caso.

Nota

Hay una distinción entre exacto y aproximado que conviene respetar. La búsqueda vectorial devuelve lo parecido, no lo verdadero. Para "encontrar contexto relacionado" es perfecta; para cualquier cosa que tenga que ser correcta (la dirección de envío actual de un usuario, el saldo de hoy) usa búsqueda exacta. Confundir las dos es justo así como terminas mandando, con toda confianza, la dirección del año pasado.

Resume los turnos viejos

En una conversación larga, el transcript completo crece en cada turno y pagas por reenviarlo entero. Resume los turnos viejos en una nota corta acumulada y deja textuales solo los turnos recientes. En los modelos compatibles, la compactación del lado del servidor hace esto sola a medida que te acercas a la ventana de contexto, pero un simple "resume todo lo más viejo que los últimos N turnos" que controles tú mismo suele bastar y es del todo predecible.

Cuando recortes, cuenta los tokens contra el modelo real en vez de adivinar. No uses tiktoken: es el tokenizador equivocado para Claude y cuenta de menos. Usa el endpoint de conteo de tokens:

const { input_tokens } = await client.messages.countTokens({
  model: "claude-opus-4-8",
  messages: trimmedMessages,
})
// compara input_tokens antes/después de un recorte para ver cuánto ahorraste de verdad

04 · Procesamiento por lotes: la mitad de precio para lo que puede esperar

No todo necesita respuesta en este mismo segundo. Reportes resumen nocturnos, reclasificación masiva de un backlog, generar metadata sobre una tabla entera: ese es trabajo que no tiene a nadie esperando, y la Batches API lo corre con 50% de descuento sobre los precios estándar. La mayoría de los lotes termina en menos de una hora; el tope son 24.

La concesión es latencia a cambio de dinero: mandas hasta 100,000 requests de una sola vez, haces polling hasta que terminan y recoges los resultados. Para cualquier cosa síncrona y de cara al usuario, los lotes están de más: la espera mata la experiencia. Para cualquier cosa programada o de fondo, no usar lotes es regalar la mitad del ahorro.

const batch = await client.messages.batches.create({
  requests: rows.map((row) => ({
    custom_id: row.id,
    params: {
      model: "claude-haiku-4-5",      // combina lotes con enrutamiento para el máximo ahorro
      max_tokens: 512,
      messages: [{ role: "user", content: classifyPrompt(row) }],
    },
  })),
})
// haz polling de batch.processing_status hasta "ended", luego recoge los resultados

El mayor beneficio sale de apilar palancas: lote + modelo barato + un prefijo compartido cacheado en un trabajo de clasificación masiva puede dejar el costo por ítem en una fracción mínima de lo que costaría un baseline ingenuo con Opus a demanda.

Aplica estas palancas más o menos en el orden de arriba: la caché primero, porque es casi gratis; después el enrutamiento, luego el recorte y por último los lotes donde el workload lo permita. Pero nada de esto sirve hasta que midas: instrumenta primero, encuentra la única funcionalidad que se está comiendo la factura y, por lo general, vas a arreglar más en una tarde de trabajo bien dirigido que en un mes de andar adivinando.

Puntos clave

  • Mide primero. Loguea los tokens por request agrupados por funcionalidad: la factura casi siempre la domina una sola cosa arreglable que no se ve en el total.
  • La caché es la victoria más barata: lo estable primero, lo volátil al final, verifica con cache_read_input_tokens y ojo con el prefijo mínimo, que cambia según el modelo.
  • Enruta por dificultad y recorta la entrada, no la salida: ahí el ahorro es grande y predecible sin tocar la calidad.
  • Manda por lotes todo lo que nadie esté esperando para pagar la mitad, y apila lote + modelo barato + prefijo cacheado para los recortes más profundos.

Preguntas frecuentes

¿Un modelo más barato no va a bajar la calidad?

Solo si enrutas a ciegas. La idea del enrutamiento es ajustar el modelo a la dificultad del trabajo: lo fácil (clasificar, extraer campos, reescribir algo corto) se va a un modelo más barato que rinde de sobra, y lo difícil se queda en el fuerte. La calidad cae cuando mandas trabajo difícil a un modelo débil, no cuando dejas de mandarle lo trivial al caro. Agrega una señal de confianza o de riesgo para que la escalada se dé sola.

Agregué cache_control pero la factura no se movió. ¿Por qué?

Revisa usage.cache_read_input_tokens en requests repetidos. Si da cero, la caché nunca está pegando. Las dos causas típicas: tu prefijo está por debajo del tamaño mínimo cacheable (~4096 tokens en Opus 4.8, 2048 en Sonnet 4.6), así que nunca cachea y sin avisarte; o algo no determinista en el prefijo (un timestamp, un UUID, JSON serializado sin keys ordenadas) cambia los bytes en cada llamada. Haz un diff del prompt renderizado de dos requests idénticos para dar con el invalidador.

¿Por qué no simplemente bajar max_tokens para controlar el costo de la salida?

Porque por lo general te sale más caro. Si pones max_tokens muy bajo, el modelo corta la respuesta a la mitad; entonces repites toda la llamada con un límite más alto y terminas pagando la entrada dos veces más la repetición. Recorta la entrada sin miedo, que ahí el ahorro es predecible, y dale a la salida el espacio para terminar. Baja max_tokens solo cuando sepas de antemano que la salida va a ser corta, como una clasificación de una sola palabra.

¿Puedo combinar lotes con caché y enrutamiento?

Sí, y es justo apilándolos donde sale el mayor ahorro. Un trabajo de clasificación masiva que corre con un modelo barato (Haiku), por la Batches API (50% menos) y contra un prefijo compartido cacheado deja el costo por ítem en una fracción mínima de lo que costaría un baseline ingenuo con Opus a demanda. La única restricción es la latencia: los lotes son para trabajo que nadie está esperando, así que no los uses para nada síncrono y de cara al usuario.

¿Cómo cuento tokens con precisión al recortar el contexto?

Usa el endpoint de conteo de tokens (client.messages.countTokens) con el mismo model ID con el que vas a correr la inferencia: el conteo depende del modelo. No uses tiktoken ni otros tokenizadores de OpenAI; cuentan de menos los tokens de Claude, cerca de un 15–20% en texto plano y mucho más en código. Cuenta el mismo contenido antes y después de un recorte para medir exactamente cuánto ahorraste, en vez de andar adivinando.

¿Prefieres que lo hagamos por ti?

Esto mismo lo construimos para negocios como el tuyo. La primera conversación es gratis y sin compromiso.

Escríbenos por WhatsApp

Escríbenos por WhatsApp

Escanéalo con tu teléfono para escribirnos por WhatsApp.

Escanéalo con tu teléfono para escribirnos por WhatsApp.

¿Estás desde el teléfono y no puedes escanear? Escríbenos a info@ilustrari.com

Primera conversación gratis. Te responde el fundador.

Recursos relacionados