ツールがそろった道具箱
ツールが1つなら簡単です。実際のアシスタントには、値段の検索、単位の換算、メモの保存など、いくつものツールがあります。モデルは依頼ごとに1つを選び、プログラムはその依頼を正しい関数へ送らなければなりません。
存在しないツールをモデルが頼んだときにも、うまく対処する必要があります。
このレッスンのゴール
メモ
複数のツールを提供し、それぞれの依頼を正しい関数へ振り分けられる。
キーワード
| ことば | いみ |
|---|---|
| 振り分け役 | 依頼を正しい関数へ送るコード |
| ツール表 | ツールの名前と関数を結び付けた表 |
| 不明なツール | ツール表にない名前 |
| 必須引数 | それがないとツールが動けない値 |
| 検証 | 依頼に従う前に中身を確かめること |
学生が入力するセルは、このプレビューでは空白です。
ツール表は名前と関数を結ぶ
ツールは1つの表にまとめます。キーはモデルが使う名前、値はあなたの関数です。
依頼を振り分けるには、表から名前を探し、見つかったものを呼びます。ツールを足すのは1行で済み、表がモデルに使わせてよいものの唯一の一覧になります。
例で確認:3つのツールの道具箱
| 名前 | することの説明 | 必須引数 |
|---|---|---|
| get_price | 商品の値段を調べる | item |
| km_to_miles | キロメートルをマイルに換算する | km |
| add_note | 短いメモを保存する | text |
学生が入力するセルは、このプレビューでは空白です。
モデルを呼ぶヘルパーを保存する
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)はモデルのメッセージを返します。')"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
応えられない依頼は断る
モデルは、表にない名前を頼むことがあります。読めない引数を送ることも、必須の引数を忘れることもあります。何かを呼ぶ前に、3つとも確かめます。
断りは、モデルへのメッセージです。たとえばエラー:不明なツール delete_allのようにします。クラッシュではなく、何も実行されません。
4つの依頼を振り分ける(2つは不正)
docker exec -w /home/node/project agent node -e "const F={get_price:a=>({りんご:120,なし:150})[a.item],km_to_miles:a=>a.km*0.62,add_note:a=>'保存しました'};const R={get_price:['item'],km_to_miles:['km'],add_note:['text']};const route=c=>{if(Object.hasOwn(F,c.name)===false)return 'エラー:不明なツール '+c.name;let a;try{a=JSON.parse(c.arguments)}catch(e){return 'エラー:引数が不正です'}for(const k of R[c.name])if((k in a)===false)return 'エラー:'+k+'がありません';return String(F[c.name](a))};for(const c of [{name:'get_price',arguments:JSON.stringify({item:'なし'})},{name:'delete_all',arguments:'{}'},{name:'get_price',arguments:'{item:'},{name:'km_to_miles',arguments:'{}'}])console.log(c.name+' -> '+route(c))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデルに3つのツールを渡す
docker exec -w /home/node/project agent node -e "const{ask}=require('./ai.cjs');const t=(n,d,k)=>({type:'function',function:{name:n,description:d,parameters:{type:'object',properties:{[k]:{type:k=='km'?'number':'string'}}}}});const T=[t('get_price','商品の値段を返す。','item'),t('km_to_miles','kmをマイルに換算する。','km'),t('add_note','メモを保存する。','text')];const F={get_price:a=>({りんご:120,なし:150})[a.item],km_to_miles:a=>Math.round(a.km*621.371)/1000,add_note:a=>'保存:'+a.text};(async()=>{let c;try{c=(await ask([{role:'user',content:'10kmは何マイルですか。'}],T,'required')).tool_calls[0]}catch(e){console.log('モデルなし('+e.message+')。記録済みサンプル:');c={function:{name:'km_to_miles',arguments:JSON.stringify({km:10})}}}const f=c.function;console.log('依頼:'+f.name+' '+f.arguments);console.log('結果:'+(Object.hasOwn(F,f.name)?F[f.name](JSON.parse(f.arguments)):'不明なツール'))})()"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
モデルは、提供したツールしか頼まないと思っている人が多くいます。どんな名前でも頼めますし、合わない引数も送れます。名前は必ず表で、引数はツールが必要とするものと照らして確かめましょう。
さらに:4つ目のツールを足す
振り分けの実行に、引数textを2回つなげて返すtwiceというツールを足します。まず、関数の表Fにtwice:a=>a.text+a.textを、必須引数の表Rにtwice:['text']を足します。次に、最後のforの依頼のリストに、{name:'twice',arguments:JSON.stringify({text:'ab'})}を足して実行します。
表示された行を書き留め、コードを何か所変える必要があったかも書き留めます。