SharePointファイルをAzure OpenAI RAGで自動取り込みする方法|Azure AI Searchベクトル化と権限トリミング設計

SharePointの特定フォルダーに置かれた資料を自動で取り込み、Azure OpenAIのRAGで検索・回答したい。しかしサンプル(azure-search-openai-demo)だけではSharePoint連携や文書権限の反映が不足し、実運用で詰まりがちです。この記事では「取り込みの橋渡し」と「権限トリミング」を前提に、設計パターンと実装の勘所を具体的に解説します。

目次

なぜ「SharePoint→RAG→Azure AI Search」がサンプルだけでは完結しないのか

RAG(Retrieval Augmented Generation)は大きく分けて、次の2つの工程で成り立ちます。

  • 取り込み(Ingestion):データを集めて、分割(チャンク化)し、埋め込み(ベクトル)を作り、検索基盤(Azure AI Search)へ登録する
  • 検索・回答(Query):ユーザーの質問に対して、検索(ベクトル検索+フィルター)で候補を取り、LLMで回答を生成する

多くのサンプルは「ローカルファイル」や「Blob Storage」など、取り込み元が比較的単純な前提で作られています。そのため、SharePointを“取り込み元として直結する”部分や、SharePointのフォルダー/文書レベル権限を検索結果へ反映する部分は、別途設計・実装が必要になるケースがほとんどです。

領域やることサンプルで不足しがちな点
データ取得SharePointからファイルを列挙・差分検知・ダウンロードSharePoint接続・認証・差分同期の仕組み
解析本文抽出、OCR、表やPDFの扱い、チャンク分割業務文書の“実データ”に耐える前処理設計
登録埋め込み生成、Azure AI Searchへベクトル+メタデータ登録スキーマ設計(ACL含む)と更新/削除の運用
権限SharePointの閲覧権限(ACL)を検索・回答に反映権限トリミング(フィルター)実装と安全性

目指すべき全体アーキテクチャ(設計の正解パターン)

結論から言うと、SharePoint連携は「橋渡し(取り込み)」を別で用意するのが前提です。おすすめは、取り込みを次のように分割して考えることです。

構成要素と役割

コンポーネント役割Azureでの実装候補
取得レイヤーSharePointからファイル取得・差分検知・削除検知Azure Functions / Logic Apps / ADF
中継ストレージ(任意)生ファイル保管、再処理、監査、バックアップBlob Storage / ADLS Gen2
前処理レイヤー本文抽出、チャンク化、メタデータ整形、ACL抽出Functions / Container Apps / AKS
ベクトル化埋め込み生成(Azure OpenAI Embeddings)Azure OpenAI
検索基盤ベクトル+全文+フィルター検索、権限トリミングAzure AI Search
アプリ層認証、ユーザー属性/所属取得、検索フィルター付与、回答生成App Service / Static Web Apps + API / Functions

データフロー(取り込み)

  1. SharePointの特定フォルダー(ドキュメントライブラリ)からファイル一覧を取得
  2. 更新日時やETag等で差分を判定し、変更があったファイルだけダウンロード
  3. 本文抽出(Office/PDF/画像など)→チャンク分割(検索に耐える単位へ)
  4. チャンクごとに埋め込み(ベクトル)を生成
  5. Azure AI Searchへ「チャンク本文+ベクトル+メタデータ+ACL」を登録
  6. 削除・移動・リネームを検知したら、Search側の該当ドキュメントも同期

データフロー(検索・回答)

  1. ユーザーがMicrosoft Entra ID(旧Azure AD)でサインイン
  2. サーバー側でユーザーID/グループ所属を確定(クライアント申告は信用しない)
  3. Azure AI Searchへ「ベクトル検索+ACLフィルター」を付けて問い合わせ
  4. 許可されたチャンクだけが検索結果として返る
  5. その結果のみを根拠としてAzure OpenAIが回答生成(RAG)

課題1:SharePointのファイルを自動取得→解析→Azure AI Searchへベクトル格納する

この課題の本質は「SharePointは“取り込み元”として独自の認証・差分同期・権限制御を持つ」点です。したがって、サンプルにSharePoint直結が無いなら、橋渡しを作るのが前提になります。

方法A:SharePoint APIでファイル取得→取り込み処理へ渡す(直結型)

SharePoint Onlineの場合、実装の現場ではMicrosoft Graph API(またはSharePoint REST API)でファイルを取得し、RAGの取り込みパイプラインへ流す形が定番です。

実装イメージ

  • 認証:Entra IDにアプリ登録し、アプリケーション権限(app-only)または委任権限(delegated)でアクセス
  • 対象フォルダー特定:サイトID、ドライブ(文書ライブラリ)ID、フォルダーパス(またはItem ID)で対象を絞る
  • 差分取得:更新日時/ETag/Delta Query等で「変わったものだけ」処理
  • ダウンロード:ファイルのコンテンツを取得し、本文抽出へ渡す

おすすめの「最小で堅い」取り込み設計

直結型で最初につまずきやすいのは、安定運用(再試行・重複・欠落防止)です。以下の構成にすると運用が安定します。

観点推奨理由
起動方式タイマー+差分同期(ポーリング)を基本、余裕があれば通知(Webhook)併用Webhookは便利だが再送・取りこぼし対策が必要。まずは差分同期が堅い
処理単位「ファイル」ではなく「チャンク」をIDで管理再処理時に同一チャンクを重複登録しないため
冪等性ファイルのバージョン/ETag/ハッシュをキーにして、同一内容はスキップ無駄な埋め込み生成(コスト)を減らし、インデックスも汚れない
再試行キュー(Storage Queue/Service Bus)で“取得”と“解析/登録”を分離SharePoint側の一時エラーやレート制限があっても復旧しやすい
監査処理ログ+最終同期時刻(ウォーターマーク)を永続化「いつ何を取り込んだか」を説明できる

処理ステップ例(擬似コード)

WordPress貼り付けで読みやすいよう、概念だけを載せます。

// 1) SharePointから差分リスト取得
changedItems = listChangedItems(folderId, watermark)

// 2) 変更分だけ処理キューへ投入
for item in changedItems:
enqueue({ itemId, driveId, siteId, eTag, lastModified })

// 3) ワーカー側でダウンロード→抽出→チャンク→埋め込み→Search登録
msg = dequeue()
fileBytes = downloadFile(msg.itemId)
text = extractText(fileBytes)          // PDF/Office/OCRなど
chunks = chunk(text, strategy="semantic")
acl = fetchAcl(msg.itemId)             // allowedUsers/allowedGroupsを作る
vectors = embed(chunks)                // Azure OpenAI Embeddings
upsertToSearch(chunks, vectors, acl, metadata)

補助ツール(OSS)の位置づけ

SharePointからファイルをpullするOSSとして、例としてkoltyakov/sppullのようなツールが話題に上がることがあります。こうしたOSSは「素早く試せる」一方で、企業利用では次を確認してください。

  • 認証方式(証明書認証に対応しているか、トークン更新が安定しているか)
  • 差分同期(増分取得)をどう実現するか
  • 失敗時の再試行・冪等性・ログ(監査証跡)
  • ライセンスと保守(運用で止まったときに誰が直すか)

実運用のRAGは「止まらないETL」が重要なので、OSSは検証・移行の足がかりとして使い、最終的にはAzure Functions/ADFなどの運用基盤に寄せる判断が多いです。

方法B:Azure Data FactoryでSharePoint→Blob/ADLSへ複製してから処理(分離型)

運用しやすさ重視なら、SharePointから一度ストレージへ複製して、以後はBlob/ADLSを取り込み元にする設計が強力です。特に複数システムが同じデータを参照する場合、SharePointへの直撃を減らせます。

なぜADF(複製)を挟むと楽になるのか

  • 責務分離:SharePoint接続はADFに任せ、RAG側は「ストレージ→解析→Search登録」に集中できる
  • 再処理が簡単:同じファイルを何度でも再解析できる(モデル変更・チャンク戦略変更時に便利)
  • 監査性:取り込んだ原本(生ファイル)を保持できる
  • スケール:解析ワーカーを増やしてもSharePoint側への負荷を抑えられる

方法Aと方法Bの比較

比較項目方法A:API直結方法B:ADFで複製
構築スピード早い(最短で動く)やや工程が増える
運用のしやすさ設計次第。差分/再試行を自作しがち高い(データ基盤として再利用しやすい)
SharePoint負荷取り込み頻度が高いと負荷が増える複製タイミングに集約できる
再処理(モデル変更など)原本再取得が必要になりやすいストレージ上で再処理しやすい
権限(ACL)の扱い同時に取得しやすいACLは別ルートで取得・同期する設計が必要

ポイントは最後の行です。方法Bはファイル複製が楽になる一方、SharePointの文書権限(ACL)を“複製だけで完全に引き継ぐ”のは難しいので、ACLは別途取得してSearchインデックスへ保持する設計を最初から組み込みます。

Azure AI Searchに登録するインデックス設計(ベクトル+メタデータ)

RAGで後悔しやすいのが、インデックス設計を「本文+ベクトルだけ」で始めてしまうことです。SharePoint連携では、あとから必要になるメタデータが多いので、最初に枠を作るのがおすすめです。

最低限入れておきたいフィールド

フィールド例用途設計の勘所
id一意キー「ファイルID+チャンク番号」など、冪等性を担保できる形にする
contentチャンク本文後段の回答生成に使う。前処理で不要なノイズを減らす
contentVector埋め込みベクトル同一モデル/次元で統一。モデル変更時の再生成計画も用意
title検索結果表示ファイル名だけでなく、文書タイトル抽出ができるとUXが上がる
sourceUrl原本リンクSharePointのURLを保持し、回答根拠として提示できる
siteId / driveId / itemIdトレーサビリティSharePoint側の参照キーを保持して、更新/削除同期に使う
lastModified / eTag差分処理同一ファイルの更新判定、取り込みのウォーターマークに利用
allowedUsers / allowedGroups権限トリミング後述。ここを最初から入れておくと手戻りが激減
classification(任意)機密度ラベル「社外秘」などがあるなら、インデックス分割の判断材料になる

チャンク分割(分割/埋め込み作成)の現場ノウハウ

SharePointに置かれる業務文書は、単純なテキストだけでなく「表」「箇条書き」「議事録」「設計書」などが混在します。検索精度とコストのバランスを取りやすい指針は次の通りです。

  • チャンクは“意味のまとまり”を優先:段落・見出し・箇条書き単位を崩しすぎない
  • 大きすぎるチャンクは避ける:回答根拠がぼやけ、無関係な文まで一緒に拾う
  • 小さすぎるチャンクも避ける:文脈が不足し、誤答や曖昧回答が増える
  • タイトルや見出しをチャンクに付与:本文の前に「見出し」を付けて埋め込むと検索が強くなることが多い
  • 表は“行の意味”で再構成:CSV化や「列名:値」の文章化で、検索に乗りやすくする

埋め込みコストが気になる場合は、「変更があったファイルだけ再埋め込み」と、「同一チャンクの重複排除」が最優先の最適化ポイントです。

課題2:SharePointのフォルダー/文書レベル権限をRAGの検索・回答に反映する(権限トリミング)

ここがSharePoint連携RAGの最重要ポイントです。どれだけ検索精度が高くても、見えてはいけない文書が検索結果に混ざった時点でアウトです。RAGでの「権限トリミング」は、基本的に次の設計で実現します。

  • 取り込み時:SharePointからACL(許可ユーザー/許可グループ)情報も取得し、Azure AI Searchのインデックスに保持する
  • 検索時:サーバー側でユーザーのID/所属グループを確定し、Azure AI Searchへフィルター条件として付与する

権限トリミングの基本形(インデックスにACLを持つ)

インデックス設計としては、次のようなフィールドを用意するのが定石です。

