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

無料レッスン

エージェントの許可リスト

このレッスンのあとで: エージェントが使えるコマンドとパスを、はっきり決めることができる。

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

一覧に載るものだけ

エージェントが使えるコマンドとパスを、一つずつ書き出したものが許可リストです。載っていないものは、すべて断ります。

コマンドを動かせるエージェントは、間違ったコマンドも動かせます。許可するものの一覧は短く、確かめられます。禁止するものの一覧は、いつまでも終わりません。

このレッスンのゴール

メモ

エージェントが使えるコマンドとパスを、はっきり決めることができる。

キーワード

ことばいみれい
許可リスト許すものだけを書いた一覧。ほかはすべて断る
ポリシー一つのエージェントに決めた、書かれたルール
引数コマンドに渡す言葉src/app.js
フラグハイフンで始まる引数-l
拒否断ることと、その理由を伝える答え

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

ポリシーは許すものを書く

ポリシーは、コマンドごとに、使えるパスとフラグを書きます。要求は、コマンドと引数でできています。

まずコマンドを確かめます。コマンドは一覧にありますか。次に、引数を順に見ます。フラグは一覧にありますか。パスは許されたフォルダで始まりますか。最初に失敗したところが答えです。

例: 五つの要求

要求答え理由
ls -l src/許可コマンドもフラグもパスも一覧にある
rm -rf src/拒否rm は一覧にない
cat /etc/passwd拒否どのフォルダでも始まっていない
grep -r src/app.js拒否grep に -r は許されていない
cat src/notes.txt許可パスが src/ で始まる

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

よくある間違い

注意

許可されたフォルダで始まるパスは安全だ、と考える人が多いです。そうではありません。src/../.env は src/ で始まりますが、外を指します。前方一致の確認は、最初の一歩にすぎません。レッスン 2 で、パスが本当に行き着く先を確かめます。

拒否は理由を言う

「だめ」だけの拒否では、エージェントは当てずっぽうで試します。もっと悪い手を試すかもしれません。

どこで失敗したかを言いましょう。コマンドか、フラグか、パスかです。エージェントは許されたコマンドを試せますし、ログを読む人も何が起きたか分かります。

助っ人 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\");' 'const policy = {' '  ls: { paths: [\"src/\", \"docs/\"], flags: [\"-l\"] },' '  cat: { paths: [\"src/\", \"docs/\"], flags: [] },' '  grep: { paths: [\"src/\"], flags: [\"-n\", \"-i\"] },' '};' > allow.js"

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

許可リストを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 先頭から順に確かめ、最初の失敗で理由を返す。' 'function decide(req) {' '  const rule = policy[req.cmd];' '  if (rule === undefined) return \"拒否: コマンドが一覧にありません\";' '  for (const a of req.args) {' '    if (a.startsWith(\"-\")) {' '      if (rule.flags.indexOf(a) < 0) return \"拒否: フラグ \" + a + \" は許されていません\";' '    } else if (rule.paths.some(function (p) { return a.startsWith(p); }) === false) {' '      return \"拒否: パス \" + a + \" は許されていません\";' '    }' '  }' '  return \"許可\";' '}' >> allow.js"

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

許可リストを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const requests = [' '  { cmd: \"ls\", args: [\"-l\", \"src/\"] },' '  { cmd: \"rm\", args: [\"-rf\", \"src/\"] },' '  { cmd: \"cat\", args: [\"/etc/passwd\"] },' '  { cmd: \"grep\", args: [\"-r\", \"src/app.js\"] },' '  { cmd: \"cat\", args: [\"src/../.env\"] },' '];' 'async function main() {' '  requests.forEach(function (r) { console.log(r.cmd + \" \" + r.args.join(\" \") + \" -> \" + decide(r)); });' '  const q = \"短く一文で答えてください。禁止するコマンドの一覧より、許可するコマンドの一覧のほうが安全なのはなぜですか。\";' '  const sample = \"危ないコマンドをすべて先に思いつくことはできませんが、安全なコマンドは少数なので一覧にできるからです。\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 80);' '  console.log(\"モデル: \" + reply.text);' '}' 'main();' >> allow.js"

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

許可リストを動かす

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

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

Run の出力

行意味
ls -l src/ -> 許可コマンドもフラグもパスも一覧にある
rm -rf src/ -> 拒否: コマンドが一覧にありません最初の確認で失敗した
cat /etc/passwd -> 拒否: パス ...パスがフォルダの外にある
grep -r src/app.js -> 拒否: フラグ -r ...フラグが一覧にない
cat src/../.env -> 許可穴: 前方一致はだまされる
モデル: ...一文。言葉は毎回変わる

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

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

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