¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Reintentar errores de una API de IA

Después de esta lección: Puedo distinguir un fallo que hay que reintentar de uno que hay que corregir.

Lección de 60 min5 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Algunos fallos pasan si esperas

Un error de API es una respuesta cuyo código de estado dice que la llamada no funcionó. Algunos errores pasan si esperas un momento y lo intentas otra vez. Otros no pasan nunca, por muchas veces que lo intentes.

Un buen programa distingue los dos. Reintenta el primer tipo con esperas crecientes, y se detiene y avisa en el segundo.

Al final de esta lección

Nota

Puedo distinguir un fallo que hay que reintentar de uno que hay que corregir.

Palabras clave

PalabraQué significaEjemplo
Código de estadoun número de la respuesta que dice cómo fue la llamada429
Límite de frecuenciaun tope de las llamadas que puedes hacer por minuto
Retrocesoesperar más tras cada intento fallido
Azar (jitter)un cambio aleatorio de una espera, para que los clientes no reintenten a la vez
Tiempo máximolo más que esperas una respuesta

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

El código de estado dice qué hacer

Un estado de la serie 200 significa que la llamada funcionó. Un estado de la serie 400 suele significar que tu llamada está mal hecha, así que la misma llamada volverá a fallar.

Otros estados señalan un problema breve y pasajero: un tiempo agotado, un servidor ocupado o un límite de frecuencia. Para esos, esperar y volver a probar es la respuesta correcta.

Ejemplo resuelto: reintentar o corregir

EstadoQué significaQué hacer
400La petición está malCorregir la petición
401No hay clave válidaCorregir la clave
403No permitido, o sin créditoCorregir la causa
408, 504Un tiempo agotadoReintentar
429Demasiadas peticionesEsperar y reintentar
500, 502, 503El servidor tiene un problemaReintentar unas veces

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Clasifica estados y lee un 429 grabado

Comando
docker exec -w /home/node/project agent node -e '/* ¿Qué fallos merecen otro intento? */ const kind = (status) => { if (status >= 200 && status < 300) return "ok"; return [408, 429, 500, 502, 503, 504].includes(status) ? "reintentar" : "corregir"; }; const notes = { 200: "funcionó", 400: "la petición está mal", 401: "no hay clave válida", 403: "no permitido", 408: "tiempo agotado", 429: "demasiadas peticiones", 500: "el servidor falló", 503: "el servidor no está listo" }; for (const status of Object.keys(notes)) { console.log(status + " " + kind(Number(status)).padEnd(10) + " " + notes[status]); } /* Un 429 grabado, escrito en este script como dato. No se hace ninguna llamada. */ const recorded = { status: 429, headers: { "retry-after": "2" }, body: { error: { message: "Demasiadas peticiones, más despacio." } } }; console.log("429 grabado: " + recorded.body.error.message + " El servidor pide " + recorded.headers["retry-after"] + " segundos.");'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

El retroceso espera más cada vez

Si reintentas al instante, añades carga a lo que causó el fallo. El retroceso espera un poco, luego el doble, luego el doble otra vez, hasta un tope.

Añade azar (jitter): cambia cada espera en una cantidad aleatoria, entre la mitad y el total. Así cien clientes que fallaron a la vez no reintentan en el mismo instante. Si la respuesta trae una cabecera Retry-After, úsala.

Imprime un calendario de retroceso

Comando
docker exec -w /home/node/project agent node -e '/* Esperar más tras cada fallo, hasta un tope, y añadir azar (jitter). */ const base = 1; const cap = 8; const delays = []; for (let attempt = 0; attempt < 6; attempt++) delays.push(Math.min(cap, base * 2 ** attempt)); console.log("Esperas en segundos: " + delays.join(", ")); const jittered = delays.map((one) => one / 2 + Math.random() * one / 2); console.log("Con azar, una ejecución posible: " + jittered.map((one) => one.toFixed(2)).join(", ")); console.log("Cada espera con azar está entre la mitad de la espera y la espera entera.");'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que hay que reintentar todo error. No es así. Un 400 o un 401 fallará igual cada vez. Un bucle que lo reintenta solo pierde tiempo y crédito. Reintenta solo los fallos que pueden pasar, y solo unas pocas veces.

Un tiempo máximo evita esperar sin fin

Algunas llamadas no responden nunca. Fija un tiempo máximo, lo más que esperas, y trata el agotarlo como un fallo que merece un reintento.

La ayuda de abajo junta las piezas. Tiene un tiempo máximo, unos pocos intentos y retroceso con azar. Se detiene al instante ante un fallo que hay que corregir e imprime una muestra grabada cuando nada funciona.

Escribe retry.js, parte 1

Comando
docker exec -w /home/node/project agent node -e 'require("fs").writeFileSync("retry.js", ["// retry.js, parte 1: qué estados reintentar y cuánto esperar.", "const lab = require(\"./lab.js\");", "const kind = (status) => {", "  if (status >= 200 && status < 300) return \"ok\";", "  return [408, 429, 500, 502, 503, 504].includes(status) ? \"reintentar\" : \"corregir\";", "};", "const pause = (ms) => new Promise((done) => setTimeout(done, ms));", "", "// Retroceso, la cabecera Retry-After si existe, un tope y azar.", "function waitFor(attempt, response) {", "  let wait = 150 * 2 ** attempt;", "  if (response) wait = Math.max(wait, Number(response.headers.get(\"retry-after\")) * 1000 || 0);", "  wait = Math.min(wait, 3000);", "  return wait / 2 + Math.random() * wait / 2;", "}"].join("\n") + "\n")'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Añade el bucle de reintentos

Comando
docker exec -w /home/node/project agent node -e 'require("fs").appendFileSync("retry.js", ["", "// Parte 2: reintentar, salvo ante un fallo que hay que corregir.", "async function callWithRetry(body, tries) {", "  for (let attempt = 1; attempt <= tries; attempt++) {", "    let response = null;", "    try {", "      response = await lab.send(body, 15000);", "      if (response.ok) return { ok: true, data: await response.json() };", "      console.log(\"intento \" + attempt + \": HTTP \" + response.status);", "      if (kind(response.status) === \"corregir\") return { ok: false, why: \"corrige la causa\" };", "    } catch (error) {", "      console.log(\"intento \" + attempt + \": sin respuesta (\" + error.name + \")\");", "    }", "    if (attempt < tries) await pause(waitFor(attempt, response));", "  }", "  return { ok: false, why: \"abandono tras \" + tries + \" intentos\" };", "}", "", "module.exports = { callWithRetry };"].join("\n") + "\n")'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Llama al modelo con reintentos

Comando
docker exec -w /home/node/project agent node -e 'const { callWithRetry } = require("./retry.js"); (async () => { const body = { max_tokens: 20, messages: [{ role: "user", content: "Saluda con una palabra." }] }; const result = await callWithRetry(body, 3); if (result.ok) console.log("Respuesta del modelo: " + result.data.choices[0].message.content); else console.log("Muestra grabada: Hola. (" + result.why + ")"); })();'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Para ir más lejos: cambia los intentos

En retry.js, cambia la primera espera de 150 a 50. En el último Run, cambia el número de intentos de 3 a 5. Ejecútalo y lee lo que imprime.

Di qué elegirías para un botón ante el que espera una persona, y qué para un trabajo que corre por la noche.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.