RAG の良し悪しをどう判断するか
RAG の評価を検索と回答の二つに分けます。検索はヒット率で見て、回答は別のモデルを評価者にし、参考回答と照らして正誤を、資料と照らして忠実さを判定させます。評価者は 60 回判定し、私がそれを一件ずつ確認したところ、評価者自身も間違えることがわかりました。
- 約 45 分
- 難易度:中級
- 検証:2026-09-14 deepseek-flash、deepseek-v4-pro(評価者)
コードと実行結果は実際に動かしたときのまま載せているため、コメントと出力は中国語です。
ここまで評価してきたのは、どれも「検索が正しく見つけたかどうか」でした。しかしユーザーが最終的に目にするのは回答です。検索が全部正しくても、回答が間違っていることはありえます。モデルが資料を読み落とすかもしれず、二つの資料を混ぜてしまうかもしれず、自分の「知識」を付け加えずにいられないかもしれません。逆に、検索が最も正確なチャンクを見つけられなくても、回答が正しいこともあります。
ですから RAG の評価は二つに分ける必要があります。検索と、回答です。この課ではその両方のやり方を扱い、特に回答の評価と、そこにある大きな落とし穴に焦点を当てます。
二つを分けて評価する
分けて評価すれば、問題が起きたときにどこを直せばよいかがわかります。
| 検索 | 回答 | 意味 | 直すべきところ |
|---|---|---|---|
| 正しい | 正しい | 正常 | |
| 誤り | 誤り | 資料が見つからず、モデルも正しく答えられない | 検索:分割、書き換え、検索方法 |
| 正しい | 誤り | 資料は正しく渡したのに、モデルがうまく使えなかった | 生成:プロンプト、モデル |
| 誤り | 正しい | モデルが記憶で正解したのかもしれない | 「資料だけに基づく」に反していないか確認する |
最終的な回答の正誤だけを見ていると、2 行目なのか 3 行目なのか区別できず、手当たり次第に直すしかなくなります。
検索の評価はすでに済んでいます。第 3 課のヒット率と MRR です。この課では回答の評価をします。
回答で何を評価するか
RAG の回答で最もよく見るのは二つです。
- 正確さ:回答が正しいかどうか。照らし合わせる参考回答が必要です。
- 忠実さ:回答の中の主張がどれも、検索された資料に根拠を見つけられるかどうか。答えが正しいかは問わず、「はみ出して」いないかだけを見ます。
忠実さを単独で評価するのは、見えにくい問題を見つけられるからです。回答はたまたま正しいけれど、根拠はモデル自身の記憶で、資料ではない。今回は正しくても次は間違うかもしれず、しかも追跡できません。
参考回答を用意する
第 3 課の 20 問それぞれに参考回答を書きました。どれも検索の評価で指定したその一節の原文に沿って書いています。たとえば次のとおりです。
{"question": "怎么关闭 SSL 证书校验?", ..., "reference": "传 verify=False,比如 httpx.get(url, verify=False);用 Client 时在创建客户端时传入。"}
{"question": "httpx 和 requests 在处理重定向上有什么不一样?", ..., "reference": "requests 默认跟随重定向,httpx 默认不跟随;httpx 要显式传 follow_redirects=True(单个请求或 Client 上都可以)。"}
参考回答は長く書く必要はなく、含めるべき要点をはっきり書けば十分です。ただし後で見るとおり、参考回答の書き方が採点に直接影響します。
モデルを評価者にする
20 問、それぞれ回答が二つ(資料を調べないものと RAG を使うもの)あり、人が一件ずつ判定するのは大変です。よく使われる方法は別のモデルに判定させることで、LLM 評価者(LLM-as-a-judge)と呼ばれます。評価者にはより強い deepseek-v4-pro を使い、思考をオンにしました。
def judge_correct(question, reference, answer):
return judge(f"""判断"回答"是否正确地回答了"问题"。以"参考答案"为准:回答包含参考答案的要点、且没有和它矛盾的内容,就算正确。
回答比参考答案多说了一些内容没关系,只要多说的部分没有错误。
输出 json:{{"correct": true 或 false, "reason": "一句话理由"}}
问题:{question}
参考答案:{reference}
回答:{answer}""")
def judge_faithful(context, answer):
return judge(f"""判断"回答"里的每一个事实性说法,是否都能在"资料"里找到依据。
回答说"资料里没有"之类的话不算事实性说法。只要有一处说法在资料里找不到依据,就判为不忠实。
输出 json:{{"faithful": true 或 false, "unsupported": "找不到依据的说法,没有就写空字符串"}}
资料:
{context}
回答:{answer}""")
設計上のポイントがいくつかあります。
- 評価者と評価されるモデルは別のモデル。同じモデルに自分を採点させると、自分の誤りを見過ごしがちです。
- 理由の出力を求める。true か false だけでなく、なぜそう判断したかを一文で言わせます。後で見るとおり、理由は評価者の誤りを見つける唯一の手がかりです。
- 判定基準をはっきり書く。「多く言うのはかまわない。多く言った部分が間違っていなければよい」という一文は、回答が参考回答より詳しいという理由で評価者が誤りと判定するのを防ぐためです。
judge 関数は、モジュール 02 第 4 課の JSON モードで評価者を呼び出します。完全なコードは code/04-rag/rag_eval.py にあり、1 回の実行で約 80 回呼び出し、費用は約 0.1 ドルです。
評価者の判定
不查资料:答对 15/20
RAG: 答对 19/20,忠实于资料 19/20
[不查资料答错] 响应是 404 或 500 时,怎么让它直接抛异常?
评委:回答中“3xx不会抛”与参考答案“状态码不是2xx时会抛出”矛盾
[不查资料答错] httpx 和 requests 在处理重定向上有什么不一样?
评委:回答错误地声称httpx默认跟随重定向,与参考答案中“httpx默认不跟随”相矛盾。
[不查资料答错] 怎么限制连接池里最多同时有多少个连接?
评委:回答遗漏了参考答案中默认 max_connections=100 这一要点,未完整包含参考答案的默认值信息。
[不查资料答错] 怎么显示下载进度?
评委:回答未使用参考答案中的 response.num_bytes_downloaded 属性,而是手动累计长度
[不查资料答错] 网页返回的中文是乱码,怎么指定解码用的字符集?
评委:回答未提及参考答案中的 default_encoding 参数指定字符集的方法
[RAG 答错] httpx 和 requests 在处理重定向上有什么不一样?
评委:回答中关于 requests 暴露的属性 response.next 的描述有误,requests 并没有该属性。
[RAG 不忠实] 怎么显示下载进度?
找不到依据:显示下载进度需要用流式响应(streaming),并检查 `response.num_bytes_downloaded` 属性
結論ははっきりしているように見えます。RAG で正答率が 15/20 から 19/20 に上がった。しかし、まだ結論を急がないでください。「不正解」の一件一件について、元の回答を見て、httpx のドキュメントとソースコードで確認しました。
評価者を一件ずつ確認する
「3xx では例外を投げない」、評価者は正しかった。資料を調べない回答は、raise_for_status() は 3xx では例外を投げないと言いました。httpx のソースコード _models.py の raise_for_status はこう書かれています。2xx でない(is_success が偽)限り HTTPStatusError を投げ、3xx のエラーの種類は「Redirect response」と呼ばれる。ですから回答は確かに間違いです。
リダイレクト、資料を調べない回答は間違いで、評価者は正しかった。またしても httpx は既定でリダイレクトに従うと言い、「0.20 以降は既定で従う」とまで言っていて、RepoBot v1 が犯した誤りとまったく同じです。
コネクションプール、評価者が厳しすぎた。資料を調べない回答は、httpx.Limits(max_connections=..., max_keepalive_connections=...) を使うと正しく述べていて、既定値が 100 であることに触れなかっただけです。ユーザーが聞いたのは「どう制限するか」で、既定値がいくつかではありません。問題は私の参考回答にあり、ついでに「既定で最大 100 接続」と書いたので、評価者はそれを必須の要点とみなしてしまいました。
ダウンロードの進捗、評価者の判定には一理ある。資料を調べない回答は、len(chunk) でダウンロード済みのバイト数を手作業で足し上げていました。httpx のドキュメントは、圧縮が有効なときは展開後の内容の長さと実際にダウンロードしたバイト数が一致しないので、response.num_bytes_downloaded を使うようにと特に書いています。回答のやり方は圧縮がなければ使えますが、正しいやり方ではありません。
エンコーディング、評価者が間違えた。資料を調べない回答は、response.text にアクセスする前に response.encoding = "gbk" を設定すると言いました。httpx のソースコードを調べると、Response.encoding には setter があり、text を読む前にエンコーディングを設定できます(読んだ後に設定すると ValueError を投げます)。これはまったく正しい別のやり方です。評価者は「参考回答にある default_encoding に触れていない」という理由で誤りとし、参考回答を唯一の正解とみなしてしまいました。
RAG のリダイレクトの回答、評価者が間違えた。評価者は「requests には response.next という属性はない」と言いました。しかし httpx のドキュメント compatibility.md の 50 行目の原文はまさに「The requests library exposes an attribute response.next, which can be used to obtain the next redirect request.」です。RAG の回答はドキュメントに沿って書いていて、正しいのです。評価者は資料を見ずに自分の記憶で判定し、その記憶が間違っていました。
RAG のダウンロードの進捗、「忠実でない」の判定が間違い。この問題の検索をやり直したところ、取り出した上位 2 つのチャンクはどちらも advanced/clients.md から来ていて、どちらにも num_bytes_downloaded があり、原文にはストリーミングレスポンスを使ってこの属性を確認するとはっきり書かれていました。回答は資料に完全に忠実で、評価者がよく読んでいなかったのです。
確認した後の結果
| 評価者の判定 | 私が確認した後 | |
|---|---|---|
| 資料を調べない | 正解 15/20 | 正解 17/20 |
| RAG | 正解 19/20 | 正解 20/20 |
| RAG の忠実さ | 19/20 | 20/20 |
結論の方向は変わりません。RAG は資料を調べない場合より明らかによく、資料を調べない回答は 2 問で本当の誤りを犯しました。しかし具体的な数字は変わり、評価者は 60 回の判定のうち 3 回間違え、1 回は厳しすぎました。
この 4 回の問題は二種類に分けられます。
- 評価者が与えられた資料を使わず、自分の知識で判定した。
response.nextの件がそうです。評価者も LLM なので、記憶違いをします。 - 参考回答を唯一の答えとみなした。エンコーディングとコネクションプールの 2 件がそうです。正しいやり方が複数あるのに参考回答に一つしか書いていないと、評価者は他の正しいやり方を誤りと判定してしまいます。
評価者をより信頼できるものにするには
- 必ず抜き取り検査をする。評価者の判定をそのまま結論にしてはいけません。少なくともすべての「誤り」を人が確認し、「正解」もいくつか無作為に抜き出して見ます。
- 評価者に理由を出させる。今回問題を見つけられたのは、すべて理由のおかげです。「requests にはその属性はない」という理由は見るからに怪しく、調べればすぐに評価者の誤りだとわかります。
- 参考回答は要点を書き、他の正しいやり方も認める。プロンプトで「参考回答以外の正しいやり方も正解とする」と説明します。参考回答には、質問が本当に聞いている内容だけを書きます。
- 忠実さの評価者には資料を見させる。プロンプトで「資料だけに基づいて判定し、自分の知識を使わないこと」を強調します。
- 人のラベルで評価者を較正する。数十件を人がラベル付けし、評価者の判定が人とどれだけ一致するかを見て、一致率が低すぎれば評価者のプロンプトを直します。モジュール 06 第 2 課でこれを体系的に扱います。
この課の結論
- RAG の評価は検索と回答の二つに分ける必要があり、分けて初めて問題がどこにあるかがわかる。
- 回答の評価で最もよく見るのは正確さと忠実さ。
- LLM 評価者は人手を大きく節約できるが、間違えるし、しかも自信満々に間違える。評価者の出力は確認が必要なデータであって、最終的な結論ではない。
練習問題
eval_qa.jsonlのコネクションプールとエンコーディングの 2 問の参考回答から、質問が聞いていない内容を取り除き、評価者のプロンプトに「参考回答以外の正しいやり方も正解とする」を加えて、rag_eval.pyを実行し直してください。評価者の判定は変わりましたか。- 忠実さの評価者のプロンプトに「資料だけに基づいて判定し、自分の知識は使わないこと」を加えて実行し直し、ダウンロードの進捗の問題の判定が変わるか見てください。
- 5 問を選んで自分が評価者になり、資料を調べない回答が正しいかどうかを判定してください。LLM 評価者と比べて、何問で判定が食い違いましたか。
確認テスト
1. RAG の評価で、検索と回答を分けるのはなぜですか?
最終的な回答だけを見ていると、誤りがどのステップで起きたのか区別できません。正しい資料が見つからなかったのか、資料は正しく渡したのにモデルがうまく使えなかったのか。分けて評価すれば、検索(分割、書き換え、検索方法)を直すべきか、生成(プロンプト、モデル)を直すべきかがわかります。
2. ある RAG の回答は正しいのに、忠実さの評価では「忠実でない」とされました。これは何を意味しますか?
回答の一部の内容が、検索された資料に根拠を見つけられず、おそらくモデル自身の記憶から来ていることを意味します。今回はたまたま正しくても、別の質問では間違うかもしれず、しかも出典をたどれません。もちろん評価者が誤判定した可能性もあるので、人の確認が必要です。
3. LLM 評価者が、ある回答を「参考回答にある方法に触れていない」という理由で「誤り」と判定しました。どうすべきですか?
まず、回答が使った方法も正しいかどうかを確認します。参考回答にはやり方が一つしか書かれていないことが多く、評価者は他の正しいやり方も誤りと判定してしまうかもしれません。確認して回答が正しければ、評価者のプロンプトを直す(他の正しいやり方を認める)か、参考回答を直します。
質問と議論
このレッスンでつまずいたところは、ここで質問してください。他の人の質問に答えるのも歓迎です。
質問で 3 ポイント、回答で 6 ポイント。審査を通過すると公開されます。
議論を読み込んでいます…