部品を組み合わせる
小さな AI エージェントとは、計画し、ツールを呼び、メモを持ち、ルールで止まる、1つの繰り返しです。部品はすでに1つずつ作りました。腕の見せどころは、繰り返しを試しやすいようにつなぐことです。
本物のモデルがないと動かない繰り返しは、試せません。台本付きのモデルの上の繰り返しなら、試せます。
このレッスンのゴール
メモ
計画し、ツールを呼び、覚え、止まる小さなエージェントを作れる。
キーワード
| ことば | いみ |
|---|---|
| エージェントの繰り返し | アクションを選び、実行し、記録し、また繰り返す |
| 台本付きのモデル | 決まったアクションを返す代役 |
| トレース | 各ステップがしたことの一覧 |
| 終了アクション | 実行を「完了」で終わらせるアクション |
| テスト用の代役 | 本物の部品の代わりに使うもの |
学生が入力するセルは、このプレビューでは空白です。
エージェントの繰り返しを1枚の図で
毎回の最初に、予算を確かめます。モデルがアクションを選びます。終了アクションなら、実行は「完了」で終わります。それ以外のアクションは、ツールを実行して結果を記録します。
そのあと、連続の失敗を数えます。十分に続けば、実行は「行き詰まり」で終わります。そうでなければ、もう1回りします。
モデル用の補助を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデル用の補助を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
小さなエージェントを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const PRICES = { \"おにぎり\": 150, \"お茶\": 140 };' 'const notes = [];' 'const tools = {' ' get_price: function (item) {' ' if (PRICES[item] === undefined) return { ok: false, text: \"価格なし: \" + item };' ' return { ok: true, text: item + \" \" + PRICES[item] + \" 円\" };' ' },' ' add_note: function (text) { notes.push(text); return { ok: true, text: \"メモ \" + notes.length }; },' '};' > agent1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
小さなエージェントを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const script = [' ' { tool: \"get_price\", arg: \"おにぎり\" },' ' { tool: \"get_price\", arg: \"キウイ\" },' ' { tool: \"get_price\", arg: \"お茶\" },' ' { tool: \"add_note\", arg: \"おにぎり 150、お茶 140\" },' ' { tool: \"finish\", arg: \"おにぎりとお茶は、合わせて 290 円です。\" },' '];' '// 台本付きのモデルは、実行のたびに同じアクションを返すので、繰り返しを試せます。' 'const scriptedModel = function (turn) { return JSON.stringify(script[turn]); };' >> agent1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
小さなエージェントを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const budget = Number(process.argv[2] || 6), maxFails = 2;' ' let used = 0, fails = 0, outcome = \"予算切れ\", answer = \"\";' >> agent1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
小さなエージェントを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' while (used < budget && used < script.length) {' ' const action = JSON.parse(scriptedModel(used));' ' used += 1;' ' if (action.tool === \"finish\") { outcome = \"完了\"; answer = action.arg; break; }' ' const run = tools[action.tool];' ' const r = run ? run(action.arg) : { ok: false, text: \"不明なツール \" + action.tool };' ' fails = r.ok ? 0 : fails + 1;' ' console.log(\"ステップ \" + used + \": \" + action.tool + \"(\" + action.arg + \") -> \" + r.text);' ' if (fails === maxFails) { outcome = \"行き詰まり\"; break; }' ' }' ' console.log(\"結果: \" + outcome + \" (\" + used + \" ステップ)。メモ: \" + JSON.stringify(notes));' >> agent1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
小さなエージェントを書く(5)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' if (outcome === \"完了\") {' ' const request = [{ role: \"user\", content: \"次の内容を、客に1文で、感じよく伝えてください: \" + answer }];' ' console.log(\"返事: \" + await ask(request, answer, 60));' ' }' '}' 'main();' >> agent1.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
予算 6 でエージェントを実行
docker exec -w /home/node/project agent node agent1.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
予算 4 でもう一度実行
docker exec -w /home/node/project agent node agent1.js 4ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の一例
| 予算 | 表示される内容 |
|---|---|
| 6 | ステップ 2 でキウイが失敗し、結果は5ステップで完了、1文の返事が出る |
| 4 | 4ステップのあと、結果は予算切れで、返事は出ない |
学生が入力するセルは、このプレビューでは空白です。
台本付きのモデルで繰り返しを試す
台本付きのモデルは、実行のたびに同じアクションを返します。これで、完了、予算切れ、行き詰まり、台本の終わりの、結果ごとに繰り返しを試せます。
本物のモデルは別に試します。得意なこと、つまり妥当なアクションを選ぶことで試します。運よく1回うまくいっただけで、繰り返しを判断してはいけません。
よくある間違い
注意
「本物のモデルで1回動いたエージェントは、試験済みだ」と考える人が多くいます。モデルは毎回別のアクションを返すので、1回の成功はほとんど何も証明しません。繰り返しは台本で試し、モデルは多くの場合で試します。