
RAG の PoC は動いた。しかし本番環境にデプロイした途端、精度が落ち、ユーザーから「使えない」と言われる——このパターンに心当たりがあるなら、本記事はそのギャップを埋めるために書いた。Dense ベクトル検索と BM25 を組み合わせた Hybrid 検索で検索精度を 15〜30% 引き上げ、Agentic RAG でクエリの意図に応じた動的な検索パイプラインを構築する方法を、実装パターンと評価指標の設計まで含めてステップバイステップで解説する。当社がラオス語 RAG チャットボットを構築した際に直面した低リソース言語特有の課題も交えながら、PoC から本番への移行で必要な設計判断を整理していく。

PoC 環境と本番環境の間には、データの質・量・多様性という根本的な違いがある。PoC では「うまくいくデータ」で検証するが、本番では想定外のクエリが大量に飛んでくる。このセクションでは、精度ギャップの構造を分解する。
1つ目はデータの均質性だ。PoC では整形済みの社内文書を数十〜数百件でテストする。しかし本番では PDF、Markdown、HTML、議事録、チャットログなど形式もトーンもバラバラなドキュメントが数万件規模で流れ込む。チャンキングの境界がずれるだけで、回答に必要なコンテキストが欠落する。
2つ目はクエリの多様性。PoC のテストクエリは開発者が書いた「正解があるとわかっている質問」に偏る。本番ユーザーは曖昧な質問、複合的な質問、さらには RAG の守備範囲外の質問も投げてくる。
3つ目は評価の不在。PoC では「なんとなく良さそう」で通ってしまうが、本番では精度の定量指標がなければ劣化に気づけない。当社がラオス語 RAG を構築した際も、PoC では英語ドキュメントの翻訳テストだけで精度を確認していたが、実際のラオス語ユーザーのクエリは口語表現や方言を含み、PoC 時のスコアとは別物だった。
精度劣化は大きく3つのレイヤーで発生する。
検索レイヤーでは、Dense 検索単体の限界が露呈する。専門用語や固有名詞は Embedding 空間で近傍に配置されないことがあり、意味的には関連するドキュメントを取りこぼす。逆に、表面的に類似したが文脈の異なるチャンクを上位に返してしまうケースもある。
チャンキングレイヤーでは、固定長分割による情報の断裂が問題になる。表やリストの途中で切れる、前後の文脈がないと意味が取れないチャンクが生まれる。
生成レイヤーでは、検索結果の質が低いまま LLM に渡すことで、ハルシネーションや的外れな回答が増える。検索精度が悪ければ、どれだけ高性能な LLM を使っても出力は改善しない。RAG の精度はまず検索で決まる。

本記事の手順を実装するにあたり、必要な環境と知識を整理する。
| コンポーネント | 推奨技術 | 役割 |
|---|---|---|
| ベクトルDB | Qdrant / Weaviate / pgvector | Dense ベクトルの保存・検索 |
| 全文検索 | Elasticsearch / OpenSearch / pgvector + pg_bigm | BM25 スコアリング |
| Embedding モデル | OpenAI text-embedding-3-large / Cohere embed-v4 / 多言語対応モデル | テキスト→ベクトル変換 |
| LLM | Claude / GPT | 回答生成・エージェント推論 |
| オーケストレーション | LangChain / LlamaIndex / Mastra | パイプライン制御 |
| 評価 | Ragas / DeepEval | 自動評価パイプライン |
pgvector + PostgreSQL の構成はスモールスタートに向いている。当社のラオス語 RAG でもまず pgvector で始め、スケールに応じて専用ベクトルDBへの移行を検討するアプローチを取った。
以下を理解していれば、本記事の手順をスムーズに進められる。
これらに不安がある場合は、まずラオス語AIチャットボットのRAGガイドで基礎を押さえることを推奨する。

チャンキングは RAG 精度の土台だ。PoC の「とりあえず500トークンで分割」から脱却し、ドキュメントの構造に合わせた戦略を設計する。
チャンクサイズには万能の正解がない。ただし、実務上の指針はある。
筆者の経験では、まず 400 トークン・overlap 50 トークンで始め、評価指標を見ながら調整するのが最も効率的だった。最初から最適値を決めようとして2週間消費したプロジェクトも見てきたが、評価パイプラインがない段階でのチューニングは時間の無駄になる。
本番 RAG の精度を大きく左右するのが、チャンクへのメタデータ付与だ。
1{
2 "chunk_id": "doc-123-chunk-5",
3 "source": "社内規程_v3.2.pdf",
4 "section": "第4章 情報セキュリティ",
5 "page": 12,
6 "last_updated": "2026-01-15",
7 "department": "情報システム部",
8 "document_type": "policy"
9}メタデータによるプレフィルタリングを実装すると、検索対象を絞り込んでから Dense/BM25 検索を実行できる。「情報セキュリティに関する最新の社内規程」というクエリなら、document_type = "policy" かつ section LIKE '%セキュリティ%' でフィルタした後に検索するほうが、全チャンクを対象にするより圧倒的に精度が高い。
当社がラオス語 RAG チャットボットを構築した際、チャンキングで最も苦労したのは単語境界の検出だった。ラオス語はタイ語と同様にスペースで単語を区切らない言語であり、英語向けのトークナイザーではチャンク境界が文の途中で切れてしまう。
最終的に、ラオス語の文区切り記号(。に相当する記号)をプライマリ区切りとし、バイト長でセカンダリ区切りを入れるカスタムスプリッターを実装した。この対応だけで、検索精度(Hit Rate@5)が 0.42 から 0.61 に改善した。低リソース言語では、汎用のチャンキングライブラリをそのまま使うと精度が出ない。言語固有の前処理が本番品質の前提条件になる。

Dense ベクトル検索は意味的な類似性に強いが、固有名詞やキーワードの完全一致には弱い。BM25 はその逆だ。両者を組み合わせる Hybrid 検索は、単体検索に対して 15〜30% の精度向上が複数のベンチマークで報告されている。
| 特性 | Dense(ベクトル検索) | BM25(全文検索) |
|---|---|---|
| 意味的類似 | 強い | 弱い |
| キーワード一致 | 弱い | 強い |
| 専門用語 | Embedding 品質に依存 | 正確に一致すれば確実 |
| 多言語対応 | 多言語 Embedding モデルで対応可 | 言語ごとのアナライザーが必要 |
| スケーラビリティ | ANN で高速 | 転置インデックスで高速 |
実務で最も効果を感じるのは、ユーザーが製品コードや条文番号など固有の識別子を含むクエリを投げたときだ。Dense 検索だけでは「製品コード ABC-1234 の仕様」を取りこぼすことがあるが、BM25 が正確にヒットさせる。一方、「去年のセキュリティインシデントの対応方針に似た文書」のような曖昧なクエリでは Dense 検索が力を発揮する。
Hybrid 検索のスコア統合には主に2つの方式がある。
**Reciprocal Rank Fusion(RRF)**は、各検索結果の順位のみを使ってスコアを統合する。スコアの正規化が不要で、異なるスコア分布を持つ検索エンジンを組み合わせやすい。
RRF_score(d) = Σ 1 / (k + rank_i(d))
k は通常 60 に設定される。このシンプルさが RRF の強みで、チューニングパラメータが少ないぶん安定した結果が得られる。
重み付き線形結合は、各スコアを正規化してから加重平均を取る。
hybrid_score(d) = α × dense_score(d) + (1 - α) × bm25_score(d)
α を 0.7 程度に設定(Dense を重視)するケースが多いが、ドメインやクエリタイプによって最適値が変わる。評価パイプラインで α をグリッドサーチして決めるのが現実的だ。
筆者の推奨は、まず RRF で実装して精度のベースラインを作り、精度改善の余地がある場合に重み付き線形結合への移行を検討するアプローチだ。最初から α のチューニングに時間を使うのは、評価データが十分に溜まってからでいい。
pgvector + pg_bigm を使った PostgreSQL 完結型の Hybrid 検索は、インフラ構成がシンプルで運用コストが低い。
1-- Dense 検索(pgvector)
2SELECT id, 1 - (embedding <=> query_embedding) AS dense_score
3FROM chunks
4ORDER BY embedding <=> query_embedding
5LIMIT 20;
6
7-- BM25 検索(pg_bigm or pgroonga)
8SELECT id, ts_rank(to_tsvector(content), plainto_tsquery('検索クエリ')) AS bm25_score
9FROM chunks
10WHERE to_tsvector(content) @@ plainto_tsquery('検索クエリ')
11LIMIT 20;RRF でスコアを統合し、上位 5〜10 件を LLM に渡す。当社の社内ベンチマークでは、Dense 単体の Hit Rate@5 が 0.72 だったのに対し、Hybrid(RRF)で 0.87 まで改善した。特に固有名詞を含むクエリでの改善幅が大きく、0.58 → 0.84 と劇的に向上した。

