結論: RAGの「検索が外す」問題の多くは、ユーザーの質問と文書の表現がズレる語彙ギャップが原因です。検索のあとでリランクするより前に、検索前にクエリ側を変換(書き換え・拡張・HyDE)したほうが、根本のミスマッチを安く解消できます。本記事は2026年6月時点の手法を、擬似コードと使い分け基準つきで解説します。
この記事の要点:
- 要点1: 検索精度の劣化は、埋め込みモデルやチャンク設計より先に「質問文そのもの」が原因のことが多い。クエリ変換は最小の追加コストで効く打ち手。
- 要点2: 主要手法は3つ。クエリ書き換え(Query Rewriting)/クエリ拡張・Multi-Query/HyDE(仮想文書を生成して埋め込む)。それぞれ効く場面とコストが違う。
- 要点3: いずれもLLM呼び出しが1回以上増えるため、レイテンシとトークンのトレードオフがある。rerank・ハイブリッド検索とは競合せず、組み合わせて使うのが定石。
対象読者: RAGを実装したが検索ヒット率に課題を感じている開発者・AIエンジニア
難易度: 中級
読了時間: 約12分
「ベクトルDBもチャンク設計も整えたのに、なぜか狙った文書がヒットしない」——RAGを本番に乗せた開発者なら一度はぶつかる壁です。原因をリトリーバ(検索器)やチャンクサイズに求めがちですが、見落とされやすいのがユーザーの質問文そのものです。
ユーザーは「これ、返金できる?」と短く曖昧に聞きます。一方、社内ドキュメントには「返金ポリシー:購入後14日以内、未使用に限り全額返金の対象とする」と書かれています。両者は意味的には同じでも、表現・語彙・粒度がズレているため、ベクトル類似度では距離が開いてしまう。これが語彙ギャップ(vocabulary mismatch)です。
この記事では、検索のあとに順位を直すリランク・ハイブリッド検索とは別軸の打ち手として、検索前にクエリ側を変換する3手法を取り上げます。最後まで読めば、自分のユースケースでどれを選ぶべきか、コストとレイテンシをどう見積もるかが判断できるようになります。
なぜ「クエリ側」で精度を上げるのか
RAGの検索は、ざっくり言えば「質問を埋め込みベクトルに変換し、文書ベクトルとの距離が近いものを上から取る」処理です。この設計には2つの構造的な弱点があります。
弱点1: 質問の曖昧さ・短さ
ユーザーの質問は往々にして短く、文脈が省略されています。「料金は?」だけでは、何の料金なのか、どのプランかが分からず、埋め込みベクトルも情報量が乏しくなります。情報量の少ないクエリは、検索空間で「ぼやけた点」になり、的確な文書を引き寄せられません。
弱点2: 語彙ギャップ(表現の非対称性)
質問は「話し言葉・疑問形」で、文書は「書き言葉・説明形」で書かれます。質問と回答は本来「形が違う」ため、質問ベクトルと文書ベクトルを直接比べること自体に無理があります。HyDEの原論文も、この「質問と文書の非対称性」を出発点に置いています。
クエリ変換が立つレイヤー
これらの弱点は、リトリーバを差し替えても、チャンクを切り直しても根本解決しません。なぜなら問題は「検索器に渡す前の入力」にあるからです。クエリ変換は、RAGパイプラインの最上流——埋め込みを計算する直前——に1ステップ挟むだけで効きます。
| レイヤー | 主な打ち手 | 解決する問題 | 本記事の対象 |
|---|---|---|---|
| ① クエリ前処理 | 書き換え・拡張・HyDE | 質問の曖昧さ・語彙ギャップ | ◎ |
| ② 埋め込み・検索 | 埋め込みモデル選定・ハイブリッド検索 | ベクトル表現力・キーワード一致 | — |
| ③ 検索後処理 | リランク(Cross-Encoder等) | 取得済み候補の並び替え | — |
①は数行のコードと1回のLLM呼び出しで導入でき、②③と独立して効くのが利点です。なお②の埋め込みモデル選びについてはEmbeddingモデル選定ガイドで別途解説しています。前提として、文書をどう分割するかでヒット率の天井が決まるため、チャンキング戦略も合わせて整えておくのが理想です。
以降、3つの手法を「軽い順」に見ていきます。

