クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

非同期、await、ストリーム

このレッスンのあとで: 遅い呼び出しをなぜ await するのか、ストリームの答えがどう届くのかを説明できる。

60分のレッスンコマンド3件AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

遅い呼び出しで固まらない

モデルへの呼び出しは、数秒かかることがあります。待っている間にプログラムが固まっては困ります。非同期のコードなら、止まらずに待てます。

ストリームは、さらに一歩進んだ方法です。答えが少しずつ届くので、利用者はすぐに読み始められます。

このレッスンのゴール

メモ

遅い呼び出しをなぜ await するのか、ストリームの答えがどう届くのかを説明できる。

キーワード

ことばいみ
プロミスまだ届いていない結果の代わりになるもの
awaitプロミスの結果が出るまで、関数を一時停止すること
async関数await を使ってよい関数
ストリーム作られるそばから少しずつ送られる答え
チャンクストリームの答えの1つの断片

学生が入力するセルは、このプレビューでは空白です。

遅い呼び出しはプロミスを返す

遅い仕事を始めると、呼び出しはすぐにプロミスを返して戻ります。結果はまだありません。プログラムは次の行へ進みます。

結果が届くと、プロミスがそれを渡します。順序に注意しましょう。呼び出しの後ろの行は、結果より先に実行されることがあります。

起きる順番を見る

コマンド
docker exec -w /home/node/project agent node -e "const wait=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));console.log('1 開始');wait(300,'3 遅い呼び出しが終わった').then(console.log);console.log('2 プログラムは先へ進む')"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

awaitは関数を止める

async関数の中では、await が、プロミスの結果が出るまでその関数を一時停止します。プログラムのほかの部分は止まりません。

互いに関係のない遅い呼び出し2つは、Promise.all で同時に始められます。かかる時間は、遅いほうと同じくらいで、足し算にはなりません。

順に待つ場合と同時に待つ場合

コマンド
docker exec -w /home/node/project agent node -e "const wait=(ms,t)=>new Promise(r=>setTimeout(()=>r(t),ms));const tick=(t0)=>Math.round((Date.now()-t0)/100)*100;(async()=>{let t0=Date.now();await wait(200,'a');await wait(200,'b');console.log('順番に待つ:約'+tick(t0)+' ms');t0=Date.now();await Promise.all([wait(200,'a'),wait(200,'b')]);console.log('同時に待つ:約'+tick(t0)+' ms')})()"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

ストリームの答えはチャンクで届く

stream: true を指定すると、サービスは答えを作るそばから送ります。多くのモデルAPIはサーバー送信イベントを使います。data: で始まる行が並び、それぞれに小さなテキストが入っています。

最後の行は data: [DONE] です。断片を順につなげると、答え全体になります。

例:ストリームを1行ずつ見る

受け取った行得られるもの
data: {"choices":[{"delta":{"content":"こん"}}]}こん
data: {"choices":[{"delta":{"content":"にちは"}}]}にちは
data: [DONE]終わり。テキストなし
結合こんにちは

学生が入力するセルは、このプレビューでは空白です。

ストリームの答えを読む

コマンド
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:'一から五まで、漢数字で数えてください。'}],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('テキストを持つチャンク:'+n);console.log('結合したテキスト:'+t)}catch(e){console.log('ストリームなし('+e.message+')。記録済みサンプル:');console.log('テキストを持つチャンク:5');console.log('結合したテキスト:一、二、三、四、五')}})()"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

よくある間違い

注意

ストリームにするとモデルが速くなる、と考える人が多くいます。答え全体にかかる時間は、ほぼ同じです。最初の言葉が早く見えるので、速く感じるのです。ストリームは途中で切れることもあるので、終わりの印が届いたかを確かめましょう。

このラボとソースについて: AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。