Control de costos en agentes de IA: routing de modelos, presupuestos de herramientas, caché y retries

"OpenAI API Pricing"
A las 3 de la mañana, un report Agent en segundo plano devuelve una respuesta vacía. El estado HTTP es 200, pero el body viene vacío. La lógica de retry solo revisa el código de estado, así que sigue intentando. Cada request envía 500 tokens de entrada. Después de 1.500 retries, se quemaron 750.000 tokens. La factura del día siguiente hace evidente el problema.
La causa raíz no es “elegimos un modelo caro”. Faltaban tres piezas: circuit breaker, verificación de presupuesto y clasificación de errores. El costo de un Agent suele descontrolarse de tres maneras:
Retries sin límite. No hay clasificación de fallos, así que una respuesta vacía se trata como error recuperable. Falta el circuit breaker, por lo que 1.500 fallos seguidos tampoco detienen la ejecución. Cada retry vuelve a enviar todo el contexto y multiplica el costo 2-5 veces.
Contexto inflado. Una tarea larga corre 6 horas y el historial de conversación crece hasta 80K tokens. Sin checkpoint, un fallo reinicia todo desde cero y cada paso se paga otra vez.
Uso excesivo del modelo. Todas las tareas usan un frontier model porque no hay estrategia de routing. Incluso una clasificación simple pasa por el camino más caro y desperdicia el 70% de los tokens.
El control de costos de un Agent no es una optimización aislada. Es un diseño por capas: objetos de presupuesto, estrategia de routing, cache hits, circuit breakers de retry, logs de costo y umbrales de alerta. Conviene convertir esos seis objetos de ingeniería en tablas de decisión o checks ejecutables.
Diseño del objeto de presupuesto: qué registrar, dónde guardarlo y cuándo cortar
El control de costos empieza con un objeto de presupuesto, no con un total único. Sin capas, un pico de factura no te dice qué usuario, tarea o herramienta quemó el presupuesto.
Siete capas de presupuesto
Los objetos de presupuesto se separan de lo más grueso a lo más fino:
| Capa de presupuesto | Objeto de presupuesto | Límite sugerido | Disparador de alerta |
|---|---|---|---|
| Layer 1 | user | Límite diario/mensual por usuario | Alerta si queda < 20% |
| Layer 2 | tenant | Pool de presupuesto por tenant | Alerta si queda < 30% |
| Layer 3 | workflow | Presupuesto por tipo de workflow | Alerta si queda < 40% |
| Layer 4 | task | Presupuesto por tipo de tarea | Alerta si queda < 50% |
| Layer 5 | tool | Presupuesto por llamada de herramienta | Saltar herramienta si supera el límite |
| Layer 6 | retry | Límite de retries + circuit breaker | Desactivar herramienta tras N fallos consecutivos |
| Layer 7 | cache | Monitoreo del cache hit rate | Alerta si el hit rate cae bajo lo esperado |
Cada límite depende del modelo de negocio y es configuración mutable. La estructura de capas es más estable. El campo de presupuesto restante debe entrar en los logs de costo para alimentar alertas y circuit breakers.
Campos que debe registrar cada capa
Cada capa de presupuesto debe registrar estos campos:
| Campo | Uso | Tipo | Por qué registrarlo |
|---|---|---|---|
model | Identificar el modelo | string | Evaluar si el routing de modelos tiene sentido |
inputTokens | Tokens de entrada | integer | Calcular costo de entrada |
outputTokens | Tokens de salida | integer | El costo de salida debe seguirse por separado |
cachedTokens | Tokens servidos por caché | integer | Medir ahorro por caché |
costEstimate | Estimación de costo de esta llamada | float | Acumular costo en tiempo real |
budgetRemaining | Presupuesto restante | float | Base para decisiones de circuit breaker |
El punto del objeto de presupuesto es contabilizar por dimensión, no “guardar total_cost”. En multi-tenant, el costo se reparte por tenantId. En sistemas con muchas herramientas, toolName abre la caja negra.
Lógica de circuit breaker
Cuando el presupuesto restante cae bajo un umbral, activa el circuit breaker:
def check_budget_before_retry(budget_remaining, retry_cost_estimate):
if budget_remaining < retry_cost_estimate:
return "skip_retry" # Saltar el retry porque excedería el presupuesto
if budget_remaining < threshold: # threshold por ejemplo 20%
return "wait_approval" # Presupuesto bajo, esperar aprobación
return "continue"
El circuit breaker revisa el presupuesto antes del retry, no después. Estima el costo antes de cada retry y detente si la próxima prueba excede el presupuesto. Así evitas la respuesta vacía reintentada 1.500 veces.
El artículo de context engineering de esta serie cubrirá qué contexto va en un prefix estable y qué contenido debe quedarse como variable de ejecución.
Estrategia de routing de modelos: no todas las tareas necesitan el modelo más caro
Un ticket triage Agent suele tener esta distribución: 70% de tareas son clasificación simple, 20% necesitan redactar una respuesta y solo 10% deberían subir a un frontier model antes de enviar un correo al cliente. Una estrategia de routing puede ahorrar 40-85% del costo.
Routing a nivel de modelo
El routing por modelo se basa en complejidad:
| Nivel de tarea | Tareas típicas | Nivel de modelo recomendado | Proporción | Perfil de costo |
|---|---|---|---|---|
| 70% - nivel S | Clasificación, extracción, filtrado, Q&A simple | nano/flash (más barato) | 70% | Salida corta, pocos turnos, pocas llamadas de herramientas |
| 20% - nivel M | Redacción, resumen, generación de código, razonamiento medio | mid-tier (precio medio) | 20% | Salida media, posible uso de herramientas |
| 10% - nivel L | Revisión, arquitectura, razonamiento complejo, coordinación multi-herramienta | frontier (más caro) | 10% | Salida larga, muchos turnos, herramientas frecuentes |
La estrategia tiene tres pasos:
Paso 1: clasificar la tarea. Define criterios S/M/L para cada workflow: longitud de salida, número de llamadas a herramientas, profundidad de razonamiento y nivel de riesgo.
Paso 2: usar S como valor por defecto. Solo sube a M o L cuando aparezcan señales de complejidad.
Paso 3: routing en cascada. Si S falla, sube a M. Si M falla, sube a L. Si L falla, entra intervención humana. Antes de cada subida, revisa presupuesto restante; si no alcanza, salta la subida.
Routing a nivel de servicio
El mismo modelo también puede dividirse por latency priority. Descuentos y ventanas de entrega cambian, así que verifica precios oficiales antes de publicar.
| Nivel de servicio | Descuento de costo | Tiempo de finalización | Caso adecuado |
|---|---|---|---|
| Realtime API | Sin descuento | Respuesta inmediata | Chat interactivo con Agent, tareas prioritarias |
| Batch API | 50% cost discount (verificar antes de publicar) | 24-hour turnaround (verificar antes de publicar) | evals batch, clasificación, embeddings, procesamiento de repositorios de contenido |
| Flex Processing | Menor costo (verificar antes de publicar) | Respuesta más lenta, indisponibilidad ocasional | Tareas asíncronas de baja prioridad, model evaluations, data enrichment |
Checklist para routing offline:
- ¿Necesita respuesta inmediata? Sí -> Realtime API, con routing de modelo.
- ¿Puede aceptar 24 horas de espera? Sí -> Batch API.
- ¿Es de baja prioridad y tolera fallos ocasionales? Sí -> Flex Processing.
- ¿Es trabajo batch como eval, clasificación o embedding? Sí -> Batch API.
Clasificación por riesgo
También hay que clasificar por riesgo:
| Nivel de riesgo | Operación típica | Estrategia de routing | Rama de presupuesto |
|---|---|---|---|
| Bajo riesgo | Clasificación, extracción, resumen interno | Modelo S + camino automático | Sin aprobación, límite amplio |
| Riesgo medio | Borrador de respuesta a cliente, sugerencia de cambio de código | Modelo M + aprobación opcional | Si excede presupuesto, pedir aprobación |
| Alto riesgo | Enviar correo a cliente, cobrar, cambiar arquitectura | Modelo L + aprobación obligatoria | Espera, rechazo y timeout son ramas de presupuesto |
El artículo Human-in-the-Loop de la misma serie explica cómo espera, rechazo y timeout afectan esas ramas de presupuesto.
Restricciones clave
El routing necesita restricciones claras:
- No hardcodear precios: los precios cambian. Guarda model + pricingVersion en lugar de fijar una fórmula de costo en la lógica de negocio.
- Revisar presupuesto restante: antes de subir de nivel, revisa budgetRemaining. Si no alcanza, salta la subida o pide aprobación.
- Clasificar errores: separa fallos recuperables por capacidad del modelo de errores no recuperables como parámetros inválidos o permisos denegados.
Presupuesto de llamadas a herramientas: per-tool budget, timeout y límite de retries
Una llamada de herramienta tiene costo de schema y de API. Cada llamada envía schema, argumentos y contexto de parsing de respuesta; además la API externa puede rate-limitar o expirar. Son costos separados de la llamada al modelo.
Control del presupuesto de herramientas
Cada herramienta necesita controles propios:
| Control | Configuración sugerida | Campo de monitoreo | Acción |
|---|---|---|---|
| Per-tool budget | Límite por llamada | tool_cost_estimate | Saltar herramienta o degradar |
| Tool timeout | Timeout de la API externa | tool_duration | Marcar timeout como error recuperable |
| Retry limit per tool | Límite de retries por herramienta | tool_retry_count | Abandonar herramienta, no entrar en bucle |
Las herramientas que llaman API externas, como búsqueda, base de datos o servicios de terceros, necesitan contabilidad propia. Si no, tool calling se vuelve una caja negra de costos.
Clasificación de fallos de herramientas
Los fallos de herramientas se dividen en recuperables y no recuperables:
| Tipo de fallo | Errores típicos | Estrategia | Impacto de costo |
|---|---|---|---|
| Fallo recuperable | Timeout de red, 503 Service Unavailable, 429 Rate Limit | Retry automático con exponential backoff y retry-after | Cada retry envía todo el contexto |
| Fallo no recuperable | 403 Permission Denied, 400 Bad Request, herramienta inexistente | No retry; inyectar el error para que el modelo decida | Sin retry, sin gasto repetido |
La regla es simple: reintenta solo fallos temporales causados por condiciones externas. No reintentes errores de configuración interna.
Circuit breaker
Después de N fallos consecutivos, desactiva la herramienta:
def circuit_breaker_tool(tool_name, consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "disable_tool" # Desactivar herramienta
return "continue"
El estado del circuit breaker debe quedar en logs para explicar por qué se desactivó la herramienta. Tras activarse, espera intervención humana o un check de recuperación, en lugar de seguir llamando una herramienta inestable.
Las bases de tool calling están en Tool Calling. Aquí se amplía con per-tool budget, timeout y límites de retry.
Diseño de Prompt Caching: prefix estable, variables y umbral de 1024 tokens
Prompt Caching optimiza el costo de tokens de entrada para un prompt prefix estable. No es una caché de resultados de negocio. Rutea requests con el mismo prompt prefix a un servidor que procesó recientemente ese prefix, lo que puede reducir latencia y costo de entrada.
Prompt Caching no es caché de resultados
Prompt Caching guarda un prompt prefix estable, no un resultado de negocio. La diferencia importa:
- Prompt Caching: guarda un prompt prefix estable, como system prompt o tool schema. Un hit ahorra tokens de entrada, pero la inferencia se ejecuta igual.
- Caché de resultados de negocio: guarda una salida completa, como resultado de herramienta o consulta de base de datos. Un hit responde directo sin llamar al modelo.
Los objetivos son distintos. Prompt Caching reduce costo de tokens de entrada; la caché de negocio reduce el costo de la llamada completa. Pueden convivir: prefixes estables por Prompt Caching, resultados frecuentes de herramientas por caché de negocio.
Requisitos de estructura
La clave es separar prefix estable y variables de ejecución:
| Tipo de contenido | Posición | Probabilidad de cache hit | Ejemplos |
|---|---|---|---|
| Prefix estable (entra en caché) | Inicio del prompt | Alta | System prompt, Tool schema, documentos Policy, ejemplos Few-shot |
| Variables de ejecución (fuera de caché) | Más adelante en el prompt | Baja | User input, File fragments, Runtime state (turno actual, variables temporales) |
Pasos de diseño:
- Poner System prompt, Tool schema y Policy al inicio: son estables entre llamadas y tienen más probabilidad de hit.
- Poner User input, File fragments y Runtime state después: cambian en cada llamada y no deben formar parte del prefix estable.
- Monitorear Cache hit rate: registra cachedTokens y tokens de entrada totales, luego calcula el hit rate. Más de 40% es sano; menos de 20% requiere revisar la estructura.
Umbral y efecto
El umbral automático y el efecto de Prompt Caching son hechos variables; verifica documentación oficial antes de publicar:
- Umbral: activación automática desde 1024 tokens (verificar antes de publicar).
- Efecto: los hits pueden reducir costo y latencia (verificar ratios exactos).
- Ver hits: campo
usage.prompt_tokens_details.cached_tokens.
Modelos soportados, umbrales y descuentos de Prompt Caching pueden cambiar. Verifícalos contra la página oficial de pricing. El principio durable es estable: contenido estático adelante, contenido variable atrás.
Circuit breakers de retry: idempotencia, checkpoints y presupuesto restante
Los retries son una de las fuentes más grandes de costo descontrolado. Un report Agent atascado en una herramienta inestable puede reenviar todo el contexto en cada fallo; tras 1.500 retries, el costo se aleja mucho del camino normal.
Revisar presupuesto antes del retry
Revisa el presupuesto restante antes de reintentar, no después:
def should_retry(error_type, budget_remaining, retry_cost_estimate):
# Clasificación de error
if error_type in ["403", "400", "tool_not_exist"]:
return False # Error no recuperable, no retry
# Verificación de presupuesto
if budget_remaining < retry_cost_estimate:
return False # Fuera de presupuesto, no retry
return True # Retry permitido
La verificación de presupuesto debe ir antes de la lógica de retry. Así evitas gastar el presupuesto diario en una respuesta vacía reintentada 1.500 veces.
Idempotencia
Un retry no debe repetir efectos secundarios, como enviar correos o cobrar:
- Usa un ID de idempotencia, como requestId, para llamadas de herramientas. Si la API externa recibe el mismo ID, debe devolver el resultado cacheado en vez de procesar otra vez.
- Escribe el ID de idempotencia en los logs de costo para diagnosticar llamadas repetidas.
La idea de idempotencia es que la misma operación no consuma dos veces. Sin eso, los retries amplifican costo y efectos secundarios.
Guardado de estado (Checkpoint)
Las tareas largas deben recuperarse sin repetir todo el workflow:
- Guarda un checkpoint en nodos importantes: pasos completados, estado actual y resumen de contexto.
- Tras un fallo, continúa desde el checkpoint, no desde el inicio.
- Persiste el checkpoint. No basta con dejarlo en memoria.
El diseño de checkpoint y thread state se cubre en Arquitectura de Agent con LangGraph.
Estrategia de retry
Cada tipo de error necesita una estrategia distinta:
| Tipo de error | Error típico | Estrategia de retry | Impacto de costo |
|---|---|---|---|
| Timeout de red | Sin respuesta en 10 segundos | Exponential backoff + retry-after, máximo 3 retries | Cada retry envía todo el contexto |
| 503/429 | Service Unavailable, Rate Limit | Esperar ventana de rate limit + retry-after, máximo 3 retries | La espera no consume tokens, el retry sí |
| 403/400 | Permission Denied, Bad Request | No retry; inyectar error para que el modelo decida | Sin retry, evita gasto inválido |
La regla es: solo se reintentan errores recuperables. No mandes requests inválidos al modelo una y otra vez.
Circuit breaker
Después de N fallos consecutivos, detén retries y espera intervención:
def circuit_breaker(consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "stop_retry" # Detener retries
return "continue"
La decisión del circuit breaker debe quedar en logs para explicar por qué se detuvieron los retries. Después, espera intervención humana o recuperación de presupuesto, en lugar de llamar otra vez un modelo o herramienta inestable.
Logs de costo y alertas: campos y umbrales
La observabilidad de costos es la condición previa para controlar costos. Si los campos de log están incompletos, no encontrarás el problema.
OpenTelemetry trace span attributes
Diseña logs de costo en tres niveles de span:
Agent run span (nivel superior):
| Campo | Uso | Tipo | Por qué registrarlo |
|---|---|---|---|
runId | Identificar ejecución específica | string | Distingue runs repetidos del mismo workflow |
tenantId | Identificar tenant | string | Reparte costos en sistemas multi-tenant |
userId | Identificar usuario | string | Sigue tendencias de costo por usuario |
workflowName | Identificar workflow | string | Sigue costos por tipo de workflow |
totalCost | Costo total estimado | float | Acumula costo en tiempo real |
budgetRemaining | Presupuesto restante | float | Base para circuit breaker |
totalRetries | Total de retries | integer | Muestra amplificación por retries |
Model call span (span hijo):
| Campo | Uso | Tipo | Por qué registrarlo |
|---|---|---|---|
model | Identificar modelo | string | Ver si el routing tiene sentido |
pricingVersion | Versión de precios | string | Evita fórmulas de costo hardcodeadas |
inputTokens | Tokens de entrada | integer | Calcula costo de entrada |
outputTokens | Tokens de salida | integer | Sigue costo de salida por separado |
cachedTokens | Tokens cacheados | integer | Calcula ahorro de caché |
costEstimate | Estimación de esta llamada | float | Acumula costo en tiempo real |
latencyMs | Latencia de llamada | integer | Ayuda a decidir si Batch/Flex encaja |
Tool call span (span hijo):
| Campo | Uso | Tipo | Por qué registrarlo |
|---|---|---|---|
toolName | Identificar herramienta | string | Ubica overhead de tool calls |
toolBudget | Límite de presupuesto de herramienta | float | Base para circuit breaker |
toolTimeout | Timeout de herramienta | integer | Clasifica fallos de timeout |
retryCount | Número de retries | integer | Mide amplificación por retries |
errorType | Tipo de error | string | Separa recuperable de no recuperable |
No registres solo total_cost. Divide por dimensión. Sin estos campos, un pico de factura solo dice “sobre presupuesto”; no dice qué usuario, herramienta o retry lo causó.
El diseño completo de logs, alertas y recuperación se cubre en Monitoreo y recuperación de Agents. Este artículo añade campos de costo y objetos de presupuesto.
Umbrales de alerta
Define umbrales por dimensión:
| Dimensión de alerta | Umbral | Canal | Acción |
|---|---|---|---|
| Consumo global de presupuesto | 70%, 90%, 100% | Slack/email | 70% avisar, 90% degradar, 100% cortar |
| Consumo de un user/tenant | Más de 3x el promedio | Slack/email | Revisar llamadas anómalas |
| Tasa de fallo de un modelo | > 5% | Dashboard | Revisar routing o estado del servicio |
| Retries de una herramienta | > umbral | Dashboard | Revisar estabilidad de la herramienta |
| Cache hit rate | < valor esperado | Dashboard | Revisar estructura del prompt |
Los umbrales deben estar en la lógica de costo para disparar alertas y circuit breakers automáticamente.
Estrategia de degradación
Después de una alerta, la degradación puede seguir estos caminos:
| Camino de degradación | Método | Caso adecuado | Impacto de costo |
|---|---|---|---|
| Degradación de modelo | Modelo grande -> modelo pequeño | Un modelo tiene alta tasa de fallos | Baja costo, puede bajar calidad |
| Degradación de camino | Realtime API -> Batch API -> Flex Processing | El presupuesto global se consume demasiado rápido | Sube latencia, baja costo |
| Degradación de función | Desactivar herramientas no esenciales | Una herramienta acumula demasiados retries | Reduce overhead de herramientas |
| Degradación de usuario | Rate limit, cola, mensaje “intenta más tarde” | Consumo anómalo de un usuario | Evita que un usuario queme presupuesto |
La degradación debe vivir en la lógica de presupuesto. Cuando se dispara una alerta, el sistema debe degradar automáticamente, no esperar intervención manual.
Siguientes pasos
El control de costos de Agents depende de monitoreo, tool calling y context engineering:
- Publicado: Monitoreo y recuperación de Agents: campos de log, alertas y recuperación de fallos. Este artículo añade campos de costo y objetos de presupuesto.
- Publicado: Arquitectura de Agent con LangGraph: checkpoints, thread state y recuperación para tareas largas.
- Publicado: Tool Calling: bases de llamadas a herramientas. Este artículo añade per-tool budget, timeout y límites de retry.
- Misma serie: context engineering: prefixes estables, cache hits y qué contexto va en el prefix estable frente a variables de ejecución.
- Misma serie: Human-in-the-Loop: espera, rechazo, timeout de aprobación y cómo cambian costos y retries.
Empieza por la defensa a nivel de sesión: define un per-session cost limit y termina la sesión automáticamente cuando exceda el presupuesto. Es la protección inicial más rápida contra una tarea descontrolada que quema el presupuesto del día. Luego amplía el diseño a capas de presupuesto, routing de modelos, presupuestos de herramientas, Prompt Caching, circuit breakers de retry y logs de costo.
Diseñar un presupuesto de costos y circuit breaker para Agents
Usa objetos de presupuesto, routing de modelos, routing de servicio, caché, presupuestos de herramientas y circuit breakers de retry para mover el control de costos antes de ejecutar cada run.
⏱️ Estimated time: 45 min
- 1
Step 1: Listar todos los caminos de costo
Enumera llamadas a modelos, llamadas a herramientas, lecturas de archivos, API externas, procesos batch, cachés y rutas de retry del Agent. - 2
Step 2: Definir el objeto de presupuesto
Registra presupuestos por tenant, user, run, workflow, model, tool, retry, cache y time window. - 3
Step 3: Configurar la estrategia de routing
Define routing de modelos y routing de servicio para distintos tipos de tareas: online, batch, flex y queue. - 4
Step 4: Diseñar los cache hits
Pon el contexto estable en el prompt prefix, deja el contenido variable después y separa prompt caching, caché de resultados de negocio y caché de respuestas de herramientas. - 5
Step 5: Limitar herramientas y retries
Asigna a cada herramienta timeout, max retries, idempotency key, per-tool budget y fallback. - 6
Step 6: Registrar spans de costo
En cada run/span registra token, cached token, tool, retry, latency, estimated cost, budget remaining y traceId. - 7
Step 7: Configurar degradación y circuit breaker
Define umbrales de degradación, pausa, circuit breaker y alertas, y pruébalos con fallos reales.
FAQ
¿El costo de un Agent se debe medir por usuario, sesión, tarea o herramienta?
¿El routing de modelos solo consiste en usar un modelo pequeño para tareas simples?
¿Cuál es la diferencia entre Prompt Caching y una caché de negocio normal?
¿Cuántas veces conviene reintentar una llamada de herramienta fallida?
¿Qué hacer si una tarea larga de Agent se queda sin presupuesto a mitad de camino?
¿Qué campos debe registrar un log de costo?
16 min de lectura · Publicado el: 17 sep 2026
Guía de ingeniería de AI Agents
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Diseño de agentes human-in-the-loop: qué pasos necesitan aprobación humana
Guía práctica para diseñar puntos de aprobación en agentes de IA: acciones automáticas, pausas obligatorias, approve/reject/resume, timeouts, compensación y registros de auditoría.
Parte 19 de 22
Siguiente
Modelo de permisos para agentes de IA: identidad de usuario, permisos de herramientas, auditoría y aislamiento de secretos
Antes de conectar un agente de IA a herramientas reales, diseña su modelo de permisos: mapeo de identidad, service accounts, per-tool permission, scopes, secret vault, rotación de claves, approval policy, data boundary y audit log.
Parte 21 de 22



Comentarios
Inicia sesión con GitHub para dejar un comentario