7月15日、Toward Data Scienceが「Most RAG Hallucinations Are Retrieval Failures: How the Retrieval Brick Decides What the Model Can Invent」と題した記事を公開した。RAGシステムで発生するハルシネーションの大半はLLMの問題ではなく検索(Retrieval)の失敗が原因であるという実証的な分析を、実測データとコードを交えて示している。
「幻覚」の犯人は生成モデルではなく検索だった
RAGパイプラインで誤った回答が返ってきたとき、多くのエンジニアはまずプロンプトを厳しくしたり、より大きなモデルに切り替えたり、temperatureを0にしたりする。だがこの記事はその直感を真っ向から否定する。
モデルは渡された文書に忠実に答えているだけだ。問題は、そもそも間違ったページが渡されていることにある。
記事はこれを実測で示している。実験台に使ったのはNISTサイバーセキュリティフレームワーク v1.1(全55ページの実際の公開標準文書)。質問は「ランサムウェア攻撃後にデータを利用可能に保つバックアップ慣行は何か?」。答えはサブカテゴリ PR.IP-4(41ページ)に明記されている:*"Backups of information are conducted, maintained, and tested."*
sentence-transformerでページをベクトル化し、コサイン類似度でランク付けした結果は以下のとおりだ。
import numpy as np
from sentence_transformers import SentenceTransformer
from docintel.parsing.pdf import parse_pdf
model = SentenceTransformer("all-MiniLM-L6-v2")
lines = parse_pdf("nist_csf.pdf")["line_df"]
pages = lines.groupby("page_num")["text"].apply(" ".join)
question = "what backup practices keep data available after a ransomware attack?"
emb = model.encode([question, *pages], normalize_embeddings=True)
scores = emb[1:] @ emb[0]
order = np.argsort(-scores)
rank = list(pages.index[order]).index(41) + 1
print(rank) # 55(55ページ中)
出力は 55。正解ページが55ページ中最下位にランクされた。top-kウィンドウの外どころか、文書全体で最もコサイン類似度が低いページだ。
しかも皮肉なことに、「backup」という単語が含まれるページは文書全体でこの41ページだけだ。質問にも「backup」が含まれている。にもかかわらず、コサイン類似度はその1語を他の語(ransomware、attack、available、keep)によってベクトル平均の中に埋没させてしまった。
これはコサイン類似度ベースのDense Retrievalが持つ構造的な弱点だ。ベクトルは文の「意味的な重心」を捉えるが、特定キーワードの有無という離散的なシグナルを過小評価しやすい。BM25のようなキーワードベースの疎なランキング手法がこの弱点を補う理由がここにある。両者を組み合わせるHybrid Search(ElasticsearchやWeaviateなどが実装を提供)が近年注目を集めているのは、まさにこのトレードオフへの応答だ。本記事の実験は、その理由を数字で直接示している。
検索失敗の3パターン
記事はRAGにおけるハルシネーションを、検索起因の3つの失敗モードに分類している。
1. 正解が取得されなかった(Recall failure)
top-kウィンドウの外に正解が落ちるケース。モデルはコンテキストに答えがないため、学習済みパラメータから補完する。「アクセス制御を強化し、データを暗号化し、ネットワークをセグメント化せよ」——流暢で、それらしく聞こえるが、与えられた文書には何も根拠がない。これが「古典的な作り話」だ。ただし、ランクを見れば正解がウィンドウ外にあることが事前に確認できるため、3パターンの中では最も検出しやすい。
2. 間違ったページが取得された(Wrong passage)
これがレビューをすり抜けやすい最も厄介な失敗だ。
今回の実験では、1位にランクされたのはデータセキュリティカテゴリ(PR.DS)だった。PR.DS-1(保存データの保護)やPR.DS-2(転送データの保護)は「データを守る」という表面的な意味でコサインに近いと判断される。しかしこれらはバックアップとは無関係の制御だ。
モデルはPR.DSから回答を生成し、そのNISTコードを引用する。レビュアーは「正しいNIST番号が引用されている」と判断して承認してしまう。引用元が実在するだけに見抜きにくい。
さらに記事は「より大きな埋め込みモデルに切り替えてもこの問題は解決しない」と明言している。大きなモデルも「available」がバックアップを指すのか、データ保護を指すのかは区別できない。
3. 正解がノイズに埋もれた(Distractor burial)
正解はコンテキストに含まれているが、周囲の類似した記述に埋もれるケース。NISTのフレームワークコアは表形式で、PR.IP-4(バックアップ)の隣にはPR.IP-9(対応・復旧計画の策定)やPR.IP-10(対応・復旧計画のテスト)が並んでいる。どれもインシデント対応に関連し、語彙も重複している。
モデルは「バックアップ」より「復旧計画」に引き寄せられ、「テスト済みの復旧計画を維持する」という、質問と微妙にズレた回答を生成する。
「安全のためにtop-kをもっと増やす」という対策はこの失敗モードを悪化させる。 コンテキストが増えるほど、モデルが自分でランク付けしなければならないノイズが増える。
対策はプロンプトではなく、検索シグナルの切り替え
記事が提示する解決策はシンプルだ。同じ文書、同じ質問で、検索シグナルをキーワードマッチに切り替えるだけで結果が逆転した。
# "backup" という単語のページごとの出現回数でランク付け
hits = (lines[lines["text"].str.contains(r"\bbackup", case=False)]
.groupby("page_num").size()
.sort_values(ascending=False))
print(hits.index[0]) # 41(正解ページ)→ 1位
print(len(hits)) # 1(文書全体でこの語を含むページは1つだけ)
モデルは何も変わっていない。検索シグナルを変えただけで、正解ページは55位から1位に浮上した。
記事が提唱する方法論は2段階だ:
- アンカー検出:キーワードマッチ、文書構造(関数→カテゴリ→サブカテゴリのツリー)、埋め込みを並列投票として組み合わせ、正しいスパンを特定する
- コンテキストの絞り込み:アンカーが見つかったら、そのサブカテゴリとその周辺だけを生成モデルに渡す。top-10ページを渡すのではなく、正しい1つのコントロールだけを渡す
「アンカー検出」と「並列投票」は記事固有の用語だが、考え方としてはBM25によるキーワードスコアとDense Embeddingのスコアを組み合わせるHybrid Searchの思想に近い。一般的なHybrid Searchが両スコアを加重平均して最終ランクを決めるのに対し、記事の手法は文書の階層構造(NISTフレームワークにおける関数→カテゴリ→サブカテゴリの木構造)を第三のシグナルとして加え、さらにコンテキストの渡し方そのものを「1スパンに絞る」点で踏み込んでいる。
「プロンプトを厳しくするのではなく、モデルの前に間違ったものを置かないこと」が本質的な対策だと記事は結論付けている。
RAGエンジニアへの実践的示唆
この記事の主張を一言でまとめれば、「ハルシネーション対策の投資先を生成から検索に移せ」だ。
コサイン類似度一本頼りのtop-k検索は、専門文書(セキュリティフレームワーク、保険約款、契約書など)において構造的に失敗しやすい。語彙のミスマッチ(質問とドキュメントで同じ概念を指す語が異なる)が発生する場面では特に顕著だ。
実験コードはdoc-intel/notebooks-vol1で公開されており、ローカル環境・APIキー不要で動作する。
詳細はMost RAG Hallucinations Are Retrieval Failures: How the Retrieval Brick Decides What the Model Can Inventを参照していただきたい。