フィールド型のイメージ入れる値
allowedUsers文字列配列(コレクション)閲覧可能なユーザーID(例:Entra IDのobjectId等)
allowedGroups文字列配列(コレクション)閲覧可能なグループID(例:Entra IDグループobjectId等)
isPublic(任意)真偽値全社員公開など、ACL判定を省略できる文書

そして検索時に、ユーザーのID/グループに基づくフィルターを必ず付与します(ベクトル検索と併用)。

フィルター条件の考え方(例)

(isPublic eq true)
or allowedUsers/any(u: u eq '{userObjectId}')
or allowedGroups/any(g: g eq '{groupObjectId_1}' or g eq '{groupObjectId_2}')

これにより、ベクトル検索で“近い文章”を探しつつ、ACLで“見てよい文章だけ”に絞ることができます。RAGの回答に使われる根拠も、このフィルターを通過したチャンクだけになります。

「クライアント任せにしない」が絶対条件

権限トリミングが破綻する典型例が、フロントエンド(ブラウザやアプリ)から「私はこのグループです」と送らせる設計です。これは改ざんされます。

必ず、サーバー側で以下を確定します。

  • ログインユーザーの一意ID(トークンから取得)
  • 所属グループ(トークンのグループクレーム、またはサーバーがGraph等で照会した結果)

この情報をもとに、Searchへのクエリにフィルターを付けて初めて「安全な検索」になります。

グループ所属の取得:実務でハマりやすいポイント

ユーザーの所属グループを毎回APIで取りに行くと遅くなりがちです。一方で、トークンにグループが常に載るとも限りません。現実的な落としどころは次の通りです。

方式メリット注意点
トークンのグループクレームを使う高速、追加の照会が少ないグループ数が多いと省略(overage)されることがある
サーバーでGraph等を照会して所属を取得確実、粒度の制御ができるレート制限・遅延・キャッシュ戦略が必要
ハイブリッド(キャッシュ併用)確実性と速度のバランスが良いキャッシュの期限・無効化(退職/異動)設計が重要

特に異動や権限変更が多い組織では、キャッシュの持ちすぎが情報漏えいの原因になり得ます。短めの有効期限、または権限変更イベントを起点にした無効化(難しければ短TTL)を検討してください。

ACLを取り込み時にどう取得するか(SharePointの難所)

SharePointの権限は「サイト全体」「ライブラリ」「フォルダー」「ファイル」で継承・例外があり、最終的に“そのアイテムに誰がアクセスできるか”を確定するには、情報の正規化が必要です。

実装方針は大きく2つです。

方針内容向いているケース
許可プリンシパルをそのまま保持許可ユーザーID/許可グループIDを列挙してSearchへ入れるまず安全に始めたい、権限構造が比較的単純
グループ展開(メンバー展開)して保持グループをユーザーへ展開してallowedUsersに寄せるクエリ側を簡単にしたいが、ユーザー数増で更新負荷が上がる

一般には、最初は「グループIDも含めて保持→検索時にユーザーのグループでフィルター」が現実的です。グループ展開は、権限変更のたびに大量更新が発生しがちで、運用コストが跳ね上がります。

権限トリミングを前提にした「おすすめ実装パターン」3選

パターン1:API直結+キュー分離(小さく始めて拡張しやすい)

  • 取得:Azure Functions(タイマー)でSharePoint差分→キュー投入
  • 解析:Functions/Container Appsで本文抽出・チャンク化・埋め込み
  • 登録:Azure AI Searchへアップサート(ACL含む)
  • 検索:アプリサーバーがユーザー属性を確定し、フィルター付きでSearchへ

最小構成で動かしやすく、後から規模に応じてワーカーを増やせます。

パターン2:ADFで複製+ストレージ起点の取り込み(運用と再処理に強い)

  • 複製:ADFでSharePoint→Blob/ADLS
  • 解析:Blobイベント(またはスケジュール)で取り込みパイプライン起動
  • ACL:別ジョブでSharePointから権限情報を同期し、Searchへ反映

