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

無料レッスン

エージェント型の多段検索

このレッスンのあとで: エージェントに、止める規則と上限を付けて複数回検索させ、既知の質問で回答を採点できる。

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

一回の検索では足りなかった

エージェント型検索とは、最初の結果が質問に答えていないとき、エージェントが自分で判断して検索し直すことです。本当の質問は二度の目が要ることが多く、一度目で話題を、二度目で詳細を見つけるので、役に立ちます。

ロックが何分続くかを聞く人がいます。一度目はパスワードの規則が見つかり、ロックの時間は見つかりません。エージェントは検索し直します。

このレッスンのゴール

メモ

エージェントに、止める規則と上限を付けて複数回検索させ、既知の質問で回答を採点できる。

キーワード

ことばいみ
エージェント型検索エージェントが繰り返し、変えられる検索
停止の規則検索のループを終わらせる条件
検索の上限一つの質問が使える検索の最大回数
再現率必要な出典のうち、見つかったものの割合
根拠のある回答出典が本当に取得されていた回答
見たものここまでに見つかった出典

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

検索はループが再利用する道具

エージェントを作るモジュールでは、ステップの予算を持つエージェントのループを作りました。ここでは、道具が検索です。エージェントは検索し、返ったものを読み、答えるか、新しいクエリで検索し直します。

注意点は二つあります。ループが終わること。そして、最終的な回答が、本当に見つかったものに基づくことです。

止める三つの方法

エージェントが答えたら止めます。検索が新しいものを何も加えなかったら止めます。同じ語でもう一度検索しても役に立たないからです。そして、コストと時間で決める検索の上限で止めます。

ループが止まった理由を記録します。回答済み、停止、上限、回答なしのどれかです。理由が、何を直すかを教えます。

例: 同じ質問の三つの実行

実行検索回数終わり方根拠あり
良い実行2回答済みはい
行き詰まり2停止、新しいものなしいいえ
上限 11停止、上限いいえ

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

回答が根拠を持つか確かめる

回答は出典を挙げます。すべての出典が、検索が返したページの中にあるときだけ、根拠があると言えます。エージェントが一度も見ていない出典は、作り話です。

再現率も測ります。質問に必要なページのうち、検索はいくつ見つけたかです。出典が分かっている質問で、両方を採点します。

よくある間違い

注意

出典を挙げたエージェントは、それを使ったと考える人がいます。出典の記載は、ただのテキストです。挙げられた id が、この実行の検索で返ったものかを確かめないと、作り話の出典が本物として通ります。

モデルの補助を書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// 応答に含まれるトークン数。なければ null。' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"

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

モデルの補助を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '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 || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"status \" + res.status);' '    const d = await res.json();' '    return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask };' >> model.js"

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

検索ループを書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const index = {' '  \"パスワード 規則\": [\"p2\", \"p1\"],' '  \"ロック 時間\": [\"p3\"],' '  \"パスワード 再設定\": [\"p1\"],' '};' 'const docs = { p1: \"パスワードはログイン画面から再設定できます。\", p2: \"パスワードは十文字です。\", p3: \"パスワードを五回間違えると、アカウントは十五分ロックされます。\" };' > agentic1.js"

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

検索ループを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function run(steps, max) {' '  const seen = [];' '  let n = 0;' '  for (const s of steps) {' '    if (\"answer\" in s) {' '      const ok = s.sources.length > 0 && s.sources.every(function (p) { return seen.includes(p); });' '      return \"回答済み、根拠あり: \" + (ok ? \"はい\" : \"いいえ\");' '    }' '    if (n === max) return \"停止: 検索の上限 \" + max + \" 回\";' '    n++;' '    const found = index[s.search] || [];' '    const fresh = found.filter(function (p) { return seen.includes(p) === false; });' '    fresh.forEach(function (p) { seen.push(p); });' '    console.log(\"  検索 \" + n + \": \" + s.search + \" -> \" + (found.join(\" \") || \"なし\") + \"、新規 \" + fresh.length);' '    if (fresh.length === 0) return \"停止: 新しいものなし\";' '  }' '  return \"回答なし\";' '}' >> agentic1.js"

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

検索ループを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const good = [{ search: \"パスワード 規則\" }, { search: \"ロック 時間\" }, { answer: \"十五分です。\", sources: [\"p3\"] }];' 'const stuck = [{ search: \"パスワード 再設定\" }, { search: \"パスワード 再設定\" }, { answer: \"分かりません。\", sources: [\"p1\"] }];' 'console.log(\"良い実行:\"); console.log(run(good, 3));' 'console.log(\"行き詰まった実行:\"); console.log(run(stuck, 3));' 'console.log(\"上限のある実行:\"); console.log(run(good, 1));' 'async function main() {' '  const text = \"質問: ロック は 何分 続く か。 最初の検索で見つかったもの: \" + docs.p2 + \" より良い検索語を 5 語以内で書いてください。語はスペースで区切ってください。検索語だけを答えてください。\";' '  const reply = await ask([{ role: \"user\", content: text }], \"アカウント ロック 時間\", 20);' '  console.log(\"モデルの提案:\", reply.text.trim());' '}' 'main();' >> agentic1.js"

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

三つのループを実行する

コマンド
docker exec -w /home/node/project agent node agentic1.js

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

これは練習用の代用品です

ループは記録済みの手順に従うので、どの学生にも同じ三つの実行が出ます。本物のエージェントは自分で手順を選びます。ここでのモデルの呼び出しは、検索語を提案するだけで、Run はそれを表示しますが、従いません。

索引は三つのエントリーです。本物は、レッスン 5 のフィルターを付けた、これまでに作ったハイブリッド検索です。

Run の出力

実行最後の行
良い実行回答済み、根拠あり: はい
行き詰まった実行停止: 新しいものなし
上限のある実行停止: 検索の上限 1 回
モデルの提案出力の例: アカウント ロック 時間

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

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

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