Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Files d’attente pour appels lents

Après cette leçon: Je peux sortir un appel lent de la requête et rendre compte de son avancement.

Leçon de 65 min7 commandesLab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Pourquoi un appel lent devient une tâche

Une file de tâches laisse une requête répondre tout de suite pendant que le travail lent se termine ailleurs. Un appel à un modèle peut durer plusieurs secondes, et une requête web qui attend autant échoue ou bloque un processus. C’est important, car réessayer un appel lent est le moment où un client est facturé deux fois.

Un utilisateur envoie quarante pages à résumer. Le serveur répond « reçu, tache-17 » en quelques millisecondes. La page demande l’état jusqu’à ce que le résumé soit prêt.

À la fin de cette leçon

Remarque

Je peux sortir un appel lent de la requête et rendre compte de son avancement.

Mots-clés

MotCe que ça veut direExemple
Tâcheune unité de travail lent, avec un état
Fileles tâches qui attendent un exécutant
Exécutantle code qui prend une tâche et la fait
Clé d’idempotenceune étiquette du client qui rend une répétition sans effetcommande-1
Attente croissanteattendre plus longtemps après chaque échec

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Une tâche a quatre états

Une tâche est en attente, puis en cours, puis terminée ou en échec. Un essai raté qui sera repris retourne à en attente. La tâche garde son nombre d’essais et son résultat.

Le client reçoit tout de suite un identifiant de tâche. Il demande l’état avec cet identifiant. Il lit le résultat seulement quand l’état est terminé.

Exemple travaillé : tache-2 seule, horloge à 0

ÉtapeÉtatEssaisHorloge
Soumiseen attente00 s
Premier essai commenceen cours12 s
Délai dépassé en amonten attente17 s
Second essai commenceen cours29 s
Résultat rangéterminée29 s

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Une clé d’idempotence évite de lancer deux fois

Les réseaux répètent les requêtes. Un client peut appuyer deux fois sur envoyer, ou un proxy peut renvoyer. Sans garde-fou, chaque copie devient une tâche, et le travail, comme la facture, est doublé.

Le client envoie une clé d’idempotence. La file garde une table qui relie la clé à l’identifiant de tâche. Une clé déjà vue renvoie la tâche existante et n’en crée pas de nouvelle.

Les reprises sont bornées et espacées

Fixez un nombre maximal d’essais. Ensuite la tâche est en échec, et une personne ou une règle décide de la suite. Attendez plus longtemps après chaque échec pour laisser souffler un service en difficulté.

Une tâche terminée n’est jamais relancée, même si un passage la visite deux fois. Une tâche terminée est un résultat, pas une demande.

Une erreur fréquente

Attention

Beaucoup pensent qu’une reprise est toujours sûre. Elle ne l’est que si le travail est idempotent ou si la file s’en souvient. Réessayez un appel déjà payé et terminé, et vous payez encore. Vérifiez l’état avant chaque lancement.

Écrire l’aide du modèle, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function demander(messages, echantillon, 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(\"statut \" + res.status);' '    return (await res.json()).choices[0].message.content;' > model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire l’aide du modèle, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  } catch (erreur) {' '    console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' '    const liste = [].concat(echantillon);' '    const tour = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' '    return liste[Math.min(tour, liste.length - 1)];' '  }' '}' 'module.exports = { demander };' >> model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la file, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const taches = {}; // identifiant de tâche vers tâche' 'const parCle = {}; // clé d’idempotence vers identifiant de tâche' 'let horloge = 0; // une fausse horloge, en secondes' 'let suivant = 1;' 'function soumettre(cle, charge) {' '  if (cle in parCle) return parCle[cle];' '  const id = \"tache-\" + suivant++;' '  taches[id] = { id: id, charge: charge, etat: \"en-attente\", essais: 0, resultat: null };' '  parCle[cle] = id;' '  return id;' '}' > queue.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la file, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const echecsAvant = { \"tache-2\": 1 }; // tache-2 échoue une fois, puis marche' 'function travailler(tache) {' '  if (tache.essais <= (echecsAvant[tache.id] || 0)) throw new Error(\"délai dépassé en amont\");' '  return tache.charge.toUpperCase();' '}' 'function lancer(tache) {' '  if (tache.etat === \"termine\" || tache.etat === \"echec\") return;' '  while (tache.essais < 3) {' '    tache.essais++;' '    tache.etat = \"en-cours\";' '    horloge += 2;' '    try { tache.resultat = travailler(tache); tache.etat = \"termine\"; return; }' '    catch (erreur) { tache.etat = \"en-attente\"; horloge += tache.essais * 5; }' '  }' '  tache.etat = \"echec\";' '}' >> queue.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la file, partie 3

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'soumettre(\"commande-1\", \"facture a\");' 'soumettre(\"commande-2\", \"facture b\");' 'soumettre(\"commande-1\", \"facture a\"); // le client réessaie : même clé, même tâche' 'Object.values(taches).forEach(lancer);' 'Object.values(taches).forEach(lancer); // un second passage ne lance rien' 'for (const id in taches) console.log(id, taches[id].etat, \"essais\", taches[id].essais, taches[id].resultat);' 'console.log(\"tâches :\", Object.keys(taches).length, \"horloge :\", horloge + \" s\");' >> queue.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la file, partie 4

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const echantillon = \"Vos deux factures sont prêtes.\";' '  const texte = \"Deux tâches sont terminées. Écrivez une courte phrase d’état pour le client.\";' '  console.log(\"Appel réel :\", await demander([{ role: \"user\", content: texte }], echantillon, 40));' '}' 'main();' >> queue.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Lancer la file

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

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Une doublure, pas une vraie file

La file ici est un objet en mémoire et l’horloge est un nombre. Si le processus s’arrête, toutes les tâches sont perdues. Un vrai système garde les tâches dans un service fait pour cela, et les exécutants tournent dans d’autres processus.

Les idées restent valables : des états, un nombre d’essais, une table de clés et une borne sur les reprises.

Une sortie possible

LigneCe qu’elle montre
tache-1 termine essais 1 FACTURE AUne tâche réussie du premier coup
tache-2 termine essais 2 FACTURE BUne tâche qui a échoué une fois, puis réussi
tâches : 2 horloge : 11 sTrois envois ont créé deux tâches
Appel réelUne courte phrase d’état, ou un échantillon enregistré étiqueté

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

À propos de ce labo et de sa source: Lab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.