Un appel lent ne doit rien figer
Un appel à un modèle peut durer plusieurs secondes. Votre programme ne doit pas se figer pendant l’attente. Le code asynchrone lui permet d’attendre sans se bloquer.
Le flux va plus loin. La réponse arrive par morceaux, et l’utilisateur peut commencer à lire tout de suite.
À la fin de cette leçon
Remarque
Je peux dire pourquoi on attend un appel lent et comment arrive une réponse en flux.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Promesse | un remplaçant pour un résultat qui n’est pas encore arrivé |
| Await | mettre une fonction en pause jusqu’au résultat d’une promesse |
| Fonction async | une fonction qui a le droit d’utiliser await |
| Flux | une réponse envoyée par morceaux à mesure qu’elle se fabrique |
| Fragment | un morceau d’une réponse en flux |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un appel lent renvoie une promesse
Quand vous lancez un travail lent, l’appel revient aussitôt avec une promesse. Le résultat n’est pas encore là. Votre programme passe à la ligne suivante.
Quand le résultat arrive, la promesse le livre. L’ordre compte : les lignes après l’appel peuvent s’exécuter avant le résultat.
Voir l’ordre des événements
docker exec -w /home/node/project agent node -e "const attendre=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));console.log('1 début');attendre(300,'3 l’appel lent est terminé').then(console.log);console.log('2 le programme continue')"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.
Await met en pause une fonction
Dans une fonction async, await met cette fonction en pause jusqu’au résultat de la promesse. Le reste du programme n’est pas bloqué.
Deux appels lents indépendants peuvent partir ensemble avec Promise.all. Cela prend à peu près le temps du plus lent, pas la somme des deux.
Attendre un par un, puis ensemble
docker exec -w /home/node/project agent node -e "const attendre=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));const arrondi=(t0)=>Math.round((Date.now()-t0)/100)*100;(async()=>{let t0=Date.now();await attendre(200,'a');await attendre(200,'b');console.log('l’un après l’autre : environ '+arrondi(t0)+' ms');t0=Date.now();await Promise.all([attendre(200,'a'),attendre(200,'b')]);console.log('ensemble : environ '+arrondi(t0)+' ms')})()"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 réponse en flux arrive par fragments
Si vous demandez stream: true, le service envoie la réponse à mesure qu’il la fabrique. La plupart des API de modèles utilisent des événements serveur : des lignes qui commencent par data:, chacune avec un petit morceau de texte.
La dernière ligne est data: [DONE]. Joignez les morceaux dans l’ordre pour obtenir la réponse entière.
Exemple travaillé : un flux, ligne par ligne
| Ligne reçue | Ce qu’elle donne |
|---|---|
| data: {"choices":[{"delta":{"content":"Sal"}}]} | Sal |
| data: {"choices":[{"delta":{"content":"ut"}}]} | ut |
| data: [DONE] | La fin, sans texte |
| Assemblé | Salut |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Lire une réponse en flux
docker exec -w /home/node/project agent node -e "const u=(process.env.AI_URL||'http://ai:4000')+'/v1/chat/completions';(async()=>{try{const r=await fetch(u,{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer lab'},body:JSON.stringify({model:'lab',messages:[{role:'user',content:'Comptez de un à cinq en lettres.'}],max_tokens:40,stream:true})});if(r.ok===false)throw new Error(r.status);const d=new TextDecoder();let b='',n=0,t='';for await(const p of r.body){b+=d.decode(p,{stream:true});const L=b.split(String.fromCharCode(10));b=L.pop();for(const l of L){const x=l.slice(6);if(l.startsWith('data: ')&&(x==='[DONE]')===false){const c=JSON.parse(x).choices?.[0]?.delta?.content;if(c){n++;t+=c}}}}console.log('Fragments avec du texte : '+n);console.log('Texte assemblé : '+t)}catch(e){console.log('Aucun flux ('+e.message+'). Exemple enregistré :');console.log('Fragments avec du texte : 5');console.log('Texte assemblé : un, deux, trois, quatre, cinq')}})()"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 erreur fréquente
Attention
Beaucoup de gens pensent que le flux rend le modèle plus rapide. La réponse entière prend à peu près le même temps. Vous voyez les premiers mots plus tôt, ce qui paraît plus rapide. Un flux peut aussi s’arrêter en route : vérifiez que le marqueur de fin est arrivé.