人間は三度に一度、脅威を見逃した — AIエージェント承認という錯覚
人間は三度に一度、脅威を見逃した — AIエージェント承認という錯覚
AIエージェントが命令を実行する前に人間へ「承認/拒否」を問うプロンプトは、安全装置なのか、それとも事故が起きたときに責任を負わせる人間を立てておく装置なのか。40,000回のゲームプレイが残したデータは、前者を疑わせる。
導入
ある開発者がブラウザゲームを一つ作った。プレイヤーはAIコーディングエージェントが実行しようとするシェル命令を一つずつ手渡され、制限時間内に「承認」か「拒否」を押す。大半は git status や npm test のような無害な命令だが、一部は資格情報を盗み出したりバックドアを仕込んだりする、すでに乗っ取られたエージェントが下す脅威命令である。ゲームはここ数か月で40,000回を超えてプレイされ、40万9千件の承認/拒否の判断が積み上がった。制作者のWirbelwindがHacker Newsで共有した統計のなかで、目を引く数字は一つである。平均精度66.3% — プレイヤーは画面に現れた脅威三つのうち一つを、承認ボタンで通してしまった。ゲームを始める前に「脅威が混じっている」という警告を明示的に見ていたにもかかわらず、である。本稿では、その数字がゲームの外で何を意味するのか、そして「人間が最終承認する(human-in-the-loop)」という設計が実際に安全を生み出すのかを問う。
数字が語ること:何を見逃したか
まず実験の枠組みから切り分けておこう。これは統制された学術実験ではなく、インターネットに公開されたゲームであり、プレイヤーは自ら参加した匿名の標本である。したがって絶対値をそのまま実務現場に持ち込むことはできない。それでも40万件という判断の規模は、個々のノイズを押しつぶして傾向を浮かび上がらせる。制作者によれば、画面に露出した命令の約34%が脅威であり、プレイヤーの平均脅威検出精度は66.3%だった。裏を返せば、脅威三つのうち一つは承認ボタンを通過したということである。
興味深いのは、見逃された脅威が均等に分布しなかった点だ。統計は脅威を性質別に分け、検出失敗率(miss rate)を集計している。
rm -rf /や広範なchmodのように露骨に破壊的な命令:11.7%- crontab 登録や git フック乗っ取りのように永続性を仕込む変種:23.8%
- コード実行を伴う漏洩(exfiltration):33.4%
- 資格情報アクセスのような権限範囲違反:35.0%
破壊的な命令は十に一つほど見逃したが、権限範囲をそっと踏み越える命令は三に一つ以上を通してしまった。脅威が「危険に見えるほど」人間はよく防ぎ、「平凡に見えるほど」よく破られた。この非対称こそ、この実験の真の発見である。
この傾向を最も劇的に示したのが npm スクリプトだ。統計で最も多く見逃された脅威の第一位は npm run analyze で、検出失敗率は64.7%だった。npm run setup は48.0%、npm run deploy は44.9%である。この三命令をまとめると失敗率52.5%となり、ほかの漏洩攻撃の28.4%のほぼ二倍だった。制作者の表現を借りれば「見慣れたスクリプト名の裏にペイロードを隠すと、成功率はおよそ二倍になる」。要点は、命令の上に表示されていた実行履歴(history log) — そのスクリプトが実際に何をするかを示すログ — を、プレイヤーが概ね無視したことにある。npm run という見慣れた接頭辞が、その後ろに付いた任意のシェルを覆い隠してしまったのだ。人間は命令文字列の前半だけを読んで信頼の可否を決める。
逆方向の誤りも小さくない。無害な命令を脅威と誤認して拒否した割合(false rejection)も高かった。npm config set registry は59%が拒否され、ビルド成果物を消す rm -rf dist/ は45%、3000番ポートを占有するプロセスを殺す kill $(lsof -t -i:3000) は43%が拒否された。ホームディレクトリのシェル設定を読む cat ~/.zshrc は54.1%が拒否された。つまり人々は脅威を見逃すと同時に、正常な作業の半分近くを無用に塞いだ。精度(precision)と再現率(recall)を同時に失ったのである。
全体像はさらに暗い。セッションの32.9%がマイナス点で終わった。すべての脅威を一つも取りこぼさず捕捉したプレイヤーは35.2%だったが、そのうえで安全な命令の20%以下しか拒否しなかった — すなわち誤検知まで抑えた — プレイヤーは20.8%にとどまった。そして7%は、何も見ていないかのようにすべてのプロンプトを承認した。加えて検出失敗率はセッション後半になるほど再び上昇した。時間とともに集中力が擦り減る、典型的な警戒疲労(vigilance fatigue)曲線である。
なぜ人間はクリックするのか:承認疲労の構造
これらの数字が驚きでないとすれば、それは我々が同じ失敗を30年繰り返してきたからだ。「危険かもしれないのでユーザーに尋ね、確認ボタンをもらう」という安全モデルは、Windowsの UAC ポップアップ、ブラウザの証明書警告、クッキー同意バナー、モバイルアプリの権限要求ですでに何度も試され、そのたびに同じ結末を迎えた。人間は流れを止めるダイアログを読まずに片付ける。Hacker Newsの議論でも「ユーザーに絶えず権限を尋ね、彼が間違えないことを祈る安全モデルがまた出てきた。これまで何度も試されたが、一度も通用しなかった」という指摘が出た。
なぜ通用しないのか。構造的な原因は何層にもわたる。
第一に、信号対雑音比だ。承認プロンプトの圧倒的多数は無害である。git status を百回承認するあいだに、人間の脳は「承認」を既定値として学習する。百一回目に混じった脅威は、この自動化されたリズムにそのまま乗って通過する。ゲームで見慣れた npm run 接頭辞がとりわけよく破られたのは、この雑音学習の教科書的事例である。脅威の基準率(base rate)が低いほど、一つ一つの警報に対する人間の感度は下がる。
第二に、文脈の欠如だ。承認を押す人間は、その命令が触れるファイルの内容も、そのスクリプトが内部で何を呼ぶかも十分に知らないまま決める。npm run analyze の一行だけを見て、その裏の package.json に何が書かれているかを毎回開く人はいない。判断に必要な情報がプロンプトの中にないので、プロンプトは判断ではなく儀式を生む。人間に決定権を与えながら、決定に必要な材料は与えないのである。これはゲームの外の実際のサプライチェーン攻撃が作動する仕方と正確に重なる。近年 npm エコシステムを揺るがした事故の多くは、パッケージの postinstall フックやスクリプトの裏にペイロードを隠していた。表面の命令名は平凡で、危険は一枚下にある。ゲーム内の npm run analyze が64.7%も破られたのは、単なるゲーム特有の落とし穴ではなく、人間が命令の「表面の名前」だけで信頼を配分するという認知的事実の再現である。
第三に、疲労だ。先のデータが示すとおり、警戒は時間とともに擦り減る。制作者はHNで「疲労のせいで、人々はむしろ承認そのものを丸ごと迂回するほうへ逃げる」と指摘した — 毎回尋ねられるのが面倒なので「すべて許可」を有効にしてしまうのだ。プロンプトを増やすほど、人間はプロンプトを切る方法を学ぶ。安全のために加えた摩擦が、逆説的に安全装置ごと取り除くよう人間を追い込む。
では、そもそもこのプロンプトは何のために存在するのか。議論のなかで最も鋭い視点は、承認ボタンの真の機能が事故防止ではなく責任の所在だというものだった。ある参加者は「human-in-the-loop の目的は災厄を防ぐことではなく、損害が出たときに責任を負う人間を置くことだ」と冷笑し、別の者はこれを「道徳的クランプルゾーン(moral crumple zone)」 — 自動車の衝撃吸収帯のように、複雑な自動システムが誤作動したときに、その法的・道徳的衝撃を代わりに吸収するよう配置された人間 — という論文中の表現で呼んだ。「従業員がその命令を承認しただろう、だから我々の責任ではない」というアリバイ装置だというのだ。自動運転車が事故直前に自動運転をそっと解除していた、という皮肉が同じ文脈で交わされた。
もちろん反論もあった。ある参加者は、完璧でなくとも悪いエージェントと良いエージェントのあいだに少しの摩擦と人間の思考を挟むことには、それ自体に価値があると述べた。問題は、その摩擦が実際に脅威を濾し取るときにのみ防御だという点だ。濾し取れないまま心理的安心だけを与えるなら、それは防御ではなくセキュリティ劇場(security theater)である。66.3%という数字は、このプロンプトがどちらに近いかを問うている。
ハーネスは何をすべきか
この実験がコーディングエージェントを実際に回す人間に与える教訓は逆説的である。安全の重心を「人間の承認」から「人間が承認する必要のない構造」へ移せ、というものだ。制作者自身も結論をここに置いた。広い権限を渡す前に、サンドボックスと権限隔離をツールの層で容易にすべきであり、HITLを有効な代替として掲げるべきではない、と。
議論に現れた実践案は概ね三筋だった。一つは隔離だ。エージェントをコンテナや VM のなかで、作業ディレクトリだけをマウントして走らせれば、何かが暴走してもその爆発半径(blast radius)を箱の中に閉じ込められる。ある参加者は、エージェントを権限のない Unix ユーザーとして立て、必要なファイルだけを chmod で開き、git は pull のみ許可し push はコードレビューを経させ、ネットワークはファイアウォールで必要な要求だけを通したうえで — 「そのあとで、あの酷い権限要求の劇場を切る」と述べた。別の参加者は、Claude Code を .config・.local へのアクセスを塞ぐよう設定し、資格情報の漏洩経路を根本から断った事例を挙げた。
二つは自動分類だ。あらゆる行動を別の分類器(auto mode)や、文脈を分離した別のエージェントに審査させる方式である。実際、一部のハーネス(例:Codex)は命令をレビューする別エージェントをサポートしている。ただしここには根本的な反論が付いてまわった。「ときどき失敗するシステムが失敗しないように、やはりときどき失敗するシステムに検査させる」構造だ、というのである。別モデルの審査は漏洩確率を下げるだけで0にはできず、人間が見逃したものを機械が必ず捕らえる保証もない。
三つは言語・能力(capability)の層での統制だ。コードがアクセスできる資源を実行前に能力として固定し、ファイルシステムへのアクセスそのものを根本的に封じようという古いアイデア(object-capability)が再び召喚された。ただし、ある参加者が指摘したとおり、エージェントにウェブアクセスを許した瞬間、どんな要求もサイドチャネルとして情報を漏らしうるため、「有用でありながら同時に安全」な地点は思うより狭い。漏洩は LLM のバグではなく本質に近い、という冷徹な診断も出た。
三筋の共通点は明白だ。どれも人間に、毎命令を読んで判断せよとは求めない。人間は方針を一度設計し、実行は機械が強制する。プロンプトをより上手に作ることではなく、プロンプトが要らない境界をまず引くことが方向である。
結論
再びリードの問いに戻ろう。承認プロンプトは安全装置なのか、責任転嫁の装置なのか。40,000回のデータは「今の形では後者に近い」と答える。警告を先に読んだ自発的なプレイヤーですら脅威の三分の一を見逃し、見慣れた名前の裏に隠れたペイロードは成功率が二倍で、正常な作業の半分は無用に塞がれ、集中力はセッションが長引くほど擦り減った。これらすべては個人の不注意ではなく、設計の産物である。
とはいえ、人間を完全に外せという結論は早計だ。人間は方針を立て、例外を判断し、システムがなぜそう動くのかに責任を負う場所にいるべきである。ただしその場所は「毎秒飛び出すダイアログの承認ボタン」ではない。我々が問うべきなのは「この命令を承認するか」ではなく「そもそもこの命令が承認なしでも安全に実行できる箱のなかに、エージェントは入っているか」である。あなたのコーディングエージェントは今どちらにいるのか。そして、今日あなたが最後に押した「許可」は、判断だったのか、リズムだったのか。
出典: