RAG システム最適化の実践:検索精度と生成品質のバランス術
ユーザーが「パスワードのリセット方法」を尋ねると、システムは「パスワードの複雑性要件」と「アカウントのセキュリティ設定」を返す——典型的な RAG 検索の誤りです。問題は意味的ギャップにあります。ユーザーの口語「パスワードのリセット」と、ドキュメントの正式表現「パスワードリセットのフロー」には表現の差があり、ベクトルモデルが関連を見つけられないのです。RAG システムには 3 大ボトルネックがあります。意味的ギャップ(ユーザーの口語 vs ドキュメントの用語)、厳密一致の弱点(ベクトルは意味に強いがキーワード一致が弱い)、文脈の分断(チャンクがまとまった情報を切断する)です。
Query 書き換えは検索精度を 15%〜25% 向上でき、Hybrid Search(BM25 + ベクトル + RRF)は再現率を 30%〜50% 向上、セマンティックチャンクは固定チャンクより精度が 11% 高くなります。この記事では 5 大要素——Query 処理、ハイブリッド検索、リランキング、チャンク戦略、評価ループ——を体系的に分解し、精度とレイテンシのバランスを取る判断フレームを提供します。
一、RAG システムの 3 大ボトルネック
手を動かして調整する前に、私たちが結局どんな問題と格闘しているのかを話しておきたいと思います。多くの場合、いきなり Embedding モデルを変えたりベクトルデータベースを差し替えたりしますが、効果はいまひとつです。RAG システムの問題は単一の要素のせいではなく、複数の要素が積み重なった結果であることが多いからです。
意味的ギャップ:ユーザーとドキュメントは別のことを言っている
これがおそらく RAG システムで最も頭の痛い問題でしょう。
ユーザーは「システムが落ちたらどうする」と聞き、知識ベースには「サービス異常の復旧フロー」と書かれています。意味の上では確かに関連しているのですが、ベクトルモデルはこの関連を見つけられないことがあります。ユーザーは口語的な表現を好みますが、ドキュメントはたいてい正式で専門的に書かれているからです。
以前、ある金融会社の知識ベース QA を作ったとき、ユーザーが「クレジットカードが凍結されたらどうする」と聞いたのに、システムは「クレジットカードの紛失届フロー」を返したことがあります。ユーザーは危うく怒鳴るところでした——欲しいのは解除なのに、紛失届を渡すのか、と。その後 Query 書き換えを一度行い、「凍結された」を「アカウント凍結の対応」にマッピングしたところ、再現率がぐっと改善しました。
この問題の本質は、ユーザーの質問の表現とドキュメントの表現の間に意味的ギャップが存在することです。Embedding モデルは意味理解に強いものの、読心術ではありません。既存の学習データに基づいて類似度マッチングをするだけです。
厳密一致:ベクトル検索の弱点
ベクトル検索は意味的な類似に強い一方、厳密一致が必要な場面ではやや力不足です。
たとえばユーザーが「2024 年 Q3 の売上はいくら」と聞き、ドキュメントに「2024 年第 3 四半期の売上は 3.2 億元に達した」と書かれていれば、「Q3」と「第 3 四半期」は意味が近いのでベクトル検索でも見つかるかもしれません。しかしユーザーが「売上が最も高い四半期」と聞くと、ベクトル検索はお手上げです——これは意味的類似の問題ではなく、データの計算と比較が必要だからです。
さらに見えにくいケースもあります。専門用語の厳密一致です。たとえば「OAuth 2.0」と「OAuth 1.0」は、ベクトル空間では距離がとても近いかもしれませんが、実際にはまったく異なるプロトコルのバージョンです。検索結果が混ざって返されると、ユーザーは誤った情報を得てしまいます。
Tencent Cloud 開発者コミュニティの 2026 年の技術レポートによると、単一のベクトル検索の専門領域における精度は平均で 60%〜70% にすぎず、キーワード検索を混合すると 80% 以上に達します。この差は、正直なところかなり大きいです。
文脈の分断:スライスが意味を切断する
この問題は、本を破ってバラバラにしてから貼り直すようなものです——うまく貼れる断片もあれば、どうやっても合わない断片もあります。
固定チャンクは最もシンプルな方式ですが、問題も最も明白です。たとえば一つのコードでは、関数のシグネチャを一つのチャンクに、関数本体を別のチャンクに切り分けてしまい、検索時にシグネチャだけが返って実装ロジックが返らないことがあります。ユーザーはいくら見てもこの関数の使い方がわかりません。
ある事例を実測したことがあります。同じ技術ドキュメントで、固定 500 token チャンクの検索精度は 62%、セマンティックチャンク(段落・章の境界で分割)に変えると精度は 73% でした。11 ポイントの差で、しかもこれは単一変数の比較にすぎません。
さらに複雑なのは、もともと段落をまたぐ情報があることです。たとえば「この案の欠点は……」という文は第 1 段落に出てくるかもしれませんが、具体的な欠点の記述は第 2 段落にあります。第 1 段落だけが検索されると、モデルは「この案には欠点がある」と言えても、具体的にどんな欠点かは言えません——典型的な情報の分断です。
ここまで問題を並べたのは水を差すためではなく、調整の前にボトルネックがどこにあるかを把握してほしいからです。医者が診察するように、まず診断してから薬を出す、そうでしょう?
二、Query 処理:入力が下限を決める
ユーザーの質問は千差万別ですが、RAG システムの検索の起点はただ一つ——あの元の Query です。この段階がうまく処理されていなければ、後でいくら調整しても労多くして功少なしになります。
まずユーザーが何を聞いているのかを把握する
多くの場合、ユーザーの質問は実はかなり曖昧です。たとえば「これどう使うの」では、文脈がなければ「これ」が何を指すのかまったくわかりません。しかし知識ベース QA システムなら、ユーザーの過去の対話や現在のページの文脈から推測できます。
実用的なやり方は、意図を構造的に理解することです。ユーザーの質問をいくつかの次元に分解します。
- 核心的な意図:ユーザーは結局何を知りたいのか?(機能の判断、成分の確認、使い方、トラブルシューティング)
- 関係するエンティティ:質問の中でどんな具体的な対象が言及されているか?(製品名、バージョン番号、期間)
- 暗黙の条件:借用できる文脈はないか?(ユーザーの役割、操作環境)
例として、ユーザーが「パスワードのリセットに失敗したらどうする」と聞いたとき、構造化すると次のようになります。
- 核心的な意図:トラブルシューティング
- 関係するエンティティ:パスワードのリセット
- 暗黙の条件:ユーザーは既にパスワードリセットの操作を試している
こうして書き換えた Query は、元の質問よりずっと精確になります。
質問にタグを付ける
意図分類は目新しいものではありませんが、RAG システムでは特に役立ちます。
あらかじめ意図のタグ群を定義しておきます。たとえば「アカウントの問題」「決済の問題」「技術障害」「機能の相談」などです。ユーザーが質問したら、まず小さな分類モデル(あるいは直接 LLM に判断させる)でタグを付け、その後対応するドキュメントの部分集合の中だけで検索します。
こうすることの利点は、検索範囲を絞ってノイズを減らせることです。VectorHub の技術ブログでは、意図の事前分類を加えると検索精度が平均で 15%〜25% 向上すると述べられています。私自身のテストデータもだいたい同様で、おおよそ 20% 前後の改善でした。
実装も複雑ではなく、LangChain の RunnableLambda で実現できます。
from langchain_core.runnables import RunnableLambda
def classify_intent(query: str) -> str:
# 簡単な例:キーワードマッチング
if "密码" in query or "登录" in query:
return "账户问题"
elif "支付" in query or "退款" in query:
return "支付问题"
# ...さらにルールを追加
return "通用咨询"
# 検索チェーンに組み込む
intent_chain = RunnableLambda(classify_intent)
もちろん、キーワードマッチングは最も愚直な方法です。実際のプロジェクトでは LLM で意図認識を行うと精度はより高くなりますが、その分コストも上がります。
Query 書き換えのテンプレート化
最後の一手は Query 書き換えです。要するに、ユーザーの口語的な表現をドキュメントの「言語スタイル」に変換することです。
書き換えのテンプレートを一式用意し、種類の異なる質問に標準化処理を行えます。たとえば次のようにです。
| 元の質問 | 書き換え後 |
|---|---|
| これどう使うの | [製品名] の使い方と操作手順 |
| なぜエラーが出たの | [エラー情報] の原因分析と解決策 |
| もっと速い方法はない | [機能名] のショートカット操作方法 |
テンプレートの利点は保守性が高いことで、欠点は人手による設計と継続的なイテレーションが必要なことです。領域が比較的固定なら、テンプレート化した書き換えは投資する価値が十分にあります。オープンドメインの QA なら、LLM によるリアルタイムの書き換えに頼ることになり、効果はより高いもののレイテンシとコストはともに増えます。
三、ハイブリッド検索 + リランキング:検索精度の大きな飛躍
Query 処理が「入力側」の仕事だとすれば、ハイブリッド検索は「検索側」の最強コンビです。2026 年には、Hybrid Search はエンタープライズ級 RAG システムの標準構成になっています。
なぜ単一の検索では足りないのか
純粋なベクトル検索は意味的な類似に強い一方、キーワードの厳密一致では平凡です。純粋なキーワード検索(BM25)は厳密一致に強いものの、意味理解の能力はゼロです。両者にはそれぞれ弱点がありますが、組み合わせると互いを補完できます。
ここに分かりやすいたとえがあります。ベクトル検索は「理解型の読者」のようなもので、「リンゴ」と「果物」に関係があることを知っており、「この案には欠陥がある」と「案に問題がある」が意味的に近いことも理解できます。BM25 は「字面型の読者」のようなもので、字面の一致しか認めません。「OAuth 2.0」は「OAuth 2.0」、「OAuth 1.0」は別物です。
Hybrid Search は両者を結合し、まずそれぞれで検索してから結果を融合することで、意味と厳密性の両方のニーズに応えます。
3 段階アーキテクチャ:BM25 → ベクトル → リランキング
成熟した Hybrid Search のアーキテクチャには通常 3 つの段階があります。
第 1 段階:BM25 キーワード検索
キーワードを含むドキュメントを高速に再現します。速度は非常に速いものの、結果は十分に精確でないかもしれません。この段階は主に「広い再現」を担い、関連しそうなドキュメントをすべて拾い上げます。
第 2 段階:ベクトル意味検索
Embedding モデルで意味マッチングを行い、意味的に類似したドキュメントを再現します。この段階は、BM25 が取りこぼした、意味的に関連するもののキーワードが一致しないドキュメントを補います。
第 3 段階:Cross-Encoder リランキング
前 2 段階の候補ドキュメントをまとめ、Cross-Encoder モデルで精緻な並べ替えをします。Cross-Encoder は Query と各候補ドキュメントを同時に処理し、精確な関連性スコアを計算します。
この 3 段階アーキテクチャの効果データは確かです。Dasroot ブログの実測によると、Hybrid Search + RRF + Rerank は再現率を 30%〜50% 向上できます。単一のベクトル検索と比べると、この改善幅は相当なものです。
RRF 融合アルゴリズム
2 経路の検索結果はどう統合するのでしょうか。最もよく使われるのは RRF(Reciprocal Rank Fusion)アルゴリズムです。
RRF の核心的な考え方はとてもシンプルです。各ドキュメントの異なる検索結果における順位をスコアに変換し、それを足し合わせます。上位の順位ほどスコアが高く、下位の順位ほどスコアが低くなります。式は次のとおりです。
RRF_score = 1/(k + rank_BM25) + 1/(k + rank_vector)
ここで k は通常 60 を取ります。このパラメータの役割は、ある単一の順位が最終結果に過度に影響するのを避けることです。
実際の実装では、LangChain が既にラップしてくれています。
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.vectorstores import FAISS
# 2 つの検索器を初期化
bm25_retriever = BM25Retriever.from_documents(documents)
vector_retriever = FAISS.from_documents(documents, embeddings).as_retriever()
# ハイブリッド検索器に組み合わせる
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6] # BM25 の重み 40%、ベクトルの重み 60%
)
重みはデータの特性に応じて調整できます。ドキュメントのキーワードが比較的明確なら(技術規範、法律条文など)BM25 の重みを高めに、意味的な表現が豊かなら(FAQ、ディスカッションスレッドなど)ベクトルの重みを高めにできます。
Cross-Encoder リランキングのトレードオフ
Cross-Encoder の精度は確かに Bi-Encoder より高く、Medium の技術分析のレポートによると、リランキングでさらに 2% の精度向上が見込めます。ただし代償としてレイテンシが約 100ms 増えます。Cross-Encoder は各候補ドキュメントを個別に計算する必要があるからです。
このトレードオフは場面次第です。ユーザーが 300〜500ms の応答レイテンシを許容できるなら、Cross-Encoder リランキングを加える価値があります。極限の速度を追求するなら(リアルタイムのカスタマーサポートなど)、リランキングを諦めて Hybrid Search のみにすることになるかもしれません。
私のおすすめは、まず Hybrid Search(BM25 + ベクトル + RRF)を一通り走らせ、再現率と応答時間を計測することです。再現率がまだ足りなければ、そこで Cross-Encoder を加えます。最初からすべての調整を積み上げないでください。さもないと各調整項目の実際の貢献を判断しにくくなります。
四、チャンク戦略とメタデータフィルタリング
これまで話してきたのはすべて検索レベルの問題でした。この章ではドキュメントレベル——どうドキュメントを切るか、どうドキュメントにタグを付けるかを見ていきます。良いチャンク戦略は検索を効率化し、悪いチャンク戦略はまとまった情報をバラバラに切り刻んでしまいます。
セマンティックチャンク vs 固定チャンク
固定チャンクは最も実装しやすい方式です。内容に関係なく N トークンごとに切ります。実装はシンプルですが、問題は明白です——まとまった一つの情報を 2 つ、場合によっては複数のチャンクに切り分けてしまうおそれがあります。
セマンティックチャンクはもう少し賢く、内容の自然な境界で切ります。段落の終わり、章の切り替え、コードブロックの終わりなどです。こうして切られた各チャンクは相対的にまとまった意味単位になります。
LangChain は RecursiveCharacterTextSplitter を提供してセマンティックチャンクを実現します。
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=800, # 各チャンク最大 800 token
chunk_overlap=150, # オーバーラップ 150 token
separators=["\n\n", "\n", ". ", " ", ""]
# まず二重改行(段落)で切り、次に単一改行(文)、次に空白(語)で切る
)
CSDN の実践事例にデータ比較があります。同じ技術ドキュメントで、固定チャンクの検索精度は 62%、セマンティックチャンクは 73% でした。11 ポイントの差で、特別大きいとは言えませんが、大規模な用途ではこの差がユーザー満足度の差として積み重なります。
オーバーラップ戦略
オーバーラップは情報の分断を防ぐ保険です。
連続する 2 つの内容があるとします。
- 第 1 チャンク:「……ユーザーは以下の手順で」
- 第 2 チャンク:「パスワードのリセットを完了する:1. ログインページをクリック……」
オーバーラップがなければ、「パスワードリセットの手順」を検索すると第 2 チャンクだけが返り、ユーザーは「ユーザーは以下の手順で」という冒頭を見られず、情報が不完全に見えてしまいます。
オーバーラップを加えると、第 1 チャンクの末尾と第 2 チャンクの冒頭に一部重複する内容ができ、どちらのチャンクが検索されても相対的にまとまった情報を得られます。
CSDN の実測データによると、150〜200 token のオーバーラップで再現率を 25% 向上できます。このデータは私の実測でもだいたい同様でしたが、オーバーラップが大きすぎるとストレージコストと検索ノイズが増えるので、バランスが必要です。
メタデータフィルタリング:検索範囲を絞る
チャンク以外に、メタデータも検索効率を高める重要な手段です。
各ドキュメントチャンクにはメタデータを付けられます。ドキュメントの出所、作成時刻、所属カテゴリ、著者などです。検索時にまずメタデータで事前フィルタリングし、条件に合わないドキュメントを除外してから、残ったドキュメントの中でベクトルマッチングを行います。
例を挙げます。ユーザーが「2024 年の経費精算フロー」を聞いたとき、すべてのドキュメントに「年度」のメタデータがあれば、まず年度=2024 のドキュメントをフィルタリングし、その中で「経費精算フロー」を検索できます。こうすれば検索範囲を絞り、ノイズの干渉も減らせます。
実装もとても直接的です。
# ベクトルデータベースがメタデータフィルタリングをサポートしているとする
results = vectorstore.similarity_search(
query="报销流程",
filter={"year": 2024, "category": "财务制度"}
)
メタデータフィルタリングの効果は具体的な数字に定量化しにくいものです。データ構造に依存するからです。しかしドキュメントの分類が明確で時間幅が大きければ、メタデータフィルタリングは明らかな効率改善をもたらします——検索速度だけでなく、結果の品質もです。
五、生成処理と評価ループ
これまでの 4 章は検索の話でした。最後の章では生成と評価を見ます。RAG システムの良し悪しは検索が正確なだけでは足りず、生成の品質も同様に重要だからです——そしてこの両者はしばしば一緒に調整する必要があります。
検索結果をどうモデルに渡すか
検索して戻ってきたドキュメントを一気に LLM に詰め込んではいけません。モデルには文脈長の制限があり、ドキュメントを詰め込みすぎると token を浪費するうえ、ノイズも持ち込みかねません。
実用的な戦略は文脈の融合です。検索して戻った複数のチャンクを一度整理してから渡し、直接連結しません。
具体的なやり方は次のとおりです。
- 段落の類似度クラスタリング:意味的に近い段落を統合し、重複情報を減らす
- NER エンティティ抽出:ドキュメント内の重要なエンティティ(人名、地名、用語)を認識し、それらを含む段落を優先的に残す
こうして整理した文脈は情報密度が高く、ノイズが少なく、モデルが生成する回答もより焦点が絞られます。
スライディングウィンドウと重要度サンプリング
ドキュメントが多い場合は、スライディングウィンドウの仕組みも使えます。毎回、最も関連性の高い N チャンクだけをモデルに渡しつつ、前の回の文脈の一部を残し、一種の「ローリングする」情報の流れを作ります。
もう一つのテクニックは重要度サンプリングです。TF-IDF または BM25 で各ドキュメントチャンクの重要度スコアを計算し、高スコアのドキュメントを優先します。これはリランキングと少し似ていますが、検索段階ではなく生成段階で適用します。
正直なところ、これらのテクニックの効果改善は検索の調整ほど明白ではなく、おおよそ 3%〜5% 程度の改善です。しかしシステムが既にボトルネック段階まで調整されているなら、こうした微調整もやる価値があります。
定量評価:Ragas の指標体系
評価がなければ、調整は当てずっぽうの調整です。Ragas は現在最も人気のある RAG 評価フレームワークで、検索と生成の品質を測る一連の定量指標を提供します。
核となる指標は 4 つです。
| 指標 | 意味 | 目標値 |
|---|---|---|
| Faithfulness | 生成した回答が検索内容に忠実か | ≥ 0.80 |
| Context Precision | 検索内容の関連性 | ≥ 0.70 |
| Context Recall | 検索内容が必要な情報を網羅しているか | ≥ 0.75 |
| Answer Relevance | 回答がユーザーの質問に答えているか | ≥ 0.80 |
Ragas を使うコードもとても簡潔です。
from ragas import evaluate
from ragas.metrics import faithfulness, context_precision, answer_relevance
# 評価データセット:question, answer, contexts, ground_truth を含む
results = evaluate(
dataset=eval_dataset,
metrics=[faithfulness, context_precision, answer_relevance]
)
評価結果は明快な数字を与えてくれ、システムがどの次元で優れ、どの次元で改善が必要かを教えてくれます。たとえば Faithfulness が 0.65 しかなければ、生成段階にハルシネーションの問題があることを示し、Context Precision が 0.50 しかなければ、検索段階で再現した内容のノイズが多すぎることを示します。
定量指標があれば、調整に方向ができます。弱点を狙って調整でき、闇雲な試行錯誤を避けられます。
評価ループの構築
評価は一度きりのものではなく、継続的に行うべきです。評価ループを構築しましょう。
- ベースライン評価:システムを公開する前にテストセットで Ragas を一通り走らせ、ベースラインスコアを記録する
- イテレーション調整:調整のたびに再評価し、ベースラインと比較して効果の変化を見る
- オンライン監視:公開後は定期的にユーザーフィードバックをサンプリングし、人手評価または自動評価を行う
- フィードバック駆動:評価結果に基づいて次の調整点を見つけ、ループを形成する
このループは少し煩雑に見えますが、RAG システムを継続的に良くするための核心的な仕組みです。ループがなければ調整はゲリラ戦であり、ループがあれば調整は体系的なイテレーションのアップグレードになります。
結論
ここまでたくさん話してきましたが、最後に、異なる場面でどの戦略を使うべきかを判断する助けになる判断フレームをお渡しします。
まず自分の場面の種類を判断する
あなたの知識ベースは動的に更新されますか、それとも比較的安定していますか。動的な知識(ニュース、製品アナウンスなど)なら RAG がより良い選択です。知識ベースの更新はモデルのファインチューニングより柔軟だからです。安定した領域(法律条文、技術規範など)なら、ファインチューニングを検討してモデルにこれらの知識を内在化させられます。
次に精度のニーズを見る
ユーザーはどれくらいの応答時間を許容できますか。ユーザーがリアルタイム性を強く求めるなら(カスタマーサポートの場面など)、速度優先の戦略を選びます。Hybrid Search(BM25 + ベクトル + RRF)で、Cross-Encoder リランキングは加えません。ユーザーが数秒待ってでもより精確な答えを得たいなら(専門的な相談など)、精度優先の戦略を選びます。Cross-Encoder リランキングを加え、さらには複数回の検索を行います。
私のおすすめ:評価から始める
多くの開発者はいきなり「調整」したがりますが、何を調整すればよいかわかっていません。私はまず Ragas 評価を一通り走らせ、定量指標を取得し、弱点を見つけてから狙って調整することをおすすめします。
Context Precision が低ければ検索戦略を調整し、Faithfulness が低ければ生成段階の文脈処理を調整します。こうすれば的を絞った対応ができます。
RAG システムの調整は長期戦であり、銀の弾丸もなければ終点もありません。しかし体系的な考え方と定量的なフィードバックがあれば、少なくとも一歩ごとにどの方向へ進んでいるのかがわかります。この記事があなたの遠回りを少し減らせれば幸いです。
FAQ
RAG システムで最も多い検索の問題は何ですか?
なぜ Hybrid Search は単一のベクトル検索より効果が高いのですか?
チャンク戦略は検索品質にどれほど影響しますか?
Cross-Encoder リランキングは使う価値がありますか?
Ragas の評価指標はどう見ればよいですか?
RAG とファインチューニングはどう選びますか?
9分で読めます · 公開日: 2026年4月21日 · 更新日: 2026年6月8日
AI 開発実践
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Prompt Engineering ビジネス実践:カスタマーサポート・営業・運用の3大シーンガイド
カスタマーサポート、営業、運用という3つのビジネスシーンでの Prompt Engineering 実践を詳しく解説。実データ、再利用できるプロンプトテンプレート、7ステップの企業導入フローで、AI プロジェクトの「現場で使えない」問題を解決します。
第 10 / 40 記事
次の記事
MCP Server 開発入門:ゼロから作る初めての MCP サービス
MCP Server 開発をゼロから学ぼう!本記事では TypeScript ネイティブ SDK を使い、天気照会サービスを一緒に構築します。Tools・Resources・Prompts の完全実装つき。フロントエンド/フルスタック開発者に最適、30 分で始められます。
第 12 / 40 記事
関連記事
Workers AI 完全ガイド:毎日 1 万回相当の無料 LLM 呼び出し、OpenAI より最大 90% 節約
Workers AI 完全ガイド:毎日 1 万回相当の無料 LLM 呼び出し、OpenAI より最大 90% 節約
AI で 1 万行のレガシーコードをリファクタリング:1 ヶ月分の仕事を 2 週間で終えた実録
AI で 1 万行のレガシーコードをリファクタリング:1 ヶ月分の仕事を 2 週間で終えた実録
OpenAI API がタイムアウトする?Workers で専用チャネルを構築、コストゼロで安定化
コメント
GitHubアカウントでログインしてコメントできます