ゲスト寄稿者

Permission-Aware RAG:SpiceDB Testcontainerを用いたエンドツーエンドテスト

投稿日 Jan 15, 2026

私たちは今や、社内の知識ベース、カスタマーサポートシステム、コードレビューボットなど、あらゆる技術分野でGenAIを活用しています。そして、ほとんどすべてのケースで、誰かが最終的にこう問いかけます:

ユーザーが見てはいけないものをモデルが返品しないのを、何が止めるの?」

これは、RAG機能やAIエージェントを構築する企業が最終的に直面する障害であり、ユーザーがアクセス権限のない文書からLLMがデータを返す瞬間であり、すべての関係者に法的、財務的、評判上のリスクをもたらす瞬間です。残念ながら、従来の認可方法はRAGにおける階層的かつ動的なアクセス制御の性質には適していません。まさにここで、 SpiceDB のような現代的な認可権限システムが輝く点は、AI搭載アプリケーションにおけるコンテンツのフィルタリングのための細かい認証を構築する点です。

実際、OpenAIはSpiceDBを使って、Google Drive、Dropbox、GitHubなど様々なソースからデータをChatGPTに取り込む機能であるChatGPTコネクターを利用する5百万ユーザーのために、37十億件の文書を保護しています。

このブログ記事では、SpiceDBと Testcontainers を組み合わせることで、RAGパイプライン内で権限ロジックをエンドツーエンドで自動的にテストできる方法を紹介しています。インフラ依存関係はゼロです。リポジトリの例は こちらでご覧いただけます

承認に関する簡単な入門

実装に入る前に、まず2つの基本的な概念を明確にしましょう: 認証(ユーザーの認証)と 認可( アクセス 可能なものを決定する こと)。

認可は一般的に以下の技術で実装されます:

  • アクセス制御リスト(ACL)
  • ロールベースアクセス制御(RBAC)
  • 属性ベースアクセス制御(ABAC)

しかし、RAGパイプラインのような複雑で動的かつ文脈豊かなアプリケーションでは、RBACやABACといった従来の手法は不十分です。新たに登場した ReBAC (Relationship-Based Access Control)は、固定ルールではなく 関係のグラフとしてアクセス をモデル化し、必要な柔軟性とスケーラビリティを提供するため理想的です。

ReBACは、Googleがすべての製品(例:Google Docs、Drive)にわたる権限を管理するために構築した内部認証システム、Google Zanzibarで広まりました。ザンジバルシステムは低遅延、高スループットの認可チェック、そしてグローバルな一貫性に最適化されており、これらはRAGシステムに適した要件です。

SpiceDB はGoogleのZanzibar認証モデルの中で最もスケーラブルなオープンソース実装です。アクセスは関係グラフとして保存され、基本的なチェックは次のように簡約されます: 

これ? 俳優 これを許可されています アクション これについて リソース?

Googleドキュメント風の例として:

definition user {}
definition document {
  relation reader: user
  relation writer: user

  permission read = reader + writer
  permission write = writer
}

このスキーマはオブジェクトタイプ(user および document)、オブジェクト間の明示的な 関係 (readerwriter)、および派生 権限 (readwrite)を定義します。SpiceDBは関係グラフをマイクロ秒単位で評価し、大規模にリアルタイムの認可チェックを可能にします。

RAGのアクセス制御 

RAG(Retrieval-Augmented Generation)は、大規模言語モデル(LLM)が外部の知識ベースを参照できるようにすることで強化するアーキテクチャパターンであり、通常は Retriever コンポーネントが文書のチャンクを見つけ、LLMが情報に基づく応答を生成するという形で機能します。

このパターンは現在、企業や企業でチャットボットのような顧客プレイブックや個人情報(PII)などの機密データを照会するアプリに使われており、すべて性能向上のためにベクターデータベースに保存されています。しかし、このフローにおける根本的なリスクは データリーミュジです。Retrieverコンポーネントは権限を無視し、LLMは不正なデータを喜んで要約します。実際、OWASPには「Sensitive Information Disclosure(機密情報開示)、過剰なエージェンシー&ベクトル(Agency and Vector)、埋め込みの弱点(Embbedding Weaknesses)」などの「Large Language Model Applicationsのリスクトップ10リスト」があります。この漏洩の影響は深刻であり、顧客の信頼喪失からコンプライアンス違反による巨額の経済的・評判的損害に至るまで多岐にわたります。

このセットアップは細かい認証を切実に必要としており、そこで SpiceDB の登場です。SpiceDBは、検索した文書をリアルタイムの認可チェックで後ろろフィルターし、検索ユーザーが閲覧できるデータのみをモデルが使用するようにします。唯一の要件は、情報の出所を示す メタデータ が文書に記載されていることです。しかし、モックや手動のDockerセットアップ、不安定な継続的統合(CI)環境なしにこの重要な権限ロジックをテストするのは難しいです。Testcontainers は完璧なソリューションを提供し、ユニットテスト内で実際の実用レベルの使い捨てSpiceDBインスタンスを立ち上げ、RAGパイプラインがエンドツーエンドで権限を尊重していることを決定論的に検証できます。

すべてのテストに対して実際の認証をスピンアップする

認証システムを模擬したり、ワークステーションで手動で実行する代わりに、テストに次のコードラインを追加できます:

container, _ := spicedbcontainer.Run(ctx, "authzed/spicedb:v1.47.1")

そしてTestcontainersは以下のことをします:

  • 実際のSpiceDB画像を取得してください
  • 清潔で隔離された環境で始めてください
  • 動的ポートを割り当ててください
  • 準備ができるのを待って
  • gRPCエンドポイントを渡してください
  • 後片付け

Testcontainersはコンテナの取得、動的ポートの公開、自動的な削除までライフサイクル全体を処理するため、Dockerコマンドの実行やクリーンアップスクリプトの作成などの手動プロセスを不要にできます。この隔離により、すべてのテストが新鮮でクリーンな認可グラフで実行され、データの競合を防ぎ、権限テストがIDEや並列継続的統合(CI)ビルド全体で 完全に再現可能 になります。

突然、ユニットテストの中に本物の本格的なサンジバル風の許可エンジンが入ります。 

SpiceDBとTestcontainerの使用

ここでは、SpiceDBとTestcontainersを使ってエンドツーエンドの権限テストを実現する方法のウォークスルーを示します。このチュートリアルのソースコードは こちらでご覧いただけます。

1。RAGのテスト 

簡潔にするために、最小限のRAGを持ち、検索メカニズムも簡単です。 

メタデータとして機能する3つの文書をテストします。doc_ids(doc1 doc2 ...)を使います。 

  • doc1:内部ロードマップ
  • doc2: カスタマープレイブック
  • doc3: 公開FAQ

そして3人のユーザー:

  • エミリアは所有しています doc1
  • ベアトリスは見ることができます doc2
  • チャーリー(または誰でも)は閲覧可能です doc3

このSpiceDBスキーマは userdocument オブジェクトタイプを定義しています。ユーザーが文書の直接viewerまたはownerである場合、その文書に対してread権限を持ちます。

definition user {}

definition document {
  relation owner: user
  relation viewer: user | owner
  permission read = owner + viewer
}

2。テストコンテナの起動 

コードの一行が使い捨てのSpiceDBインスタンスを起動するテストを開始する方法は以下の通りです:

container, err := spicedbcontainer.Run(ctx, "authzed/spicedb:v1.47.1")
require.NoError(t, err)

次に、実行中のコンテナ化サービスに接続します。

host, _ := container.Host(ctx)
port, _ := container.MappedPort(ctx, "50051/tcp")
endpoint := fmt.Sprintf("%s:%s", host, port.Port())

client, err := authzed.NewClient(
    endpoint,
    grpc.WithTransportCredentials(insecure.NewCredentials()),
    grpcutil.WithInsecureBearerToken("somepresharedkey"),
)

これはテストランナー内で動作する完全に機能するSpiceDBインスタンスです。

3。スキーマ+テストデータを読み込む

テストはアプリケーションと同じ方法でデータをシードします:

_, err := client.WriteSchema(ctx, &apiv1.WriteSchemaRequest{Schema: schema})
require.NoError(t, err)

そうしたら:

rel("document", "doc1", "owner", "user", "emilia")
rel("document", "doc2", "viewer", "user", "beatrice")
rel("document", "doc3", "viewer", "user", "emilia")
rel("document", "doc3", "viewer", "user", "beatrice")
rel("document", "doc3", "viewer", "user", "charlie")

現在、すべてのテストランに対して予測可能で再現可能な認可グラフが手に入りました。

4。SpiceDBによる後フィルタリング

LLMが何かを見る前に、文書内の権限の真実の情報源として機能するSpiceDBで権限を確認します。

resp, err := r.spiceClient.CheckPermission(ctx, &apiv1.CheckPermissionRequest{
    Resource:   docObject,
    Permission: "read",
    Subject:    userSubject,
})

もしSpiceDBが 「いいえ」と言った場合、ドキュメントはLLMに入力されず、ユーザーが読める権限に基づいてクエリへの回答を得ることが保証されます。

これにより、以下のことが回避されます:

  • 偶発的なデータ漏洩
  • 過剰許容ベクトル探索
  • コンプライアンスの問題

従来のアクセス制御はデータが埋め込みになると機能しなくなるため、ガードレールがあることでこれを防ぎます。 

単一のテストにおけるエンドツーエンドの許可チェック

以下がテストの完全な主張です:

エミリアは「ロードマップ」を問い合わせ→ doc1
だって
彼らはオーナーだから。

ベアトリスは「プレイブック」を尋ね→ doc2
彼女は視聴者
だからです。

チャーリーは「public」と問い合わせ→ doc3
それは彼
が読める唯一の文書で、公文書だからです

単一の失敗権限ルールがある場合、エンドツーエンドテストは即座に失敗します。これはRAGパイプラインの新しい取得モード、埋め込み、ドキュメントタイプ、権限ルールなど絶えず変更されるため非常に重要です。 

もしRAGパイプラインがGoに入っていなかったらどうしますか?

まず、 SpiceDB Go Testcontainersモジュール へのオリジナル貢献をした Guillermo Mariscal に感謝します。 

もしあなたのRAGパイプラインがPythonなど別の言語で書かれていたらどうでしょうか?心配しないでください。Pythonで書かれたコミュニティのTestcontainersモジュールも同様に使えます。モジュールは こちらでご覧いただけます

通常、統合テストでは次のように積分します。

 # Your RAG pipeline test
  def test_rag_pipeline_respects_permissions():
      with SpiceDBContainer() as spicedb:
          # Set up permissions schema
          client = create_spicedb_client(
              spicedb.get_endpoint(),
              spicedb.get_secret_key()
          )

          # Load your permissions model
          client.WriteSchema(your_document_permission_schema)

          # Write test relationships
          # User A can access Doc 1
          # User B can access Doc 2

          # Test RAG pipeline with User A
          results = rag_pipeline.search(query="...", user="A")
          assert "Doc 1" in results
          assert "Doc 2" not in results  # Should be filtered out!

Goモジュールと同様に、このコンテナはすべてのテスト実行でクリーンで孤立したSpiceDBインスタンスを提供します。

なぜこのアプローチが重要なのか

RAGパイプラインにおける認可テストは、規模や遅延の要件があるため複雑になり得ますし、機密データを扱うシステムではさらに複雑になることがあります。SpiceDBの柔軟性とスケールをTestcontainerの自動化された孤立した環境と統合することで、完全に信頼性が高く決定 的な認証アプローチへと移行します。 

コードが出荷されるたびに、新しい本番環境の認可エンジンが起動され、テストデータが読み込まれ、きれいに分解されるため、 開発マシンとCI間のドリフトが一切ありません。このパターンにより、RAGシステムは3つのドキュメントから数百万もの文書にスケールする際、安全で正確かつ許可を認識したものになります。

自分で試してみる

Goの完全な動作例とサンプルRAGパイプラインはこちらです:
https://github.com/sohanmaheshwar/spicedb-testcontainer-rag
クローンだ。
go test -v走れ。
新しいSpiceDBインスタンスを起動し、権限をロードし、RAGの動作を主張する様子を見てください。
また、 GoPythonでSpiceDBのtestcontainerのコミュニティモジュールも見つけてみてください。

関連記事