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

無料レッスン

ツール呼び出しのループ

このレッスンのあとで: 聞く、呼ぶ、答える、また聞く、というループを実行できる。

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

ツールを使うモデルの裏にあるループ

ツール呼び出しは、会話の半分にすぎません。モデルが呼び出しを頼み、プログラムがそれを実行し、モデルは結果を見てからでないと答えられません。つまり、やりとりはループになります。

ツールを使うアシスタントは、どれもこのループを回しています。そして、必ず止まる仕組みが必要です。

このレッスンのゴール

メモ

聞く、呼ぶ、答える、また聞く、というループを実行できる。

キーワード

ことばいみ
ループ停止条件まで、プログラムが繰り返す手順
メッセージ履歴毎回の質問と一緒に送るメッセージのリスト
ツール結果呼び出しの答えをモデルに届けるメッセージ
ステップ1回の質問と、その応答
ステップ上限ループが進めるステップの最大数

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

呼び出しのたびにもう一度聞く

メッセージとツールをモデルに送ります。応答にtool_callsがあれば、各呼び出しを実行し、結果をメッセージに足して、もう一度聞きます。応答が言葉だけなら、それが答えで、ループは終わります。

モデルは、質問と質問の間に何も覚えていません。毎回の質問が、履歴をすべて運びます。

例で確認:ステップごとの履歴

ステップ起きること次に送るメッセージ
開始ユーザーがなしの値段を聞くuser
1モデルがget_priceを頼むuser, assistant
1プログラムが結果を足すuser, assistant, tool
2モデルが言葉で答えるuser, assistant, tool, assistant

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

モデルを呼ぶヘルパーを保存する

コマンド
docker exec -w /home/node/project agent node -e "const ask=async(m,t,c)=>{const r=await fetch((process.env.AI_URL||'http://ai:4000')+'/v1/chat/completions',{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer lab'},body:JSON.stringify({model:'lab',messages:m,tools:t,tool_choice:c,max_tokens:120})});const j=await r.json();if(r.ok)return j.choices[0].message;throw Error(r.status+' '+j.error.message)};require('fs').writeFileSync('ai.cjs','exports.ask='+ask+';');console.log('ai.cjsを保存しました:ask(messages, tools)はモデルのメッセージを返します。')"

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

ツール結果は呼び出しのidを持つ

呼び出しのあと、toolというロールのメッセージを足します。答える相手の依頼のtool_call_idと、テキストにした結果が入っています。

モデルは、このidで、各結果をそれぞれの依頼に結び付けます。idがないと、2つの呼び出しを含む応答に、2つの結果を返せません。

最大3ステップでループを実行する

コマンド
docker exec -w /home/node/project agent node -e "const{ask}=require('./ai.cjs');const P={りんご:120,なし:150};const T=[{type:'function',function:{name:'get_price',description:'商品の値段を返す。',parameters:{type:'object',properties:{item:{type:'string'}}}}}];let rec=0;const fake=m=>m.at(-1).role=='tool'?{role:'assistant',content:'なしは150円です。'}:{role:'assistant',tool_calls:[{id:'c1',function:{name:'get_price',arguments:JSON.stringify({item:'なし'})}}]};(async()=>{const m=[{role:'user',content:'なしはいくらですか。'}];for(let s=1;s<4;s++){let a;try{if(rec)throw 0;a=await ask(m,T)}catch(e){rec||console.log('モデルなし('+e.message+')。記録済みサンプル:');rec=1;a=fake(m)}m.push(a);if(a.tool_calls==null){console.log('答え:'+a.content);return}for(const c of a.tool_calls){const v=P[JSON.parse(c.function.arguments).item];console.log('ステップ'+s+':'+c.function.name+' -> '+v);m.push({role:'tool',tool_call_id:c.id,content:String(v)})}}console.log('ステップ上限に達しました')})()"

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

ステップ上限が暴走を止める

モデルは、呼び出しを何度も頼み続けることがあります。ステップごとに時間とクレジットを使うので、ステップを数え、上限で止めます。まずは3が目安です。

上限に先に達したときは、プログラムがそう伝えます。答えがあるふりはしません。

よくある間違い

注意

モデルは前の質問を覚えていると思っている人が多くいます。覚えていません。見えるのは、あなたが送ったメッセージだけです。ツール結果を省くと、呼び出しが何を返したか分からなくなります。メッセージは全部残しましょう。

さらに:質問を変えてみる

実行の質問をりんごとなしはいくらですか。に変えます。3回実行して、毎回ステップを数えましょう。

その後、s<4をs<2に変えます。これでループは最大1ステップしか進みません。上限に先に達したとき、プログラムが何を表示するか書きましょう。

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

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