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

無料レッスン

マルチエージェントと引き継ぎ

このレッスンのあとで: 仕事をエージェントに分け、1つで足りる場合を説明できる。

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

チケットを渡し合う2つのエージェント

マルチエージェントとは、それぞれ1つの役割を持つエージェントに、仕事を分けるしくみです。仕事は、引き継ぎのメッセージで渡します。引き継ぎのたびに呼び出しが1回かかり、文脈も失われかねません。

請求のエージェントが、在庫のエージェントにチケットを送りました。在庫のエージェントは、すぐに送り返します。チケットは一晩中行き来しました。

このレッスンのゴール

メモ

仕事をエージェントに分け、1つで足りる場合を説明できる。

キーワード

ことばいみれい
役割エージェントが持つ、ただ1つの仕事価格
引き継ぎ仕事を別のエージェントに渡すメッセージ
ルーター仕事を受ける役割を選ぶ部分
共有の記録全員のエージェントが読み、書き足す記録
繰り返しの防止仕事が際限なく行き来するのを止める確認

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

1つのエージェントで足りるとき

まず、エージェント1つとプロンプト1つで始めます。得られるものを言えるときだけ、2つ目を足します。たとえば、短いプロンプト、見えるツールの数の削減、別のモデル、人が承認する段階です。

エージェントが増えるほど、呼び出し、遅れ、文脈の漏れが増えます。ツール3つの1つのエージェントで足りるなら、そこでやめます。

例: 1つか、複数か

場面選択理由
値段を調べて伝えるエージェント1つツール1つ、プロンプト1つ
価格、在庫、配送の規則と、20個のツール役割に分ける各役割は、自分のツールだけを見る
10000円を超える返金人が行う段階承認はエージェントの仕事ではない

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

モデル用の補助を書く(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 STOCK = { \"おにぎり\": 12, \"お茶\": 0 };' 'function look(table, q, unit) {' '  const key = Object.keys(table).find(function (w) { return q.indexOf(w) >= 0; });' '  return key === undefined ? \"見つかりません\" : key + \": \" + table[key] + unit;' '}' 'const roles = {' '  \"価格\": function (q) { return look(PRICES, q, \" 円\"); },' '  \"在庫\": function (q) { return look(STOCK, q, \" 個\"); },' '};' 'function route(q) {' '  if (q.indexOf(\"いくら\") >= 0) return \"価格\";' '  if (q.indexOf(\"在庫\") >= 0) return \"在庫\";' '  return null;' '}' > handoff1.js"

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

引き継ぎのデモを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function pick(q) {' '  const rule = route(q);' '  if (rule) return rule;' '  const text = \"次の仕事に合う役割は、価格、在庫、なしのどれですか。1語だけで答えてください。仕事: \" + q;' '  const word = (await ask([{ role: \"user\", content: text }], \"なし\", 10)).trim();' '  return word === \"価格\" || word === \"在庫\" ? word : \"なし\";' '}' 'const trail = [];' 'function allowed(from, to) {' '  trail.push(from + \">\" + to);' '  return trail.filter(function (t) { return t === from + \">\" + to; }).length < 3;' '}' >> handoff1.js"

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

引き継ぎのデモを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const record = [];' '  for (const q of [\"おにぎりはいくらですか\", \"お茶は在庫がありますか\", \"明日の天気は\"]) {' '    const role = await pick(q);' '    const result = role === \"なし\" ? \"合う役割がありません。人に聞いてください\" : roles[role](q);' '    record.push({ to: role, task: q, result: result });' '  }' '  record.forEach(function (r) { console.log(r.to + \" | \" + r.task + \" | \" + r.result); });' '  let who = \"価格\";' '  for (let i = 0; i < 6; i++) {' '    const next = who === \"価格\" ? \"在庫\" : \"価格\";' '    if (allowed(who, next) === false) { console.log(\"繰り返し: \" + who + \" から \" + next + \" へ3回目、停止します\"); break; }' '    who = next;' '  }' '}' 'main();' >> handoff1.js"

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

実行して共有の記録を読む

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

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

出力の一例

行表示されるかもしれないもの
価格 | おにぎりはいくらですかおにぎり: 150 円
在庫 | お茶は在庫がありますかお茶: 0 個
天気のタスクなし。またはモデルが選んだ役割と、弱い結果
繰り返しある組が3回目に現れたという行

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

引き継ぎは形のあるメッセージ

引き継ぎのメッセージには、送る側、受ける側、タスク、受ける側に必要な文脈を書きます。受ける側が、送る側の履歴を全部読む必要はありません。

エージェントは、したことを共有の記録に書きます。仕事についての本当の状態は、この1か所にあります。

仕事の行き来を止める

2つのエージェントが、どちらも「相手が担当すべきだ」と判断することがあります。どちらの中にも誤りはありませんが、仕事は終わりません。

送る側から受ける側への組ごとに、回数を数えます。ある組が上限に達したら止め、仕事を人に渡します。引き継ぎの経過を記録に残すと、繰り返しが見つけやすくなります。

よくある間違い

注意

「ルーターは、いつもモデルに聞くべきだ」と考える人が多くいます。モデルの呼び出しは遅く、お金がかかり、存在しない役割を挙げることもあります。規則で正確に決まるところは規則で行い、残りだけモデルに聞き、答えを確かめます。

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

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