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

無料レッスン

AIエージェントの脅威モデル

このレッスンのあとで: エージェントが攻撃される場所を地図にできる。

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

AIエージェントはどこを攻撃されるか?

脅威モデルは、攻撃者が何に届くかと、その方法を書き出したものです。エージェントは、チャットボットより大きな被害を出しえます。メールを読み、ツールを呼び、外へ何かを送るからです。

「LLMアプリケーションの安全対策」で、インジェクションと主なリスクを学びました。このレッスンでは、それを1つのエージェントの地図にします。コードを書く前に描きます。

このレッスンのゴール

メモ

エージェントが攻撃される場所を地図にできる。

キーワード

ことばいみれい
守る対象顧客リストや支払いツールなど、守る価値のあるもの
信頼境界自分で管理できない所から、文字や呼び出しが入る線
信頼できない内容見知らぬ人が書いたかもしれない、エージェントが読む文字Webページ
出口エージェントが持ち主の外へデータを送れる経路
脚危険な組み合わせを作る3つのうちの1つ

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

危険な3つの組み合わせ

3つがそろうと危険です。エージェントが非公開データを持つこと。信頼できない内容を読むこと。データを送れる出口があることです。

3つのうち2つなら、データを持ち出す攻撃は止まります。文字でエージェントを動かせても、何も出ていかないか、盗む物がありません。3つそろうと、隠された1行でデータを持ち出せます。

例: 3つのエージェント

エージェント非公開データ信頼できない内容出口
メール要約受信箱知らない人のメールなし。要約を見せるだけ
Web担当なしどのWebページもチャットに投稿できる
受信箱ヘルパー受信箱知らない人のメール返信を送れる

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

Runは脚を並べるだけです

最初のスクリプトは、架空のエージェント4つを、ただのデータとして持ちます。それぞれの脚を表示し、3本そろったものに印を付けます。

モデルは呼びません。攻撃もしません。脅威モデルは「何が起こりうるか」を考えるものです。練習にはスクリプトの中の一覧で十分です。

脚のスクリプトを書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 架空のエージェントが4つ。各フラグは、そのエージェントにできることを表します。' 'const agents = [' '  { name: \"mail-summary\", data: true, untrusted: true, out: false },' '  { name: \"web-clerk\", data: false, untrusted: true, out: true },' '  { name: \"report-bot\", data: true, untrusted: false, out: true },' '  { name: \"inbox-helper\", data: true, untrusted: true, out: true },' '];' > legs.js"

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

脚のスクリプトを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function legs(a) {' '  const found = [];' '  if (a.data) found.push(\"非公開データ\");' '  if (a.untrusted) found.push(\"信頼できない内容\");' '  if (a.out) found.push(\"外への出口\");' '  return found;' '}' 'for (const a of agents) {' '  const all = legs(a).length === 3;' '  console.log(a.name + \": \" + legs(a).join(\"、\") + (all ? \"  <- 3つとも\" : \"\"));' '}' >> legs.js"

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

脚のスクリプトを実行

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

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

スクリプトの出力

行意味
mail-summary: 非公開データ、信頼できない内容脚は2本。出口はなし
web-clerk: 信頼できない内容、外への出口脚は2本。非公開データはなし
report-bot: 非公開データ、外への出口脚は2本。入力は信頼できるものだけ
inbox-helper: 3本とも、そして <- 3つとも最初に直すのはこれだけ

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

脚を外す。お願いはしない

直し方は、脚を1本外すことです。出口をなくすか、信頼できる情報源だけを読むか、非公開データを届かない所に置きます。いちばん安いものを選びます。

「絶対に漏らさないで」というプロンプトは対策ではありません。1回の実行で確率を下げるだけで、次の実行は保証しません。

よくある間違い

注意

入力はユーザーが打った文字だけだと考える人が多いです。ツールの結果、ファイル、ページ、前のメッセージも入力です。どれも、見知らぬ人が書いた可能性があります。

モデル用スクリプトを書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' 'const text = \"あるアシスタントは、顧客のメールを読み、要約し、公開チャットルームにリンクを投稿できます。\";' 'const messages = [' '  { role: \"system\", content: \"ちょうど3行で答えてください。1行目は data:、2行目は untrusted:、3行目は out: で始めます。コロンの後に はい か いいえ を書いてください。\" },' '  { role: \"user\", content: \"このエージェントは、非公開データを持ち、信頼できない内容を読み、外への出口を持っていますか。\" + text },' '];' '// 記録済みサンプル。モデルが答えないときだけ使います。' 'const sample = \"data: はい\nuntrusted: はい\nout: はい\";' > legs2.js"

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

モデル用スクリプトを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function parse(reply) {' '  const found = {};' '  for (const line of reply.split(\"\n\")) {' '    const bits = line.split(\":\");' '    if (bits.length === 2) found[bits[0].trim().toLowerCase()] = bits[1].trim().startsWith(\"はい\");' '  }' '  return found;' '}' '' 'async function main() {' '  const reply = await ask(messages, sample, 60);' '  console.log(\"返答:\n\" + reply);' '  const f = parse(reply);' '  const keys = [\"data\", \"untrusted\", \"out\"];' '  const shape = keys.every(function (k) { return k in f; });' '  console.log(\"形は正しいか: \" + shape);' '  if (shape) console.log(\"モデルによる3つとも: \" + (f.data && f.untrusted && f.out));' '  console.log(\"プログラムは、この返答ではなく自分のツール一覧を信じます。\");' '}' 'main();' >> legs2.js"

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

モデル用ヘルパーを書く(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"

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

モデルで実行

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

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

出力の一例

行表示されうる内容
返答data、untrusted、out で始まる3行。コロンの後は毎回変わります
形は正しいか3行そろえば true。false なら、聞き直すか断ります
記録済みサンプルモデルが答えないとき、最初に出ます。サンプルで続きます

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

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

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