¿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

Colas para llamadas lentas a un modelo

Después de esta lección: Puedo sacar una llamada lenta de la petición e informar de su progreso.

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

Por qué una llamada lenta es una tarea

Una cola de tareas deja que una petición responda enseguida mientras el trabajo lento termina en otro sitio. Una llamada a un modelo puede tardar varios segundos, y una petición web que espera tanto caduca o bloquea un proceso. Importa porque reintentar una llamada lenta es el momento en que se cobra dos veces a un cliente.

Un usuario sube cuarenta páginas para resumir. El servidor responde «recibido, tarea-17» en unos milisegundos. La página pregunta el estado hasta que el resumen está listo.

Al final de esta lección

Nota

Puedo sacar una llamada lenta de la petición e informar de su progreso.

Palabras clave

PalabraQué significaEjemplo
Tareauna unidad de trabajo lento con un estado
Colalas tareas que esperan a un trabajador
Trabajadorel código que toma una tarea y la hace
Clave de idempotenciauna etiqueta del cliente que hace inofensiva una repeticiónpedido-1
Espera crecienteesperar más tras cada fallo

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

Una tarea tiene cuatro estados

Una tarea está en cola, luego en curso y después hecha o fallida. Un intento fallido que se repetirá vuelve a en cola. La tarea conserva su número de intentos y su resultado.

El cliente recibe enseguida un id de tarea. Pregunta el estado con ese id. Lee el resultado solo cuando el estado es hecho.

Ejemplo resuelto: reloj propio de tarea-2

PasoEstadoIntentosReloj
Enviadaen cola00 s
Empieza el primer intentoen curso12 s
Tiempo agotado aguas arribaen cola17 s
Empieza el segundo intentoen curso29 s
Resultado guardadohecha29 s

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

Una clave de idempotencia evita ejecutar dos veces

Las redes repiten peticiones. Un cliente puede pulsar enviar dos veces, o un proxy puede reenviar. Sin una protección, cada copia se convierte en una tarea, y el trabajo, igual que la factura, se duplica.

El cliente envía una clave de idempotencia. La cola guarda un mapa de clave a id de tarea. Una clave ya vista devuelve la tarea existente y no crea otra.

Los reintentos tienen tope y espera

Fija un número máximo de intentos. Después la tarea queda fallida, y una persona o una regla decide qué hacer. Espera más tras cada fallo para dar margen a un servicio con problemas.

Una tarea hecha nunca se vuelve a ejecutar, aunque una pasada la visite dos veces. Una tarea hecha es un resultado, no una petición.

Un error frecuente

Advertencia

Mucha gente cree que un reintento siempre es seguro. Solo lo es si el trabajo es idempotente o la cola lo recuerda. Reintenta una llamada ya pagada y terminada, y pagas otra vez. Comprueba el estado antes de cada ejecución.

Escribe el ayudante del modelo, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"estado \" + res.status);' '    return (await res.json()).choices[0].message.content;' > model.js"

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

Escribe el ayudante del modelo, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  } catch (error) {' '    console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' '    const list = [].concat(sample);' '    const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' '    return list[Math.min(turn, list.length - 1)];' '  }' '}' 'module.exports = { ask };' >> model.js"

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

Escribe la cola, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const jobs = {}; // id de tarea a tarea' 'const byKey = {}; // clave de idempotencia a id de tarea' 'let clock = 0; // un reloj falso, en segundos' 'let next = 1;' 'function submit(key, payload) {' '  if (key in byKey) return byKey[key];' '  const id = \"tarea-\" + next++;' '  jobs[id] = { id: id, payload: payload, state: \"en-cola\", tries: 0, result: null };' '  byKey[key] = id;' '  return id;' '}' > queue.js"

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

Escribe la cola, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const failFirst = { \"tarea-2\": 1 }; // tarea-2 falla una vez y luego funciona' 'function work(job) {' '  if (job.tries <= (failFirst[job.id] || 0)) throw new Error(\"tiempo agotado aguas arriba\");' '  return job.payload.toUpperCase();' '}' 'function runOne(job) {' '  if (job.state === \"hecho\" || job.state === \"fallido\") return;' '  while (job.tries < 3) {' '    job.tries++;' '    job.state = \"en-curso\";' '    clock += 2;' '    try { job.result = work(job); job.state = \"hecho\"; return; }' '    catch (error) { job.state = \"en-cola\"; clock += job.tries * 5; }' '  }' '  job.state = \"fallido\";' '}' >> queue.js"

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

Escribe la cola, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'submit(\"pedido-1\", \"factura a\");' 'submit(\"pedido-2\", \"factura b\");' 'submit(\"pedido-1\", \"factura a\"); // el cliente reintenta: misma clave, misma tarea' 'Object.values(jobs).forEach(runOne);' 'Object.values(jobs).forEach(runOne); // una segunda pasada no ejecuta nada' 'for (const id in jobs) console.log(id, jobs[id].state, \"intentos\", jobs[id].tries, jobs[id].result);' 'console.log(\"tareas:\", Object.keys(jobs).length, \"reloj:\", clock + \" s\");' >> queue.js"

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

Escribe la cola, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const sample = \"Tus dos facturas están listas.\";' '  const text = \"Dos tareas han terminado. Escribe una frase corta de estado para el cliente.\";' '  console.log(\"Llamada real:\", await ask([{ role: \"user\", content: text }], sample, 40));' '}' 'main();' >> queue.js"

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

Ejecuta la cola

Comando
docker exec -w /home/node/project agent node queue.js

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

Un sustituto, no una cola real

La cola de aquí es un objeto en memoria y el reloj es un número. Si el proceso se detiene, se pierden todas las tareas. Un sistema real guarda las tareas en un servicio hecho para eso, y los trabajadores corren en otros procesos.

Las ideas siguen valiendo: estados, un número de intentos, un mapa de claves y un tope de reintentos.

Una salida posible

LíneaQué muestra
tarea-1 hecho intentos 1 FACTURA AUna tarea que salió bien a la primera
tarea-2 hecho intentos 2 FACTURA BUna tarea que falló una vez y luego funcionó
tareas: 2 reloj: 11 sTres envíos crearon dos tareas
Llamada realUna frase corta de estado, o una Muestra grabada con su etiqueta

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

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.