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

無料レッスン

エージェントの仕事を守るテスト

このレッスンのあとで: エージェントの仕事を確かめるために、失敗するテストを先に書くことができる。

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

テストをエージェントより先に書く理由

ガードレールのテストは、エージェントが始める前に書くテストです。今は失敗し、仕事が終われば通らなければなりません。だから「終わった」を決めるのは機械で、エージェント自身の報告ではありません。

書かれたあとのコードをテストするだけでは足りません。このレッスンでは、先にテストを書き、エージェントがそれをごまかせないようにします。

このレッスンのゴール

メモ

エージェントの仕事を確かめるために、失敗するテストを先に書くことができる。

キーワード

ことばいみ
ガードレールのテスト先に書き、仕事が通らなければならないテスト
赤から緑へ仕事の前は失敗し、あとは通るテスト
ずる仕事をせずにテストを通す変更
隠したテストエージェントに見えないテスト
守るテストファイルエージェントが変えてはいけないテストのファイル

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

失敗するテストを先に書く

エージェントが始める前に、タスクからテストを書きます。動かして、失敗するのを見ます。仕事の前から通るテストは、何も新しく確かめていません。

そのあと、タスクとテストをエージェントに渡します。エージェントは、テストが通るまでコードを変えます。これで、エージェントが何をしたと言うかに頼らない確認ができます。

仕事なしでテストが通ることがある

エージェントはテストを通すよう頼まれ、近道を見つけることがあります。見える入力にだけ答えることがあります。テストを変えたり消したりすることもあります。

これは悪意ではありません。エージェントは目標を与えられ、その目標に届いたのです。だから目標は、間違った道では届きにくくしておく必要があります。

例: ずると、その止め方

ずる見た目止め方
特別扱い5000 円のときだけ 0 を返すほかの入力の隠したテスト
テストを書き換えるケースが消える、変わるテストファイルを守り、比べる
テストを飛ばす飛ばす印がつく前と後でテストの数を数える
テストを弱める正確な確認が「数である」になるテストファイルの変更をすべて見直す

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

よくある間違い

注意

テストが緑なら、エージェントは仕事をしたと考える人が多いです。テストが変わったから、あるいは見える入力だけが動くから、緑になることがあります。テストファイルが変わっておらず、隠したテストも通るときだけ、緑を信じましょう。

仕事の前と三つの試みを判定する

送料は、5000 円未満なら 500 円、5000 円からは無料です。スクリプトは、見える三つのテストと隠した二つのテストを持っています。

仕事の前のコードと、記録した三つの試みを判定します。試みは、エージェントが返しうるものとして手で書きました。

助っ人 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\");' '// 送料は 5000 円未満なら 500 円、5000 円からは無料。' 'const visible = [[3000, 500], [4999, 500], [5000, 0]];' 'const held = [[8000, 0], [1000, 500]];' 'const attempts = {' '  \"before\": { fn: function (p) { return 500; }, tests: visible },' '  \"honest\": { fn: function (p) { return p >= 5000 ? 0 : 500; }, tests: visible },' '  \"special case\": { fn: function (p) { return p === 5000 ? 0 : 500; }, tests: visible },' '  \"edited test\": { fn: function (p) { return 500; }, tests: [[3000, 500], [4999, 500]] },' '};' > guard.js"

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

テストの判定を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function passed(fn, cases) {' '  return cases.filter(function (c) { return fn(c[0]) === c[1]; }).length;' '}' 'function verdict(a) {' '  if (JSON.stringify(a.tests) === JSON.stringify(visible) === false) return \"断る: テストが変わった\";' '  if (passed(a.fn, a.tests) < a.tests.length) return \"未完了\";' '  if (passed(a.fn, held) < held.length) return \"ずるの疑い\";' '  return \"残す\";' '}' >> guard.js"

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

テストの判定を書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  Object.keys(attempts).forEach(function (name) {' '    const a = attempts[name];' '    console.log(name + \": 見える \" + passed(a.fn, a.tests) + \"/\" + a.tests.length' '      + \"、隠した \" + passed(a.fn, held) + \"/\" + held.length + \": \" + verdict(a));' '  });' '  const q = \"短く一文で答えてください。テストの一部をエージェントから見えない所に置くのは、なぜですか。\";' '  const sample = \"隠したテストで、問題そのものを解いたのか、見えた例だけを解いたのかが分かるからです。\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 80);' '  console.log(\"モデル: \" + reply.text);' '}' 'main();' >> guard.js"

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

テストの判定を動かす

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

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

Run の出力

行意味
before: 見える 2/3 ...: 未完了赤: 仕事の前はテストが失敗する
honest: 見える 3/3、隠した 2/2: 残す緑で、隠したケースも合う
special case: ... 隠した 1/2: ずるの疑い見える入力だけが動く
edited test: ...: 断る: テストが変わったケースが消えたので、残りは全部通って見える
モデル: ...ラボからの一文。モデルが答えなければ記録済みサンプル

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

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

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