Velocidad y costo de una app de IA
Cinco formas sencillas de que una app de IA responda más rápido y gaste menos.
Definición
Optimizar una app de IA es reducir lo que cuesta y lo que tarda cada respuesta sin bajar su calidad. El costo depende de cuántos tokens (día 9) entran y salen de cada llamada y del precio del modelo; la espera depende del modelo, del tamaño del prompt y de cómo se entrega la respuesta. Las técnicas principales son cinco: caché de prompts, elegir el modelo según la tarea, recortar el contexto, streaming y caché de respuestas.
Cómo se cobra
Los proveedores cobran por millón de tokens (MTok), con precios distintos para la entrada (input: instrucciones, contexto, historial y pregunta) y la salida (output: lo que el modelo genera). La salida suele costar varias veces más que la entrada (se genera token por token), así que limitar la longitud de la respuesta ahorra mucho.
costo = tokens_entrada × precio_entrada + tokens_salida × precio_salida (precios divididos entre 1,000,000)
Precios publicados en las páginas oficiales, consultados el 15 de octubre de 2026 (Anthropic) y el 16 de octubre de 2026 (OpenAI), en USD por millón de tokens (cambian con frecuencia: consúltalos antes de presupuestar):
| Modelo | Entrada | Entrada leída de caché | Salida |
|---|---|---|---|
| Claude Opus 5.5 | 4.00 | 0.20 | 20.00 |
| Claude Sonnet 5.5 | 2.00 | 0.10 | 10.00 |
| Claude Haiku 5.5 (prompts de hasta 100,000 tokens) | 0.10 | 0.01 | 0.50 |
| gpt-6.1-sol | 2.00 | 0.10 | 10.00 |
| gpt-6-luna | 0.10 | 0.01 | 0.50 |
Latencia: cómo se mide la espera
| Métrica | Qué mide |
|---|---|
| Tiempo al primer token (TTFT, time to first token) | Cuánto tarda en aparecer la primera palabra; es lo que el usuario percibe como «está pensando» |
| Latencia total | Desde la petición hasta el último token; crece con la longitud de la respuesta |
No basta con el promedio. Se usan percentiles: el p50 (la mediana: la mitad de las peticiones tarda menos) y el p95 (el 95 % tarda menos; describe la experiencia de los usuarios con peor suerte).
Primero se mide
Antes de optimizar, registra por llamada la función de la app, el modelo, los tokens de entrada y salida, el TTFT y la latencia total (observabilidad, día 14). Con eso sabes qué función gasta más y dónde está el tiempo. Optimizar a ciegas suele atacar lo que no importa.
Las cinco palancas
1. Caché de prompts (prompt caching)
Muchas llamadas repiten el mismo inicio: instrucciones, manual, ejemplos, definiciones de herramientas. Con el caché de prompts el proveedor guarda ese prefijo fijo ya procesado y, en las llamadas siguientes, lo lee en vez de recalcularlo. En Anthropic, escribir en caché cuesta 1.25 veces la entrada normal (caché de 5 minutos, que se renueva con cada uso; existe una opción de 1 hora a 2 veces), y leer cuesta 0.1 veces (0.05 en Opus 5.5 y Sonnet 5.5). OpenAI cobra igual la escritura y entre 0.05 y 0.1 veces la lectura en sus modelos recientes. Hay un tamaño mínimo de prefijo (512 tokens en los Claude 5.5; 1,024 en GPT-5.6 y posteriores).
Regla de diseño: lo fijo va al principio y lo variable (pregunta, datos del usuario) al final; cualquier cambio en el prefijo invalida el caché.
Caso real. Anthropic afirma que el caché de prompts reduce el costo hasta 90 % y la latencia hasta 85 % en prompts largos, y anunció que Notion lo incorporaría a Notion AI para hacerlo más rápido y barato.
2. Elegir y enrutar el modelo según la tarea
No todas las tareas necesitan el modelo más grande. Enrutar (model routing) es mandar lo fácil (clasificar, extraer campos, resumir textos cortos) a un modelo pequeño y barato, y reservar el grande para razonar, redactar o decidir. El enrutador puede ser una regla por función, un clasificador o una cascada: primero el modelo pequeño y, si su respuesta no pasa una validación, el grande. Se decide con evaluaciones (día 14), no a ojo.
3. Recortar el contexto
Cada token de entrada se paga en cada llamada. Fuentes habituales de desperdicio: historial de conversación completo, documentos enteros cuando bastaba un fragmento, demasiados resultados de RAG (día 11), ejemplos que ya no hacen falta. Técnicas: recuperar menos fragmentos y mejores (reordenamiento), resumir el historial antiguo, limitar la salida con max_tokens e instrucciones de formato.
4. Streaming
Con streaming el modelo envía la respuesta token por token y la interfaz la muestra mientras se genera. No baja el costo ni la latencia total, pero reduce la espera percibida: el usuario lee desde el primer token en lugar de mirar una pantalla vacía.
5. Caché de respuestas
Si muchos usuarios hacen la misma pregunta, se guarda la respuesta y se devuelve sin llamar al modelo. La caché exacta compara la pregunta normalizada (minúsculas, sin acentos ni signos); la caché semántica compara embeddings (día 11) y reconoce «¿a qué hora abren?» como equivalente a «¿cuál es el horario?». La semántica acierta más, pero puede devolver una respuesta a una pregunta que solo se parecía. Necesita caducidad cuando la información cambia y nunca debe compartir respuestas personales entre usuarios.
Extra: procesamiento por lotes (batch)
Para trabajo que no es interactivo (clasificar miles de registros, resúmenes nocturnos), las APIs de lotes de Anthropic y OpenAI cobran 50 % menos a cambio de entregar los resultados de forma asíncrona, en horas y no en segundos. Se combina con el caché de prompts.
Cuándo usar cada técnica
| Técnica | Úsala cuando | Riesgo para la calidad |
|---|---|---|
| Caché de prompts | Prefijo largo y repetido (instrucciones, manual, herramientas) | Ninguno: el modelo recibe lo mismo |
| Enrutar modelos | Hay tareas fáciles y de alto volumen | Medio: el modelo pequeño puede fallar; se valida con evals |
| Recortar contexto | Prompts que crecen con historial o documentos | Medio: quitar el fragmento que tenía la respuesta |
| Streaming | Respuestas largas en una interfaz de chat | Ninguno (no cambia el contenido) |
| Caché de respuestas | Preguntas repetidas con respuesta estable | Medio: respuestas viejas o, en la semántica, equivocadas |
| Lotes | Tareas que pueden esperar horas | Ninguno; solo cambia el tiempo de entrega |
Práctica: costo mensual y optimizaciones simuladas
El primer script genera un registro ficticio de un día de llamadas (ejemplo con semilla fija) de una app con tres funciones: soporte (prefijo de 6,000 tokens con instrucciones y manual), clasificar (tickets, salida de pocas palabras) y resumir (contratos largos). Calcula el costo mensual con los precios oficiales de Claude Sonnet 5.5 y Claude Haiku 5.5, y simula llamada por llamada el caché de 5 minutos (si la anterior de la misma función fue hace menos de 5 minutos, se lee del caché; si no, se escribe) y el enrutamiento de clasificar al modelo pequeño. El volumen y los tiempos son de ejemplo; los precios, reales a la fecha de consulta.
1. Guarda el script como costo_ia.py:
"""Costo mensual de una app de IA con caché de prompts y enrutamiento simulados. Registro FICTICIO (ejemplo).
Precios publicados por Anthropic, consultados el 15 de octubre de 2026 (USD por millón de tokens)."""
import random
import statistics
PRECIOS = { # entrada, escritura de caché (5 min), lectura de caché, salida
"sonnet-5.5": {"in": 2.00, "w": 2.50, "r": 0.10, "out": 10.00},
"haiku-5.5": {"in": 0.10, "w": 0.125, "r": 0.01, "out": 0.50}, # tarifa para prompts de hasta 100,000 tokens
}
FUNCIONES = { # prefijo fijo (instrucciones + manual), parte variable, salida, llamadas por día (ejemplo)
"soporte": {"prefijo": 6000, "variable": (150, 600), "salida": (200, 500), "por_dia": 1200},
"clasificar": {"prefijo": 900, "variable": (80, 300), "salida": (5, 15), "por_dia": 3000},
"resumir": {"prefijo": 1500, "variable": (4000, 12000), "salida": (400, 900), "por_dia": 150},
}
TTL_S = 300 # el caché de 5 minutos se renueva con cada lectura
def generar_registro(semilla: int = 14) -> list[dict]:
rnd, filas = random.Random(semilla), []
for nombre, f in FUNCIONES.items():
for _ in range(f["por_dia"]):
filas.append({"funcion": nombre, "modelo": "sonnet-5.5", "t": rnd.uniform(8, 22) * 3600,
"prefijo": f["prefijo"], "variable": rnd.randint(*f["variable"]),
"salida": rnd.randint(*f["salida"]), "ttft_ms": rnd.lognormvariate(6.3, 0.35)})
return sorted(filas, key=lambda x: x["t"])
def costo(filas: list[dict], cache: bool = False, ruta: dict | None = None) -> dict:
ultimo, por_funcion = {}, {}
for x in filas:
modelo = (ruta or {}).get(x["funcion"], x["modelo"])
p, entrada = PRECIOS[modelo], x["prefijo"] + x["variable"]
if cache:
clave = (x["funcion"], modelo)
vigente = clave in ultimo and x["t"] - ultimo[clave] <= TTL_S
ultimo[clave] = x["t"]
usd = x["prefijo"] * (p["r"] if vigente else p["w"]) + x["variable"] * p["in"]
else:
usd = entrada * p["in"]
usd = (usd + x["salida"] * p["out"]) / 1e6
por_funcion[x["funcion"]] = por_funcion.get(x["funcion"], 0) + usd
return {k: v * 30 for k, v in por_funcion.items()} # un día de registro -> 30 días
if __name__ == "__main__":
registro = generar_registro()
ttft = sorted(x["ttft_ms"] for x in registro)
print(f"Registro de ejemplo: {len(registro):,} llamadas en un día")
print(f"Tiempo al primer token: p50 {statistics.median(ttft):.0f} ms · "
f"p95 {statistics.quantiles(ttft, n=20)[18]:.0f} ms (valores simulados)\n")
escenarios = {
"Base (todo en Sonnet 5.5)": costo(registro),
"+ caché de prompts": costo(registro, cache=True),
"+ enrutar «clasificar» a Haiku 5.5": costo(registro, ruta={"clasificar": "haiku-5.5"}),
"Caché + enrutamiento": costo(registro, cache=True, ruta={"clasificar": "haiku-5.5"}),
}
base = sum(escenarios["Base (todo en Sonnet 5.5)"].values())
print(f"{'Escenario (USD al mes)':<36}{'soporte':>10}{'clasificar':>12}{'resumir':>10}{'total':>11}{'ahorro':>9}")
for nombre, c in escenarios.items():
total = sum(c.values())
print(f"{nombre:<36}{c['soporte']:>10,.2f}{c['clasificar']:>12,.2f}{c['resumir']:>10,.2f}"
f"{total:>11,.2f}{(1 - total / base):>9.0%}")
2. Ejecútalo: python costo_ia.py. Salida real (Python 3.12, Windows):
Registro de ejemplo: 4,350 llamadas en un día
Tiempo al primer token: p50 548 ms · p95 966 ms (valores simulados)
Escenario (USD al mes) soporte clasificar resumir total ahorro
Base (todo en Sonnet 5.5) 585.77 205.03 116.05 906.85 0%
+ caché de prompts 175.80 51.19 109.92 336.92 63%
+ enrutar «clasificar» a Haiku 5.5 585.77 10.25 116.05 712.07 21%
Caché + enrutamiento 175.80 2.96 109.92 288.69 68%
3. Interpreta el resultado. En este ejemplo, el caché baja soporte de 585.77 a 175.80 USD al mes porque su prefijo de 6,000 tokens se lee casi siempre del caché; resumir apenas cambia porque su costo está en la parte variable (el contrato) y en la salida. Enrutar clasificar a Haiku 5.5 lo baja de 205.03 a 10.25. Combinadas, el total pasa de 906.85 a 288.69 USD (68 % menos). Antes de enrutar en producción, corre las evaluaciones del día 14 con el modelo pequeño.
4. Caché de respuestas. Guarda como cache_respuestas.py:
"""Caché exacta de respuestas: si la pregunta normalizada ya se respondió, no se vuelve a llamar al modelo."""
import unicodedata
from functools import lru_cache
llamadas_al_modelo = 0
def normalizar(texto: str) -> str:
sin_acentos = unicodedata.normalize("NFD", texto.lower())
sin_acentos = "".join(c for c in sin_acentos if unicodedata.category(c) != "Mn")
return " ".join(sin_acentos.strip(" ¿?¡!.").split())
@lru_cache(maxsize=1024)
def responder_normalizada(pregunta: str) -> str:
global llamadas_al_modelo
llamadas_al_modelo += 1 # aquí iría la llamada real al LLM
return f"(respuesta generada para «{pregunta}»)"
def responder(pregunta: str) -> str:
return responder_normalizada(normalizar(pregunta))
PREGUNTAS = [
"¿Cuál es el horario de atención?", "cual es el horario de atencion", "¿Hacen envíos a Mérida?",
"¿Cuál es el horario de atención?", "¿A qué hora abren?", "¿Hacen envios a Merida?",
]
if __name__ == "__main__":
for p in PREGUNTAS:
responder(p)
info = responder_normalizada.cache_info()
print(f"Preguntas: {len(PREGUNTAS)} · llamadas al modelo: {llamadas_al_modelo}")
print(f"Aciertos de caché: {info.hits} · fallos: {info.misses} · tasa de acierto: {info.hits / len(PREGUNTAS):.0%}")
Salida real:
Preguntas: 6 · llamadas al modelo: 3
Aciertos de caché: 3 · fallos: 3 · tasa de acierto: 50%
Las dos variantes de «horario de atención» y de «envíos a Mérida» aciertan gracias a la normalización. «¿A qué hora abren?» falla aunque significa lo mismo: es justo el caso que resuelve la caché semántica.
Skill gratis para Claude: calcula-costo-ia lee el uso de tu app de IA (un registro en CSV, JSONL o JSON), calcula cuánto gastas al mes por modelo y por función, simula cinco optimizaciones (caché de prompts, modelo más barato para lo simple, recorte del prompt, streaming y caché de respuestas) y te entrega un informe antes/después generado por un script.
Preguntas frecuentes
¿El streaming hace más barata la respuesta?
No. Cuesta lo mismo y tarda lo mismo en total, pero el usuario empieza a leer antes y la espera se siente mucho menor.
¿Qué diferencia hay entre caché de prompts y caché de respuestas?
El caché de prompts lo hace el proveedor: reutiliza el inicio fijo del prompt, pero el modelo sigue generando. El caché de respuestas lo haces tú: devuelve una respuesta guardada sin llamar al modelo.
¿Usar un modelo más barato empeora las respuestas?
En tareas difíciles, puede. En tareas simples, muchas veces no. La única forma de saberlo es correr el mismo conjunto de pruebas con los dos modelos y comparar.
En el reel lo explicamos así
Cada respuesta de una IA cuesta dinero (se paga por tokens) y tarda (latencia). Es como un restaurante que prepara de antemano lo que más piden y reserva a su mejor chef para los platos difíciles. Cinco formas: guardar en caché lo que se repite, modelo pequeño para lo fácil y grande para lo difícil, mandar solo el contexto necesario, mostrar la respuesta mientras se escribe (streaming) y guardar respuestas repetidas. Según Anthropic, el caché baja hasta 90 % el costo y 85 % la espera en prompts largos; Notion lo usa. Primero se mide, después se mejora.
Referencias
- Anthropic, «Prompt caching with Claude»: hasta 90 % menos costo y 85 % menos latencia en prompts largos; adopción en Notion AI.
- Anthropic, Pricing y Prompt caching: precios, multiplicadores de caché, mínimos y lotes.
- OpenAI, API pricing y Prompt caching.
- Anthropic, Batch processing y OpenAI, Batch API.
- Python,
functools.lru_cache.
Un restaurante que prepara de antemano lo que más piden y reserva a su mejor chef para los platos difíciles.
Glosario: del inglés al español
| Latency | latencia (tiempo de espera) |
|---|---|
| Cache | caché (guardar respuestas repetidas) |
| Streaming | mostrar la respuesta mientras se escribe |