Saltar al contenido principal

¿Reintentar la API LLM o cambiar de modelo? Cinco comprobaciones

7 min de lecturaGuía de API

Reintenta solo un fallo transitorio, reproducible de forma segura y dentro del presupuesto. Cambia únicamente a una ruta ya aprobada para el mismo contrato.

Un fallo de API LLM pasa por propietario, estado comprometido, presupuesto de recuperación, equivalencia del respaldo y salud de ruta antes de reintentar, cambiar, encolar, degradar o cerrar.

Reintenta la misma ruta de una API LLM solo cuando el fallo sea probablemente transitorio, la operación pueda repetirse sin duplicados y todavía quede presupuesto de tiempo, intentos, tokens y dinero. Cambia a un modelo de respaldo únicamente si esa ruta ya ha superado el mismo contrato de entrada, salida, herramientas, seguridad, datos, latencia, coste y calidad. Si no, encola, devuelve un modo degradado controlado o falla de forma cerrada.

La decisión no empieza por maxRetries

Empieza por estas cinco puertas:

PuertaPreguntaSi no se cumple
Propietario¿Es un fallo temporal de red/proveedor?Corregir request, auth, policy o presupuesto
Compromiso¿Se puede repetir sin salida visible ni efecto duplicado?Conciliar el estado o detener
Presupuesto¿Quedan intentos, tiempo, tokens, coste y tolerancia?Encolar o degradar
Equivalencia¿El respaldo aprobó los fixtures del workflow?No cambiar de forma silenciosa
Salud¿Una prueba limitada aporta información y queda registrada?Abrir el circuito

Un fallback no es “otro reintento”. Puede cambiar el proveedor, context window, JSON Schema, tools, filtros de seguridad, residencia de datos, precio, latencia y calidad.

Dos respuestas 429 pueden exigir acciones opuestas

La guía de errores de OpenAI distingue un 429 por velocidad de un 429 por cuota o gasto agotado. El primero necesita pacing; el segundo necesita que el propietario del presupuesto cambie el límite. El backoff no crea créditos.

Normaliza el error antes de decidir:

  • 5xx o sobrecarga que el detalle del proveedor clasifica como transitorio: backoff limitado con jitter; el código HTTP por sí solo no basta;
  • 429 de velocidad con ventana corta: respetar retry-after y bajar concurrencia;
  • cuota, saldo o spend limit: detener los reintentos síncronos;
  • 400, schema o context incorrecto: modificar la petición;
  • 401/403: reparar credenciales, permisos o ruta;
  • safety/policy: no buscar un modelo menos restrictivo;
  • stream parcial o tool con estado incierto: conciliar antes de repetir.

Cuenta también los intentos internos. La referencia actual de errores de Anthropic indica que sus SDK oficiales reintentan por defecto dos veces los fallos transitorios de conexión, rate limit y 5xx, respetando retry-after. La guía de Gemini también documenta reintentos automáticos. Un maxRetries: 3 en tu aplicación no representa necesariamente tres llamadas.

Usa un presupuesto de recuperación para todo el workflow

text
intentos totales = petición inicial + SDK + gateway + aplicación + fallback + reentrega de cola

Añade límites de tiempo total, coste/tokens adicionales y degradación de calidad aceptable. Un chat interactivo y un informe nocturno no deberían usar la misma política.

AWS explica en timeouts, retries y backoff con jitter que los reintentos en varias capas multiplican la carga y pueden retrasar la recuperación de un servicio saturado. Debe existir un único punto que decida; las demás capas informan de sus intentos reales.

yaml
workflow: resumen-interactivo max_total_attempts: 3 max_elapsed_ms: 8000 max_extra_cost_usd: 0.02 safe_replay_until: first_visible_token approved_fallback: resumen-backup queue_allowed: false fail_closed_on: [auth, policy, unknown_commit]

Son valores ilustrativos. Derívalos del SLO y valídalos con fallos inyectados.

El límite decisivo es lo ya comprometido

Un timeout solo demuestra que el cliente no recibió el resultado esperado. No demuestra que el proveedor no aceptó la petición ni que una herramienta no ejecutó la acción.

  • Antes de la aceptación: el replay suele ser más sencillo.
  • Después de aceptar, sin respuesta: el resultado puede ser desconocido.
  • Después del primer token visible: otro modelo no puede continuar de forma transparente.
  • Después de una escritura, webhook, correo o tool: puede existir un efecto lateral.

Para acciones, usa un operation ID o idempotency key controlado por la aplicación. Conserva requested → started → committed → acknowledged. Si el estado es incierto, consúltalo y concílialo. Llamar a un segundo modelo no es una comprobación.

En streaming, permite cambio transparente solo antes del primer evento visible. Después, muestra un estado interrumpido, deja que el usuario reinicie o usa un protocolo real de reanudación. No unas dos respuestas distintas.

Aprobar un fallback para un workflow concreto

Prueba la ruta alternativa con los mismos fixtures:

  1. Entrada: contexto, imágenes/archivos, system instructions e idioma.
  2. Salida: JSON Schema, campos obligatorios, refusal y truncation.
  3. Tools: nombres, argumentos, llamadas paralelas, resultado e idempotencia.
  4. Seguridad: bloqueos, tareas de alto impacto y escalado humano.
  5. Datos: región, retención, tenant y clases permitidas.
  6. Operación: p95/p99, streaming, request IDs y visibilidad de estado.
  7. Economía: unidades de precio, cache, quota owner y gasto máximo.
  8. Calidad: un conjunto de evals y un umbral propio del workflow.

Un modelo pequeño puede ser válido para clasificación y no para explicar una política al cliente. Si ningún modelo generativo pasa, una respuesta cacheada, una regla determinista o un estado “inténtalo más tarde” puede ser mejor.

Centraliza la decisión

ts
type Action = "retry" | "fallback" | "queue" | "degrade" | "fail_closed"; function decide(p: { owner: "transient" | "rate" | "quota" | "request" | "auth" | "policy" | "unknown"; commitState: "none" | "committed" | "unknown"; attemptsLeft: number; elapsedMsLeft: number; costLeft: number; retryAfterMs: number; retryExpectedMs: number; retryExpectedCost: number; fallbackExpectedMs: number; fallbackExpectedCost: number; primaryRoute: "healthy" | "degraded" | "open"; fallbackRoute: "healthy" | "unhealthy"; fallbackApproved: boolean; queueAllowed: boolean; degradeApproved: boolean; }): Action { if (["request", "auth", "policy"].includes(p.owner)) return "fail_closed"; if (p.commitState !== "none") return "fail_closed"; if (p.owner === "unknown") return p.queueAllowed ? "queue" : "fail_closed"; const canRetry = p.attemptsLeft > 0 && p.elapsedMsLeft >= p.retryAfterMs + p.retryExpectedMs && p.costLeft >= p.retryExpectedCost; const canFallback = p.attemptsLeft > 0 && p.elapsedMsLeft >= p.fallbackExpectedMs && p.costLeft >= p.fallbackExpectedCost; if (canRetry && ["transient", "rate"].includes(p.owner) && p.primaryRoute !== "open") { return "retry"; } if ( canFallback && ["transient", "rate", "quota"].includes(p.owner) && p.fallbackApproved && p.fallbackRoute === "healthy" ) return "fallback"; if (p.queueAllowed) return "queue"; return p.degradeApproved ? "degrade" : "fail_closed"; }

retryAfterMs solo limita el reintento de la ruta principal. El fallback usa su propia latencia y coste previstos, y fallbackApproved incluye una cuota independiente; por tanto, una espera larga del primario no bloquea una alternativa sana que sí cabe en el SLO. Un estado committed o unknown exige conciliación o un protocolo verificado de reanudación/idempotencia fuera del despachador automático. Antes de cada nueva llamada, persiste una fila con workflow ID, índice, rutas, provider request ID, clase de error, espera, tokens, coste, commit y resultado. Un 500 también puede deberse a la petición —la guía actual de Gemini menciona un contexto de entrada demasiado largo—, así que el código nunca basta para marcarlo como transitorio.

Demuestra cada rama en staging

Inyecta un 429 de velocidad, un 429 de cuota, un 503 antes del stream, una desconexión después de tokens visibles, un timeout tras el commit de una tool, una salida fallback sin campo requerido y un circuito abierto. Añade el contraejemplo donde el retry-after de la ruta principal supera el SLO restante, pero una alternativa sana y aprobada cabe en su propio presupuesto de latencia y coste: la acción esperada es fallback, no cola.

Cada fixture debe producir una sola acción esperada. Separa las métricas primary success, retry recovery, fallback recovery, degraded y fail closed. Si solo miras el éxito final, el fallback puede ocultar que la ruta principal está fallando.

Cuando ya tienes un 429 concreto, empieza por el owner correcto: límites de OpenAI API, rate limit de Claude API o límites de Gemini API.

Si necesitas probar varios modelos ya aprobados detrás de un endpoint compatible, consulta en staging la documentación actual de LaoZhang AI. Un endpoint unificado simplifica adaptadores, pero no demuestra equivalencia de schema, tools, datos, coste o calidad.

La aceptación no es “acabó con 200”. Es poder explicar cada acción, contar todos los intentos en un único presupuesto, demostrar la equivalencia del respaldo y conservar los fallos aunque la última ruta tenga éxito.

#API LLM#Reintentos#Modelo de respaldo#Resiliencia de IA
Share: