自然言語の分析依頼を、安全なSQLへ渡す「実行契約」
「先月の解約理由をまとめて」と入力すると、AIがSQLを作り、そのまま本番データベースへ送る。分析担当者には便利でも、データ基盤担当者は、書き込みだけでなく大量走査や個人情報の持ち出しも止めなければなりません。本稿では、自然言語の依頼とSQLの間に置く「実行契約」を作ります。
SQLを作る役割と、実行を許可する役割を分ける
モデルへデータベース接続を直接渡し、プロンプトで「SELECTだけ」と頼む構成は採りません。モデルは自由文の依頼を、固定項目を持つ実行契約へ変換します。別の決定論的なゲートが契約とSQLを照合し、条件を満たしたものだけを専用の実行基盤へ渡します。
外部入力からツールへ値を渡すときは、自由文ではなく固定スキーマへ絞り、重要な操作には承認を置くことが推奨されています[1]。SQLでも、モデルの判断を最終的な権限判定に使わないことが要点です。
コピーして使える「SQL実行契約」
まず、モデルの出力を次の形に固定します。allowed_views、上限値、個人情報の扱いは、依頼者ではなくデータ管理者が設定します。
request_id: Q-2026-001
purpose: 月次の解約理由を集計する
allowed_views: [billing_events_anon]
dimensions: [cancel_reason]
metrics: [count]
filters:
event_month: 2026-09
contains_personal_data: false
max_rows: 100
timeout_ms: 3000
approval: auto | analyst | data_owner
ゲートは少なくとも次を順番に判定します。
- SQL文が一つだけで、許可した構文か
- 参照先が
allowed_views内だけか - 選択列と集計粒度が契約どおりか
- 個人識別列や自由記述を含まないか
EXPLAINの推定行数とコストが組織の上限内か- 結果行数、実行時間、承認区分が設定済みか
PostgreSQLのEXPLAINは、実行前に計画、推定コスト、推定行数を確認できます[2]。ただし推定値は統計に依存するため、絶対的な安全証明ではありません。大きすぎる計画を早期に止める入口として使い、実行時の制限と組み合わせます。
4層の防波堤を実装する
1. データ面をビューで限定する
AI用の接続ユーザーには、承認済みビューへのSELECTだけを付与します。元表、個人識別列、管理スキーマは見せません。アプリ側の文字列判定をすり抜けても、データベース権限で拒否できる状態にします。
2. 構文と計画を実行前に検査する
SQLを構文木として解析し、複数文、書き込み、DDL、ロック取得、許可外関数を拒否します。続いてEXPLAIN (FORMAT JSON)を実行し、推定行数、全表走査、結合数を契約と比較します。EXPLAIN ANALYZEは実際にSQLを実行するため、事前審査には使いません[2]。
3. 実行環境で上限を強制する
読み取り専用トランザクションでは、非一時表へのINSERT、UPDATE、DELETE、MERGEやDDLなどが禁止されます[3]。さらにセッション単位でstatement_timeoutを設定すると、指定時間を超えた文を中止できます[4]。結果には必ずLIMITを付け、アプリ側でも返却行数と出力量を打ち切ります。
4. 結果を返す前に再確認する
個人情報フラグ、列名、件数を確認し、契約外の列が混じれば破棄します。依頼、契約、生成SQL、判定結果、実行時間を同じrequest_idで監査ログへ残します。
具体例:解約理由の集計を通す
たとえば営業企画が「先月の解約理由を担当者別に出して」と依頼した場面を考えます。担当者名は個人情報を含むため、自動実行の契約では許可しません。モデルは匿名化済みビュー、理由別件数、100行上限へ依頼を縮めます。
最初のSQLが顧客メモ列を参照した場合、ビューの許可リストで拒否します。再生成したSQLがbilling_events_anonだけを使い、理由別の集計となり、実行計画も上限内なら、読み取り専用接続で実行します。担当者別の分析が本当に必要なら、data_owner承認へ送り、自動処理から外します。完了条件は「結果が返った」ではなく、契約、事前判定、実行制限、監査記録の四つがそろうことです。
この方法は、定義済みビューへ落とせず、毎回データ管理者が意味を解釈しなければならない探索分析という場合には向きません。その場合はAIをSQL案の作成までに留め、人が分析環境で実行します。
明日から使える実装チェックリスト
- AI専用の読み取りユーザーを作った
- 許可ビューと禁止列を台帳化した
- モデル出力を固定スキーマにした
- SQLを構文木で検査している
EXPLAINの拒否条件を決めた- 時間、行数、出力量に上限がある
- 個人情報を含む依頼は承認へ送る
- 契約から結果まで同じIDで追跡できる
明日は、実際の分析依頼を一つ選び、モデルへSQLを書かせる前に実行契約を手作業で埋めてください。空欄になる項目が、そのまま自動化前に決めるべき仕様です。
参考資料
- OpenAI, Safety in building agents
- PostgreSQL 18 Documentation, Using EXPLAIN
- PostgreSQL 18 Documentation, SET TRANSACTION
- PostgreSQL 18 Documentation, Client Connection Defaults
本記事はAIを活用して作成し、公開前に自動レビューを実施しています。情報の確認日:2026-10-08。
著者
ELYSAI編集部