HAKUYUCHIAI導入・業務自動化の支援

自律AIエージェントの実用アーキテクチャ — 権限・監査・緊急停止をどう設計するか

悠 Yu NiiboriAIエンジニア運営者について

自律AIエージェントを本番で動かすために要るのは、賢いプロンプトではなく「権限・監査・緊急停止」を仕組みで強制する設計である。Cloudflare 上で実際に動いているシステムの実装から、設計判断とハマりどころを公開する。

目次
  1. 自律AIエージェントの実用アーキテクチャとは
  2. 設計1: 権限はデータで持ち、コードで強制する
  3. 設計2: 書き込みは全部 before / after / reason を残す
  4. 設計3: 緊急停止は1つのフラグで、書き込み経路の入口に置く
  5. 設計4: 「全部止める」と壊れるものがある
  6. FAQ
  7. まとめ

自律AIエージェントの実用アーキテクチャとは

自律AIエージェントの実用アーキテクチャとは、AIの判断を信頼する代わりに、AIが取りうる行動の範囲をコードとデータで先に狭める設計です。

賢いプロンプトを書くことではありません。「危険な操作はしないでください」という指示は、モデルが変わった日・文脈が長くなった日に効かなくなります。効き続けるのは、到達できない経路だけです。

自律エージェントに本番の書き込み権限を渡すとき、事故の形は3つに分かれます。やってはいけない書き込みをする(権限)/ 何をしたか後から追えない(監査)/ 暴走を止められない(停止)。プロンプトはこのどれも解決しません。「意図の伝達」であって「実行の制約」ではないからです。

以下は、筆者が Cloudflare 上で運用している自律型メディアシステムの実装から、この3つをどう塞いだかです。すべて実際に動いているコードの話で、理想論ではありません。

設計1: 権限はデータで持ち、コードで強制する

このシステムでは、Agent が D1(データベース)や R2(オブジェクトストレージ)に直接触ることを禁じています。読み書きはすべてリポジトリ層の関数を通り、その関数が第一引数に「誰として実行しているか」を要求します。書き込み関数の先頭は例外なくこうなっています。

`` await assertWrite(ctx, "content:status"); ``

assertWriteagent_policies テーブルから当該Agentの許可スコープを読み、含まれていなければ例外を投げます。権限はコードではなくデータなので、コードを書き換えずに絞れます。現在15体が登録されており、その一部を挙げます。

Agent読める範囲書ける範囲
writertopics / research_packs / claimscontent_versions(下書きのみ)
publishercontent / approvalscontent:status(公開ステータスのみ)
privacyconversations:raw(生の会話)なし(空配列)

効いているのは2点です。

第一に、下書きを書けるAgentと公開できるAgentが別です。 writer は本文をいくら書いても公開できず、publisher は本文を書き換えられません。1体が乗っ取られても公開までは届きません。

第二に、スコープの包含関係が非対称です。content を書ける」許可は content:status を含むが、その逆は成立しません。publishercontent:status 許可では content 全体には書けません。この非対称性はユニットテストで固定してあります。権限設計は「そのつもりで書いた」では足りず、テストで固定しないと次の改修で静かに広がります。

さらに、権限行が存在しないAgentは一切動けないloadPolicy が例外を投げます。意図した fail-closed です)。権限行の追加自体も「AIが自分の権限を広げる変更」と見なし、人間の承認が要るPRとして扱っています。

設計2: 書き込みは全部 before / after / reason を残す

AIによる変更は、例外なく audit_logs テーブルに1行残ります。カラムは actor / action / entity_type / entity_id / before_json / after_json / reason / created_at追記専用で、更新も削除もしません。

重要なのは reason です。AIが「なぜそう変更したか」を自分の言葉で書く欄を必須にしておくと、あとから「意図が壊れた瞬間」を特定できます。before/after は何が起きたかしか示しません。この欄は監査だけでなくロールバック判断のためにあります。差分だけを見て戻すべきか判断できることは稀で、当時の理由が読めるかどうかで速度が変わります。

