シンプルなRAGシステムをリリースした経験があれば、そのパターンはご存じでしょう。ドキュメントをチャンクに分け、埋め込みを生成してベクトルDBに保存し、クエリに対して上位k件を検索してプロンプトに詰め込みます。デモでは機能しますが、本番環境では期待外れになることがよくあります。
デモ用RAGと本番環境向けRAGの隔たりは大きなものです。実際のドキュメントは整っていません。実際のクエリは曖昧です。クエリの種類によって品質が大きく変動します。コストとレイテンシーは現実の制約です。更新とバージョン管理も重要です。
本記事では、本番品質のRAGパイプラインがどのようなものかを解説します。6つの段階、各段階のパターン、そして機能するシステムと期待外れのシステムを分ける評価の規律を取り上げます。
パイプライン
本番環境向けRAGシステムには、6つの論理的な段階があります。
[ドキュメント]
↓
1. 取り込み(解析、クリーニング、メタデータ抽出)
↓
2. チャンク分割(検索単位への分割)
↓
3. 埋め込み(ベクトル+ストレージ)
↓
[クエリ]
↓
4. 検索(セマンティック+字句+フィルター)
↓
5. 再ランキング(上位k件 → 上位N件)
↓
6. 生成(LLM+コンテキスト)
↓
[応答]
各段階には、それぞれ品質上の注意点があります。どの段階を改善しても、システム全体が改善します。
順に見ていきましょう。
第1段階:取り込み
ごみを入れれば、ごみしか出てきません。取り込みの品質が、後続処理すべての品質上限を決めます。
遭遇するソースの種類:
- PDF(ほとんどの場合、最も扱いにくい)。
- Wordドキュメント。
- HTMLページ。
- Markdown。
- スプレッドシート。
- スライド(PowerPoint、Google Slides)。
- メール。
- コード。
- 構造化データ(CSV、JSON)。
それぞれに固有の解析上の課題があります。
PDFの解析。 PDFはデータ形式ではなく表示形式です。解析が難しいことで知られています。戦略は次のとおりです。
- テキストベースのPDF:
pdfplumber、pymupdf、unstructured。整ったテキストに有効です。 - スキャンされたPDF: Tesseract、Google Cloud Vision、またはAWS TextractによるOCR。
- レイアウト認識: 複雑なレイアウト(表、複数段組)には
LayoutLM、Mathpix、またはLLMベースの解析(GPT-4 vision)。
難しいPDFに対する現代的なアプローチは、視覚機能を備えたLLMを使ってOCRとコンテンツの構造化を行うことです。コストは高くなりますが、従来のOCRより品質が劇的に向上します。
表の扱い。 非構造化テキスト内の表は厄介です。次のいずれかを選びます。
- Markdownの表に変換する(構造を保持)。
- 文章に展開する(「行1:顧客Aの注文は50件でした…」)。
- 構造化スキーマを持つ独立した検索単位として扱う。
適切な選択肢は、想定するクエリによって異なります。
メタデータの抽出。 各ドキュメントには重要なメタデータがあります。
- タイトル、著者、日付、バージョン。
- トピック、カテゴリー、タグ。
- ソースURLまたは場所。
- 権限/可視性。
取り込み時に取得してください。検索時のフィルターパラメーターになります。
クリーニング。 定型要素を取り除きます。
- 各ページで繰り返されるヘッダー/フッター。
- ナビゲーション、広告、Cookieバナー。
- 「目次」ページ。
- 空または重複した段落。
コーパスがきれいであるほど検索品質は高まります。定型要素は誤一致の原因になります。
品質チェック。
- パーサーは実際にテキストを抽出できたか(一部のPDFは空文字列を返します)。
- エンコーディングの問題はないか。
- 表は保持されているか。
- 図にキャプションが付いているか、それとも省略されているか。
取り込みパイプラインに健全性チェックを組み込んでください。壊れた解析結果でインデックスが汚染される前に検出します。
第2段階:チャンク分割
きれいなドキュメントが用意できました。次に、それを検索単位(チャンク)に分割します。
根本的なトレードオフ:
- 小さいチャンク:検索は正確(質問に焦点が合う)ですが、コンテキストが不足します。
- 大きいチャンク:コンテキストは増えますが、精度が下がります(関連箇所が埋もれます)。
どちらの極端な方法でも品質が低下します。最適な範囲はコンテンツの種類によって異なります。
一般的な戦略:
重複を含む固定サイズのチャンク。 Nトークンのチャンク(300~800トークン)に、50~100トークンの重複を持たせて分割します。シンプルで、ベースラインとして機能します。
文を考慮したチャンク分割。 文の境界で分割します(nltk、spaCyなどを使用)。文の途中での分割を避けられます。
段落ベース。 各段落を1つのチャンクにします。段落が適切に構成されたドキュメントに有効です。
階層型(小+大)。 2つのインデックスを使用します。
- 正確な検索用の小さいチャンク(300トークン)。
- コンテキスト用の大きいチャンク(1500トークン)またはセクション全体。 検索時には小さいチャンクを検索し、LLMには大きい親チャンクを渡します。
ドキュメント構造認識。 ドキュメントの構造(見出し、セクション)を使ってチャンクを決めます。各セクションを1つのチャンクとし、階層を保持します。
セマンティックチャンク分割。 埋め込みを使って自然な区切り(トピックが変わる箇所)を見つけます。コストは高くなりますが、一部のコンテンツではより良いチャンクを作れます。
LLMによる要約チャンク分割。 長いドキュメントを複数レベル(段落要約、セクション要約、ドキュメント要約)の階層的なチャンクに要約します。LLMが取り込み時に一度だけ生成します。
適切な戦略はコンテンツによって異なります。記事とWikiには段落ベースまたは階層型、コードには関数単位、会話にはターン単位、技術文書には構造認識型が適しています。
チャンクのメタデータ。 各チャンクには次の情報を持たせます。
- ソースドキュメントのIDとURL。
- セクションパス(章 > セクション > サブセクション)。
- ページ番号(引用用)。
- このチャンクより上位の見出し(コンテキスト用)。
- ドキュメントレベルのメタデータ(日付、著者、種類)。
このメタデータにより、最終応答でのフィルタリングと引用が可能になります。
第3段階:埋め込み
チャンクをベクトルに変換します。
埋め込みモデルの選択。
2026年時点:
- OpenAI text-embedding-3-large: 万能で高性能ですが、高価です。
- Voyage Voyage-3: 競争力があり、技術コンテンツでは優れることがよくあります。
- Cohere embed-v4: 多言語に強みがあります。
- BAAI bge-large: 強力なオープンソースモデルです。
- Nomic embed-text: 優れたオープンソースモデルで、無料で自社運用できます。
- ドメイン特化モデル: コード向け(Voyage Code。OpenAI text-embedding-3-largeもコードに有効)、法務、医療など。
モデルを選ぶ際は、自分のドメインに対してテストしてください。リーダーボード首位のモデルが、自分のデータにも最適だと思い込まないでください。
埋め込み次元。 モデルは256~3072次元を提供します。次元が高いほど品質は上がりますが、コストとストレージも増えます。ほとんどのユースケースでは、768~1536次元が最適な範囲です。
バージョン管理。 埋め込みモデルは更新されるため、切り替えが必要になる場合があります。次の点を計画してください。
- 各ベクトルを生成した埋め込みモデルのバージョンを記録する。
- 移行時にすべてを再埋め込みする(またはデュアルインデックスで移行する)。
- 異なるモデルのベクトルを同じ検索に混在させない。
コスト。 数百万チャンクの埋め込みには費用がかかります。100万トークン当たり€0.10~€0.50です(プロバイダーにより異なります)。大規模なコーパスでは、事前に計算してください。
ストレージ。 ベクトルは大きなデータです。100万チャンク × 1536次元 × 4バイト = 6GBです。それに応じてストレージを計画してください。
第4段階:検索
クエリを受け取ったら、関連するチャンクを見つける必要があります。
ベクトル検索(密検索)。 クエリを埋め込み、最近傍を見つけます。意味的類似性を捉える標準的な方法です。
キーワード検索(BM25/字句検索)。 従来のテキスト検索です。完全一致、希少な用語、固有名詞を捉えます。多くの場合、ベクトル検索を補完します。
ハイブリッド検索。 両方を実行し、結果を融合します。Reciprocal Rank Fusion(RRF)が標準的な融合手法です。
def reciprocal_rank_fusion(rankings, k=60):
scores = {}
for ranking in rankings:
for rank, doc_id in enumerate(ranking):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
return sorted(scores.items(), key=lambda x: -x[1])
実務では、ほとんどのベンチマークでハイブリッド検索がベクトル検索のみ、またはキーワード検索のみを上回ります。デフォルトで使用してください。
メタデータフィルタリング。 スコアリング前に、メタデータで検索対象を絞り込みます。「2025年以降のドキュメントのみ」「ユーザーがアクセスできるドキュメントのみ」「ポリシー種別のドキュメントのみ」などです。
マルチテナントシステムでは極めて重要です。すべてのクエリを、そのテナントがアクセスできるドキュメントの範囲に限定します。
クエリ理解。 クエリを前処理します。
- スペル修正。 「エンジニアニグ」→「エンジニアリング」。
- 頭字語の展開。 「MFA」→「Multi-Factor Authentication MFA」。
- 同義語/クエリ拡張。 別の表現を生成し、それぞれで検索します。
- 分解。 複数部分からなる質問をサブクエリに分け、それぞれを個別に検索します。
これらの前処理により、実際のクエリに対する検索品質が大幅に向上します。
HyDE(Hypothetical Document Embeddings)。 LLMで架空の「理想的な回答」を生成し、その埋め込みを作成して検索クエリとして使います。架空の回答は実際の回答ドキュメントに近いため、質問を直接埋め込むよりうまく機能することがよくあります。
def hyde_retrieve(query, k=20):
hypothetical = llm("次の質問に答える文章を書いてください: " + query)
embedding = embed(hypothetical)
return vector_search(embedding, k=k)
トレードオフは、クエリごとにLLM呼び出しが1回増えること(レイテンシー、コスト)です。難しいクエリには価値がありますが、単純なクエリには過剰です。
マルチベクトル検索。 チャンクごとに1つではなく、複数のベクトルを使用します(たとえば、チャンク用、要約用、仮想質問用に各1つ)。ストレージと複雑さは増しますが、一部のコンテンツでは検索品質が向上します。
第5段階:再ランキング
最初の検索(上位k件、k=20~50)の後、再ランキングモデルを使って候補をより慎重にスコアリングします。
なぜ再ランキングするのか。
最初の検索は高速ですが、精度が高くありません。ベクトル類似度は関連性の大まかな代替指標にすぎません。再ランキングモデル(通常はクロスエンコーダーモデル)は、クエリと各候補を一緒に読み、より正確なスコアを出します。
再ランキングモデルの選択肢:
- Cohere Rerank。 業界標準です。高品質なホステッドサービスです。
- Voyage Rerank。 強力な競合製品です。
- BGE Rerank-large。 オープンソースで、無料で自社運用できます。
- MiniLMクロスエンコーダー。 小型で高速ですが、品質は低めです。
- LLMベースの再ランキング。 小型LLMでクエリと候補のペアをスコアリングします。最高品質ですが、コストも最高です。
効果。
再ランキングを追加すると、ほとんどのRAGシステムで通常は最終タスクの品質が向上します。よく引用される10~20%という範囲は計画上の推定値として扱い、自分の評価で検証してください。レイテンシーとコストをかける価値は、ほぼ常にあります。
一般的な構成:
- ハイブリッド検索で30~50件の候補を取得します。
- 上位5~10件に再ランキングします。
- 上位の結果をLLMに渡します。
適応型再ランキング。
最初の検索結果の最上位が高いベクトル類似度を持つクエリ(明確な一致)では、再ランキングを省きます。スコアが近いクエリ(複数候補が同点に近い)では、積極的に再ランキングします。
第6段階:生成
LLMが検索済みのコンテキストを使って回答を生成します。
プロンプト構造:
あなたは、提供されたコンテキストに基づいて質問に答えるアシスタントです。
コンテキスト:
[ドキュメント1 - タイトル、URL]
[チャンク1の内容]
[ドキュメント2 - タイトル、URL]
[チャンク2の内容]
...
ユーザーの質問: {query}
指示:
- 提供されたコンテキストのみに基づいて回答してください。
- コンテキストに回答が含まれていない場合は、その旨を伝えてください。情報を捏造しないでください。
- [出典N]の表記を使って出典を引用してください。
- 簡潔かつ漏れなく回答してください。
重要なプロンプトパターンは次のとおりです。
- ソースのタグ付け。 各コンテキストチャンクに引用用のソースを付けます。
- 根拠付けの指示。 コンテキストだけを使うようモデルに明示します。
- 引用の要件。 引用を必須にします。ハルシネーションを検出できます。
- フォールバックの指示。 「コンテキストに回答がない場合は、その旨を伝える」と指示します。作話を防げます。
引用の扱い。
一般的なパターンは、応答にソースリンクを含めることです。UIがクリック可能なリンクとして表示します。
「会社のリモートワークポリシーでは、週に最大4日まで在宅勤務が認められています[1]。
[1]: https://wiki.company.com/policies/remote-work」
これにより、応答を検証可能にできます。ユーザーは、根拠のある回答をより信頼します。
コンテキストサイズの管理。
長いコンテキストは品質を低下させます。ほとんどのモデルは、すべてを詰め込むよりも、関連性の高い5~10個のチャンクに絞ったコンテキストで最もよく機能します。コンテキストの肥大化に伴って品質が低下します。
大量のコンテキストを含める必要がある場合は、関連性の低い項目を全文で入れず、要約してください。
各段階で評価する
測定できないものは最適化できません。各段階に固有の評価が必要です。
取り込みの評価。 ドキュメントを正しく解析できたか。ドキュメントをいくつか抜き出して確認し、主要な内容が保持されているかを調べます。
チャンク分割の評価。 チャンクのサイズは適切か。コンテキストを保持しているか。妥当な境界で分割されているか。
埋め込みの評価。 類似した概念が同じように埋め込まれているか。類似していることが既知のペアと、異なることが既知のペアからなるテストセットで、コサイン類似度が期待と一致するか。
検索の評価。 クエリに対して、関連するチャンクが上位K件に含まれるか。一般的な指標はrecall@Kです(上位K件に関連チャンクが少なくとも1つ含まれるクエリの割合)。
再ランキングの評価。 検索された候補のうち、再ランキングモデルが最も関連性の高いものを先頭に置くか。指標はNDCG(normalized discounted cumulative gain)またはMRR(mean reciprocal rank)です。
生成の評価。 コンテキストとクエリを与えたとき、LLMは正しい回答を生成するか。指標は忠実性(回答がコンテキストを使っているか)、正確性(回答が正しいか)、有用性(ユーザーの意図に応えているか)です。
エンドツーエンドの評価。 実際のクエリに対して、システムは正しい回答を生成するか。最も重要であり、すべての段階に依存します。
各段階にテストセットが必要です。一般的な立ち上げ方法は次のとおりです。
- 正解が既知のクエリを50~100件作成します。
- 各クエリについて、回答を含むドキュメントを特定します。
- これを使って、検索(適切なドキュメントを取得できるか)と生成(回答が正しいか)を評価します。
ツールにはRagas、TruLens、独自のテストスイートがあります。どのツールを使うかより、評価を実行することの方が重要です。
運用上の考慮事項
本番環境の現実をいくつか挙げます。
インデックス作成パイプライン。 新しいドキュメントが届きます。再埋め込みし、インデックスを更新し、削除と更新を処理します。このパイプラインはセットアップ時に一度だけではなく、継続的に動作します。一度限りのスクリプトではなく、システムとして構築してください。
レイテンシー。
一般的な内訳:
- 埋め込み(クエリ): 50~200ms。
- ベクトル検索:50~200ms。
- 再ランキング:200~500ms(Kによる)。
- 生成:1~5秒(コンテキストとモデルによる)。
- 合計:1.5~6秒。
最適化:
- 頻出クエリの埋め込みをキャッシュします。
- 繰り返されるクエリと候補のペアに対する再ランキングをキャッシュします。
- 生成をストリーミングします。
- 可能な箇所を並列化します。
レイテンシーを2秒未満にするには、通常、高速な埋め込みモデル、高速なベクトルDBが必要です。さらに、高速な再ランキングモデルを使うか、キャッシュ済みまたは単純なクエリでは再ランキングを省く必要があります。
コスト。
クエリ当たりのコスト:
- 埋め込み:約€0.0001。
- ベクトル検索:約€0.0001(インフラに依存)。
- 再ランキング:€0.001~0.01(モデルに依存)。
- 生成:€0.005~0.05(モデルとコンテキストに依存)。
- 合計:クエリ当たり約€0.01~0.05。
規模が大きくなると積み上がります。100万クエリで€10K~50Kです。
コスト最適化:
- キャッシュする。
- 品質が許す範囲で安価なモデルを使う。
- コンテキストを圧縮する(完全なチャンクではなく要約)。
更新。 ドキュメントは変わります。パターンは次のとおりです。
- バージョン管理: すべてのドキュメントにバージョンを付け、ポリシーに基づいて旧版を保持または削除します。
- 増分処理: 新しいバージョンを再チャンク分割、再埋め込みし、古いチャンクを削除します。
- 差分認識: 変更されたセクションだけを再処理します。
更新頻度が高いシナリオでは、これは特に重要です。
権限。 非公開データを対象とするRAGでは、ドキュメントにアクセス制御があり、検索でもそれを尊重する必要があります。
- メタデータベースのフィルタリング(各チャンクにaccessible-byメタデータを持たせる)。
- 強固な分離のため、テナントごとにインデックスを分離する。
- 誰が何にアクセスしたかを監査ログに記録する。
権限の適用をLLMに任せないでください。検索時に強制してください。
よくある失敗パターン
失敗するRAGシステムで見られるパターンをいくつか挙げます。
失敗1:不適切なチャンク分割。 考えの途中でチャンクが分かれる、表が複数のチャンクに分断される、見出しとその内容が分離される。修正方法:より良いチャンク分割戦略を採用します。
失敗2:検索で回答を見つけられない。 関連チャンクは存在するのに検索されません。クエリとドキュメントの不一致が原因であることがよくあります。修正方法:HyDE、クエリ拡張、より良い埋め込みを使います。
失敗3:正しいチャンクだが、順序が誤っている。 関連チャンクは検索されていますが、順位が低くなっています。LLMは上位の無関係なチャンクを使います。修正方法:再ランキングします。
失敗4:正しいチャンクだが、ハルシネーションがある。 チャンクは正しくても、LLMが追加情報を捏造します。修正方法:より厳格な根拠付けプロンプト、引用の必須化、temperatureの引き下げを行います。
失敗5:正しいチャンクだが、回答が誤っている。 チャンクには回答が含まれているものの、LLMが誤った箇所を抽出します。修正方法:生成プロンプトを改善し、必要であればより大きなモデルを使います。
失敗6:データが古い。 インデックスが更新されていません。修正方法:継続的な取り込みパイプラインを構築します。
失敗7:権限を越えた情報漏洩。 ユーザーに、本来見られないチャンクが表示されます。修正方法:検索時にフィルタリングを強制し、監査します。
失敗8:コストの急増。 コーパスとトラフィックの増加に伴って、レイテンシーとコストが増大しました。修正方法:キャッシュ、モデルルーティング、場合によってはアーキテクチャ変更を行います。
実例:社内ナレッジベースRAG
具体例として、一般的な社内ナレッジベースRAGシステムを見てみましょう。
コーパス: 約50,000件のドキュメント(Wikiページ、ポリシー、ランブック、会議メモ、Slackスレッド)。
パイプライン:
-
取り込み:
- Notion: APIエクスポート。
- Slack: アーカイブをエクスポートし、関連チャンネルに絞り込み。
- Google Drive: 承認済みフォルダーをAPIでエクスポート。
- クリーニング:絵文字だけのメッセージ、定型の署名を削除。
-
チャンク分割:
- 階層型:段落レベルの小さいチャンクと、セクションレベルの親チャンク。
- 小さいチャンクは合計約250K件。
- メタデータ:source、section path、last_updated、accessible_to。
-
埋め込み:
- Voyage-3の埋め込み、1024次元。
- Pinecone(マネージド)に保存。
- 変更されたドキュメントを毎週再埋め込み。
-
検索:
- ハイブリッド:ベクトル検索+BM25(Pineconeのハイブリッド機能を使用)。
- クエリにHyDEを使用。
- アクセス可能性のメタデータフィルター。
- 上位30件を取得。
-
再ランキング:
- Cohere Rerank。
- 上位30件 → 上位8件。
-
生成:
- Claude Sonnet 5。
- 上位8件の親チャンク(小さいチャンクではない)をコンテキストとして提供。
- 応答で引用を必須化。
評価:
- 手作業で作成したクエリと回答のペア100件。
- 検索のrecall@30: 約92%。
- 最終回答の正確性:約85%。
- 忠実性(ハルシネーションなし): 約95%。
運用:
- 毎日の増分取り込み。
- 変更されたドキュメントを毎週すべて再埋め込み。
- クエリごとの可観測性。
- 毎月の評価再実行と傾向監視。
- 四半期ごとにクエリログを確認し、新しい失敗パターンを発見。
コスト:
- 埋め込み:約€500/月(定常状態)。
- ベクトルDBホスティング:約€800/月(Pinecone)。
- クエリ当たりの検索+生成:約€0.02。
- 合計:約€2,500/月+€0.02 × クエリ量。
ほとんどの企業にとって、生産性向上によって十分に正当化できる金額です。
90日間のRAG構築計画
ゼロから始める場合は、次のように進めます。
1~30日目:MVP。
- コーパスと主要なユースケースを特定する。
- 基本的な取り込みを構築する(1つのソース)。
- 基本的なチャンク分割(重複を含む固定サイズ)。
- 標準的な埋め込みモデル。
- ベクトル検索のみ(再ランキングなし)。
- シンプルな生成プロンプト。
31~60日目:品質。
- 評価セットを手作業で作成する(50~100件のクエリ)。
- 失敗パターンを特定する。
- 再ランキングを追加する。
- ハイブリッド検索を追加する。
- チャンク分割を改善する。
61~90日目:運用。
- 継続的な取り込みパイプライン。
- 可観測性。
- 評価の自動化。
- 権限/認証。
- コスト監視。
90日後には、デモできるだけでなく、実際に使えるシステムが完成します。実際のユーザーが信頼して利用できます。
品質はすべての段階に宿る
本番環境向けRAGは、すべての段階に品質上の注意点がある6段階のパイプラインです。機能するシステムと期待外れのシステムを分けるパターンは次のとおりです。
- 取り込み: 徹底した解析、構造の保持、メタデータの取得。
- チャンク分割: コンテンツの種類に適した戦略。多くの場合は階層型。
- 埋め込み: 高品質なモデル、バージョン管理、移行計画。
- 検索: デフォルトでハイブリッド、クエリ理解、メタデータフィルタリング。
- 再ランキング: ほぼ常に導入する価値があります。
- 生成: 根拠付け、引用、不明な場合のフォールバック。
- 評価: すべての段階で継続的に実施。
どれも省略できません。回答品質60%のRAGと90%のRAGの差は、こうした細部にあります。
必要な投資は小さくありません。本番環境向けRAGの構築には数週間ではなく数か月かかります。その見返りは、ユーザーが実際に信頼できるシステムです。重要なのは、その水準に到達することです。



