社内の情報は複数のツールに分散し、「どこに何があるか分からない」が生産性を蝕んでいます。ConfluenceとNotion、SharePoint、Google Driveを横断して検索できるRAGは、この課題に対する現在もっとも実用的な解決策です。本記事では社内ドキュメント検索RAGのアーキテクチャ、データソース連携の実装、アクセス制御の組み込み、段階的な導入ロードマップを実務レベルで解説します。小さく始めて大きく育てるアプローチが王道です。

社内ドキュメント検索の現状と課題

多くの企業では、社内の知識がConfluence・Notion・SharePoint・Google Drive・社内Wiki・共有フォルダといった複数のツールに散在しています。それぞれに検索機能はあるものの、ツール横断での統一検索はできず、表現のゆれや同義語にも弱いため、「欲しい情報が見つからない」「古い資料を使ってしまう」といった声が絶えません。

RAGはこの課題に対して、複数ソースを横断的にインデックス化し、意味的類似で検索した結果を自然言語で要約して返すことで、ユーザーが「欲しい情報」に最短距離で辿り着ける体験を提供します。さらに、出典付きで回答することにより「どの文書のどこに書かれていたか」を常に提示でき、信頼性の高いナレッジアクセスが実現します。

システムアーキテクチャの設計

社内文書RAGの標準的なアーキテクチャは、「データソース連携層」「前処理・インデックス層」「検索層」「生成層」「UI層」の5層で構成されます。各層を疎結合に保ち、ソース追加やモデル差し替えが容易な設計にしておくことが長期運用の鍵です。

【社内ドキュメントRAGの全体アーキテクチャ】

[Confluence] [Notion] [SharePoint] [GDrive]
     |           |           |          |
     +-----------+-----------+----------+
                 |
                 v
        [統一コネクタ層]
                 |
                 v
   [前処理: 正規化 + チャンキング + メタデータ]
                 |
                 v
       [エンベディング / 格納]
                 |
                 v
           [ベクトルDB]
                 |
     +-----------+-----------+
     |                       |
     v                       v
 [検索 + リランク]     [権限フィルタ]
     |                       |
     +-----------+-----------+
                 |
                 v
           [LLMで回答生成]
                 |
                 v
       [Web UI / Slack Bot]

※ 各層を疎結合にし、ソース追加・モデル差替を容易に
ソース連携方法API/コネクタ更新頻度注意点
ConfluenceREST APIatlassian-python-api増分同期スペース単位のアクセス権
NotionNotion APInotion-client増分同期ブロック構造の分解
SharePointMicrosoft Graph APImsgraph-sdk-python増分同期サイト・ライブラリ階層
Google DriveGoogle Drive APIgoogle-api-python-client増分同期ファイル共有設定
SlackSlack Web APIslack_sdkリアルタイムプライベートチャンネル
Box / Dropbox各API公式SDK増分同期大容量ファイル

アーキテクチャ設計のポイントは、「コネクタを抽象化したインターフェースにまとめる」ことです。各ソース固有の実装差を上位層に見せない設計にしておけば、将来新しいソースを追加する際にも最小限の改修で済みます。

データソース別の連携実装

実装の最初の壁は各ソースのAPI連携です。代表的なConfluence連携の例と、LlamaIndexで複数ソースを統合する例を示します。

# Confluence APIからページを取得してチャンキング
from atlassian import Confluence
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document

conf = Confluence(
    url="https://your-domain.atlassian.net/wiki",
    username="api-user@example.com",
    password="API_TOKEN"
)

pages = conf.get_all_pages_from_space(space="ENG", limit=100, expand="body.storage,version")

splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=80)
all_chunks = []
for p in pages:
    text = p["body"]["storage"]["value"]
    meta = {
        "source": "confluence",
        "page_id": p["id"],
        "title": p["title"],
        "url": f"https://your-domain.atlassian.net/wiki{p['_links']['webui']}",
        "updated_at": p["version"]["when"],
    }
    for chunk in splitter.split_text(text):
        all_chunks.append(Document(page_content=chunk, metadata=meta))

print(f"生成チャンク数: {len(all_chunks)}")
# LlamaIndexでマルチソースRAGを構築
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.readers.confluence import ConfluenceReader
from llama_index.readers.notion import NotionPageReader
from llama_index.readers.microsoft_sharepoint import SharePointReader
from llama_index.vector_stores.qdrant import QdrantVectorStore
import qdrant_client

confluence_docs = ConfluenceReader(
    base_url="https://your-domain.atlassian.net/wiki"
).load_data(space_key="ENG")