設計3: 緊急停止は1つのフラグで、書き込み経路の入口に置く

停止機構は、KV に置いた1つのキー(autopilot:paused)だけです。それを見る assertNotPaused() を、権限チェック assertWrite() の先頭で呼びます。つまり権限チェックを通る経路は必ず停止チェックも通ります。停止機構の付け忘れが構造的に起きにくくなる(逆に権限チェックを迂回すると両方が抜けます。だからリポジトリ層の迂回を禁止しています)。

ハマりどころ: KV による停止は「即時」ではない

正直に書いておくべき落とし穴があります。KV は結果整合のストアで、公式ドキュメントはこう書いています。

Changes may take up to 60 seconds or more to be visible in other global network locations as their cached versions of the data time out.

つまり停止フラグを立てても、別の地点で走っているワーカーは最大60秒以上そのことを知らない可能性があります。「ボタンを押した瞬間に全部止まる」設計ではありません。

「緊急停止があるから安全」と考えるのは危険です。設計では2点で受けました。停止の粒度を「これから始まる書き込み」に置くことと、次に述べるとおり、止めてはいけないものを対象から外すことです。

設計4: 「全部止める」と壊れるものがある

このシステムで最初に見つかった重大な設計ミスは、緊急停止そのものにありました。

当初、キューのコンシューマは停止中にすべてのジョブを retry させていました。AIの活動を止めるのだから当然に見えます。だが問い合わせの受付(リード獲得)も同じキューに乗っていました。retry 上限(3回)を超えたメッセージは Dead Letter Queue に落ち、そこにコンシューマはありません。Cloudflare のドキュメントはこう書いています。

Without a DLQ configured, messages that reach the retry limit are deleted permanently.

つまり緊急停止中に届いた問い合わせが静かに消える経路になっていました。事業の目的そのものを、安全装置が消していました。

修正は設計の再定義でした。「リードの受付はAIの活動ではない」と定め、緊急停止の対象外にしました。待たせるのはAIのジョブ(調査・分析・生成)だけです。緊急停止を設計するときは「止めるもの」ではなく「止めてはいけないもの」から先に列挙します。 AIを止めることが目的で、事業を止めることは目的ではありません。両者は同じキューに乗っていると区別されません。

FAQ

Q. プロンプトに「DBを直接触るな」と書けば足りないのですか。 足りません。指示は守られないことがありますが、そもそも到達できない経路は守られます。権限はコードで塞ぐのが原則です。

Q. 権限をコードではなくDBのテーブルに置く利点は何ですか。 デプロイなしで権限を絞れることです。事故の最中に要るのは、コードを書き直す時間ではなく1行のUPDATEです。

Q. 緊急停止は何秒で効きますか。 同一地点はほぼ即時ですが、KV の伝播により他地点では最大60秒以上かかりえます。即時停止を前提にした設計は避けてください。

Q. 小さなエージェントでもこの3点は必要ですか。 書き込み権限があるなら必要です。読み取り専用なら過剰ですが、1回でも書けるなら監査と停止を先に用意すべきです。

まとめ

自律エージェントを本番で動かす条件は、能力ではなく「間違えたときに取り返しがつくか」です。

  1. 権限 — 到達できない経路を作る(データで持ち、コードで強制し、テストで固定する)
  2. 監査 — before / after / reason を追記専用で残す
  3. 停止 — 書き込み経路の入口に1つのフラグを置き、その伝播遅延を前提に設計する

そして「止めてはいけないもの」を先に列挙します。安全装置が事業を止めるなら、それは安全装置ではありません。

医療・美容医療の領域でAIを実装するなら、この3点は「あると良い設計」ではなく前提条件になります。同じ考え方でカスタムAIの設計・実装をご相談いただけます。

出典

  1. Cloudflare Docs(公式ドキュメントのソース)— How KV works(参照 2026-08-08)
  2. Cloudflare Docs(公式ドキュメントのソース)— Dead Letter Queues(参照 2026-08-08)