GEMINI LABEN
FLASH — Gemini 3.5 Flashが一般提供(GA)に。エージェントやコーディングで持続的なフロンティア性能をうたいますAGENTS — Gemini APIにManaged Agentsがパブリックプレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを実行できますWEBHOOK — イベント駆動のWebhooksが追加され、Batch APIや長時間処理のポーリングを置き換えられますSEARCH — File Searchがマルチモーダル対応に。gemini-embedding-2で画像も埋め込んで検索できますSUNSET — gemini-3.1-flash-image-preview・3-pro-image-previewは6/25に停止予定ですANTIGRAVITY — Antigravity Agentのマネージドエージェント(antigravity-preview-05-2026)がプレビュー提供されていますFLASH — Gemini 3.5 Flashが一般提供(GA)に。エージェントやコーディングで持続的なフロンティア性能をうたいますAGENTS — Gemini APIにManaged Agentsがパブリックプレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを実行できますWEBHOOK — イベント駆動のWebhooksが追加され、Batch APIや長時間処理のポーリングを置き換えられますSEARCH — File Searchがマルチモーダル対応に。gemini-embedding-2で画像も埋め込んで検索できますSUNSET — gemini-3.1-flash-image-preview・3-pro-image-previewは6/25に停止予定ですANTIGRAVITY — Antigravity Agentのマネージドエージェント(antigravity-preview-05-2026)がプレビュー提供されています
記事一覧/API / SDK
API / SDK/2026-06-15上級

権限を尊重する RAG — Gemini 検索で『その人が見てよい文書』だけを根拠にする本番設計

社内ナレッジ検索に RAG を入れた途端、本来見えてはいけない下書きや収支メモが回答の根拠に混ざる。権限を尊重したまま Gemini に検索させるための、メタデータフィルタ・二重チェック・監査ログの本番設計を動くコードで解説します。

gemini-api245rag18security8access-controlproduction90

プレミアム記事

社内向けのナレッジ検索に Gemini で RAG を組み込んだ初日のことを、今でもよく覚えています。デモは完璧でした。「先月の広告収益の推移は?」と聞けば、それらしい数字を引いて答えてくれます。問題は、その質問をアルバイトのアカウントで投げたときに起きました。

経営にしか共有していない収支メモが、しれっと回答の根拠に混ざっていたのです。

本文そのものは表示されません。けれど Gemini は「先月は前月比で増加しています」と、見えてはいけない数字を要約として喋ってしまいました。ベクトル検索は「意味が近いチャンク」を返すだけで、「この人が見てよいか」を一切問わないからです。

権限を尊重する RAG は、後付けの機能ではありません。検索の設計そのものに権限を編み込む必要があります。ここでは、個人開発で複数サービスのナレッジを横断検索する仕組みを作りながら私自身がぶつかった失敗と、その対処を、動くコードとともにお話しします。

なぜ普通の RAG は静かに漏らすのか

典型的な RAG は三段構えです。文書をチャンクに割り、埋め込みベクトルにしてベクトルストアへ。質問が来たら近傍検索で上位 k 件を引き、それを文脈として Gemini に渡す。

この設計には、権限という概念が一文字も登場しません。インデックスは「全員の文書が混ざった一つの巨大な袋」であり、近傍検索はその袋の中から意味の近いものを取り出すだけです。袋の中に役員専用の収支メモが入っていれば、アルバイトの質問にもそれが当たります。

最初に思いつく対処は「回答後フィルタ」です。Gemini に答えさせてから、根拠にした文書をチェックして権限がなければ捨てる。これは二重に間違っています。第一に、捨てる頃にはもう Gemini が秘密を読んで要約してしまっている。第二に、上位 k 件が権限のない文書で埋まると、本来見えるはずの文書が圏外に押し出され、回答品質まで落ちます。

正しい順序は逆です。権限で絞り込んでから検索する。これを検索前フィルタ(pre-filtering)またはセキュリティトリミングと呼びます。

設計の骨子 — 権限を検索の一級市民にする