「データ基盤」を作る発想で、RAG以外の用途にも展開しやすいです。

パターン3:インデックス分割(サイト別/機密度別)+最小ACL(高度だが強い)

  • 機密度や部門サイト単位でAzure AI Searchのインデックスを分割
  • アプリ側でユーザー属性に応じて検索対象インデックスを切り替える
  • 各インデックス内では、簡易ACL(全社/部門)程度に留める

権限モデルが複雑すぎる場合、“検索対象を先に分ける”と安全性と性能の両方を取りやすくなります。

実運用で必ず考えるべき運用設計(更新・削除・移動・版管理)

SharePoint連携RAGは、初回取り込みが動いて終わりではありません。日々の運用で「検索結果が古い」「消したのに出てくる」が起きると信頼を失います。

更新・削除を同期するためのチェックリスト

イベント必要な対応実装のコツ
ファイル更新該当ファイルのチャンクを再生成し、Searchを更新ETag/ハッシュで変更検知し、同一ならスキップ
ファイル削除Searchから該当ドキュメントを削除itemId等で逆引きできるようにしておく
移動/リネームsourceUrl/パス等メタデータ更新URLだけ変わるケースがある。参照キーはID中心に
権限変更ACLフィールドを更新本文が変わらなくても権限は変わる。ACL同期を別ジョブにするのも有効
例外権限(継承ブレイク)例外を正しく反映「フォルダーだけ例外」などがあるので、ACL算出ロジックを明確化

よくある落とし穴と対策(SharePoint×Azure OpenAI RAGの現場目線)

落とし穴:取り込みが「全件再処理」になってコストが爆発する

対策:差分同期(ウォーターマーク)+ETag/ハッシュによる冪等化。チャンクID設計を最初に固める。

落とし穴:権限トリミングを後付けしてインデックス作り直しになる

対策:初期段階でも「allowedUsers/allowedGroups」はスキーマに入れておく。中身が空でも枠があるだけで手戻りが減る。

落とし穴:ACL取得が重くて取り込みが遅い

対策:本文更新とACL更新を分離し、ACLは“変更があった時だけ更新”できる仕組みを用意する。必要ならキャッシュとバッチ更新を組み合わせる。

落とし穴:回答がそれっぽいが、根拠が薄い(幻覚が混ざる)

対策:検索で返ったチャンクのみを根拠に回答させる、引用(参照リンク)を表示する、検索結果が少ないときは「不明」と言えるプロンプト/ガードレールを入れる。

実装を進めるための「具体的な次の一手」

ここまでの内容を、最短でプロトタイプに落とすなら次の順番がおすすめです。

  1. 対象フォルダーを固定:まずは1つのライブラリ/フォルダーに絞る
  2. 方法A(API直結)で差分取り込みを作る:更新検知→ダウンロード→本文抽出→Search登録まで通す
  3. インデックスにACLフィールドを追加:最初は全社公開(isPublic=true)でも良いので枠を作る
  4. 検索時にフィルターを必ず付与:まずは「公開のみ」→次に「ユーザー/グループ」へ拡張
  5. 運用要件が見えたら方法B(ADF複製)へ寄せる:再処理や監査が必要になったタイミングで段階的に強化

SharePoint連携RAGは、最初から“完璧な権限モデル”を目指すより、安全性(漏えい防止)を最優先に、段階的に精度と運用性を高めるほうが成功しやすいです。

まとめ:SharePoint×Azure OpenAI(RAG)を成功させる鍵

  • SharePoint連携は、サンプルだけで完結しない前提で「橋渡し(取り込み)」を設計する
  • 取り込みは差分同期+冪等性が命。全件再処理は避ける
  • Azure AI Searchのスキーマには、早い段階でACL(allowedUsers/allowedGroups)を入れておく
  • 権限トリミングは検索時フィルターで実現し、ユーザー属性はサーバー側で確定する
  • 運用要件が出たら、ADF複製やインデックス分割などで運用性・安全性を段階的に強化する

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次