Capítulo 16 de 22 · avanzado
Prompt caching, costo y latencia
Qué cubre esta sesión
Todo lo que has construido funciona. Esta semana trata de volverlo lo bastante
barato y rápido como para ponerlo en producción. Tres números gobiernan eso —
tokens, dólares y milisegundos — y los tres se leen en cada respuesta si te
fijas. El hábito más útil en trabajo de LLM en producción es revisar
response.usage; hazlo y la cuenta mensual deja de ser una sorpresa, porque
viste el costo de cada llamada en el momento en que ocurrió.
Después, dos palancas. El prompt caching vuelve casi gratis un prefijo grande y repetido — un system prompt, un documento largo, un conjunto de ejemplos — después de la primera llamada, porque el proveedor lo mantiene caliente y cobra la parte cacheada a una fracción de la tarifa. Y la elección de modelo cambia calidad por velocidad y costo: un modelo pequeño y rápido responde bien la mayoría de los prompts por una décima parte del precio y de la espera, así que la verdadera habilidad es reconocer la minoría de tareas que de verdad necesitan el grande.
OpenAI
Lee los números primero. response.usage tiene los conteos de tokens de input y
output, y input_tokens_details.cached_tokens te dice cuánto del input se sirvió
desde cache. Ponles precio y tienes el costo.
# Read tokens off response.usage and price them. cached_tokens are input tokens
# served from cache — usually billed far cheaper, so a big cached prefix is
# nearly free to re-send.
def report(response, seconds: float) -> None:
u = response.usage
cached = getattr(getattr(u, "input_tokens_details", None), "cached_tokens", 0) or 0
p_in, p_out = _PRICES[_MODEL]
cost = (u.input_tokens * p_in + u.output_tokens * p_out) / 1_000_000
print(f" in={u.input_tokens} (cached {cached}) out={u.output_tokens} "
f"cost=${cost:.6f} latency={seconds:.2f}s")
El caching ocurre automáticamente para prompts lo bastante grandes — manda el
mismo prefijo grande dos veces y el cached_tokens de la segunda llamada da un
salto. La implicación de diseño vale la pena decirla sin rodeos: pon el material
estable al frente de tu prompt y el material variable atrás. Un cache hit
requiere que el prefijo coincida exactamente, así que un system prompt y un
documento fijo cachean de maravilla, mientras que un timestamp arriba del prompt
tumba el cache en cada llamada.
def ask(question: str):
start = time.time()
response = client.responses.create(
model=_MODEL, instructions=CONTEXT,
input=[{"role": "user", "content": question}],
)
report(response, time.time() - start)
return response.output_text
Gemini
Los mismos tres números, leídos de usage_metadata — prompt_token_count,
candidates_token_count y cached_content_token_count. Los modelos con
capacidad de caching reportan los tokens cacheados de la misma forma.
def report(response, seconds: float) -> None:
u = response.usage_metadata
cached = getattr(u, "cached_content_token_count", 0) or 0
out = u.candidates_token_count or 0
p_in, p_out = _PRICES[_MODEL]
cost = (u.prompt_token_count * p_in + out * p_out) / 1_000_000
print(f" in={u.prompt_token_count} (cached {cached}) out={out} "
f"cost=${cost:.6f} latency={seconds:.2f}s")
Un hábito que se paga solo: loguea el usage en cada llamada en producción, no solo en un demo. Los costos se cuelan por lugares que no esperas — un retry loop, un prompt que creció, un documento que se hizo más grande — y los logs de usage por llamada convierten el "¿por qué se duplicó la cuenta?" de una investigación en un query. El conteo de tokens es la verdad; todo lo demás es estimación.
Ponlo a trabajar
Tres apps de docker-compose bajo code/showcase/<slug>/, cada una exponiendo uno
de los tres números. Misma rutina: bash bootstrap-secrets.sh, docker compose up --build, http://localhost:3000.
Showcase 1 — Medidor de costo
Cada respuesta viene con sus tokens de input, tokens de output, costo en dólares y latencia. Corre unos cuantos prompts y observa cómo el largo del output determina el costo — lo que controlas más directamente es cuánto le pides al modelo que escriba.
Showcase 2 — Ahorro con cache
Un prefijo fijo grande al frente de cada pregunta. Pregunta, luego pregunta otra vez, y observa el conteo de tokens cacheados dar un salto en la repetición. Esta es la palanca que vuelve económica a una app de contexto largo o de system prompt pesado: la parte fija se paga una sola vez.
Showcase 3 — Velocidad vs calidad
El mismo prompt a un modelo pequeño y rápido y a uno grande, latencias lado a lado. La mayoría de los prompts los maneja bien el rápido; el ejercicio es desarrollar el olfato para aquellos en los que la brecha de calidad vale la espera y el costo.
Los tres leen los mismos campos de usage que leerás en producción. La lección
es el hábito, no la app.
Córrelo
El README de esta carpeta tiene la versión de Python, la línea de instalación,
las dos variables de entorno y los comandos exactos. Las keys vienen del .env
sin trackear en la raíz del curso. Los precios en el código son ilustrativos —
revisa el pricing actual antes de confiar en una cifra en dólares.
Lo que te llevas
Tres números gobiernan el trabajo de LLM en producción — tokens, costo,
latencia — y los tres están en cada respuesta, así que lee usage por hábito y
loguéalo siempre; el conteo de tokens es la verdad de fondo y convierte las
sorpresas de la cuenta en queries. Dos palancas hacen el trabajo pesado: el
prompt caching vuelve casi gratis un prefijo repetido, así que pon el material
estable al frente y el variable atrás para mantener el cache caliente; y la
elección de modelo cambia calidad por velocidad y dinero, así que usa por
defecto el modelo pequeño y rápido y escala solo las tareas que de forma medible
necesitan más. La próxima semana volvemos medible a la calidad misma —
construyendo un harness de evaluación para que "¿es mejor el nuevo prompt?"
tenga una respuesta en lugar de un vibe.