考え方はシンプルです。チャンクひとつひとつに「このチャンクを見てよいプリンシパルの集合」を ACL(アクセス制御リスト)として持たせます。プリンシパルとは、ユーザー ID やグループ ID のことです。

検索時には、質問してきたユーザーが属するプリンシパル集合を展開し、「ACL がそのいずれかと交わるチャンクだけ」を候補にします。ベクトル類似度の計算は、その絞り込んだ集合の中だけで行います。

ここで大切なのは、ACL をベクトルストアのメタデータフィルタとして効かせることです。多くのベクトルストア(Pinecone、Qdrant、pgvector、sqlite-vec など)は、近傍検索とメタデータ条件を同時に適用できます。これにより「意味が近く、かつ見てよい」上位 k 件が一度のクエリで取れます。

まずインデックス側です。チャンクを格納するとき、本文の埋め込みと一緒に allowed_principals を必ず付けます。

from google import genai
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
def embed(text: str) -> list[float]:
    res = client.models.embed_content(
        model="gemini-embedding-001",
        contents=text,
    )
    return res.embeddings[0].values
 
def index_chunk(store, doc_id: str, chunk_text: str, allowed_principals: list[str]):
    """1チャンクをACL付きで格納する。
    allowed_principals には『このチャンクを見てよい』ユーザーID・グループIDを列挙する。
    例: ["user:masaki", "group:executives"]
    """
    if not allowed_principals:
        # 空のACLは『誰でも見える』ではなく『誰も見えない』と解釈する。
        # ここを取り違えると、ラベル付けを忘れた文書が全員に漏れる。
        raise ValueError(f"doc={doc_id} に allowed_principals がありません(誤って全公開を防ぐため拒否します)")
    store.upsert(
        id=f"{doc_id}",
        vector=embed(chunk_text),
        metadata={
            "text": chunk_text,
            "doc_id": doc_id,
            "allowed_principals": allowed_principals,
        },
    )

allowed_principals が空のときに例外を投げているのが、地味ですが最重要のポイントです。「ACL が空=制限なし=全公開」と暗黙に扱う実装が一番事故ります。**安全側の初期値は『全部見える』ではなく『誰も見えない』(deny by default)**です。ラベル付けを忘れた文書は、漏れるのではなく検索に出てこないのが正しい挙動です。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
RAG を入れた社内検索で『他人の下書きや収支が回答に漏れる』恐怖に詰まっていた人が、権限を検索の一級市民として扱う本番アーキテクチャを今日手に入れられる
チャンクに ACL ラベルを持たせ、検索時にユーザーのプリンシパル集合でハード絞り込みし、回答直前に正本へ再照合する『多層防御』を、コピー&ペーストで動かせるようになる
権限変更が反映されない『古い ACL 問題』や、引用・会話履歴経由の情報漏れまで塞いだうえで、誰が何を根拠に答えたかを残す監査ログを設計できる
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

API / SDK2026-06-15
外部テキストを Gemini API に渡すときのプロンプトインジェクション対策 — レビューや収集データを安全に処理する防御設計
ユーザーレビューや収集した記事など、信頼できない外部テキストを Gemini API に渡すと間接プロンプトインジェクションの入口になります。信頼境界の隔離・スキーマ拘束・二段階検証・出力サニタイズまで、本番パイプラインに組み込める防御をコード付きで整理します。
API / SDK2026-05-06
Gemini APIでRAG評価フレームワークを構築する:RAGAS・LLM-as-Judge・カスタム指標の統合本番実装マスタークラス
RAGAS・LLM-as-Judge・カスタム評価指標を組み合わせてGemini APIベースのRAGシステムを定量評価するフレームワークの完全実装ガイド。CI/CD組み込みと本番モニタリングまで解説します。
API / SDK2026-05-02
Gemini API × Cloudflare Vectorize で完全エッジ RAG を構築する — 低レイテンシ・低コスト・グローバル配信の本番設計
Gemini Embedding と Cloudflare Vectorize を組み合わせ、Workers ランタイム上で完結する低レイテンシ・低コストの本番エッジ RAG を実装します。サブリクエスト制限の回避、リトライ、フォールバック、コスト試算まで踏み込みます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →