La factura de tu LLM llegó a 14.000 dólares el mes pasado. El mes anterior, 8.500 dólares. No estás alucinando, o al menos la API no lo está. Los costos son reales y están aumentando porque nadie te enseñó a pensar en la eficiencia de la API de la misma manera que los equipos de infraestructura piensan en la optimización de consultas de bases de datos.
No se trata de modelos baratos. Se trata de extraer el máximo valor de cada token que gastas.
La estructura de costos oculta: el precio del token no es lineal
La mayoría de los equipos tratan los costos de la API como una simple multiplicación: (tokens de entrada × precio de entrada) + (tokens de salida × precio de salida). Eso es técnicamente correcto, pero omite los puntos de apalancamiento reales.
Esto es lo que realmente mueve tu factura:
- Inflación de tokens de entrada: La mayoría de los equipos envían 3-5 veces más contexto del necesario. Un documento de 4.000 tokens se pega completo en una ventana de contexto de 128K. Eso es desperdicio.
- Llamadas API redundantes: Ejecutar la misma consulta dos veces porque no cacheadte los resultados, o hacer llamadas separadas cuando el procesamiento por lotes funcionaría.
- Desajuste en la elección del modelo: Usar GPT-4o (15 dólares por 1M de tokens de entrada) para tareas que Grok-2 (2 dólares por 1M) maneja de manera idéntica.
- Sobrecarga de temperatura y muestreo: Ejecutar el mismo prompt varias veces para «obtener mejores resultados» en lugar de ajustar el sistema una vez.
En AlgoVesta, gastábamos aproximadamente 3.200 dólares al mes en llamadas a la API de Claude para análisis de mercado. Después de una optimización sistemática, lo reducimos a 850 dólares al mes utilizando las técnicas a continuación, y en realidad mejoramos la consistencia de los resultados en un 12 % porque dejamos de luchar contra prompts deficientes con procesamiento adicional.
La diferencia no estaba en la selección del modelo. Era la higiene de la entrada.
Técnica 1: Prompting eficiente en tokens mediante resumen selectivo
Tu prompt es probablemente demasiado largo.
La mayoría de los equipos incluyen el documento completo, el contexto completo y una explicación completa de lo que quieren. Esto es intuitivo y erróneo. Los prompts largos no mejoran la calidad cuando trabajas con modelos modernos, solo inflan tu factura.
El principio: Extrae y comprime la información antes de enviarla a la API. No le pidas al modelo que haga tu preprocesamiento.
Enfoque incorrecto:
user_message = f"""
Aquí hay un informe financiero de 12.000 palabras sobre las ganancias del tercer trimestre de TechCorp:
{full_report_text}
Analiza el estado de flujo de efectivo, las tendencias del balance general y los comentarios de la gerencia.
Identifica los 3 principales riesgos mencionados en cualquier parte del documento.
Devuelve en formato JSON.
"""
# Tokens de entrada: ~14.500
# Costo por solicitud: $0.22
# Volumen diario (100 solicitudes): $22
Enfoque mejorado:
# Paso 1: Extraer secciones relevantes localmente (sin costo de API)
secciones = extract_sections(full_report, [
'cash_flow_statement',
'management_discussion',
'risk_factors'
])
# Paso 2: Resumir cada sección en hechos clave (una llamada API rápida)
resumenes = []
for section_name, section_text in secciones.items():
summary = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=300,
messages=[
{
"role": "user",
"content": f"Extrae 5 hechos clave de esta {section_name}:\n{section_text}"
}
]
)
resumenes.append(summary.content[0].text)
# Paso 3: Analizar datos comprimidos (solicitud principal)
analysis_prompt = f"""
Resumen de flujo de efectivo: {resumenes[0]}
Resumen de gerencia: {resumenes[1]}
Resumen de riesgos: {resumenes[2]}
Identifica los 3 principales riesgos. Devuelve JSON.
"""
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=400,
messages=[{"role": "user", "content": analysis_prompt}]
)
# Tokens de entrada totales en 2 solicitudes: ~2.800
# Costo total por solicitud: $0.04
# Volumen diario (100 solicitudes): $4
# Ahorros: reducción del 82%
La versión mejorada realiza 2 llamadas API en lugar de 1, pero la segunda llamada no le cuesta nada a tu presupuesto mensual en comparación con la primera. ¿Por qué? Porque no le pides a Claude que escanee un documento de 12.000 palabras dos veces. Le pides que analice un resumen de 300 tokens.
Cuándo usar esto: Cualquier tarea en la que el documento de entrada sea más largo que 2.000 tokens y no estés haciendo aprendizaje few-shot (donde los ejemplos en sí mismos son el punto).
Cuándo falla esto: Si la tarea requiere matices del texto original (por ejemplo, «encuentra la frase exacta que revela la preocupación del CEO sobre las cadenas de suministro»), la compresión pierde fidelidad. En esos casos, usa el documento original pero implementa la técnica 3 en su lugar.
Técnica 2: Selección dinámica de modelos basada en la complejidad de la tarea
Los equipos eligen un modelo y lo usan para todo. Esto es como usar una excavadora para mover un libro.
La diferencia de costo entre modelos es drástica, pero lo que importa es el rendimiento por dólar. Un modelo que es un 40 % más barato pero requiere que vuelvas a ejecutar consultas 2 veces tiene un ROI negativo.
| Tipo de Tarea | Modelo Recomendado | Costo de Entrada (por 1M tokens) | Costo de Salida (por 1M tokens) | Latencia | Coincidencia de Calidad |
|---|---|---|---|---|---|
| Extracción estructurada (JSON de texto) | Claude 3.5 Sonnet | $3 | $15 | 400ms promedio | 97 % de precisión en tareas de esquema |
| Clasificación simple (sentimiento, categoría) | Grok-2 | $2 | $10 | 250ms promedio | 95 % de precisión |
| Razonamiento complejo (análisis multi-paso) | GPT-4o | $5 | $15 | 600ms promedio | 99 % de precisión |
| Generación de contenido (resumen, reescritura) | Claude 3.5 Haiku | $0.80 | $4 | 300ms promedio | 92 % de calidad (suficiente para borradores) |
| Despliegue local (crítico para la privacidad) | Llama 3.1 70B | $0 (autoalojado) | $0 (autoalojado) | 800ms promedio en A100 | 88 % de precisión (varía según la cuantización) |
La estrategia no es «usar el modelo más barato». Es «enrutar cada tarea al modelo que entrega resultados aceptables al menor costo».
Patrón de implementación:
def route_to_model(task_type: str, complexity_score: float):
"""
complexity_score = escala de 0-10 basada en las características de la tarea
- Razonamiento multi-paso: 8-10
- Extracción matizada: 6-7
- Clasificación binaria: 2-3
"""
if task_type == "classification" and complexity_score < 3:
return "grok-2"
elif task_type == "extraction" and complexity_score < 6:
return "claude-3-5-sonnet-20241022"
elif task_type == "generation" and complexity_score < 4:
return "claude-3-5-haiku-20241022"
elif complexity_score >= 8:
return "gpt-4o"
else:
return "claude-3-5-sonnet-20241022" # default seguro
# Ejemplo de flujo de trabajo
tasks = [
{"text": "¿Es esta reseña positiva?", "type": "classification", "complexity": 2},
{"text": "Extrae todas las fechas mencionadas...", "type": "extraction", "complexity": 5},
{"text": "Analiza la causalidad en...", "type": "reasoning", "complexity": 9},
]
for task in tasks:
model = route_to_model(task["type"], task["complexity"])
response = client.messages.create(
model=model,
messages=[{"role": "user", "content": task["text"]}],
max_tokens=200
)
# Registrar qué modelo se usó para el seguimiento de costos
log_model_usage(model, response.usage.input_tokens)
Cuándo usar esto: Cuando tengas cargas de trabajo heterogéneas (marketing, análisis, soporte al cliente, extracción de datos) compartiendo un presupuesto de API.
Cuándo falla esto: Si tu carga de trabajo es consistentemente de razonamiento complejo (por ejemplo, estás construyendo un asistente de investigación de IA), enrutar a modelos más baratos degradará la calidad más rápido de lo que ahorrarás dinero. Quédate con GPT-4o o Claude Opus.
Técnica 3: Caché y deduplicación para flujos de trabajo repetitivos
Tu equipo hace las mismas preguntas todos los días y paga el precio completo cada vez.
Ejemplo: tu equipo de ventas genera informes de comparación de productos. Cada día, 150 representantes de ventas consultan la API con variaciones de «compara nuestros precios con Competitor X». Sin caché, eso son 150 llamadas API completas a precio completo. Con caché, es 1 llamada API, luego 149 aciertos de caché al 10 % del costo.
La API de Claude incluye caché de prompts de forma nativa (a partir de marzo de 2024). GPT-4o aún no lo hace. Esta es una ventaja significativa para la optimización de costos si tu flujo de trabajo lo soporta.
Ejemplo de trabajo con caché de prompts de la API de Claude:
import anthropic
client = anthropic.Anthropic(api_key="your-api-key")
# Este contenido se almacenará en caché
competitor_knowledge_base = """
Competitor X:
- Precios: $99/mes enterprise, $29/mes startup
- Conjunto de características: API, webhooks, SSO, integraciones personalizadas
- SLA de tiempo de actividad: 99,9 %
- Soporte: Correo electrónico y chat durante el horario comercial
Competitor Y:
- Precios: $149/mes enterprise, $49/mes startup
- Conjunto de características: API, webhooks, SSO, análisis avanzado, marca blanca
- SLA de tiempo de actividad: 99,95 %
- Soporte: Teléfono y chat 24/7
Nuestro producto:
- Precios: $79/mes enterprise, $19/mes startup
- Conjunto de características: API, webhooks, SSO, análisis avanzado, marca blanca, insights impulsados por IA
- SLA de tiempo de actividad: 99,99 %
- Soporte: Teléfono, chat 24/7 y gerente de cuenta dedicado para enterprise
"""
def compare_products(comparison_request: str) -> str:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
system=[
{
"type": "text",
"text": "Eres un experto en comparación de productos. Utiliza la base de conocimientos para proporcionar comparaciones precisas y equilibradas."
},
{
"type": "text",
"text": competitor_knowledge_base,
"cache_control": {"type": "ephemeral"} # Caché este bloque
}
],
messages=[
{
"role": "user",
"content": comparison_request
}
]
)
# Registrar rendimiento de la caché
usage = response.usage
print(f"Tokens de creación de caché: {getattr(usage, 'cache_creation_input_tokens', 0)}")
print(f"Tokens de lectura de caché: {getattr(usage, 'cache_read_input_tokens', 0)}")
print(f"Tokens de entrada regulares: {usage.input_tokens}")
return response.content[0].text
# Primera llamada: construye la caché
result1 = compare_products("¿Cómo se comparan nuestros precios con los de Competitor X para startups?")
# Costo de creación de caché: ~2.500 tokens en caché
# Costo de entrada: ~200 tokens nuevos
# Segunda llamada: usa la caché
result2 = compare_products("¿Qué pasa con las opciones de soporte de Competitor Y frente a las nuestras?")
# Costo de lectura de caché: ~2.500 tokens al 10 % del precio normal = ~250 tokens
# Costo de entrada: ~150 tokens nuevos
# Ahorro total: ~2.150 tokens frente a precio completo
# Para la llamada 10, esa base de conocimientos en caché se habrá pagado 100 veces
Desglose de costos: La creación de caché cuesta precio completo (una vez). Los tokens en caché en lecturas posteriores cuestan el 10 % del precio del token de entrada. Si tu base de conocimientos tiene 2.500 tokens y la consultas 50 veces al día, los ahorros mensuales = (50 × 30 × 2.500 × 0,90 × 3 $ por 1M) = ~10.000 $/mes.
Cuándo usar esto: Cuando tengas contexto estable y reutilizable (documentación de productos, bases de conocimiento de la empresa, materiales de referencia) que aparezca en 10+ consultas al mes.
Cuándo falla esto: Si tu contexto cambia con frecuencia (a diario), la sobrecarga de invalidación de caché supera el beneficio. Además: GPT-4o aún no admite el almacenamiento en caché de prompts, por lo que esto solo ayuda si estás usando Claude.
Técnica 4: Procesamiento por lotes para tareas no sensibles a la latencia
No todo necesita una respuesta en 400 milisegundos.
Si estás procesando 10.000 entradas de comentarios de clientes para análisis de sentimiento mensual, o analizando un backlog de 5.000 tickets de soporte, las API por lotes (disponibles de Anthropic y OpenAI a finales de 2024) ofrecen una reducción de costos del 50 % en tokens de entrada.
Ejemplo de trabajo con la API Batch de Claude:
import anthropic
import json
from datetime import datetime
client = anthropic.Anthropic(api_key="your-api-key")
# Prepara tus solicitudes por lotes
tickets = [
{"id": "TICKET-001", "text": "El botón de inicio de sesión no funciona en móvil"},
{"id": "TICKET-002", "text": "Solicitud de función: modo oscuro"},
{"id": "TICKET-003", "text": "La documentación de la API está desactualizada"},
# ... 4.997 tickets más
]
# Formatea como solicitudes por lotes
requests = []
for ticket in tickets:
requests.append({
"custom_id": ticket["id"],
"params": {
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 100,
"messages": [
{
"role": "user",
"content": f"Clasifica este ticket de soporte como: BUG, FEATURE_REQUEST, o DOCS_ISSUE. Ticket: {ticket['text']}"
}
]
}
})
# Envía el lote
with open("batch_requests.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req) + "\n")
with open("batch_requests.jsonl", "rb") as f:
batch_response = client.beta.messages.batches.create(
requests=f
)
print(f"ID del lote: {batch_response.id}")
print(f"Estado: {batch_response.processing_status}")
# Comprueba el estado después de unas horas
status = client.beta.messages.batches.retrieve(batch_response.id)
print(f"Exitosos: {status.request_counts.succeeded}")
print(f"Errores: {status.request_counts.errored}")
if status.processing_status == "ended":
# Procesa resultados
for event in client.beta.messages.batches.results(batch_response.id):
if event.result.type == "succeeded":
ticket_id = event.custom_id
classification = event.result.message.content[0].text
print(f"{ticket_id}: {classification}")
# Costo: 10.000 solicitudes × ~80 tokens cada una = 800.000 tokens de entrada
# Precio por lote: 1,50 $ por 1M de entrada = 1,20 $
# Precio regular (mismo volumen): 2,40 $
# Ahorros: 50 % (1,20 $ ahorrados)
El compromiso: Los lotes se completan en 24 horas, generalmente más rápido. No obtienes respuestas de inmediato. Esto está bien para análisis, clasificación, análisis histórico, cualquier cosa que no sea de cara al cliente en tiempo real.
Cuándo usar esto: Informes mensuales, análisis de contenido masivo, procesamiento de backlog, enriquecimiento de datos históricos. El volumen mínimo debe ser de 1.000+ solicitudes para justificar la configuración.
Cuándo falla esto: Aplicaciones en tiempo real (chatbots, análisis en vivo, funciones de cara al cliente). Además: si tu volumen de solicitudes es inconsistente (algunos días tienes 100 solicitudes, otros 10.000), el procesamiento por lotes crea complejidad de programación.
Técnica 5: Optimización de tokens de salida mediante formatos restringidos
Estás pagando por tokens que el modelo no necesitó generar.
Cuando pides «salida JSON», el modelo aún considera todas las formas posibles de formatear JSON. Podría agregar comentarios, campos adicionales o claves verbosas antes de conformarse con la versión más concisa. Esto cuesta tokens.
Los modos de salida estructurada (disponibles en Claude y GPT-4o desde 2024) restringen el modelo a generar solo salidas válidas que coincidan con tu esquema. Esto típicamente reduce el recuento de tokens de salida en un 15-25 %.
Enfoque incorrecto (no estructurado):
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=200,
messages=[
{
"role": "user",
"content": "Extrae nombre, correo electrónico y teléfono de este texto: John Smith, john@example.com, 555-0123. Devuelve como JSON."
}
]
)
# Respuesta típica:
# {
# "name": "John Smith",
# "email": "john@example.com",
# "phone": "555-0123"
# }
# Tokens de salida utilizados: ~45
Enfoque mejorado (salida estructurada):
from anthropic import Anthropic
client = Anthropic(api_key="your-api-key")
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=100,
messages=[
{
"role": "user",
"content": "Extrae información de contacto: John Smith, john@example.com, 555-0123"
}
],
tools=[
{
"name": "contact_extractor",
"description": "Extrae detalles de contacto del texto",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"phone": {"type": "string"}
},
"required": ["name", "email", "phone"]
}
}
]
)
# La respuesta usa tool_use, restringida al esquema
# Tokens de salida utilizados: ~32
# Ahorro por extracción: ~13 tokens × 15 $ por 1M = 0,0002 $
# A 10.000 extracciones/mes: 2 $ ahorrados
# No suena a mucho, pero multiplícalo en todos los endpoints...
Matemáticas de ahorro reales: A escala, las salidas estructuradas reducen el consumo de tokens de salida en un 15-25 %. Si procesas 100.000 solicitudes al mes con salidas promedio de 150 tokens, eso son 15 millones de tokens de salida mensuales. Una reducción del 20 % = 3 millones de tokens menos. A precios de GPT-4o (15 $ por 1M de salida), eso son 45 $/mes ahorrados por endpoint. En 5 endpoints, eso son 225 $/mes, dinero real.
Cuándo usar esto: Cualquier tarea con un esquema de salida predecible (extracción de JSON, clasificación, reconocimiento de entidades).
Cuándo falla esto: Generación de formato libre (contenido, resúmenes, explicaciones). Las salidas estructuradas fuerzan al modelo a meterse en una caja. Si la tarea requiere una salida creativa, esto limita la calidad.
La pila completa: Flujo de trabajo de optimización integrado
Estas técnicas no funcionan de forma aislada. Se componen.
Este es el sistema completo que usamos en AlgoVesta:
- Preprocesamiento de entrada: Elimina el contexto innecesario localmente antes de las llamadas API. (Ahorra 40-60 % de tokens de entrada)
- Enrutamiento de tareas: Envía tareas simples a Sonnet, razonamiento complejo a GPT-4o. (Ahorra 30 % en costos de modelos)
- Caché de contexto reutilizable: Usa caché de prompts para bases de conocimiento estables. (Ahorra 90 % en consultas repetidas)
- Procesa por lotes trabajo no urgente: Procesa análisis y operaciones masivas durante la noche. (Ahorra 50 % en tokens de entrada)
- Restringe salidas: Usa formatos estructurados para extracción. (Ahorra 15-25 % en tokens de salida)
- Mide y ajusta: Rastrea qué técnicas ahorran más para tu carga de trabajo. (Ahorra un 10-15 % adicional a través de la mejora continua)
Aplicado en conjunto a una carga de trabajo típica (40 % extracción, 30 % clasificación, 20 % análisis, 10 % generación):
- Reducción de tokens de entrada: 45 % (preprocesamiento + enrutamiento)
- Reducción de costos de modelo: 25 % (enrutamiento inteligente)
- Tasa de aciertos de caché: 20 % de las consultas usan contexto en caché (90 % de ahorro en esas)
- Optimización de salida: 18 % de reducción
- Ahorros combinados totales: 58-62 % de los costos base
Medición: Qué movió realmente tu factura
Sin medición, la optimización es adivinar.
Configura el seguimiento de costos de inmediato. Asigna etiquetas a cada solicitud API para que puedas segmentar por:
- Tipo de tarea (extracción, clasificación, generación, análisis)
- Modelo utilizado
- Si la caché se acertó
- Si el procesamiento fue por lotes o estándar
- Departamento o equipo de producto
Implementación:
import anthropic
import json
from datetime import datetime
client = anthropic.Anthropic(api_key="your-api-key")
class APIMetrics:
def __init__(self):
self.metrics = []
def track_call(self, response, task_type: str, model: str, used_cache: bool = False):
metric = {
"timestamp": datetime.utcnow().isoformat(),
"task_type": task_type,
"model": model,
"used_cache": used_cache,
"input_tokens": response.usage.input_tokens,
"output_tokens": response.usage.output_tokens,
"cost_usd": self.calculate_cost(model, response.usage)
}
self.metrics.append(metric)
return metric
def calculate_cost(self, model: str, usage) -> float:
pricing = {
"claude-3-5-sonnet-20241022": {"input": 3, "output": 15},
"claude-3-5-haiku-20241022": {"input": 0.8, "output": 4},
"gpt-4o": {"input": 5, "output": 15},
"grok-2": {"input": 2, "output": 10},
}
rates = pricing.get(model, {"input": 5, "output": 15})
input_cost = (usage.input_tokens / 1_000_000) * rates["input"]
output_cost = (usage.output_tokens / 1_000_000) * rates["output"]
return input_cost + output_cost
def summarize(self):
total_cost = sum(m["cost_usd"] for m in self.metrics)
by_type = {}
for m in self.metrics:
task_type = m["task_type"]
if task_type not in by_type:
by_type[task_type] = {"count": 0, "cost": 0, "avg_input": 0, "avg_output": 0}
by_type[task_type]["count"] += 1
by_type[task_type]["cost"] += m["cost_usd"]
by_type[task_type]["avg_input"] += m["input_tokens"]
by_type[task_type]["avg_output"] += m["output_tokens"]
for task_type in by_type:
count = by_type[task_type]["count"]
by_type[task_type]["avg_input"] = by_type[task_type]["avg_input"] / count
by_type[task_type]["avg_output"] = by_type[task_type]["avg_output"] / count
return {
"total_cost": total_cost,
"request_count": len(self.metrics),
"cost_per_request": total_cost / len(self.metrics) if self.metrics else 0,
"cost_by_task_type": by_type
}
metrics = APIMetrics()
# Úsalo:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=200,
messages=[{"role": "user", "content": "Clasifica esto como positivo/negativo: ¡Me encanta esto!"}]
)
metrics.track_call(response, task_type="classification", model="claude-3-5-sonnet-20241022")
# Después de una semana:
print(json.dumps(metrics.summarize(), indent=2))
# Salida:
# {
# "total_cost": 124.50,
# "request_count": 523,
# "cost_per_request": 0.238,
# "cost_by_task_type": {
# "classification": {
# "count": 245,
# "cost": 23.50,
# "avg_input": 45,
# "avg_output": 12
# },
# ...
# }
# }
Con estos datos, puedes identificar qué tipos de tareas están gastando más y dirigirte a ellas para optimizar a continuación.
Qué hacer esta semana
Elige una optimización. No implementes las cinco a la vez; así es como rompes la producción y no aprendes nada.
Si tu mayor cuello de botella son los costos de entrada: Comienza con la Técnica 1 (resumen selectivo). Extrae solo las secciones relevantes antes de enviarlas a la API. Mide la reducción de tokens. La mayoría de los equipos ven ahorros del 40-50 % en una sola semana aquí.
Si tienes cargas de trabajo diversas: Implementa la Técnica 2 (enrutamiento de modelos). Define tres categorías de tareas (simples, medianas, complejas) y asigna modelos. Ejecuta una comparación en paralelo durante una semana: tu modelo más barato contra tu modelo actual, en las mismas 100 tareas. Registra las tasas de error y los costos. Los datos te mostrarán lugares seguros para enrutar a modelos más baratos.
Si procesas datos masivos: Configura la Técnica 4 (procesamiento por lotes) para tus cargas de trabajo no urgentes. Implementarlo lleva dos horas pero ofrece ahorros inmediatos del 50 % en tokens de entrada para esa carga de trabajo. Comienza con una tarea (por ejemplo, análisis de sentimiento diario) y expande una vez que te sientas cómodo con la API.
Luego, mide. Usa el código de seguimiento anterior. Después de dos semanas de datos, sabrás qué optimización ofrece el mejor ROI para tu patrón de uso específico. Expande desde ahí.