はじめに
AIエージェントは、ユーザーの代わりにリレーショナルデータベースと直接やり取りできる場合に、はるかに有用になります。「前四半期のトップセラープロダクトは何ですか?」や「この顧客の配送住所を更新してください」といった質問に、人間が手動でSQLを書くことなく答えられます。課題は、エージェントに実際のデータベース権限を与えながら、破壊的または安全でないクエリの実行を防ぐことです。このレッスンでは実践的な構成要素を順を追って説明します。エージェントにスキーマを認識させ、SQLを生成し、そのSQLを検証し、それから初めて実際のデータベースに対して実行する手順です。
エージェントにスキーマ認識を付与する
存在するテーブルやカラムがわからなければ、エージェントは正しいSQLを書けません。標準的な手法は、データベーススキーマの簡潔な説明をシステムプロンプトの一部として、または取得したコンテキストブロックとしてモデルに供給することです。PostgreSQLデータベースの場合、information_schemaを使ってテーブルとカラムを出力し、モデルに渡す前にその出力を絞り込みます。
返された行は、エージェントがクエリを構築する際に参照できるテキスト要約になります。これがないと、モデルがカラム名を幻覚(hallucination)し、何度も失敗します。
SQLの生成と検証
エージェントがスキーマコンテキストを取得したら、ユーザーリクエストに応じて単一のSQLステートメントを生成するよう指示します。その出力をパーサーでラップし、実行前に検査できるようにします。堅牢なパイプラインは次の3つを順番に行います:(1) 単一ステートメントでないものをすべて拒否する、(2) 先頭の動詞が拒否リスト(DROP、TRUNCATE、WHEREなしDELETE、WHEREなしUPDATE、GRANTなど)にあるステートメントを拒否する、(3) 意図に応じて読み取り専用またはパラメータ制限付きの接続でステートメントを実行する。
この検証レイヤーは、SQLがデータベースに触れる*前*に実行されます。パイプライン全体で最も重要な保護層です。
制限された接続での実行
検証があっても、多層防御は重要です。すでに権限が制限されているユーザーとしてデータベースに接続します。分析用の質問には読み取り専用ロールを、アプリが必要とする特定のINSERT/UPDATE権限のみを持つ別のロールを用意します。セッションレベルで短いステートメントタイムアウトを設定し、暴走したクエリがテーブルを長時間ロックできないようにします。
これらのタイムアウトと組み合わせて、生成されたSELECTに行数制限を追加します(例えば、ユーザーが明示的にオプトアウトしない限り、常にLIMIT 100を追加する)。これにより、エージェントが100万行のテーブルをメモリに取り込んで停止することがなくなります。
よくある落とし穴
最も多い失敗は、エージェントがSQLを生のままチャットにエコーバックし、実際には実行しないことです。ユーザーはそれをコピーして自分で実行するため、監査証跡が失われます。もう一つは、モデルに同じプロンプト内で「クエリが安全かどうかを判断する」ことを信頼することです。自己ポリシングは信頼できないため、必ずコード内で検証を実施してください。最後に、大規模なデータベースの完全なスキーマを一度に渡すのは避けてください。モデルは決して使わないテーブルでコンテキストを浪費するため、リクエストごとに関連テーブルのみに絞り込むか、埋め込みを使って一致するテーブルのみを取得してください。
まとめ
エージェントをSQLに接続することは、多層的な問題です。まずスキーマメタデータを公開してモデルが実際のSQLを書けるようにし、次に実行前にコード内ですべてのステートメントを生成、解析、検証します。バリデーターをロールベースの権限と接続レベルのタイムアウトでバックアップし、ミスや悪意のあるプロンプトがテーブルを削除できないようにします。スキーマコンテキスト、コードレベル検証、制限されたロール、タイムアウトという4つの層があれば、エージェントはデータベースを危険にさらすことなく、データ質問に答えたり安全な書き込みを行ったりできます。
レッスンのチェックポイント
学んだ内容を復習し、回答へのフィードバックを受けましょう。