Ein langsamer Aufruf darf nichts einfrieren
Ein Aufruf an ein Modell kann mehrere Sekunden dauern. Ihr Programm soll beim Warten nicht einfrieren. Asynchroner Code lässt es warten, ohne zu blockieren.
Der Datenstrom geht noch einen Schritt weiter. Die Antwort kommt in Stücken an, und man kann sofort zu lesen beginnen.
Am Ende dieser Lektion
Notiz
Ich kann sagen, warum ein langsamer Aufruf abgewartet wird und wie eine Antwort im Datenstrom ankommt.
Schlüsselwörter
| Begriff | Was er bedeutet |
|---|---|
| Promise | ein Platzhalter für ein Ergebnis, das noch nicht da ist |
| Await | eine Funktion anhalten, bis eine Promise ihr Ergebnis hat |
| Async-Funktion | eine Funktion, die await benutzen darf |
| Datenstrom | eine Antwort, die in Stücken gesendet wird, während sie entsteht |
| Teilstück | ein Stück einer Antwort im Datenstrom |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein langsamer Aufruf liefert eine Promise
Starten Sie eine langsame Arbeit, kehrt der Aufruf sofort mit einer Promise zurück. Das Ergebnis ist noch nicht da. Ihr Programm macht mit der nächsten Zeile weiter.
Kommt das Ergebnis, liefert es die Promise. Die Reihenfolge zählt: Zeilen nach dem Aufruf können vor dem Ergebnis laufen.
Die Reihenfolge der Ereignisse sehen
docker exec -w /home/node/project agent node -e "const warten=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));console.log('1 Start');warten(300,'3 der langsame Aufruf ist fertig').then(console.log);console.log('2 das Programm läuft weiter')"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Await hält eine Funktion an, nicht das Programm
In einer Async-Funktion hält await diese Funktion an, bis die Promise ihr Ergebnis hat. Der Rest des Programms wird nicht blockiert.
Zwei langsame Aufrufe, die nicht voneinander abhängen, können mit Promise.all zusammen starten. Das dauert ungefähr so lange wie der langsamere, nicht beide zusammen.
Erst nacheinander, dann gleichzeitig abwarten
docker exec -w /home/node/project agent node -e "const warten=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));const runden=(t0)=>Math.round((Date.now()-t0)/100)*100;(async()=>{let t0=Date.now();await warten(200,'a');await warten(200,'b');console.log('nacheinander: etwa '+runden(t0)+' ms');t0=Date.now();await Promise.all([warten(200,'a'),warten(200,'b')]);console.log('gleichzeitig: etwa '+runden(t0)+' ms')})()"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Eine Antwort im Datenstrom kommt in Teilstücken
Fordern Sie stream: true an, sendet der Dienst die Antwort, während er sie erzeugt. Die meisten Modell-APIs nutzen Server-Sent Events: Zeilen, die mit data: beginnen und je ein kleines Stück Text tragen.
Die letzte Zeile ist data: [DONE]. Fügen Sie die Stücke der Reihe nach zusammen, um die ganze Antwort zu erhalten.
Beispiel: ein Datenstrom, Zeile für Zeile
| Empfangene Zeile | Was sie ergibt |
|---|---|
| data: {"choices":[{"delta":{"content":"Hal"}}]} | Hal |
| data: {"choices":[{"delta":{"content":"lo"}}]} | lo |
| data: [DONE] | Das Ende, ohne Text |
| Zusammengefügt | Hallo |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Eine Antwort im Datenstrom lesen
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:'Zählen Sie von eins bis fünf.'}],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 z=b.split(String.fromCharCode(10));b=z.pop();for(const l of z){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('Teilstücke mit Text: '+n);console.log('Zusammengesetzter Text: '+t)}catch(e){console.log('Kein Strom ('+e.message+'). Aufgezeichnetes Beispiel:');console.log('Teilstücke mit Text: 5');console.log('Zusammengesetzter Text: eins, zwei, drei, vier, fünf')}})()"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ein häufiger Fehler
Warnung
Viele glauben, ein Datenstrom mache das Modell schneller. Die ganze Antwort dauert ungefähr gleich lang. Sie sehen die ersten Wörter früher, und das fühlt sich schneller an. Ein Strom kann auch mittendrin abbrechen: Prüfen Sie, ob die Endmarke angekommen ist.