ツールは失敗する。モデルが助けになる
ツールは失敗することがあります。商品が存在しない、サービスが遅い、モデルが悪い値を送ってきた、などです。プログラムがクラッシュすると、アシスタント全体が止まってしまいます。
プログラムがエラーを結果として返せば、モデルはそれを読んで立て直せます。
このレッスンのゴール
メモ
モデルが立て直せるように、ツールのエラーを結果として返せる。
キーワード
| ことば | いみ |
|---|---|
| 例外 | 関数が終われないときに起こすエラー |
| エラー結果 | 何が悪かったかを伝えるツールメッセージ |
| 再試行 | 同じ呼び出しをもう一度試すこと |
| タイムアウト | 時間がかかりすぎて打ち切られた呼び出し |
| あきらめる | 試すのをやめて、失敗を報告すること |
学生が入力するセルは、このプレビューでは空白です。
エラーは読める結果になる
呼び出しをtryで包みます。エラーが起きても止まらず、メッセージをツールメッセージに入れます。たとえばエラー:見つからない:キウイのようにして、もう一度モデルに聞きます。
モデルは、その言葉を見ます。ユーザーに謝ることも、別の商品を試すこともできます。ループにはステップ上限があるので、永遠には続きません。
例で確認:失敗ごとの扱い
| 何が悪かったか | 再試行する? | モデルへのメッセージ |
|---|---|---|
| 商品が店にない | しない | エラー:見つからない:キウイ |
| 値が許されない | しない | エラー:負の数です |
| 引数が合わない | しない | エラー:引数が不正です |
| サービスがタイムアウトした | する(数回まで) | エラー:時間切れ(最後の試行のあと) |
学生が入力するセルは、このプレビューでは空白です。
モデルを呼ぶヘルパーを保存する
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)はモデルのメッセージを返します。')"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
失敗するツールと不安定なツールを包む
docker exec -w /home/node/project agent node -e "let n=0;const flaky=()=>{n++;if(n<3)throw Error('timeout');return 'ok'};const price=a=>{if((a.item=='りんご')===false)throw Error('見つからない:'+a.item);return 120};const call=(f,a,tries)=>{for(let i=1;;i++){try{return{ok:true,result:String(f(a)),attempts:i}}catch(e){if(e.message=='timeout'&&i<tries)continue;return{ok:false,error:e.message,attempts:i}}}};console.log(JSON.stringify(call(price,{item:'りんご'},3)));console.log(JSON.stringify(call(price,{item:'キウイ'},3)));console.log(JSON.stringify(call(flaky,{},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'}}}}}];const go=async(m,f)=>{try{return await ask(m,T)}catch(e){console.log('モデルなし('+e.message+')。記録済みサンプル:');return f}};(async()=>{const m=[{role:'user',content:'キウイはいくらですか。'}];const a=await go(m,{role:'assistant',tool_calls:[{id:'c1',function:{name:'get_price',arguments:JSON.stringify({item:'キウイ'})}}]});m.push(a);const c=a.tool_calls?.[0];if(c==null)return console.log('答え:'+a.content);const i=JSON.parse(c.function.arguments).item;const r=i in P?String(P[i]):'エラー:見つからない:'+i;console.log('ツール結果:'+r);m.push({role:'tool',tool_call_id:c.id,content:r});const b=await go(m,{role:'assistant',content:'キウイはありません。'});console.log('そのあと:'+(b.content||JSON.stringify(b.tool_calls[0].function)))})()"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
ツールが失敗したら、会話全体が失敗したと思っている人が多くいます。そうではありません。エラーは、もう1つの結果にすぎません。テキストで返し、ステップ上限を保ち、どうするかはモデルに決めさせましょう。
さらに:試行回数を変える
ツールを包む実行の最後の呼び出しcall(flaky,{},3)で、試行回数の3を2に変えて、もう一度実行します。最後の行を見ましょう。ほかの2つの呼び出しの3は、そのままにします。
何が変わるか、そして試行に上限がなぜ大切かを書き留めます。