RAG はどう壊れるのか 第6回:評価と失敗モード
ここまで、RAG の部品を順に見てきました。
ここまで、RAG の部品を順に見てきました。
第1回では、RAG を「検索して答える仕組み」ではなく、どの情報をどの順番で見て、どこで止めるかを決める仕組みとして整理しました。
第2回では、BM25、ベクトル検索、RRF を見ました。
第3回では、grep とベクトル検索の使い分けを見ました。
第4回では、Agentic RAG と「もう十分か」の判断を見ました。
第5回では、PageIndex と階層型検索を見ました。
最後に見るべきものがあります。
評価です。
RAG は、動いているように見えやすいシステムです。
質問を入れると、答えが返ってくる。
根拠らしい文書も出る。
文章も自然です。
だから危ない。
見た目が自然でも、必要な文書を拾っていないかもしれない。
拾った文書は正しいが、読み方を間違えているかもしれない。
答えは合っているが、根拠が違うかもしれない。
たまたま合っているだけで、次の類似質問では壊れるかもしれない。
第6回では、RAG の評価と失敗モードを整理します。
RAG の評価は「答えが合ったか」だけでは足りない
RAG を評価するとき、まず見たくなるのは最終回答です。
正しいか。
間違っているか。
分かりやすいか。
もちろん、ここは外せません。
でも、RAG では最終回答だけを見ると原因が分かりません。
答えが間違っていたとします。
原因はどこでしょうか。
必要な文書を検索できなかったのか。
検索できたが、順位が低すぎて context に入らなかったのか。
context に入ったが、モデルが読み違えたのか。
根拠にないことを補ってしまったのか。
質問自体が曖昧だったのか。
古い文書と新しい文書が混ざったのか。
最終回答だけでは分かりません。
だから、RAG の評価は少なくとも段階に分ける必要があります。
1. retrieve: 必要な情報を拾えたか
2. select: ノイズを落とせたか
3. ground: 根拠に沿って答えたか
4. trace: 後から原因を追えるか
この 4 つを分けないと、改善の方向が散漫になります。
失敗モード1:そもそも拾えていない
一番分かりやすい失敗は、必要な文書が検索結果に入っていないことです。
原因はいろいろあります。
query が悪い。
表記ゆれを拾えていない。
chunk の切り方が悪い。
文書の取り込みに失敗している。
古い embedding のまま更新されていない。
検索対象の範囲が間違っている。
これは retrieval recall の問題です。
ただし、RAG の recall は普通の検索より厳しいです。
人間が検索結果を見るなら、10 件目や 20 件目にあっても見つけられるかもしれません。
でも RAG では、LLM に渡す上位数件に入らなければ、実質的には存在しないのと同じです。
だから見るべきなのは、「検索結果のどこかにあるか」ではありません。
答えるために必要な根拠が、LLM に渡る範囲に入っているか
ここです。
失敗モード2:拾いすぎている
次の失敗は、拾いすぎです。
必要な文書は入っている。
でも、関係ない文書も大量に入っている。
一見するとよさそうです。
「必要なものは入っているのだから、モデルが読めばよい」と思うかもしれません。
でも、実際にはそう簡単ではありません。
似ているが違う文書。
古い仕様。
別バージョンの手順。
反対の操作を説明しているページ。
同じ言葉を使っているだけの別部署資料。
こうしたノイズが context に入ると、モデルは引っ張られます。
RAG では、recall だけを上げると precision が落ちます。
たくさん拾えば安心、ではありません。
評価では、次のように見る必要があります。
必要な根拠が入っているか
不要な根拠がどれくらい混ざっているか
混ざったノイズで答えが変わるか
特に Agentic RAG では、検索を増やすほどノイズも増えます。
だから「もう一度探す」には、常に「余計なものを増やす」リスクがあります。
失敗モード3:根拠はあるのに読み違える
三つ目は、根拠は context に入っているのに、モデルが読み違える失敗です。
これはかなり厄介です。
検索は成功している。
根拠も入っている。
でも答えが違う。
たとえば、文書にこう書いてあるとします。
無料解約は、契約開始日から30日以内に通知した場合に限る。
モデルがこれを、「30日以内ならいつでも返金される」と答えてしまう。
あるいは、通知条件を落としてしまう。
これは retrieval の失敗ではありません。
reading / reasoning の失敗です。
この場合、検索器を変えても直りません。
必要なのは、根拠の読み方を評価することです。
答えに必要な条件をすべて拾っているか
否定や例外を落としていないか
古い情報と新しい情報を区別しているか
数値や日付を正しく扱っているか
RAG では、「根拠に基づいているように読める」ことと、「根拠を正しく読んでいる」ことを分けて見る必要があります。
失敗モード4:根拠にないことを補う
四つ目は、根拠にないことを補ってしまう失敗です。
これは一般に hallucination と呼ばれますが、RAG では少し形が違います。
完全に何もないところから作るだけではありません。
一部の根拠があるせいで、残りを自然に埋めてしまうことがあります。
たとえば、料金表は見つかった。
でも例外条件は見つかっていない。
それでもモデルが、よくありそうな例外条件を補って答える。
これは第4回で見た「半分だけ根拠がある」状態です。
評価では、次のような観点が必要です。
答えの各文が、どの根拠に支えられているか
根拠がない部分を「不明」と言えているか
足りない情報を足りないと明示しているか
RAG のよい答えは、いつも長く詳しい答えではありません。
分からないところを分からないと言える答えです。
失敗モード5:古い情報と新しい情報が混ざる
実務で多いのが、時間の失敗です。
同じ仕様の古い版と新しい版がある。
同じ顧客の古い契約と新しい契約がある。
同じ障害の途中報告と最終報告がある。
RAG は、似ている文書を拾います。
でも、どれが最新で、どれが有効かを自動で分かるとは限りません。
この失敗は、答えがかなり自然に読めてしまうので危険です。
評価では、次を見ます。
文書の日付を見ているか
バージョンを見ているか
廃止された情報を使っていないか
最新情報が優先されているか
必要なら、retrieval の前にフィルタを入れるべきです。
あるいは、context に入ったあとで「有効日」「版」「更新日時」を必ず確認させるべきです。
失敗モード6:trace が残らない
最後に、運用上の失敗があります。
答えが間違っていた。
でも、なぜ間違ったのか分からない。
これはかなり致命的です。
どの query を投げたのか。
どの文書が返ったのか。
どの文書を LLM に渡したのか。
どこで十分と判断したのか。
最終回答のどの部分が、どの根拠に対応しているのか。
これが残っていないと、RAG は改善できません。
OpenAI Cookbook の Agent 改善ループでも、trace を集め、評価に落とし、harness の改修につなげる考え方が出てきます。
これは RAG にもそのまま使えます。
失敗した質問を保存する。
そのときの検索 query と結果を保存する。
人間が「何が悪かったか」をラベル付けする。
同じ失敗が再発しないか eval にする。
prompt、検索設定、chunking、reranker、Agentic loop を変えたら、同じ eval を回す。
こうして初めて、RAG は改善されます。
RAG eval は小さく始める
評価というと、大きな benchmark を作らなければいけないように感じます。
でも、最初から大きく作る必要はありません。
まずは、10 問でよいです。
ただし、その 10 問は本物の失敗から作るべきです。
ユーザーが実際に聞いた質問。
答えが間違った質問。
検索結果は出たのに、答えがずれた質問。
人間が「これは危ない」と思った質問。
それぞれに、最低限これを付けます。
question
expected_answer
required_evidence
allowed_sources
failure_type
required_evidence が要点です。
答えだけを持っていても、RAG の評価には足りません。
どの根拠が必要だったのかを持たないと、検索がよかったのか、生成だけがたまたま当たったのか分かりません。
評価セットは分類して持つ
RAG の eval は、全部を一つの点数にしないほうがよいです。
失敗モードごとに分けます。
exact lookup:
エラーコード、設定名、ID を探す
semantic lookup:
言い換えられた内容を探す
multi-hop:
複数文書をまたぐ
temporal:
日付、版、更新順を扱う
negative:
情報がないときに「ない」と言えるか
noise:
似ているが違う文書が混ざっても壊れないか
こう分けると、改善の方向がはっきりします。
exact lookup が弱いなら、BM25 や grep を見直す。
semantic lookup が弱いなら、embedding や query rewrite を見る。
multi-hop が弱いなら、Agentic RAG や PageIndex を見る。
temporal が弱いなら、日付メタデータやフィルタを見直す。
negative が弱いなら、Sufficient Context の判定を強くする。
noise が弱いなら、reranker や context pruning を見る。
一つの総合点だけでは、ここまで分かりません。
評価は人間を外すためではない
ここで誤解してはいけないことがあります。
eval は、人間を完全に外すためのものではありません。
むしろ、人間がどこを見るべきかをはっきりさせるためのものです。
RAG の失敗は、最初は人間が見つけます。
この答えは違う。
この根拠は古い。
この文書を見ていない。
この質問では「分からない」と言うべきだった。
その判断を、次から機械的に再確認できる形にする。
それが eval です。
人間の違和感を、その場限りの注意で終わらせない。
失敗例として残し、次の改修で再発を防ぐ。
ここが要点です。
評価できない RAG は育たない
RAG は、一度作って終わるシステムではありません。
文書は増えます。
古い文書は残ります。
ユーザーの質問は変わります。
検索対象も増えます。
モデルも変わります。
そのたびに、以前は正しかった設定がずれることがあります。
chunk サイズ。
embedding モデル。
BM25 の重み。
RRF の調整。
reranker の有無。
Agentic loop の回数。
PageIndex の粒度。
これらを感覚だけで変えると、別の場所が壊れます。
だから、RAG には評価の足場が必要です。
小さな eval set。
失敗の分類。
trace。
必要根拠。
回帰テスト。
人間のレビュー。
これがあって初めて、RAG は改善できます。
第1期のまとめ
この 6 回で見てきたことを、もう一度まとめます。
RAG は、単なるベクトル検索ではありません。
BM25 や grep のように文字列を直接扱えるツールが必要な場面があります。
ベクトル検索のように意味で候補を広げるツールが必要な場面もあります。
RRF のように、複数の検索結果を混ぜる方法もあります。
Agentic RAG のように、足りなければもう一度探す仕組みもあります。
PageIndex のように、長い文書を地図として扱う方法もあります。
そして、それらが本当に効いているかを見る eval が必要です。
つまり、RAG は一つの技術ではなく、情報を扱う一連の流れです。
問いを分解する。
探す。
選ぶ。
読む。
不足を見る。
答える。
失敗を記録する。
次に直す。
この流れ全体が RAG です。
次回からは、第2期としてこの骨格を広げます。
グラフ検索とマルチホップ、マルチモーダルと文書処理、企業導入と改善ループ、そして Context Engine への流れを見ていきます。
―― AI未来編集室「AIウォッチ」
← 一覧へ