社内ドキュメント検索AIエージェントとは、仕様書・マニュアル・社内Wikiなど散在するドキュメントに対して自然言語で質問し、AIが関連情報を検索・要約して回答するシステムである。2026年現在、従来の「1回検索して1回生成する」RAGから、AIエージェントが検索結果を評価して再検索を繰り返す「Agentic RAG」へと進化している。本記事では、renueの社内ナレッジ検索プロダクトの実装知見をもとに、本番品質の社内ドキュメント検索AIを構築するための設計パターンを解説する。
社内ドキュメント検索AIが本番運用で直面する5つの課題
| 課題 | 内容 |
|---|---|
| 1. ファイルパス解決 | DBに相対パス、実ファイルは環境ごとに異なる場所 |
| 2. 環境差異 | ローカル/Azure/AWSで異なるストレージ形態 |
| 3. 大規模ドキュメント | 1ファイルが80,000文字を超えるとトークン制限に当たる |
| 4. マルチフォーマット | PDF/Word(.doc/.docx)/Excel/画像混在 |
| 5. 精度と速度のトレードオフ | Agentic RAGは精度高いが検索コストが大きい |
本番品質に必要な6レイヤー
レイヤー1: Agentic RAGの設計
従来のRAGは「クエリ→ベクトル検索→コンテキストに追加→LLM回答」の1パス構成だった。Agentic RAGは以下のループを回す。
- ユーザーの質問を受け取る
- 検索クエリを生成(LLM)
- ドキュメントを検索(ベクトル検索 or メタデータ検索)
- 検索結果を評価(LLM) → 不十分なら再検索
- 十分な情報が集まったら回答生成
Agentic RAGのメリット
- 質問が曖昧でも自動的に検索クエリを最適化
- 1回の検索で情報が不足しても追加検索する
- 複数のデータソース(ベクトルDB/メタデータDB/外部API)を組み合わせられる
- 検索結果の品質評価ができる
レイヤー2: ファイルパス解決の設計
本番運用で最も厄介なのは「DBに保存されているファイルパスと、実ファイルの置き場所が異なる」問題である。これを解決する`PathResolver`パターンを実装する。
PathResolverの設計
class PathResolver:
def __init__(self):
self.local_base_path = os.getenv("LOCAL_FILE_BASE_PATH")
self.azure_base_path = os.getenv("AZURE_FILE_BASE_PATH")
self.use_azure = os.getenv("USE_AZURE_FILES") == "true"
# DB保存の相対パスを実ファイルの絶対パスに変換
def resolve_file_path(self, relative_path):
base = self.azure_base_path if self.use_azure else self.local_base_path
return os.path.join(base, relative_path)
環境変数による切替
# ローカル環境(.env.local)
LOCAL_FILE_BASE_PATH=/Users/dev/Box/project_documents
USE_AZURE_FILES=false
# Azure環境(.env.azure)
AZURE_FILE_BASE_PATH=/mnt/azure-files/project
USE_AZURE_FILES=true
同じコードがローカル開発環境とAzure本番環境の両方で動作する。DB内の相対パスは環境非依存で保持される。
レイヤー3: Azure Files / Azure Blob Storageの選択
Azure環境では2つの選択肢がある。それぞれ特性が異なる。
Azure Files(SMB/NFSマウント)
- App Serviceにファイル共有としてマウント可能
- 既存のローカルファイル操作コードをそのまま使える
- 設定: Azure Portal → App Service → 構成 → パスマッピング → 新しいAzure Storageマウント
- デメリット: 大容量ファイルの読み取りがやや遅い
Azure Blob Storage(API経由)
- より高速・安価・スケーラブル
- 接続文字列とコンテナー名で管理
- アプリケーションコードでBlob APIを呼び出す必要がある
- 大容量・大量ファイル向け
選択基準
ファイル数が1,000件以下、サイズが100MB以下程度ならAzure Files。それ以上の規模、または大量の読み取りが発生する場合はBlob Storageを選択する。renueの実装では両方に対応できるよう、`PathResolver`を抽象化している。
レイヤー4: マルチフォーマット対応
社内ドキュメントは多様なフォーマットで存在する。それぞれに対応するパーサーが必要。
| フォーマット | 推奨ライブラリ | 備考 |
|---|---|---|
| PyMuPDF (pymupdf) | 表/画像も扱える | |
| Word (.docx) | python-docx | 標準的な.docx |
| Word (.doc) | antiword | 古い.doc用、別途brew install必要 |
| Excel (.xlsx) | openpyxl / pandas | セル単位の抽出 |
| 画像(OCR必要) | Azure Computer Vision / Gemini Vision | 図面・スキャンPDF |
| PowerPoint | python-pptx | スライド単位の抽出 |
`.doc`対応の罠
古い`.doc`ファイル(Word 97-2003)は`python-docx`で読めない。`antiword`を別途インストールする必要があり、Macなら`brew install antiword`、Linuxなら`apt-get install antiword`が必要。Dockerイメージにも含める必要があるため、Dockerfileに明示的に記載する。
レイヤー5: トークン制限とマルチエージェント分割
1つのドキュメントが80,000文字を超えると、LLMのコンテキスト制限に当たる。これを回避するには、処理を複数のエージェントに分割する。
マルチエージェント分割パターン
- 検索エージェント: クエリから関連ドキュメントを特定する
- 要約エージェント: 長いドキュメントをチャンク単位で要約する
- 統合エージェント: 複数の要約を統合して最終回答を生成する
- 評価エージェント: 回答の品質をチェックし、不十分なら再検索を指示する
各エージェントは独立した状態管理を持ち、互いにメッセージを受け渡す。OpenAI Agents SDKや Google ADK, LangGraphなどのフレームワークでこのパターンを実装できる。
レイヤー6: メタデータDBの設計
ベクトル検索だけでは「このドキュメントはどのフォルダにあるか」「いつ更新されたか」などの属性情報を活かせない。メタデータDB(PostgreSQL等)と組み合わせるハイブリッド検索が効果的である。
推奨スキーマ
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
file_path TEXT NOT NULL, -- DB内相対パス
file_name TEXT NOT NULL,
file_type VARCHAR(20), -- pdf/doc/docx/xlsx
folder_path TEXT, -- 部署別/プロジェクト別の階層
document_type VARCHAR(50), -- 仕様書/マニュアル/報告書/議事録
created_at TIMESTAMP,
updated_at TIMESTAMP,
metadata JSONB, -- 自由な追加属性
embedding VECTOR(1536) -- pgvector拡張
);
ハイブリッド検索のクエリ例
- 「資材部の仕様書で2024年以降の文書」→ folder_path + document_type + updated_at で絞り込み
- 「〇〇工事に関する書類」→ ベクトル検索で関連度順
- 「先月更新された仕様書」→ updated_at + document_type で絞り込み
PostgreSQLなら`pgvector`拡張でベクトル検索を同じDBで実装できるため、システム構成がシンプルになる。
LLMプロバイダーの切替対応
本番運用では、LLMプロバイダーを固定せず切替可能にしておく。OpenAI、Anthropic Claude、Google Gemini、Azure OpenAIなどを環境変数で切り替えられる設計が望ましい。
# 環境変数
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=...
LLM_PROVIDER=anthropic # openai / anthropic / azure
プロバイダー切替をコード1行で済ませるには、抽象化レイヤー(`LLMClient`)を実装する。
ドキュメント更新検知の仕組み
ドキュメントが更新された際に、ベクトルDBのインデックスを再構築する必要がある。以下のパターンが一般的。
定期バッチ方式
夜間バッチで全ドキュメントをスキャンし、`updated_at`が変わったものだけを再インデックス化する。シンプルだが更新反映に時間がかかる。
ファイル監視方式
ファイルシステムの変更イベントを監視し、リアルタイムで再インデックス化する。Linuxなら`inotify`、Azure Filesなら`Event Grid`を使う。
Webhook方式
ドキュメント管理システム(SharePoint、Box等)のWebhookを利用する。最もリアルタイム性が高い。
Streamlitでの実装上の注意点
社内向けの簡易UIはStreamlitで構築するケースが多い。ただし以下の制約に注意が必要。
ファイル名の重複禁止
Streamlitはファイル名からURLパスを生成するため、同じファイル名は使えない。`34_問い合わせ対応_マルチエージェント版.py`のように番号プレフィックスで一意化する。
Expanderのネスト不可
`st.expander`の中に`st.expander`を入れることはできない。代わりにマークダウンヘッダーや`st.divider()`を使う。
セッション状態の管理
マルチエージェントの状態を`st.session_state`で管理する。エージェント間のメッセージ履歴もセッション状態に保持することで、再読み込み時の状態維持ができる。
renueの実装事例 — 社内ナレッジ検索の本番運用
renueは「Self-DX First」の方針のもと、自社・クライアント向けに社内ドキュメント検索AIの実装知見を蓄積している。社内12業務を553のAIツールで自動化済み(2026年1月時点)であり、ドキュメント検索AIもその一部である(全て公開情報)。
公開されている技術的特徴
- バックエンド: Python + FastAPI or Streamlit
- データベース: PostgreSQL + pgvector
- LLMプロバイダー: OpenAI / Anthropic Claude 切替対応
- ファイルストレージ: ローカル / Azure Files / Azure Blob Storage 切替対応
- マルチエージェント: 検索・要約・統合・評価の分業
- フォーマット: PDF / Word (.doc/.docx) / Excel 対応
- デプロイ: Azure App Service + Docker
導入時のよくある失敗パターン
- ファイルパスをハードコード: 本番環境で動かない
- 単一の長大プロンプトで実装: トークン制限で失敗
- ベクトル検索のみに依存: メタデータ検索と組み合わせないと精度が出ない
- .doc対応を忘れる: 古い仕様書が読めない
- LLMプロバイダーを固定: API障害時に動かなくなる
- 更新検知の仕組みがない: 古い情報を返し続ける
- エラーの客観的事実を記録しない: 再現・デバッグができない
業界別の活用パターン
| 業界 | 主な活用ドキュメント |
|---|---|
| 製造業 | 設計仕様書、工程手順書、品質管理マニュアル |
| 金融 | 商品説明書、規程類、稟議書、コンプライアンス資料 |
| 法務 | 契約書、判例、法令、規則 |
| 医療 | 診療プロトコル、薬剤情報、症例報告 |
| 建設 | 設計図、施工要領、安全管理規程 |
| 電力・インフラ | 運用マニュアル、保守手順、仕様書(購入/工事/委託) |
よくある質問
Agentic RAGと従来のRAGの違いは?
従来のRAGは「1回検索して1回生成」の固定フロー。Agentic RAGはAIエージェントが検索結果を評価し、不十分なら別クエリで再検索する。精度は上がるが、LLM呼び出し回数が増えるためコストも上がる。
PostgreSQLとVector DB(Pinecone等)、どちらを選ぶべき?
文書数が100万件以下ならPostgreSQL+pgvectorで十分。構成がシンプルで運用が楽。1,000万件を超える規模、または極めて低レイテンシが必要な場合は専用Vector DBを検討する。
日本語PDFの抽出精度は?
PyMuPDFで概ね90%以上の精度で抽出できる。ただし、スキャンPDFや図表が多いPDFはOCRが必要になる。Azure Computer VisionまたはGemini Visionを使うと、高精度な日本語OCRが可能。
ローカルとAzureの切替はどうやる?
環境変数`USE_AZURE_FILES`と`PathResolver`クラスで切替可能にする。コードレベルでは`if`分岐を最小化し、環境変数読み込みだけで自動切替できる設計にする。
社員が使わない問題はどう解決する?
「検索しても出てこない」体験が1〜2回続くと使われなくなる。導入初期は検索精度を徹底的にチューニングし、FAQやよくある検索クエリを別途準備しておく。Slack等の既存ワークフローに統合すると利用率が上がる。
