データベース・閉域AI検索(RAG)正規特集
監修:加藤 明生(データベーススペシャリスト / 登録情報処理安全確保支援士 第007617号)
PostgreSQL 19 FORCE RLS × pgvector HNSW マルチテナント分離:RAG検索時の他社・他部署データ混入を構造的にゼロ化
マルチテナント型クラウドや社内AI(RAG:検索拡張生成)において、外部ベクトルデータベースとの同期ズレやSQLの WHERE tenant_id = ... 記述漏れは重大な情報漏洩に直結します。本稿では、当社の専用データベース基盤 db01.ok.st(FreeBSD 15.1 + OpenZFS + PostgreSQL 19 + pgvector 0.8+)が、リレーショナル権限とベクトル近傍検索を単一ACIDトランザクション内で厳密に隔離する仕組みを解説します。
1. 基本と全体像
別建てベクトルDBの同期リスクをなくし、PostgreSQL 19 カーネル内で一貫保護
| 章構成 | 主なテーマ | 要点と確認ポイント |
|---|---|---|
| 1. 基本と全体像 | RAG検索におけるデータ混入リスクと解決策 | 外部ベクトルDBへの分散をやめ、PostgreSQL 19 の FORCE RLS 下で pgvector 検索を実行する優位性を整理します。 |
| 2. 現場での進め方と対策 | 3ステップ導入と反復HNSWフィルタリング | SET LOCAL によるトランザクション束縛、hnsw.iterative_scan による取りこぼし防止、CI自動負テストの手順を示します。 |
| 3. 関連する制度とルール | 生成AI安全利用規程・SCS★3/★4・個人情報保護 | 総務省・経産省『AI事業者ガイドライン』およびSCS評価制度が求める社内機密分離・監査証跡との対応を確認します。 |
| 4. 詳しい技術仕様としくみ | PG19 × pgvector 0.8+ × OpenZFS ARC 実装 | InitPlan 定数化、ON CONFLICT DO SELECT チャンク冪等登録、uuidv7()、およびコサイン距離検索SQLを公開します。 |
2. 現場での進め方と対策
HNSW 反復探索(Iterative Scan)と 3ステップ安全実装フロー
マルチテナント環境で HNSW ベクトルインデックスと RLS を併用する際は、上位近傍候補が他テナント行で埋まって検索結果数が不足する現象(Over-filtering)を防ぐため、pgvector 0.8+ の hnsw.iterative_scan = strict_order とパーティション/複合B-Tree索引を組み合わせます。
3. 関連する制度とルール
総務省・経産省『AI事業者ガイドライン』および SCS評価制度 ★3/★4 適合基準
| ガバナンス観点 | 求められる管理基準 | db01.ok.st(PostgreSQL 19)での実装 |
|---|---|---|
| テナント・部署間の機密隔離 | 人事・経理・役員会議事録など閲覧権限の異なる文書が一般検索に混ざらないこと | FORCE ROW LEVEL SECURITY + ロール属性検査によりDBカーネル層で厳格分離 |
| チャンク更新時の真正性・冪等性 | 規程改定時に古い版が残存せず、重複登録による誤回答が起きないこと | BLAKE3 ダイジェスト一意制約 + ON CONFLICT DO SELECT で重複投入を0B WAL排除 |
| 監査可能性・バックアップ復旧 | 誰がいつどの社内文書をAI検索したか追跡でき、ランサムウェア被害時も即復旧できること | uuidv7() 監査台帳 + OpenZFS 不変スナップショットによる数秒ロールバック |
4. 詳しい技術仕様としくみ
db01.ok.st / halfvec(1536) HNSW
PostgreSQL 19 × pgvector 0.8+ HNSW マルチテナント知識テーブル DDL
PostgreSQL 19 + pgvector 0.8+ DDL & Isolated Cosine Similarity Query
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS tenant_knowledge_chunks (
chunk_id UUID NOT NULL DEFAULT uuidv7(),
tenant_id UUID NOT NULL,
doc_Hash BYTEA NOT NULL CHECK (octet_length(doc_Hash) = 32),
chunk_text TEXT NOT NULL,
embedding halfvec(1536) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
PRIMARY KEY (tenant_id, chunk_id),
UNIQUE (tenant_id, doc_Hash)
);
CREATE INDEX IF NOT EXISTS idx_knowledge_chunks_hnsw
ON tenant_knowledge_chunks USING hnsw (embedding halfvec_cosine_ops)
WITH (m = 16, ef_construction = 128);
ALTER TABLE tenant_knowledge_chunks ENABLE ROW LEVEL SECURITY;
ALTER TABLE tenant_knowledge_chunks FORCE ROW LEVEL SECURITY;
CREATE POLICY knowledge_chunks_rls_guard ON tenant_knowledge_chunks
AS RESTRICTIVE FOR ALL TO PUBLIC
USING (tenant_id = (SELECT sovereign_auth.current_tenant_id()))
WITH CHECK (tenant_id = (SELECT sovereign_auth.current_tenant_id()));