notion_docs = NotionPageReader(
    integration_token="secret_xxx"
).load_data(database_id="db_id")

sharepoint_docs = SharePointReader(
    client_id="...", client_secret="...", tenant_id="..."
).load_data(sharepoint_site_name="Intranet", sharepoint_folder_path="Docs")

all_docs = confluence_docs + notion_docs + sharepoint_docs

client = qdrant_client.QdrantClient(path="qdrant_data")
vector_store = QdrantVectorStore(client=client, collection_name="company_docs")
storage = StorageContext.from_defaults(vector_store=vector_store)

index = VectorStoreIndex.from_documents(all_docs, storage_context=storage)
query_engine = index.as_query_engine(similarity_top_k=5)
print(query_engine.query("出張精算の締切日は?"))

アクセス制御の実装

社内文書RAGで最も慎重な設計が求められるのがアクセス制御です。元のソースにアクセス権限がなかった文書まで検索結果に出てくるようだと、情報漏えい事故につながります。原則として「ソース側の権限をRAGの検索結果にも反映する」設計が必須です。

パターン仕組みメリットデメリット実装難易度
インデックス時フィルタ権限をメタデータに格納し、検索時にクエリで絞り込む実装簡単・高速権限更新時の同期必要
ポストフィルタ検索後に現在の権限でフィルタ常に最新の権限遅延・ヒット件数減
ユーザー別コレクションユーザーまたはロール単位でインデックスを分ける漏洩リスク極小ストレージコスト増
オンデマンド検索検索時にソースAPIを呼び出し権限で取得完全性レイテンシ大

推奨は「インデックス時フィルタ + ポストフィルタ」のハイブリッドです。インデックス時に文書ごとのアクセス許可グループを保存し、検索時にユーザーが属するグループでフィルタします。重要文書には追加でポストフィルタを組み合わせ、直近の権限変更も拾えるようにしておくと安心です。

構築から本番化までのロードマップ

社内文書RAGは段階的な展開が失敗を減らします。Phase 1では単一ソース(例: Confluenceのみ)と限定ユーザー(IT部門等)で立ち上げ、フィードバックを集めて精度と使い勝手を検証します。この段階では派手なUIよりも、検索精度と出典表示の誠実さを優先しましょう。

Phase 2では対象ソースをNotionやSharePointに拡張し、アクセス制御を本格的に組み込みます。同時に評価データセットを整備し、定量的な品質モニタリング基盤を作ります。Phase 3では全社展開、Slack/Teamsとの統合、フィードバックループの運用体制整備、SLA監視まで含めた本格運用を目指します。最初から全社展開を狙うのではなく、この3段階を半年〜1年かけて進める方が、結果として早く安定稼働に辿り着きます。

まとめ――社内検索のAI化は「小さく始めて大きく育てる」

  • 社内文書RAGは複数ソースの統一検索と出典付き回答で業務生産性を高める
  • アーキテクチャは5層構成で、コネクタ抽象化と疎結合が長期運用の鍵
  • アクセス制御はインデックス時フィルタとポストフィルタのハイブリッドが実務的
  • 展開は単一ソース→複数ソース→全社という3段階が失敗を減らす

DE-STKでは社内文書RAGの要件定義、コネクタ実装、アクセス制御設計、全社展開の伴走支援を提供しています。既存の情報散在に悩む組織はぜひご相談ください。

よくある質問(FAQ)

Q. 社内ドキュメント検索のRAG構築にはどのくらいかかりますか?

単一ソース(Confluenceのみ等)のプロトタイプなら2〜4週間、複数ソース対応とアクセス制御の本番システムなら2〜4ヶ月が目安です。文書データの整備状況により大きく変動します。特に既存文書の重複や命名の揺れが多い場合は、前処理の設計に想定以上の時間がかかる点を見込んでおくと良いでしょう。

Q. 社内の機密文書もRAGに含めて大丈夫ですか?

アクセス制御の実装が前提です。ソース側の権限をRAGに反映し、ユーザーがアクセス権を持つ文書のみ検索結果に含める仕組みが必要です。加えて、LLM APIへのデータ送信ポリシーの確認と、必要に応じたオンプレやVPC環境での運用を検討してください。機密度の高い情報はVPC対応のLLM構成で扱う選択肢が有効です。

Q. 既存の社内検索ツールとRAGの違いは?

既存のツールはキーワードマッチが中心で、表現の揺れや同義語への対応が困難です。RAGはセマンティック検索で「意味的に近い」文書を検索し、さらにLLMが質問に対する直接的な回答を生成するため、情報の発見効率が大幅に向上します。出典付き回答によって信頼性と説明責任も担保しやすくなります。