従来の RAG は「クエリ → 検索 → 生成」の固定パイプラインだ。Agentic RAG はこれを拡張し、LLM エージェントが検索戦略を動的に判断する。Gartner が注目トレンドとして認定したこのアプローチは、複雑なクエリに対する精度を大幅に引き上げる。
従来の RAG(Naive RAG)では、ユーザーのクエリがそのまま検索クエリになる。しかし「昨年度のセキュリティインシデント対応と今年度の予防策を比較して」のような複合クエリは、1回の検索では必要な情報を網羅できない。
Agentic RAG では、LLM エージェントが以下の判断を自律的に行う。
これは単なる技術的な改善ではなく、RAG のアーキテクチャパラダイムの転換だ。検索パイプラインが「プログラムで固定するもの」から「エージェントが状況に応じて構成するもの」に変わる。
Agentic RAG のエージェントには、以下のツールを持たせる。
1const tools = [
2 {
3 name: "hybrid_search",
4 description: "Dense + BM25 の Hybrid 検索を実行する",
5 parameters: { query: "string", filters: "object", top_k: "number" }
6 },
7 {
8 name: "metadata_filter",
9 description: "メタデータ条件でドキュメントを絞り込む",
10 parameters: { department: "string", doc_type: "string", date_range: "object" }
11 },
12 {
13 name: "summarize_results",
14 description: "検索結果を要約し、情報の充足度を評価する",
15 parameters: { chunks: "array", original_query: "string" }
16 },
17 {
18 name: "refine_query",
19 description: "検索結果が不十分な場合にクエリを書き換えて再検索する",
20 parameters: { original_query: "string", missing_info: "string" }
21 }
22];ツール設計で重要なのは、各ツールの description を具体的に書くことだ。エージェントは description を読んでツールを選択するため、曖昧な記述だと誤ったツール選択が頻発する。「検索する」ではなく「Dense + BM25 の Hybrid 検索を実行し、関連度スコア付きのチャンクリストを返す」のように書く。
実際の Agentic RAG のフローを示す。
ユーザー: 「ISO 27001 の管理策のうち、
当社が未対応のものをリストアップして」
エージェント思考:
1. ISO 27001 の管理策一覧が必要
→ metadata_filter(doc_type="standard", title="ISO 27001")
2. 当社の対応状況が必要
→ hybrid_search("情報セキュリティ 対応状況 管理策")
3. 検索結果を突合して未対応を特定
→ summarize_results(chunks, original_query)
4. 結果が不十分(対応状況が一部しか見つからない)
→ refine_query("部署別セキュリティ対策 実施状況 報告書")
5. 追加結果を統合して最終回答を生成このように、Agentic RAG は1つのクエリに対して複数回の検索と評価を自律的に繰り返す。PoC の固定パイプラインでは「ISO 27001 未対応 管理策」で1回検索して終わりだったところを、文脈に応じた多段階の検索に拡張できる。
ただし、マルチステップ推論はレイテンシとコストが増加する。本番ではステップ数の上限(3〜5 回)を設定し、タイムアウト(30 秒など)を設けることが不可欠だ。

本番 RAG の最大の課題は「精度が劣化しても気づけない」ことだ。評価パイプラインを CI/CD に組み込むことで、デプロイ前に精度劣化を検知する仕組みを作る。
RAG の評価指標は、検索と生成の両面をカバーする必要がある。
| 指標 | 測定対象 | 意味 |
|---|---|---|
| Context Relevance | 検索 | 取得したチャンクがクエリに関連しているか |
| Faithfulness | 生成 | 回答が取得したチャンクの内容に忠実か(ハルシネーション検知) |
| Answer Correctness | 生成 | 回答が正解と一致しているか |
| Hit Rate@K | 検索 | 上位 K 件に正解チャンクが含まれているか |
| MRR(Mean Reciprocal Rank) | 検索 | 正解チャンクの順位の逆数の平均 |
筆者が最も重視するのは Faithfulness だ。エンタープライズ用途では、ハルシネーション(事実と異なる回答)が最大のリスクであり、ユーザーの信頼を一瞬で失う。Faithfulness が 0.95 を下回ったらデプロイをブロックする、というルールを設けている。
Ragas や DeepEval を使えば、評価パイプラインを比較的短期間で構築できる。
1from ragas import evaluate
2from ragas.metrics import faithfulness, context_relevancy, answer_correctness
3
4# テストデータセット(質問 + 正解 + 検索結果 + 生成回答)
5result = evaluate(
6 dataset=test_dataset,
7 metrics=[faithfulness, context_relevancy, answer_correctness],
8)テストデータセットの作成が最も工数がかかる。PoC で使ったテストケースに加え、本番ログから「回答品質が低かった」とユーザーがフィードバックしたケースを継続的に追加していく。最低 100 件、理想的には 500 件以上のテストケースがあると、統計的に信頼できる評価になる。
CI/CD に組み込む際のポイントは、閾値ベースのゲートを設けることだ。
当社では HITL(Human-in-the-Loop)の仕組みと組み合わせ、自動評価で閾値を下回ったケースを人間がレビューするフローを構築している。自動評価は万能ではないが、劣化の早期検知という点では人間だけのレビューより圧倒的に速い。

PoC → 本番の移行で繰り返し見てきた失敗パターンを3つ紹介する。
英語のベンチマークスコアだけで Embedding モデルを選び、日本語や多言語のドキュメントで精度が出ないケースが多い。当社のラオス語 RAG では、OpenAI の text-embedding-3-large が英語では優秀だったが、ラオス語では Cohere の多言語モデルのほうが Hit Rate で 12 ポイント高かった。
対処法: 自社のドメインデータで必ずベンチマークを取る。公開ベンチマーク(MTEB 等)はあくまで参考値であり、ドメイン固有の用語が多い場合は順位が大きく変わる。
Hybrid 検索の後にリランカー(Cross-Encoder)を挟むと、さらに 5〜15% の精度向上が見込める。しかし「Hybrid だけで十分」と判断し、精度が頭打ちになるプロジェクトがある。
リランカーは検索結果の上位 20〜50 件をクエリとペアで再スコアリングするモデルだ。Bi-Encoder(Embedding)より計算コストが高いが、精度は圧倒的に高い。Cohere Rerank や bge-reranker-v2 が実用的な選択肢になる。
対処法: Hybrid 検索 → リランカー → 上位 5 件を LLM に渡す、という3段パイプラインを標準構成にする。レイテンシの増加は 100〜300ms 程度であり、エンタープライズ用途では許容範囲だ。
検索精度を上げるために大量のチャンクを LLM に渡し始めると、トークン消費が急増する。10 チャンク × 500 トークン = 5,000 トークンのコンテキストは、1リクエストあたりのコストを数セント単位で押し上げ、月間数千ドルの差になる。
対処法: リランカーで上位を厳選し、LLM に渡すチャンクは 3〜5 件に制限する。また、チャンクの要約(Abstractive Summarization)を検索パイプライン内で実行し、トークン数を圧縮する方法も有効だ。コスト最適化は精度チューニングと同時に進める必要がある。

ドメインとデータ特性に依存するが、Dense 単体に比べて Hit Rate@5 で 15〜30% の改善が複数の学術論文および当社の実測で確認されている。特に固有名詞や数値を含むクエリでの改善幅が大きい。ただし、すべてのクエリタイプで均等に改善するわけではなく、意味的に曖昧なクエリでは Dense 単体とほぼ同等の場合もある。
なる。従来の RAG が 1〜3 秒で回答するところ、Agentic RAG は 5〜15 秒かかることがある。対策としては、ストリーミングレスポンスで体感待ち時間を短縮する、ステップ数に上限を設ける、クエリの複雑度を事前判定して単純なクエリは Naive RAG にルーティングする、といった設計が現実的だ。すべてのクエリを Agentic にする必要はない。
実用的だが、追加の工夫が必要だ。当社のラオス語 RAG の経験から言えるのは、Embedding モデルの選定(多言語対応モデル必須)、チャンキングの言語対応(単語境界の検出)、評価データの整備(人手によるアノテーション)の3点が特に重要ということだ。汎用ツールをそのまま使って「精度が出ない」と諦める前に、言語固有の前処理を試す価値がある。詳しくはラオス語AIチャットボットの構築ガイドを参照いただきたい。

PoC から本番への移行で確認すべき設計判断を整理する。
RAG の本番化は、1つの技術で解決する問題ではない。チャンキング、検索、リランキング、生成、評価の各レイヤーで適切な設計判断を積み重ねることで、PoC と本番の精度ギャップを埋められる。まずは Hybrid 検索と評価パイプラインの導入から始め、クエリの複雑度に応じて Agentic RAG への拡張を進めていくのが、最も堅実なアプローチだ。
Yusuke Ishihara
13歳でMSXに触れプログラミングを開始。武蔵大学卒業後、航空会社の基幹システム開発や日本初のWindowsサーバホスティング・VPS基盤構築など、大規模システム開発に従事。 2008年にサイトエンジン株式会社を共同創業。2010年にユニモン株式会社、2025年にエニソン株式会社を設立し、業務システム・自然言語処理・プラットフォーム開発をリード。 現在は生成AI・大規模言語モデル(LLM)を活用したプロダクト開発およびAI・DX推進を手がける。