手法1: クエリ書き換え(Query Rewriting)
最もシンプルな手法がクエリ書き換えです。LLMに「検索に適した形に質問を言い換えて」と頼み、出力された1本のクエリで検索します。曖昧・口語・省略された質問を、検索器が拾いやすい明示的な表現に整えるのが狙いです。
どう効くか
- 口語「これ返金できる?」→ 書き換え「商品の返金ポリシーと返金可能な条件」
- 省略「料金は?」→(会話文脈つきで)書き換え「Proプランの月額料金と年間契約の割引」
- 誤字・表記ゆれの正規化(「チャットGPT」→「ChatGPT」)
特にマルチターン対話で価値が出ます。「それ、無料でできる?」の「それ」が直前の話題を指す場合、文脈を解決して独立したクエリに直す処理(query rewriting / contextualization)が必須になります。
実装イメージ(擬似コード)
def rewrite_query(user_query: str, chat_history: str = "") -> str:
prompt = f"""あなたは検索クエリの最適化担当です。
以下のユーザーの質問を、ドキュメント検索に適した
明示的で具体的な検索クエリ1本に書き換えてください。
会話履歴の指示語は解決し、自己完結した文にすること。
余計な説明は出力せず、クエリ文字列のみを返す。
会話履歴:
{chat_history}
ユーザーの質問: {user_query}
検索クエリ:"""
return llm.complete(prompt).strip()
# 使い方
search_query = rewrite_query("それ、無料でできる?",
chat_history="ユーザー: Claudeの料金は? ...")
docs = vectorstore.similarity_search(search_query, k=5)
ポイント: 出力を「クエリ文字列のみ」に制約しないと、LLMが「以下のように書き換えました:」のような前置きを混ぜて検索ノイズになります。LangChainの解説でも、クエリ変換は「埋め込みモデルに渡す前の質問の変換」として整理されています。
手法2: クエリ拡張・Multi-Query(複数クエリ生成)
書き換えが「1本を作り直す」のに対し、クエリ拡張は「1本から複数の視点を生成」します。LLMに元の質問を渡し、言い回しや観点を変えた複数のクエリ(典型的には3〜5本)を作り、それぞれで検索して結果を統合します。LangChainではMultiQueryRetrieverとして標準提供されています。
どう効くか
距離ベースの類似検索は「1つの言い回し」に縛られます。質問の表現を1つに固定すると、別の言い回しで書かれた関連文書を取りこぼします。複数の視点でクエリを投げることで、検索の再現率(recall)を引き上げられます。
RAG-Fusion(Multi-Query + RRF)
複数クエリで得た結果リストをどう1本に束ねるか。その定番がReciprocal Rank Fusion(RRF)を使うRAG-Fusionです(arXiv:2402.03367)。RRFは各リストでの順位の逆数を足し合わせるシンプルな統合アルゴリズムで、「複数のクエリで上位に来た文書ほど高スコア」になります。スコアのスケールが異なる検索結果でも、順位だけを使うため安定して統合できるのが利点です。
# RRF: 各リストの順位の逆数を合算
def reciprocal_rank_fusion(ranked_lists, k=60):
scores = {}
for docs in ranked_lists: # 各クエリの検索結果
for rank, doc_id in enumerate(docs):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
return sorted(scores, key=scores.get, reverse=True)
# Multi-Query + RRF の流れ
queries = generate_multi_queries(user_query, n=4) # LLMで4本生成
results = [vectorstore.similarity_search(q, k=10) for q in queries]
fused = reciprocal_rank_fusion([[d.id for d in r] for r in results])
k=60はRRFで広く使われる定数で、上位順位の影響を緩やかにならす役割があります。
LangChain MultiQueryRetriever の例
from langchain.retrievers.multi_query import MultiQueryRetriever
retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(),
llm=llm,
include_original=True, # 元クエリも検索に含める
)
docs = retriever.invoke("RAGの検索精度を上げる方法は?")
ポイント: クエリ本数を増やすほど再現率は上がりますが、その分だけ検索回数とトークンが線形に増えます。多くの実装で3〜5本に収めるのは、精度とコストのバランス点だからです。
手法3: HyDE(仮想文書を生成して埋め込む)
書き換え・拡張が「質問を質問のまま整える」のに対し、HyDE(Hypothetical Document Embeddings)は発想が異なります。質問への仮の答え(仮想文書)をLLMに書かせ、その仮想文書を埋め込んで検索するのです。原論文は「Precise Zero-Shot Dense Retrieval without Relevance Labels」(Gao, Ma, Lin, Callan, 2022 / arXiv:2212.10496)です。
なぜ仮想文書を埋め込むのか
前述の「質問と文書の非対称性」を逆手に取るのがHyDEの核心です。質問ベクトルで文書を探すと形がズレる。ならば、質問を一旦「文書らしい文章」に変換してから探せば、文書同士の比較になり距離が縮まる——という理屈です。生成された仮想文書には事実誤り(ハルシネーション)が含まれてもよい点が重要で、HyDEが利用するのは個別の事実ではなく「関連する文書はこういう語彙・構造で書かれるはず」というパターンだからです。
原論文では、仮想文書の生成にInstructGPT、埋め込みに教師なし対照学習エンコーダのContrieverを使い、ラベルなし(ゼロショット)でありながら、教師ありでファインチューニングしたリトリーバに匹敵する性能を、複数の言語・ドメインで示したと報告しています(具体的なベンチマーク数値は本記事では原典確認の範囲にとどめます)。
実装イメージ(擬似コード)
def hyde_search(user_query: str, k: int = 5):
# 1) 仮想文書(hypothetical document)を生成
prompt = f"""次の質問に答える文書を1段落で書いてください。
事実の正確さより、その答えが書かれていそうな
文書の語彙・文体・構造を再現することを優先します。
質問: {user_query}
文書:"""
hypothetical_doc = llm.complete(prompt).strip()
# 2) 仮想文書(質問ではなく)を埋め込んで検索
vec = embed(hypothetical_doc)
return vectorstore.similarity_search_by_vector(vec, k=k)
LlamaIndexではHyDEQueryTransformとTransformQueryEngineとして、HaystackでもHypotheticalDocumentEmbedderとして標準で実装されており、自前のプロンプト設計なしに試せます。
# LlamaIndex の例
from llama_index.core.indices.query.query_transform import HyDEQueryTransform
from llama_index.core.query_engine import TransformQueryEngine
hyde = HyDEQueryTransform(include_original=True)
hyde_engine = TransformQueryEngine(base_query_engine, query_transform=hyde)
response = hyde_engine.query("RAGの検索精度をクエリ側で上げるには?")
HyDEの注意点
HyDEは強力ですが万能ではありません。仮想文書がドメインから外れる(LLMの知識が薄い専門領域・社内固有用語)と、的外れな文書を引き寄せてかえって精度が落ちることがあります。社内ナレッジのように一般知識から遠いコーパスでは、書き換え/拡張のほうが安定する場合があります。
使い分けとコスト・レイテンシのトレードオフ
3手法はいずれもLLM呼び出しが増えるため、「検索1回あたりのコストとレイテンシ」が上乗せされます。下表は相対比較の目安です(実測ではなく構造上の傾向)。
| 手法 | 追加LLM呼出 | 追加検索回数 | 主に上がる指標 | 得意な場面 |
|---|---|---|---|---|
| クエリ書き換え | 1回 | 1回(変化なし) | 精度(曖昧さ解消) | マルチターン対話・口語クエリ |
| Multi-Query / RAG-Fusion | 1回(複数生成) | クエリ本数ぶん | 再現率 | 言い回しの揺れが大きい・取りこぼし対策 |
| HyDE | 1回(仮想文書生成) | 1回(変化なし) | 精度(語彙ギャップ解消) | 質問と文書の表現差が大きいオープンドメイン |
選定の指針
- まず書き換えから入る: 最も軽く、対話型では効果が大きい。ここで足りるなら拡張・HyDEは不要。
- 取りこぼしが課題なら Multi-Query: 再現率を上げたいとき。ただし検索回数が増えるのでレイテンシ予算を確認。
- 質問と文書の表現差が大きいなら HyDE: オープンドメインで効きやすい。専門コーパスでは事前にA/Bで検証する。
rerank・ハイブリッド検索との組み合わせ
クエリ変換は検索後処理と競合せず補完関係にあります。実務では「クエリ拡張で候補を広く取り(再現率↑)→ リランクで上位を精緻化(精度↑)」のように直列で重ねるのが定石です。RAG-Fusionが内部でRRFを使うのも、複数結果を束ねる軽量な後処理を最初から組み込んでいるからです。検索後段の設計はリランク・ハイブリッド検索の完全ガイドに、パイプライン全体の組み立てはAIエージェント実装ロードマップにまとめています。
【注意】クエリ変換でハマりやすいポイント
失敗1: レイテンシ予算を無視して全リクエストに適用
❌ どんな質問にも常にHyDE+Multi-Queryをフル適用
⭕ 検索が外れやすいクエリだけに適用、または書き換え1回で済む大半は軽量経路に流す。LLM呼び出しは応答時間に直結する。
失敗2: 生成出力をそのまま検索に流し込む
❌ 「以下のように書き換えました:…」という前置き込みで検索
⭕ 出力フォーマットを厳密に制約し、クエリ文字列のみを抽出する。前置きは検索ノイズになる。
失敗3: HyDEを専門ドメインで無検証導入
❌ 社内固有用語の多いコーパスにHyDEを入れて精度が下がる
⭕ LLMの一般知識が薄い領域では仮想文書が的外れになりうる。導入前に既存クエリでA/B比較する。
失敗4: クエリ本数を増やしすぎる
❌ Multi-Queryで10本以上生成してコストとレイテンシが膨張
⭕ 3〜5本が精度とコストのバランス点。本数増加は検索回数とトークンに線形で効く。
まとめ: 今日から始める3つのアクション
- 今日: 自分のRAGで「外したクエリ」を5〜10件集め、口語・省略・語彙ギャップのどれが原因か分類する。
- 今週中: まずクエリ書き換えを1回のLLM呼び出しで実装し、外したクエリで前後比較する。対話型ならここだけで効くことが多い。
- 今月中: 取りこぼしが残るならMulti-Query(RAG-Fusion)、表現差が大きいならHyDEを追加し、rerankと直列で重ねてレイテンシ予算内に収める。
AIエージェント・ツールの最新情報をキャッチアップしたい方へ
Agent Labでは、週1回のニュースレターでAIツールの最新比較・活用事例をお届けしています。
参考・出典
- Precise Zero-Shot Dense Retrieval without Relevance Labels(HyDE 原論文) — Gao, Ma, Lin, Callan, arXiv:2212.10496(参照日: 2026-06-04)
- RAG-Fusion: a New Take on Retrieval-Augmented Generation — arXiv:2402.03367(参照日: 2026-06-04)
- How to use the MultiQueryRetriever — LangChain 公式ドキュメント(参照日: 2026-06-04)
- Query Transformations — LangChain Blog(参照日: 2026-06-04)
- HyDE Query Transform — LlamaIndex 公式ドキュメント(参照日: 2026-06-04)
- Hypothetical Document Embeddings (HyDE) — Haystack 公式ドキュメント(参照日: 2026-06-04)
著者: 佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー約10万人。100社以上の企業向けAI研修・導入支援。著書『AIエージェント仕事術』(SBクリエイティブ)。
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。
