當 RAG 答錯時,最直覺的三個動作通常是:換更大的模型、把 Top-K 調高、再補幾句 Prompt。問題是,同一句「回答錯了」,背後可能是完全不同的故障:正解沒進候選池、證據閘門錯殺、模型拿到正解卻拒答、引用格式不合法,或答案只漏了一個關鍵條件。修錯層不只沒用,還會製造新的變因。
今天先不急著修參數,而是建立失敗追蹤(Failure Trace),用這個系列已經發生過的案例,逐層判斷 Chunk、Top-K、Prompt 與標註各自該負什麼責任。
failure_analysis.py 將失敗依資料流排序:
def diagnose(results, relevant_docs, decision, answer, reference_coverage):
retrieved_docs = {r.chunk.document_id for r in results}
relevant = set(relevant_docs)
if relevant and not (retrieved_docs & relevant):
return FailureDiagnosis("retrieval", "candidate pool missed labels")
if relevant and not decision.answerable:
return FailureDiagnosis("evidence_gate", "answerable question rejected")
if answer.validation_errors:
return FailureDiagnosis("output_validation", "; ".join(answer.validation_errors))
if relevant and answer.status != "answered":
return FailureDiagnosis("generation", "model returned insufficient")
if not relevant and answer.status != "insufficient":
return FailureDiagnosis("refusal", "unsupported question answered")
if relevant and reference_coverage < 1.0:
return FailureDiagnosis("answer_content", "reference point missing")
return FailureDiagnosis("success", "no automatic failure signal")
順序很重要。如果候選池沒有相關文件,後面模型再強也沒有證據可用;只有候選池正確,才值得檢查 Prompt 與回答。
Day 6 的 structured Chunking 留下一個孤兒標題,Day 9 的 BM25 長度正規化立刻把它放大:超短 Chunk 只命中一個普通詞,卻因長度很短拿到高分。這是典型的 Retrieval Failure,修法不是告訴 LLM「不要相信短標題」,而是讓標題和正文一起切分,或在索引時加入最小內容長度。
另一種 Chunk 失敗是太大。大 Chunk 可能同時談 401、403、CSRF 與認證,檢索容易命中,模型卻拿到大量不相關內容;太小則只剩「它會影響搜尋結果」這種失去主詞的句子。Chunk 大小必須透過檢索與回答評測共同驗證,不能只看平均字數是否漂亮。
Day 18 的 401/403 Context 取 3 個來源,其中 S3 是 CSRF 文件。它和 Spring Security 主題相關,卻不是回答狀態碼差異的必要證據。如果把 max_sources 從 3 增加到 5,模型得到更多背景,也得到更多分心機會、Token 成本與引用選擇。
反過來,max_sources=1 雖然簡潔,遇到跨文件比較就可能漏證據。Top-K 的兩端是:
context_ablation.py 固定同一個問題,分別以 1、3、5 個來源執行,列出答案與引用。這種單變量實驗比一次同時改 Chunk、K 與 Prompt 更有資訊量。若結果不穩定,還要多次執行或固定模型版本,不能用單次生成下結論。
Day 26 的 a01 是最清楚的 Prompt / Output Validation Failure。正解在候選池第一名,證據閘門通過,回答事實也合理;但正文引用 S1、S2,citations 只列 S1,最後被 Fail Closed 拒絕。
這種問題調 Chunk 或 Top-K 都沒有用。正確修法可能是:加強格式示例、使用服務端結構化輸出、驗證失敗後做一次有上限的修復,或選擇更能遵守 Schema 的模型。無論哪一種,都要保留原始輸出與驗證錯誤,不能把修復過程藏起來。
Day 22 的 FastAPI Depends 問題沒有任何相關文件,Reranker 卻給 0.144,通過 0.10 門檻。這是 Evidence Gate False Positive,不代表最後一定會幻覺,因為模型還有機會閱讀 Context 後回覆 insufficient。
若只看到它穿過門檻就把閾值提高到 0.15,可能在下一批資料錯殺低分的可回答題。修法應該是擴充近域無答案題、評估第二層模型判斷,必要時訓練或選擇專門的 Answerability 模型,而不是追著一題調常數。
q18「不同講法為何找不到同一份文件」被標成 Embedding 文件,但 BM25、E5 與 Cross-encoder 都把斷詞文件排在前面。不同講法造成斷詞差異,本來就是合理答案。這種情況的 Failure Trace 會說 Retrieval 未命中標準文件,卻不能立刻斷言模型錯。
自動診斷只能指出「結果和標籤不一致」,最後仍需第二位標註者仲裁。評測程式本身不能決定標籤真偽,這是所有數據驅動優化都要保留的人類責任。
| 症狀 | 優先證據 | 應先檢查 | 不該先做 |
|---|---|---|---|
| 正解不在候選池 | Relevant doc rank | Chunk、斷詞、Retriever | 調回答 Prompt |
| 正解在池中但被拒答 | Gate score、status | 拒答策略與模型判斷 | 增加知識庫外事實 |
| 回答引用不存在來源 | Validation errors | Schema、引用修復 | 放寬為任意網址 |
| 引用正確但漏要點 | Claim / reference points | Context 與生成 Prompt | 只增加 Top-K |
| 所有方法都偏離標籤 | 多模型排名、文件內容 | 標註仲裁 | 看分數後直接改標籤 |
只有 failure_type=retrieval 仍不足以重現問題。每次執行至少要保存原問題與改寫查詢、資料與索引版本、候選 Chunk 和各階段分數、去重後文件排名、證據閘門理由、Context 實際內容、Prompt 版本、模型原始輸出、解析錯誤與最終狀態。
{
"query_id": "a01",
"search_query": "HTTP 401 和 403 差異",
"index_version": "kb-v1",
"retrieved_docs": ["spring-security-authentication"],
"decision": "candidate_evidence_found",
"validation_errors": ["inline citations and citations field do not match"],
"failure_type": "output_validation"
}
Trace 不應無限制保存完整機密文件,也不應把金鑰與 System Prompt 寫入一般 Log。可以保存文件 ID、Chunk 雜湊與受控環境中的內容快照,再以 Trace ID 串起 API、Retrieval 與 LLM 呼叫。目標是讓維運人員能重播決策,而不是建立另一份沒有權限管理的資料副本。
一次把 Chunk 從 300 改成 800、Top-K 從 3 改成 8,又換一版 Prompt,即使分數提高也不知道是哪項造成。較可靠的實驗矩陣會固定資料、模型與評測,只改一個因素:
| 實驗 | Chunk | Top-K | Prompt | 要觀察的問題 |
|---|---|---|---|---|
| Baseline | 目前設定 | 3 | v1 | 回歸基準 |
| C1 | 較小 | 3 | v1 | 指涉是否被切斷 |
| K1 | 目前設定 | 1 | v1 | 跨文件證據是否遺失 |
| K2 | 目前設定 | 5 | v1 | 干擾與過度引用是否增加 |
| P1 | 目前設定 | 3 | v2 | 格式與要點是否改善 |
每個版本都同時看檢索、回答、引用、拒答與延遲。Prompt v2 若讓 Citation Valid 上升,卻讓拒答題開始硬答,就不是無條件改善。所有實驗也要保存逐題差異,避免總平均掩蓋一類問題的退步。
不過,控制變因只能先定位主效果,RAG 各層並非完全獨立。Chunk 變大後,同樣 Top-K 會消耗更多 Context;Top-K 增加後,原本有效的 Prompt 可能因來源太多而漏列引用;加入 Reranker 又會改變證據閘門的分數分布。確認單項影響後,仍需要少量組合實驗檢查交互作用。
因此修復順序應由上游到下游:先保證資料與 Chunk 正確,再看候選召回與排序,之後校準 Evidence Gate,最後才調生成與輸出。若來源一開始就切錯,後面模型偶爾答對只是依靠既有知識或運氣,不能算系統修復。
也要知道何時停止調參。當失敗來自標籤爭議、資料缺漏或樣本太少時,繼續微調權重只會對評測集過度擬合。這時應新增或仲裁資料,而不是要求模型配合錯誤的尺。若某次調整只修好一題、同時讓另一題退步,也要先判斷兩題代表的真實流量與風險,不以單一總分決勝。
Failure Analysis 的目的不是找到一組永遠最好的參數,而是建立可重複的診斷流程:發現、定位、提出假設、做受控實驗、檢查副作用、保留回歸案例。做到這一步,RAG 優化才從試手氣變成工程。
今天把「回答錯了」拆回真正發生的層:Chunk 可能製造碎片,Top-K 可能漏證據或帶進干擾,Prompt 可能讓正確內容輸出成不合法結構,拒答門檻會放過近域問題,而評測標籤本身也可能有爭議。FailureDiagnosis 不會自動修好系統,但能阻止我們在錯誤元件上浪費調參。
下一篇進入安全性。知識庫文件不只可能「不相關」,還可能故意寫入指令,要求模型忽略規則、洩漏 Prompt 或偽造引用。這不是一般品質失敗,而是不可信資料跨越指令邊界的攻擊;防禦必須從 Prompt、來源處理、輸出驗證與系統權限一起設計。