Learning Objectives
- 用 recall@k 與 MRR 評估檢索,並依題目種類分開看
- 用程式查核回答裡的引用編號,並說明它查得到什麼、查不到什麼
- 用真實資料說明為什麼單靠相似度門檻判斷不了「查無資料」
- 從錯誤的回答往回追,分辨是檢索的問題還是回答的問題
幾個例子看起來對,不代表整體可靠
上一課從頭寫了 RAG,試的是挑出來的幾題。要知道它到底可不可靠,得用一整批題目來量,而且要分開看三件事:
| 要問的事 | 怎麼量 |
|---|---|
| 檢索:正確的段落有被找到嗎? | recall@k、MRR |
| 回答依據:引用的節編號存在嗎?內容真的支持回答嗎? | 程式查核編號,內容要人工抽查 |
| 查無資料:手冊沒寫時,它會老實說嗎? | 準備幾題手冊沒有的問題,看實際反應 |
這一課用虛構的《優學大學學生手冊》16 題:14 題有標準答案,2 題(q15、q16)手冊沒有寫。回答都是 gpt-5.6-luna 真實跑出來的(2026-11-03)。
逐步看:檢索指標
除了第 11 課的 recall@k,這裡多算 MRR(mean reciprocal rank):每一題取「第一個正確結果的名次」的倒數,再平均。還要依題目種類分開看。
兩個指標回答不同的問題
| 指標 | 結果 | 回答的問題 |
|---|---|---|
| recall@1 | 0.714 | 只取一段時,有多少題能拿到正確段落 |
| recall@3 | 0.857 | 取三段時呢?(14 題裡有 2 題拿不到) |
| recall@5 | 0.929 | 取五段時呢? |
| MRR | 0.791 | 正確段落平均排得多前面 |
recall@k 決定要取幾段。 如果取前 3 段,那 2 題不管提示詞怎麼寫,模型都看不到答案。
題數很少時不要過度解讀。 這裡一共 14 題,一題就差 7 個百分點;新舊規定只有 2 題。這些數字適合用來找問題、比較改動前後,不適合拿來宣稱「準確率 86%」。實際專案的題目要從真實使用者的問題來,而且要多很多。
逐步看:新舊規定與查無資料的真實回答
每題取前 3 節當參考資料,每一節前面標出節編號,要求模型用 [fee-1] 這種格式引用。先猜猜看:q07 檢索第一名是舊制,模型會照舊制回答嗎?
這次模型答對了衝突的規定,但原因要看清楚
| 題目 | 檢索第一名 | 模型的回答 |
|---|---|---|
| q07 一百一十五學年度入學 | grade-2(舊制,不適用) |
「不會,你是一百一十四學年度起入學,適用學業預警與輔導制度……」引用 [grade-3] |
| q08 一百一十二學年度入學 | grade-2(舊制,適用) |
「應予退學。」引用 [grade-2] |
| q16 校內停車證 | fee-2(不相關) |
「手冊沒有寫」 |
檢索沒有分辨新舊,第一名都是舊制。q07 答對,靠的是新制也被取回(排第 3),而且手冊裡明寫了適用的學年度,模型讀得出來。把它當成「模型會自動處理版本衝突」就過頭了:取回的段落沒有新制,或文件沒寫適用對象時,這一題就不會是這個結果。
另外要記得,這是少數幾次錄製的結果。要知道穩不穩定,同一題要多跑幾次,新舊規定的題目也要更多。
逐步看:用程式查核 16 題的回答
這裡讀的是 16 題全部的真實回答(已經錄好,存在檔案裡)。程式只看文字:有沒有拒答、引用了哪些編號、這些編號在不在取回的範圍裡。
誤拒是檢索造成的;引用查核有它的極限
| 結果 | 題數 |
|---|---|
| 引用正確 | 12 |
| 引用有問題 | 0 |
| 沒有引用 | 0 |
| 正確拒答 | 2(q15、q16) |
| 誤拒 | 2(q10、q14) |
往回追原因。 q10、q14 的回答是「手冊沒有寫」,但手冊其實有寫。recall@3 那兩題漏掉的正好是它們:正確段落沒有被取回,模型只看到不相關的內容,所以依照規定回答沒寫。這不是模型的錯,要改的是檢索,例如加大 k、加上關鍵字搜尋,或改寫問題。同一題換一種提示詞,模型也可能依取回的其他段落給一個部分的答案,這也是要實際評估的原因。
使用者看到「手冊沒有寫」會以為真的沒有,這種錯誤不容易被發現,所以要把誤拒單獨算出來。
程式查核查得到什麼: 編號是否存在、是否在取回的範圍內、是否包含標準答案。
查不到什麼: 編號對,內容不一定對。模型可以引用 [fee-1],卻寫出 fee-1 沒有說的話。要查內容,只能人工對照,或請另一個模型判斷(第 20 課);後者也會出錯,同樣要抽查。
撰寫這一課時我們逐題對照過手冊,12 題「引用正確」的回答內容也都相符。這是這一批的人工檢查結果,不是程式保證的。
逐步看:門檻與提示詞
有沒有可能不呼叫模型,直接用相似度門檻判斷查無資料?先看數字。接著看另一件事:把提示詞裡「沒寫到就只回答手冊沒有寫」這一句拿掉,q15、q16 會怎樣?
拒答要靠提示詞和回答查核一起處理
為什麼沒有答案的 q16 分數比有答案的 q02 還高?我們的推測是:q16「校內的停車證要怎麼申請」問的是辦手續的步驟,就學貸款那一節也在講辦理步驟,用語相近;q02「翹太多次」和手冊的「缺課」「扣考」用字差很多。第 10 課說過:相似度量的是「在談什麼」,不是「有沒有回答到」。
所以這一批資料裡:
- 相似度門檻不夠。 可以用來擋掉明顯無關的內容、省下呼叫費用,但門檻要設得很保守,不能當成唯一的判斷。
- 提示詞規定固定說法。 模型讀完資料後判斷,這批的 q15、q16 都正確拒答;固定的說法也讓程式查得出來。拿掉這一句時,模型這次也沒有編造,但說法不固定,還多了手冊沒寫的建議。
- 回答查核。 用程式檢查有沒有引用、編號對不對;沒有引用的回答可以不顯示,或標示為未經查證。
- 這些都不是保證。 兩題查無資料遠遠不夠,模型遇到更像的問題可能硬湊答案,要多準備這類題目定期測。
順帶一提,安全面的問題(文件裡藏著指示要模型做別的事)留到第 18 課。
實作:recall_at_k
完成 recall_at_k(results, golds, k):
results是每一題的檢索結果(節編號的串列,已由高到低排好);golds是每一題的標準答案,手冊沒有寫的題目是None。- 回傳標準答案出現在前 k 名的比例,用
round(…, 3)取小數 3 位。 gold是None的題目不算在分母裡。全部都是None時回傳0.0,不可以除以 0。
最後兩行用的是這一課 16 題的真實檢索結果,每題只存了前 3 名。這題不呼叫模型。
小測驗:評估 RAG
三題的標準答案分別排在第 1、2、4 名,MRR 是多少?
ranks = [1, 2, 4]
print(round(sum(1 / r for r in ranks) / len(ranks), 3))
本課的 q10、q14 手冊有寫,模型卻回答「手冊沒有寫」。最先該檢查哪裡?
程式查核「回答引用的 [fee-1] 確實在取回的範圍內」通過了。這表示什麼?
為什麼在這批資料上,單靠相似度門檻判斷不了查無資料?
評估結果 recall@3 = 0.857(14 題對 12 題),同學想對外寫「本系統準確率 86%」。哪個說法最合適?
重點整理
- 評估分三件事:檢索(recall@k、MRR)、回答依據(引用查核)、查無資料的行為。依題目種類分開看,也要看題數夠不夠。
- 誤拒常常來自檢索沒找到,要從回答往回追,不要只改提示詞。
- 程式查得到引用編號對不對,查不到內容對不對;內容要抽查。
- 這批資料沒答案的題目分數比有答案的高,所以單靠相似度門檻不夠,要靠固定的拒答說法與回答查核一起處理。
- 模型每次回答可能不同,少數幾次的結果不能當結論。
下一課: 到這裡模型只會讀資料和回答。下一課讓它呼叫工具,真的去查東西、做事情。