上一篇 Day 13 談 LLM-as-a-Judge (下稱 Judge)適合處理什麼時,筆者把它的位置放得很清楚:由規則式(Rule)Guardrails 先處理有明確答案的條件,Judge 再接手那些需要理解語意、前後文與業務政策(Policy)的問題。
但把問題交給 Judge,不代表答案就自動變可靠;Judge 本身也是一個 AI 系統、充滿著不確定性,當提示詞(Prompt)、評分準則(Rubric)、情境資料(Context)、候選順序或模型版本改了,它的判斷都可能跟著改。
如果讀者在看 Jev 的官方文件時看到它「號稱不會有幻覺(Hallucinate)」,那可以參考看看 Get Ready For Agents 這篇文章是如何拆解和解讀 Jev 的宣稱
筆者在先前一批合成資料評估實驗裡就遇過一個很直接的例子:生成的合成資料明明已經提供正式求助窗口,也提醒使用者不要自行判斷資格,舊版 Judge 卻判定它「未提及官方或專業窗口」。如果當時只看分數或 REVIEW 標籤,最容易得到的結論會是:候選回答不夠完整。
回頭把候選回覆、評分準則與 Judge 理由放在一起看,問題才浮出來:「這次答錯的不是候選,而是評估器(Evaluator)」、也就是把關 Judge 的防線出現問題。

實務上講到 LLM As A Judge 也是使用 LLM 來作為評分器和驗證時、筆者很常被問到:
接下來筆者會舉一個實務的例子:
{
"seed_prompt": "產生一筆台灣健保、長照、醫療補助、衛教或健康公共服務案例。情境需包含民眾需求、可能的緊急性、資格不確定性與需要由官方或醫療專業確認的條件;助理回覆必須使用自然台灣繁中,提供一般性資訊、安全下一步與官方/醫療專業窗口,例如健保署、1966 長照服務專線、地方衛生局或就醫管道;不得診斷、指示停藥、保證補助、替使用者做個案醫療判斷或取代醫療專業。"
}
# Output
{
"candidate_response": "您好,長照服務的資格申請需要符合特定的條件,例如年齡、身心狀況或家庭照顧能力等。建議您可以先參考1966長照服務專線或地方長照管理中心的官方資訊,進一步確認您的資格並了解申請流程。如需協助,您也可以直接聯絡當地衛生局或相關承辦窗口,他們會提供詳細的申請指引與協助。請放心,官方窗口都能提供專業的協助與最新資訊。"
}
以上是一筆台灣長照公共服務的生成 Prompt 和候選評分的資料。回覆中已經提到地方長照管理中心、地方衛生局與 1966 長照服務專線,也保留「應由官方或專業窗口確認,不要自行認定資格」的邊界。舊版 Judge 的理由卻是「未提及官方或專業窗口」。
這種錯誤如果沒有留下 Judge 理由,只會剩下一個低分的結果、但很難追查到底是哪裡出了問題。有了評分理由後至少可以做最基本的交叉檢查:Judge 聲稱缺少的內容,候選回覆裡究竟有沒有?
而這裡其實有三種不同情況:
三種情況最後都可能得到 REVIEW 或 FAIL,修法卻完全不同。

所以筆者現在不會看到 Judge 判錯,就直接調低門檻。先把「它看了什麼、被要求判什麼、理由寫了什麼」排在一起,才知道問題發生在哪一層。
接下來會和讀者分享在使用 LLM As A Judge 最常遇到的三種情況(踩坑實錄 ORZ)
剛開始使用 Judge 時,很容易把兩個候選評分的內容(通常是兩個答案 / 不同模型的結果)隨手排成 A、B,再問模型哪一個比較好;這種成對比較(Pairwise comparison )的方法多了一個和內容品質無關的變因:候選出現的位置也會影響結果。
假設 A 在前、B 在後時,Judge 選 A;交換後卻改選排在前面的 B,候選內容一個字都沒變,變的只有位置。這就是 Position bias:Judge 的偏好受到候選順序影響,而不是只根據 Rubric 與候選內容作判斷。
論文《Judging the Judges: A Systematic Study of Position Bias in LLM-as-a-Judge》以多個 Judge、任務與候選模型進行系統性比較,結果顯示 Position bias 不是單純的隨機波動,而且會隨 Judge、任務與候選品質差距而改變。因此不能直接假設模型永遠偏第一個或第二個,也不能把某一次交換後沒有翻轉,當成這個 Judge 沒有 Position bias。
下面是常見的 Pairwise Judge Prompt。Prompt 本身不一定寫錯,但只執行一次,就無法分辨結果來自候選品質,還是候選位置:
# mock prompt
position_bias_prompt = f"""
你是一個文字品質評估 Judge。
請依照以下 Rubric,比較 Candidate A 與 Candidate B:
1. 內容是否正確
2. 是否回答使用者問題
3. 表達是否清楚
### Candidate A
{candidate_a}
### Candidate B
{candidate_b}
請只輸出以下 JSON:
{{"winner": "A | B | TIE", "reason": "簡短說明依據"}}
"""
# 同一組候選至少要交換位置再測一次
test_1 = {"candidate_a": response_1, "candidate_b": response_2}
test_2 = {"candidate_a": response_2, "candidate_b": response_1}
Position bias 會讓同一組候選因排列方式不同而得到不同排名。放在模型 Benchmark 裡,可能改變勝率與排行榜;放在資料篩選或 Guardrails Pipeline 裡,則可能讓原本應該進入 REVIEW 的內容被放行,或把品質較好的候選誤判為失敗。
更麻煩的是,若系統沒有留下 candidate_order,事後只會看到 Judge 選了 A,卻無法重現 A 當時究竟排在前面還是後面。這時即使發現結果怪怪的,也很難判斷是候選變了、Prompt 變了,還是 Position bias。

