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

無料レッスン

リクエストを複数のモデルに振り分ける方法

このレッスンのあとで: 私は、各リクエストを足りる中で最も安いモデルへ振り分け、その選択を記録したケースで確かめられます。

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

足りる中で最も安いモデルへ送る

モデルの振り分けとは、リクエストごとに、足りる中で最も安いモデルを選ぶことです。最も高いモデルは最も小さいモデルの何倍も費用がかかり、たいていのリクエストにはそこまでの力は要らないので、効果があります。

サポート用のツールが、チケットの分類、短い質問への回答、長い返信の下書きをするとします。分類に、下書き用のモデルは要りません。すべてを大きなモデルへ送ると、ほとんど使われない力に払うことになります。

このレッスンのゴール

メモ

私は、各リクエストを足りる中で最も安いモデルへ振り分け、その選択を記録したケースで確かめられます。

キーワード

ことばいみ
ルーターリクエストごとにモデルを選ぶ決まり
段階大きさと値段で分けたモデルのグループ
足りるその仕事に十分なほど、よく合っていること
記録したケース人が選んだ段階が付いたリクエスト
不一致ルーターと人の判断が違うケース
ラッパー呼び出しの外側にきまりを足すコード

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

ルーターは単純な決まり

読める決まりから始めます。短い文章や分類の仕事は small の段階へ。長い文章や推論の仕事は large の段階へ。残りは medium の段階へ送ります。

大きさと仕事による決まりは、安く、即座に動き、説明も簡単です。小さなモデルに選ばせることもできますが、リクエストのたびに呼び出しが一回増えます。まず決まりを試しましょう。

例: 四つのリクエストに決まりを当てる

リクエスト仕事大きさ段階
チケットの分類classify150 文字small
ページの要約summarise1500 文字medium
移行の計画reason900 文字large
長い報告書の要約summarise5000 文字large

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

決まりをケースで確かめる

ルーターはお金を節約しますが、品質を失うかもしれないので、試します。記録したケースを用意します。リクエスト、その大きさと仕事、そして人が必要だと判断した段階です。ルーターをそれらに通し、不一致を並べ、費用を合計します。

不一致は、いつも決まりの誤りとは限りません。一つずつ読み、決まりを直すか、ケースを直します。

よくある間違い

注意

「最も安いモデルが、いつも一番の節約だ」と考える人が多くいます。安いモデルの出来が悪いと、再試行、苦情、離れたユーザーという費用がかかります。記録したケースが「まだ足りる」と示す場所だけで、段階を下げましょう。

モデル用の助っ人を書く(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\");' '' '// 架空の価格。100万トークンあたりの円。トークン数に価格を掛けるとマイクロ円になる。' 'const PRICE = { small: { in: 1, out: 4 }, medium: { in: 3, out: 12 }, large: { in: 10, out: 40 } };' 'function route(request) {' '  if (request.task === \"classify\" || request.chars <= 200) return \"small\";' '  if (request.task === \"reason\" || request.chars > 2000) return \"large\";' '  return \"medium\";' '}' 'function cost(tier, c) { return c.in * PRICE[tier].in + c.out * PRICE[tier].out; }' > route6.js"

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

ルーターの確認を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 記録されたケース。各リクエストの内容と、レビュアーが必要だと判断した段階。' 'const CASES = [' '  { id: 1, task: \"classify\", chars: 150, in: 60, out: 5, expected: \"small\" },' '  { id: 2, task: \"qa\", chars: 180, in: 70, out: 40, expected: \"small\" },' '  { id: 3, task: \"summarise\", chars: 1500, in: 500, out: 150, expected: \"medium\" },' '  { id: 4, task: \"reason\", chars: 900, in: 400, out: 600, expected: \"large\" },' '];' >> route6.js"

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

ルーターの確認を書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const MORE = [' '  { id: 5, task: \"summarise\", chars: 5000, in: 1400, out: 300, expected: \"large\" },' '  { id: 6, task: \"classify\", chars: 3000, in: 800, out: 5, expected: \"small\" },' '  { id: 7, task: \"qa\", chars: 700, in: 250, out: 120, expected: \"medium\" },' '  { id: 8, task: \"summarise\", chars: 190, in: 80, out: 30, expected: \"medium\" },' '];' >> route6.js"

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

ルーターの確認を書く(4)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const all = CASES.concat(MORE);' '  let total = 0, dear = 0, wrong = [];' '  for (const c of all) {' '    const tier = route(c);' '    total = total + cost(tier, c);' '    dear = dear + cost(\"large\", c);' '    if (tier === c.expected) console.log(\"#\" + c.id + \" \" + c.task + \": \" + tier + \" ok\");' '    else { wrong.push(c.id); console.log(\"#\" + c.id + \" \" + c.task + \": \" + tier + \"、期待は \" + c.expected); }' '  }' '  console.log(\"不一致: \" + all.length + \" 件中 \" + wrong.length + \" 件、ケース \" + wrong.join(\"、\"));' '  console.log(\"振り分けたコスト: \" + total + \"。すべて large なら \" + dear + \"。節約: \" + (dear - total));' >> route6.js"

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

ルーターの確認を書く(5)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  const reply = await ask([{ role: \"user\", content: \"請求、不具合、その他のどれか答えてください。二重に請求されました。\" }], \"請求\", 10);' '  console.log(route(all[0]) + \" に振り分け、応答: \" + reply.text.slice(0, 40));' '}' 'main();' >> route6.js"

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

ルーターの確認を実行する

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

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

ラボのモデルは一つだけです

ラボでは、決まりがどの段階を選んでも、答えるモデルは一つで、それはプラットフォームが選びます。そのため Run が示すのは、選択と費用です。本物の呼び出しが示すのは、振り分けたリクエストに答えが返るということだけです。

価格と八件のケースは架空の記録なので、全員が同じ行を表示します。

出力の例

行示していること
#1 classify: small okルーターと人の判断が一致
#8 summarise: small、期待は medium読んで決めるべき不一致
不一致: 8 件中 1 件、ケース 8件数とケース番号
振り分けたコスト: 60820。すべて large なら 85600。節約: 24780マイクロ円の合計
small に振り分け、応答: ...本物の呼び出し。言葉は実行のたびに変わる

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

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

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