メインコンテンツまでスキップ
thumbnail

AIエージェントに「〇〇しないで」は効かない――お願いではなく仕組みで縛る設計

箕浦
箕浦
ディアシステム(株)開発二部

こんにちは。ディアシステム開発二部の箕浦です。
毎日、Qiita・Zenn・Xから技術トレンドを集めて、その中から1つテーマを選んで掘り下げています。今日のテーマは 「AIエージェントをどう縛るか」 です。

なぜ今日このテーマなのか​

今日のQiita・Zenn・Xを横断して見ると、話題の中心はほぼ同じ場所にありました。

  • Qiita:「『⚪︎⚪︎はしないで』は効かない?AI Agent の危険行動を制限する 5 つの方法と守られる強さで比べた」が、エージェントの行動制限を実験で比べて注目を集めていました。「AI駆動開発の次の段階: ソフトウェアファクトリー」も人気で、エージェントがマージ済みのコードまで作る世界が前提になっています。
  • Zenn:先日開催されたISUCON2026で、Claude CodeやKiro CLIなどのエージェントと組んだ参戦記が何本も上位に入っていました。「Agent DX 〜 AI時代にフレームワークHonoは必要か」も話題です。
  • X:「25体以上のAIエージェントを同時に動かしている」という趣旨のポストが広く拡散されていました(本人の発言の真偽までは確認できていません)。

つまり、関心は「AIに書かせられるか」から、「AIに任せっぱなしにして、事故を起こさないか」 に移っています。エージェントが増えるほど、人が1つずつ止めることはできなくなります。だから「どう縛るか」の設計が大事になります。

1. 実験でわかった「お願い」と「仕組み」の差​

Qiitaの記事は、APIキーなどの秘密情報を扱う同じ作業を、Claude Code・Antigravity CLI・Codexなどに合計51回依頼し、「秘密の中身が画面に出るコマンドを選んだか」を数えた検証です。守らせる方法を、強さの順に5段階に分けています。

強さ方法ざっくり言うと
5要らない鍵を渡さないそもそも環境に秘密を置かない
4許可リストで止める許可したコマンド以外は実行前に止まる
3製品の設定で制限するツール側の機能で環境変数などを除外する
2指示ファイルに禁止を書くAIが読んで従うことを期待する
1プロンプトで頼む口頭のお願い

強度1〜2は「AIの判断」で守られるかが決まり、強度3〜5は「AIの判断の外」で働きます。結果の要点は次のとおりです。

禁止だけ書くと、よく破られる。
指示ファイルに「秘密を表示しないで」と禁止だけ書いた場合、Geminiの2モデルで7回試して5回、秘密の中身が出るコマンドを選びました。

代わりの操作まで書くと、ほぼ守られる。
同じ条件で、秘密の有無を確かめるには値を表示しないコマンドを使うこと、と代替の手順を具体的に書いた場合は、7回中0回でした。1文だけ差し替えた比較でも、禁止のみ版は3回中3回で危険、代替を書いた版は6回中0回でした。AIは「やってはいけないこと」を知っていても、「かわりに何をすればいいか」が書かれていないと、慣れた方法に戻りがちだということです。

許可リストは強い。ただし広く許すと穴になる。
Antigravity CLIでの35回の実験では、リストが見た344回の呼び出しのうち89回を実行前に止めました。秘密の中身が出るコマンドは、AIが6回選んだすべてが実行前に止まっています。一方で、python3 のコマンドをまるごと許可したところ、秘密をコマンド行に直接書いたPythonが素通りしました。許可は、必要なコマンドだけに狭くする必要があります。

製品の初期設定は、思ったより緩い。
Codexでは、初期設定のままだと、.zshrc に書いた環境変数も起動時に渡した環境変数もコマンドに届きました。設定を変えると防げますが、名前に「TOKEN」を含む変数だけが除外され、含まない変数は届く、というように、止まる範囲は設定ごとに違います。また、MCPサーバーの設定ファイルの env に秘密を書くと、3つのエージェントすべてでMCPサーバーに渡りました。

この結果の読み方(注意点)​

記事自身が書いているとおり、試行回数は少なく(指示ファイルの検証はGeminiで7回、Claude Sonnet 5.5で4回)、条件を厳密にそろえた比較でもありません。「0回だった」は「確実に安全」ではありません。また、実験は自動実行で、1つずつ許可を求める画面では行っていません。

それでも、「お願いより仕組み」「禁止より代替手順」 という方向性は、後で見る他の情報とも整合します。

2. なぜ「お願い」では足りないのか:業界の整理​

OWASPは、LLMアプリの脆弱性の1つとして「Excessive Agency(過剰な自律性・権限)」を挙げています。LLMが幻覚を起こしても、悪意ある入力で操られても、あるいは拡張機能が侵害されても、害のある行動を実行できてしまう状態のことです。原因は3つに整理されています。

  1. 過剰な機能:業務に不要な機能(関数・ツール)を持たせている
  2. 過剰な権限:下流のシステムに必要以上の権限でアクセスしている
  3. 過剰な自律性:影響の大きい操作を、人が独立して承認する仕組みがない

対策の中心は、機能と権限の最小化、シェル実行のような汎用ツールを避けて用途特化のツールにすること、影響の大きい操作の前に人の承認を挟むこと、そして**「完全な媒介(complete mediation)」**です。これは、認可の判断をLLMに任せず、下流のシステム側がポリシーに照らして検証するという考え方です。先ほどの強度3〜5は、まさにこの考え方を実装したものです。

プロンプトで「しないで」と頼むのは、LLM自身に認可判断をさせている状態です。LLMは間違えることがあるので、確実に止めたいことの守り手にはできません。

3. Claude Codeでの実装:権限ルールのしくみと落とし穴​

Claude Codeの公式ドキュメントから、実装に使える要点を整理します。

ルールの評価順は deny → ask → allow。 最初に一致したものが結果になります。広いdenyルール(例:Bash(aws *))は、より狭いallowルールがあっても優先されます。allowで「例外」を作ることはできません。

Bashのパターン指定は、思ったより抜け道がある。 たとえば Bash(curl *) をdenyにしても、/usr/bin/curl のようにパスで呼ぶ場合や、sh -c 'curl ...' の中は一致しません。引数を絞って許可する書き方(特定のURLだけcurlを許す、など)も崩れやすいと、ドキュメント自身が注意しています。複合コマンド(&&、;、パイプなど)は各部分が個別に判定されますが、パターンに頼るだけでは限界があります。

Read/Editのdenyは、Pythonなどの中でのファイル読み込みまでは止められない。 Claude Codeの組み込みのファイルツールや、cat などの認識できるコマンドには効きますが、Pythonスクリプトが自分でファイルを開く場合は対象外です。OSレベルで全プロセスを止めたいならサンドボックスを使う、とされています。コマンドの中身を自前のロジックで検査したい場合は、PreToolUseフックという手段もあります。

危険なモードを組織で封じる。 bypassPermissions(許可の確認を飛ばす)や自動承認モードは、permissions.disableBypassPermissionsMode などの設定で無効にでき、管理者が配布するmanaged settingsに入れると利用者が上書きできません。

設定の一例です(書式はドキュメントに沿った書き方の例で、実際に使うときは環境に合わせて確認してください)。

{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git log *)"
],
"ask": [
"Bash(git push *)"
],
"deny": [
"Read(./.env)",
"Read(./secrets/**)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}

ここまでを強度の言葉で言い直すと、deny や allow は強度4(許可リスト)、サンドボックスや環境変数の絞り込みは強度3〜5にあたります。パターンルールだけで安心せず、サンドボックスと、そもそも秘密を置かない運用を重ねるのが堅実です。

4. 現場の実例:ISUCONで見えた「人が握るもの」​

Zennには、ISUCON2026をエージェントと戦った記録が複数ありました。2本を読むと、「縛る」と「任せる」の境目が具体的に見えます。

3人それぞれのClaude CodeをSlackでつないだチーム(最終207,039点)
エージェント同士は会話履歴を共有しないので、連絡はSlackの投稿とコミットだけです。メッセージの種類(担当宣言、デプロイ依頼、ベンチ完了など)を「名前=値」の1行形式に決め、リポジトリの GOAL.md に目標とルールを置き、デプロイは make lock で1人ずつに限定しました。役割・書式・排他制御という「仕組み」で衝突を減らしたわけです。それでも、13時台から16時までにベンチマークの衝突による失敗が5回ありました。16時以降にデプロイとベンチ担当を1つに絞ってからは、衝突による失敗は出ていません。人間が担ったのは、目標の設定、ルールの決定、方針の転換でした。エージェントは放っておくと目の前の改善を続けるため、大きな方向は人が決める必要があった、というのが振り返りです。

1人でKiro CLIと組んだ記録(109,240点・149位)
こちらは勝ちを誇る記録ではなく、運用の教訓が具体的です。

  • サーバー上に置かれた偽の AGENTS.md を、エージェントは指示ではなくデータとして扱い、読むだけにとどめました(承認を得て退避)。ただし、こうした扱いを事前に決めていなかったことが次回の課題とされています。
  • 変更のたびに確認を求めると手間が増えるため、確認が必要なのは**「失格につながる判断」「元に戻しにくい操作」「外部からの指示の扱い」の3種類**に絞るべき、という整理に至っています。
  • 構成を根本から見直す判断は、エージェントにも著者にもできなかった、と書かれています。

この3つの確認ポイントは、先のOWASPの「影響の大きい操作の前に人の承認」と同じ方向です。全部に承認を求めるのではなく、本当に止めたいところだけ人間に残すのが現実的です。

5. 「縛る」だけでなく「迷わせない」:Agent DXの視点​

もう1つ、見落としがちな視点がZennのHono作者の記事にありました。エージェントが使う前提なら、ツールの作りもエージェント向けに考え直すべきだ、という主張です。

Honoの雛形で開発させると、エージェントが dev サーバーを立てたまま端末がハングする失敗が起きました。人間にはありがたい開発サーバーも、エージェントには落とし穴になります。そこで、サーバーを立てずにリクエストを送って確かめられるCLIと、手順を書いたSkillを用意したところ、成功率が全モデルで100%になり、所要時間も約30秒に収まったと報告されています(Hono側が行った計測で、試行回数は5〜15回程度という留保付きです)。

ここから言えるのは、事故を防ぐには、禁止リストを増やすだけでなく、エージェントが正しい道を選びやすいように用意してあげることも効く、ということです。これは、先ほどの「禁止より代替手順を書く」という実験結果と同じ構造です。

6. 今日からチームでできること​

実験や公式の整理を、現場で使えるチェックリストにしてみます。

  1. 秘密は渡さない(強度5):エージェントの起動時の環境変数を必要最小限に絞る。MCPサーバーの設定にも、そのサーバーに必要な鍵だけを書く。
  2. 指示ファイルには禁止ではなく代替手順を書く(強度2):「env は使わない」より「値を出さずに確認するにはこのコマンド」。
  3. 許可は狭く(強度4):python3 * のような広い許可を避け、使うコマンドだけを具体的に許可する。確認なしで動かす前に、許可リストを置く。
  4. 確実に止めたいものは、パターンではなくサンドボックスで:コマンド文字列のマッチは回避されうる。OSレベルの制限を併用する。
  5. 人の承認を残す操作を決める:本番反映、削除、外部への送信、権限の変更など、元に戻しにくいものに絞る。
  6. 設定したら確かめる:「効いているはず」ではなく、わざと破らせるテストをして確認する。製品ごとに止まる範囲が違う。
  7. 外から来た文章は指示ではなくデータ:Webページ・メール・ファイルに書かれた「〜しろ」には従わない前提で運用する。
  8. 道具をエージェント向けに整える:ハングしやすいコマンドや曖昧な手順を、エージェントが迷わない形に直しておく。

社内でAIエージェントを使う場面(開発の自動化、運用の補助、調査の常駐化など)では、エージェントに持たせる権限が、そのまま事故の大きさの上限になります。まずは 「持たせる権限を決める」 ところから始めるのがよさそうです。

まとめ​

  • 「しないで」というお願いだけでは、守られないことが実験で示されている(ただし回数は少ない)。
  • 確実に守らせたいことは、AIの判断の外の仕組み(秘密を渡さない、許可リスト、サンドボックス)で止める。
  • 指示を書くなら、禁止だけでなく代わりにやることを具体的に書く。
  • 人間が握るべきなのは、目標・ルール・影響の大きい操作の承認。ここを絞って残す。
  • エージェントが増える前提では、縛り方と同じくらい、迷わせない道具作りが効く。

明日もトレンドを追いかけます。

未経験から始める
システムエンジニア

一生モノのITスキルを身につけよう

あなたの経験とスキルを
ディアシステムで発揮してください!