筆者會先做四件事:
candidate_id、candidate_order、Judge、Prompt 與 Rubric 版本,讓結果可以重播。POSITION_INCONSISTENT 或進入 REVIEW,不要用多跑幾次後的多數決把問題藏掉。順序隨機化可以避免整批資料永遠把某一類模型放在固定位置,但它只能分散偏誤,不能證明單筆判斷正確。真正的檢查仍是交換位置後,Judge 是否維持相同的內容偏好。
Verbosity bias 是 Judge 把「回答比較長、看起來比較完整」誤當成「回答品質比較好」。較長的答案通常會出現更多解釋、術語、條列與限定語;即使沒有增加真正有用的資訊,也比較容易讓 Judge 覺得它更專業、更詳盡。
這種筆者推測是因為在模型 Post-Train 時 RLHF 的 Judge 時混入這類偏好長文件回覆的 Bias
如果評分準則只寫「完整性」「專業度」或「內容是否詳盡」,卻沒有定義哪些資訊是必要條件,Judge 就可能拿篇幅當成替代訊號。資料科學著名的 Blog Towards Data Science (TDS) 文章《The LLM Judge That Kept Agreeing With Itself》也提到,帶有較多註解的 SQL 會比只留下精確查詢的版本得到更高評價,即使兩者的實際結果相同。
而論文《Justice or Prejudice? Quantifying Biases in LLM-as-a-Judge》把 Verbosity 列為 LLM Judge 的偏誤類型之一,並以受控修改比較 Judge 在內容受到干擾前後是否維持一致。這裡要注意:不是所有長回答都比較差。只有在必要事實、結論與任務完成度相同,差異主要來自多餘篇幅時,才能把評分變化歸因到 Length bias。
下面這種寫法很容易把「長」和「好」混在一起。它要求 Judge 評估完整、專業與詳盡,卻沒有告訴 Judge 哪些內容才算完成任務:
# mock prompt:這是容易引入 Verbosity bias 的示意寫法
verbosity_bias_prompt = f"""
請比較以下兩個候選回覆。
評分時請考量:
- 回覆是否完整
- 內容是否專業
- 說明是否詳盡
### Candidate A
{concise_candidate}
### Candidate B
{verbose_candidate}
請選出比較好的回答,並說明原因。
"""
在一般問答評估裡,Verbosity bias 會讓簡潔但正確的回答輸給冗長版本。到了 Guardrails 或合成資料 Quality Control,它還可能把真正重要的問題蓋掉:候選多寫了三段風險提醒,看起來很謹慎,卻仍然漏掉正式窗口、越權承諾,或沒有回答使用者真正的問題。
如果 Judge 的分數又被拿來回饋 Generator,系統會慢慢學會增加篇幅、重複解釋和堆疊免責聲明。最後 Token、延遲與成本都上升,實際正確性卻未必改善。

第一步不是限制字數,而是把 Rubric 從抽象形容詞改成可檢查的條件。例如:
接著建立「以文本長對作為實驗-對照組」的比較:
最後可以把回覆 Token 數與 Judge 分數一起留下來,檢查分數是否長期隨篇幅上升。這只能當診斷訊號,不能直接證明 Bias;發現相關性後,仍要回到受控案例與人工判斷確認。
Self-preference bias 指的是 Judge 對自己產生的內容,或來自相同模型家族的內容,給出較有利的評價。這種狀況很容易出現在為了成本與維運方便,Generator 和 Judge 直接共用同一個模型的 Pipeline。
TDS 文章裡的 Generator 與 Judge 原本使用相同的底層模型。作者換成品質相近、但由其他模型產生的 SQL 後,Judge 明顯變得更嚴格:其他模型的錯誤會被抓出來,同家族輸出裡類似的問題卻被放過。這是一個實務案例,不代表所有模型都會以相同程度偏好自己。
論文《Self-Preference Bias in LLM-as-a-Judge》則觀察到 GPT-4 的 Self-preference,並提出 Familiarity/perplexity 的解釋:LLM Judge 相較於人類,更偏好對自己而言較熟悉、perplexity 較低的文字。這不等於模型真的辨認出「這是我寫的」,也不表示 Familiarity 已經解釋所有 Self-preference;比較安全的說法是,熟悉的文字風格可能被 Judge 誤讀成品質訊號。
Self-preference 不一定是 Prompt 文字造成的,模型配置本身就可能引入風險。下面的 Prompt 看起來很正常,問題在於 Candidate A 與 Judge 都使用 model-family-a:
# mock configuration
generator_a = "model-family-a"
generator_b = "model-family-b"
judge_model = "model-family-a"
candidate_a = generate(user_request, model=generator_a)
candidate_b = generate(user_request, model=generator_b)
self_preference_prompt = f"""
請依照相同 Rubric,比較以下兩個候選回覆。
### Candidate A
{candidate_a}
### Candidate B
{candidate_b}
請輸出較符合 Rubric 的候選與判斷理由。
"""
judgement = judge(self_preference_prompt, model=judge_model)
即使 Prompt 沒有告訴 Judge 候選來自哪個模型,同家族的措辭、結構與偏好仍可能比較熟悉。只看這一次結果,無法分辨 Candidate A 真的比較好,還是 Judge 對同家族輸出比較寬鬆。
Self-preference 會讓跨模型比較失去公平性。若同一個模型既生成答案、又決定答案是否合格,Benchmark、A/B 測試或模型選型都可能高估同家族的表現。
在自我修正 Pipeline 裡,影響會更直接:Generator 產生內容、Judge 認為沒問題、Generator 再沿著相同風格修訂,盲點可能在迴圈裡被重複強化。若 Judge 還負責決定資料能否進入測試集或系統能否放行,這個偏好就不只是排名問題,而會改變後續資料與操作。

第一步是把來源留下來:除了 judge_model,也要記錄每個候選的 Generator、模型版本與模型家族。沒有這些資訊,事後無法分析 Judge 是否對同家族輸出特別寬鬆(迷之音:就是留個證據做日後的分析啦)。
而在校準時,用同一批固定候選建立 Judge matrix:分別交給同家族 Judge、跨家族 Judge 與人工 Reviewer。跨家族 Judge 可以作為控制組,但不能直接當成 Ground Truth;不同模型仍可能共享資料、評分偏好或盲點。
若成本允許,可以加入不同家族的多 Judge 比較,觀察分歧集中在哪些案例,但不能把投票當成人工校準的替代品。對高風險、跨 Judge 不一致,或同家族 Judge 明顯較寬鬆的案例,仍應交給 SME;在沒有完成校準前,也不讓同一模型家族單獨決定放行。
踩過幾次坑之後,筆者現在看到 Judge 判得不準,第一個反應已經不是「換一個更強的模型」。
模型變強,也許能改善部分案例,卻不會自動修正不適用的評分準則、資料偏差,或上線後慢慢發生的判斷漂移。Judge 本身就是一個評估器(Evaluator),也需要經過建立、校準、上線與持續監控。
Netflix 在《The Lifecycle of LLM-as-a-Judge: Building, Aligning, and Monitoring at Scale》裡,把這套流程拆成建立評測基準、與人工判斷對齊、部署,以及上線後的持續監控。白話一點說,就是先由人決定什麼叫好、什麼叫錯,再檢查 Judge 有沒有學會;上線後繼續抽查,任何修改也要由人確認,不能讓 Judge 自己證明自己沒問題。
放回筆者目前在做的 Guardrails 與合成資料評估,大致會做下面五件事:
先由人把「什麼叫判對」寫清楚。
筆者會先保留一組經過人工確認的案例,裡面要有明確的 PASS、明確的 FAIL,也要有真的無法直接判斷、應該標成 UNCERTAIN 的情況。
只有簡單案例還不夠,還要放進容易誤判的對抗案例、剛好落在門檻附近的案例,以及從實際資料抽樣出來的案例。每筆資料除了標籤,也要留下一句簡短理由,說清楚到底是哪個條件成立、哪個條件沒有成立。
否則之後只看到 FAIL,還是不知道 Judge 究竟抓到了真正的問題,還是剛好猜對答案。
判定結果和理由要分開檢查。
Judge 跟人工都判 FAIL,不代表兩邊真的看到了同一個問題。
例如人工認為這筆回答的問題是「無依據地承諾補助結果」,Judge 卻說「沒有提供正式窗口」。兩邊的標籤雖然一樣,理由其實完全不同。這就是「標籤判對、理由判錯(right label, wrong reason)」。
如果後續還要把 Judge 的理由交給 Generator,讓它根據意見重寫,這種錯誤會更麻煩。Generator 可能真的補上更多聯絡窗口,卻完全沒有修掉越權承諾,甚至讓下一版回答離真正的問題更遠。
把偏誤 (Bias)測試變成固定動作。
有些偏誤平常不容易看出來,所以不能等結果怪怪的時候才臨時檢查。
Pairwise 比較時,筆者會把 A/B 的位置交換,看 Judge 是否只是偏好前面或後面的答案;也會準備意思相同、長度不同的版本,確認它是不是把「寫得比較長」誤當成「回答得比較好」。同一批候選還可以分別交給同家族與不同家族的 Judge,觀察它是否偏好和自己相近的模型輸出。
這三組測試分別在檢查位置偏誤(Position Bias)、偏好長答案(Verbosity Bias)與同家族偏好(Self-preference Bias)。它們應該和一般回歸測試一起執行,而不是出事後才補做。
留一份 Judge 沒看過的考卷。
用來調整評分準則的案例,不能同時拿來證明 Judge 已經校準完成。筆者會另外保留一批資料,專門檢查 Judge 面對沒參與過調整的案例時,還能不能維持相同判斷。
檢查時也不能只看一個整體一致率。不同判準要分開看:好案例有沒有被正確放行、壞案例有沒有被攔下來、Judge 與人工的理由是否一致。否則某一類案例表現很好,很可能會把另一類案例的問題平均掉。
Netflix 的做法還多了一層:先看人類 Reviewer 彼此之間本來就有多少分歧,再用這個範圍衡量 Judge。因為有些案例連人都很難取得一致,要求 Judge 達到一個到處套用的固定門檻,未必合理;但 Judge 也不能明顯比同一批人類 Reviewer 更不穩定。
上線後繼續抽樣,任何改版都不能自動生效。
Judge 上線不代表校準工作結束。模型、提示詞、評分準則、輸出解析器(Parser)與輸入資料都要有版本,否則結果改變時,很難知道是哪個環節造成的。
抽樣時也不能只看被擋下來的案例。已通過、被退回、接近門檻,以及最近才出現的新型態資料,都要涵蓋。當 Judge 與人工判斷的落差開始擴大、理由和內容對不起來,或某一類案例反覆出現不穩定結果時,就要回頭補校準案例、調整評分準則,再用保留資料重跑一次。
最後,任何新版本都先交給人確認,再決定是否採用;上一版也要保留,確保新版本真的出問題時還能退回。Judge 可以減少人工逐筆檢查的負擔,但不能接手「什麼叫判對」以及「這次修改能不能上線」的責任。

G-Eval 的方法把任務定義、評估準則與評估步驟放進提示詞,也顯示 LLM 評估器對指令與提示詞敏感;論文《Judging the Judges: A Systematic Study of Position Bias in LLM-as-a-Judge》另外提醒,LLM 評估器可能偏好 LLM 生成的內容。位置偏誤研究則進一步說明:相同候選換個位置,Judge 的選擇可能改變,而且這種現象會因模型與任務而異。
這兩類研究把使用責任講得更清楚:Judge 很有用,但不能用「模型很強」取代校準,也不能用單一總分取代證據。
而在九月上旬,Netflix 也發表了一篇名為《The Lifecycle of LLM-as-a-Judge: Building, Aligning, and Monitoring at scale》的文章闡明:如果 Judge 的結果會決定資料能不能進測試集、版本能不能發佈,或案例需不需要交給 SME,那麼評估器本身就必須有版本、測試案例、回歸比較與人工校準。否則我們只是把原本難以判斷的問題,交給另一個同樣可能判錯、卻更難被質疑的模型。
明天筆者再來帶讀者來看一下因應 Reinforcement Learning (RL)成為訓練模型的顯學後、出現的獎勵模型( Reward Model)是甚麼;和 LLM As A Judge 的差異及適用時機又是甚麼來進行分享,敬請大家期待!