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

無料レッスン

Node で小さな AI エージェントを作る

このレッスンのあとで: 計画し、ツールを呼び、覚え、止まる小さなエージェントを作れる。

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

部品を組み合わせる

小さな 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文の返事が出る
44ステップのあと、結果は予算切れで、返事は出ない

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

台本付きのモデルで繰り返しを試す

台本付きのモデルは、実行のたびに同じアクションを返します。これで、完了、予算切れ、行き詰まり、台本の終わりの、結果ごとに繰り返しを試せます。

本物のモデルは別に試します。得意なこと、つまり妥当なアクションを選ぶことで試します。運よく1回うまくいっただけで、繰り返しを判断してはいけません。

よくある間違い

注意

「本物のモデルで1回動いたエージェントは、試験済みだ」と考える人が多くいます。モデルは毎回別のアクションを返すので、1回の成功はほとんど何も証明しません。繰り返しは台本で試し、モデルは多くの場合で試します。

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

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