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

無料レッスン

サーキットブレーカーと代わりの答え

このレッスンのあとで: モデルが失敗しているあいだも、機能を役に立てることができる。

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

サーキットブレーカーのしくみ

サーキットブレーカーは、失敗が続くサービスへの呼び出しを止める、小さな決まりです。開いているあいだは、失敗する呼び出しを待たずに、代わりの手を使います。

コスト、トレース、信頼性のモジュールでは、代わりの手の一覧を学びます。このレッスンは、さらに進みます。いつ呼ぶのをやめるか、いつ試すか、人が押せる緊急停止です。

このレッスンのゴール

メモ

モデルが失敗しているあいだも、機能を役に立てることができる。

キーワード

ことばいみれい
サーキットブレーカー失敗するサービスへの呼び出しを止める決まり
閉(closed)ふだんの状態。呼び出しが通る
開(open)止まった状態。呼ばず、代わりの手が答える
半開(half_open)サービスが戻ったか見る、試しの呼び出し一回
冷却試す前に待つ歩数4
緊急停止機能をすぐに止める操作

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

三つの状態

すべてがうまくいくあいだ、ブレーカーは閉じていて、呼び出しは通ります。続けた失敗を数え、しきい値、ここでは 3 に達すると、開きます。

開いているあいだは、呼び出しません。冷却の 4 歩が過ぎたら、半開になり、一回だけ通します。成功すれば閉じ、失敗すればまた開きます。

例: 3 歩目から 9 歩目

歩呼べば見つけた状態答え方理由
3失敗closedfailed続けた失敗の 1 回目。数は 1
4失敗closedfailed数は 2
5失敗closedfailed数が 3 になり、開く
6成功openskipped呼ばない。開いてから 1 歩
7成功openskipped開いてから 2 歩
8成功openskipped開いてから 3 歩
9成功half_openmodel4 歩で試す。成功して閉じる

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

よくある間違い

注意

一回失敗したら、すぐ開くべきだと考える人が多いです。一回の失敗は、たまたまのことが多いです。続けた失敗を数え、成功で数を戻さないと、元気なサービスが、悪い一回で止められます。

代わりの手は、何と言うか

代わりの手は、正直で役に立つものにします。いちばんよいのは、同じ質問への、保存した答えです。なければ、アシスタントが混み合っていると正直に伝え、いつ試せばよいかを知らせます。

作り話の答えを、モデルが答えたかのように見せてはいけません。

緊急停止は、人の判断

ブレーカーは、失敗に反応します。緊急停止は、モデルは動いているのに使うべきでないとき、人が押します。間違った答えを出すときや、費用が高すぎるときです。

オンのあいだは、すべての呼び出しを飛ばします。ブレーカーには触れないので、ブレーカーは自分の数と状態を保ちます。

助っ人 model.js を書く(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"

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

助っ人 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 data = await res.json();' '    const text = data.choices[0].message.content;' '    return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"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\");' 'function step(b, outcome, i, threshold, cooldown, kill) {' '  if (kill) return [b.state, \"skipped\"];' '  if (b.state === \"open\" && i - b.openedAt >= cooldown) b.state = \"half_open\";' '  const found = b.state;' '  if (found === \"open\") return [found, \"skipped\"];' '  if (outcome === 1) { b.state = \"closed\"; b.fails = 0; return [found, \"model\"]; }' '  b.fails = b.fails + 1;' '  if (found === \"half_open\" || b.fails >= threshold) { b.state = \"open\"; b.openedAt = i; b.fails = 0; }' '  return [found, \"failed\"];' '}' > breaker.js"

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

ブレーカーのスクリプトを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const outcomes = [1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1];' 'function drive(kills) {' '  const b = { state: \"closed\", fails: 0, openedAt: 0 };' '  return outcomes.map(function (o, k) {' '    const r = step(b, o, k + 1, 3, 4, kills.includes(k + 1));' '    return r[0] + \":\" + r[1];' '  });' '}' >> breaker.js"

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

ブレーカーのスクリプトを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const trace = drive([]);' '  console.log(trace.slice(0, 10).join(\" \"));' '  console.log(trace.slice(10).join(\" \"));' '  const made = trace.filter(function (t) { return t.endsWith(\"skipped\") === false; }).length;' '  console.log(\"呼び出した回数 \" + made + \" / \" + trace.length);' '  console.log(\"停止 9 と 10: \" + drive([9, 10]).slice(8, 11).join(\" \"));' '  const q = \"短く一文で答えてください。モデルが止まっているとき、機能は何と伝えるべきですか。\";' '  const reply = await ask([{ role: \"user\", content: q }], \"アシスタントは混み合っていると正直に伝え、保存した答えや別の方法を案内します。\", 80);' '  console.log(\"モデル: \" + reply.text);' '}' 'main();' >> breaker.js"

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

ブレーカーを動かす

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

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

Run の出力

行意味
closed:model closed:model closed:failed ...1 歩から 10 歩。見つけた状態と答え方
open:skipped open:skipped open:skipped half_open:model開き、待ち、試しが成功する
呼び出した回数 14 / 206 回の呼び出しを節約した
停止 9 と 10: open:skipped open:skipped half_open:model9 歩と 10 歩は停止で飛ばされ、11 歩は冷却が終わっているので試す
モデル: ...ラボからの一文。モデルが答えなければ記録済みサンプル

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

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

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