RAGを社内で試したが「期待した精度が出ない」という相談が急増しています。RAGの精度改善には10ほどの定番手法があり、原因を特定したうえで優先順位をつけて導入すれば着実に効果が出ます。本記事ではPre-Retrieval(検索前)、Retrieval(検索)、Post-Retrieval(検索後)の3段階に分けて、合計10の精度改善手法を実装コード付きで解説します。闇雲に最新技術を適用するのではなく、測定→仮説→検証のサイクルを回すことが成功の鍵です。
RAGの精度が上がらない原因を特定する
RAGの精度問題には大きく3つの原因があります。第一に検索精度が低く、そもそも関連する文書がヒットしていないケース。第二にチャンク品質が低く、チャンクに情報が不足または過剰であるケース。第三に生成プロンプトが不十分で、検索結果は取れているのにLLMが活用できていないケースです。
改善の第一歩は「どこが壊れているか」を正しく診断することです。回答に不満があったとき、検索段階と生成段階のどちらに問題があるかを切り分けないまま施策を並べても、効果測定が曖昧になります。以下の切り分けフローを出発点として、原因にマッチする手法を選んでください。
【RAG精度問題の原因切り分けフロー】
Q1. 検索結果に正解チャンクが含まれている?
├── No → 検索精度の問題
│ ├── クエリ改善(HyDE / クエリ拡張)
│ ├── 検索強化(ハイブリッド / リランク)
│ └── チャンキング見直し(B-03)
│
└── Yes → Q2. LLMがその情報を使えている?
├── No → 生成側の問題
│ ├── プロンプト改善
│ ├── コンテキスト圧縮
│ └── 並び順最適化
│
└── Yes → 評価基準の再設計を検討
Pre-Retrieval(検索前)の改善手法
手法1 — クエリ拡張(Query Expansion)
ユーザーの質問をそのまま検索に使うのではなく、LLMに言い換えや同義語展開を頼み、複数のクエリで検索したうえで結果をマージする手法です。例えば「有給の申請方法は?」を「年次有給休暇の申請手順」「有給休暇の取得フロー」「有休取得申請について」のように拡張します。固有名詞の表記揺れや略語への対応に有効で、実装コストも低いため、最初に試すべき手法の筆頭に位置づけられます。
手法2 — HyDE(Hypothetical Document Embeddings)
HyDEは、ユーザーの質問文を直接ベクトル化するのではなく、LLMに「この質問への仮の回答」を一度生成させ、その回答文のベクトルで検索を行う手法です。質問文と文書の表現ギャップを埋められるため、抽象的な質問や短いクエリで特に効果があります。LLM呼び出しが1回追加されるためコストとレイテンシは増えますが、精度向上の費用対効果は高い傾向にあります。
手法3 — クエリ分解(Query Decomposition)
「A社とB社の料金プランと返金ポリシーの違いは?」のような複合的な質問を、複数のサブ質問に分解して個別に検索し、回答をまとめて生成する手法です。単一のベクトル検索では拾いきれない複数観点の情報を網羅的に集められます。LangChainではMultiQueryRetrieverやSubQueryRetrieverとして実装が提供されています。
# HyDE(Hypothetical Document Embeddings)の実装例
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectordb = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)
def hyde_search(query: str, k: int = 4):
# 1. LLMに仮の回答を生成させる
prompt = f"次の質問に対する仮想的な回答を2-3文で書いてください: {query}"
hypothetical = llm.invoke(prompt).content
# 2. 仮の回答文でベクトル検索
return vectordb.similarity_search(hypothetical, k=k)
docs = hyde_search("リモートワーク中のセキュリティ対策について教えて")
for d in docs:
print(d.page_content[:100])
Retrieval(検索)の改善手法
手法4 — ハイブリッド検索(ベクトル検索 + キーワード検索)
ベクトル検索は意味的類似に強い反面、固有名詞や型番のような「文字列一致」が必要な検索は苦手です。そこでBM25などのキーワード検索を並行実施し、結果をRRF(Reciprocal Rank Fusion)などでマージするのがハイブリッド検索です。製品名、エラーコード、ファイル名を含む質問の精度を大きく改善できます。
手法5 — リランキング(Cross-Encoder Reranking)
ベクトル検索で広めに候補を取得した後、Cross-Encoderモデルでクエリとの関連度を再スコアリングし、上位に並べ替える手法です。ベクトル検索のBi-Encoderは高速な代わりに精度に限界があり、Cross-Encoderは計算量が多い代わりに関連度判定の精度が高い特性があります。両者の得意を組み合わせるのがリランキングの思想です。
手法6 — メタデータフィルタリング
文書に「部署」「文書種別」「更新日」などのメタデータを付与し、検索時に条件で絞り込む手法です。例えば「2025年以降の規程」「営業部向けの資料」といった絞り込みを行うことで、無関係な文書のヒットを大幅に削減できます。
# Cohere Rerank APIを使ったリランキング
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.retrievers.document_compressors import CohereRerank
from langchain.retrievers import ContextualCompressionRetriever
vectordb = Chroma(persist_directory="./chroma_db",
embedding_function=OpenAIEmbeddings())
base_retriever = vectordb.as_retriever(search_kwargs={"k": 20})
reranker = CohereRerank(model="rerank-multilingual-v3.0", top_n=4)
retriever = ContextualCompressionRetriever(
base_compressor=reranker,
base_retriever=base_retriever
)
results = retriever.invoke("退職手続きの流れを教えて")
for r in results:
print(r.page_content[:100])
| 手法 | 精度改善度 | レイテンシ影響 | 実装コスト | 必要条件 |
|---|---|---|---|---|
| ハイブリッド検索 | 中〜大 | 小 | 中 | BM25インデックス併設 |
| リランキング | 大 | 中 | 中 | Rerank API or モデル |
| メタデータフィルタ | 中 | 極小 | 低 | メタデータ設計 |
Post-Retrieval(検索後)の改善手法
手法7 — コンテキスト圧縮(Context Compression)
検索で取得したチャンクには、回答に無関係な文が混ざっていることがよくあります。コンテキスト圧縮はLLMやルールで無関係な文を除去し、純度の高い情報だけをプロンプトに渡す手法です。トークンコスト削減と回答精度向上を同時に実現できます。
手法8 — Lost in the Middle対策(チャンクの並び順最適化)
LLMは長いコンテキストの「真ん中」に置かれた情報を見落としやすい性質が知られています。最も関連性の高いチャンクを先頭または末尾に配置することで、見落としを抑制できます。LangChainのLongContextReorderは、この並び替えを自動で行う便利なクラスです。
手法9 — 出典付き回答の強制
プロンプトで「回答の各文に出典番号を付ける」「該当情報がなければ不明と答える」と明示することで、ハルシネーションを強く抑制できます。ユーザーにとっても根拠を確認できるため、信頼性と透明性の両方が向上します。
# LLMによるコンテキスト圧縮の例
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.retrievers import ContextualCompressionRetriever
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
vectordb = Chroma(persist_directory="./chroma_db",
embedding_function=OpenAIEmbeddings())
base_retriever = vectordb.as_retriever(search_kwargs={"k": 6})
compressor = LLMChainExtractor.from_llm(
ChatOpenAI(model="gpt-4o-mini", temperature=0)
)
retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=base_retriever
)
docs = retriever.invoke("出張旅費の精算期限は?")
for d in docs:
print(d.page_content)
手法10 — 評価ドリブンの継続的改善
10番目の手法は個別のテクニックではなく、「評価に基づくPDCAサイクル」そのものです。RAGasやTrulensなどの評価ライブラリを導入し、Faithfulness(回答の忠実度)、Answer Relevancy(回答の関連性)、Context Precision(文脈の精度)といった指標を継続的に測定しましょう。改善施策を入れる前後でスコアを比較すれば、効果のある施策だけを残していけます。
| 手法 | カテゴリ | 効果 | 難易度 | 優先度 | 前提条件 |
|---|---|---|---|---|---|
| クエリ拡張 | Pre | 中 | 低 | 高 | 表記揺れのある文書 |
| HyDE | Pre | 中〜大 | 中 | 中 | LLMコスト許容 |
| クエリ分解 | Pre | 中 | 中 | 中 | 複合質問がある |
| ハイブリッド検索 | Retrieval | 大 | 中 | 高 | 固有名詞検索が多い |
| リランキング | Retrieval | 大 | 中 | 最高 | Rerank API or モデル |
| メタデータフィルタ | Retrieval | 中 | 低 | 高 | メタデータ設計 |
| コンテキスト圧縮 | Post | 中 | 中 | 中 | 長文チャンクが多い |
| 並び順最適化 | Post | 小〜中 | 低 | 高 | チャンク数が多い |
| 出典付き回答 | Post | 中 | 低 | 最高 | 全ケース |
| 評価ドリブン改善 | プロセス | 大 | 中 | 最高 | 評価データセット |
まとめ――精度改善は「測定→仮説→検証」のサイクル
- 原因切り分けを最初に行い、検索か生成かの問題を特定する
- Pre/Retrieval/Postの3段階で10の手法を組み合わせて適用する
- リランキングと出典付き回答は最優先で導入すべき2手法
- 評価データセットを先に整え、施策前後のスコアで効果を判定する
- 1つの銀の弾丸に頼らず、複数の手法を積み上げるのがRAG精度改善の定石
DE-STKでは現行RAGの診断レポート作成から改善施策の優先順位付け、評価パイプライン構築までを支援しています。精度改善の打ち手に迷ったら、まず診断からご相談ください。
よくある質問(FAQ)
Q. RAGの精度を最も効果的に改善する方法は?
まず精度低下の原因を特定することが重要です。検索精度が問題ならリランキングやハイブリッド検索、チャンク品質が問題ならチャンキング戦略の見直し、生成品質が問題ならプロンプト改善が効果的です。どこが壊れているかを切り分けずに最新手法を片っ端から試すのは、時間とコストの両面で非効率となります。
Q. リランキングとは何ですか?
ベクトル検索で取得した候補チャンクを、Cross-Encoderモデルでクエリとの関連度を再評価し、並び替える手法です。ベクトル検索よりも精度の高い関連度判定ができるため、検索結果の品質が大幅に向上します。Cohere RerankやBGE RerankerなどのAPI・モデルを用いれば、既存RAGへの後付けも比較的容易です。
Q. HyDE(Hypothetical Document Embeddings)とは?
ユーザーのクエリに対してLLMに仮の回答文書を生成させ、そのエンベディングで検索する手法です。クエリのベクトルよりも文書のベクトルに近い表現で検索できるため、質問と文書の意味的ギャップを埋める効果があります。短いクエリや抽象的な質問で特に有効とされています。