医療とAIの、これまでとこれから。

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

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

自律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)