第4章:LLM-as-Judge 方法论 —— G-Eval、Bias 与校准
LLM-as-Judge 三种范式、G-Eval 评分细则、4 类已知 bias(position/verbosity/self-preference/token)、校准方法、何时该用何时该避
不是所有任务都有”标准答案”——开放对话、创意写作、风格质量、复杂语义,verifier 写不出来。这时候只能让 另一个 LLM 当裁判。但 LLM Judge 不是公正的——它有 4 类系统性 bias,会让你的评测数字误导决策。本章把 LLM-as-Judge 的范式、bias、校准方法、何时该用何时该避全讲清,让你用得明白。
📑 目录
- 1. 什么时候必须用 LLM Judge
- 2. 三种 Judge 范式
- 3. G-Eval:让 LLM 用细则评分
- 4. 4 类已知 Bias
- 5. 校准方法
- 6. LLM Judge vs Verifier 边界
- 7. 实战:跑一组 ChatBot Arena 风格评测
- 自我检验清单
- 参考资料
1. 什么时候必须用 LLM Judge
1.1 三类必须场景
| 场景 | 为什么 verifier 不行 |
|---|---|
| 开放对话 | 答案没有唯一形式,文本复杂语义判断 |
| 创意 / 风格 | ”好不好”主观,无标准答案 |
| 复杂多跳推理 | 推理过程评估难规则化 |
1.2 三类不该用
| 场景 | 为什么用 verifier 更好 |
|---|---|
| 数学题 | 答案唯一,EM verifier 99.9% 准 |
| 代码 generation | unit test 决定对错 |
| API 调用 | 调用成功 / 状态变化可程序化检查 |
🍎 铁律:有 verifier 选 verifier,没办法才用 LLM Judge。LLM Judge 引入二阶噪声(judge 自己也有错),应是兜底而非首选。
2. 三种 Judge 范式
2.1 ① Pointwise Judge(单点打分)
Judge 看一个回答,直接给 1-5 / 0-10 分
Prompt 示例:
评分这个客服回答的质量,1-5 分:
- 1: 完全不相关
- 5: 完全准确且专业
User question: ...
Agent response: ...
Score:
优点:简单,可大规模跑 缺点:绝对分数极不稳定——同一答案不同 prompt / 模型可能差 1-2 分
2.2 ② Pairwise Judge(两两对比)
Judge 看 A 和 B 两个回答,选哪个更好
Prompt:
对比下面两个回答,哪个更好?
Q: ...
A: ...
B: ...
Output: A / B / Tie
优点:相对判断比绝对打分稳定(ChatBot Arena 用这种) 缺点:n² 比较成本高,扩展难
2.3 ③ Reference-based Judge(参考答案)
Judge 看到 ground truth,判断 prediction 是否等价
Prompt:
Reference answer: 鲁迅
Predicted answer: 周樟寿
Are they equivalent? Yes / No
适用:有 reference 但需要语义判断(同义词、不同表达)。介于 verifier 和 LLM Judge 之间。
2.4 哪个范式选哪个
| 场景 | 推荐 |
|---|---|
| 大批量评分(1000+ 题) | Pointwise(便宜) |
| 模型对决(找哪个更好) | Pairwise(更稳) |
| 有 reference 的语义判断 | Reference-based |
| 高 stakes(决策) | Pairwise + 多 judge 投票 |
3. G-Eval:让 LLM 用细则评分
Liu et al., 2023
3.1 思想
不要让 Judge 自由打分,给它一个”评分细则(rubric)“
给定 rubric:
Relevance:
1 - 完全不相关
2 - 部分相关但偏题
3 - 基本相关
4 - 高度相关
5 - 完全切题
LLM Judge 按 rubric 评分,并 step by step 解释为什么
3.2 关键技巧
① Chain-of-thought 评分
Step 1: 列出回答的关键内容
Step 2: 对照 rubric 每一档的特征
Step 3: 给出最匹配的分数 + 理由
让 LLM 显式推理,比直接给数字稳得多。
② Token probability 加权
不只看输出的数字,而是看 LLM 输出 1-5 各自的 token 概率,加权平均:
更细粒度,降低离散数字的方差。
3.3 DeepEval 中的 G-Eval
from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCase
correctness = GEval(
name="Correctness",
criteria="Determine if 'actual output' factually matches 'expected output'.",
evaluation_steps=[
"Check if all facts in expected output are present in actual output",
"Identify any factual contradictions",
"Score severely (low) if hallucinations exist",
],
evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.EXPECTED_OUTPUT],
)
test_case = LLMTestCase(input="...", actual_output="...", expected_output="...")
correctness.measure(test_case)
print(correctness.score, correctness.reason)
4. 4 类已知 Bias
4.1 ① Position Bias(位置偏见)
LLM Judge 倾向选第一个或最后一个
实验:同一对回答 (A, B),交换顺序变成 (B, A) 跑两次:
顺序 (A, B) → 选 A (60%)
顺序 (B, A) → 选 B (55%)
真实差距:可能就是 10% 而非 60-55=5%
防御:每对都跑两次(swap),取平均。
4.2 ② Verbosity Bias(冗长偏见)
LLM Judge 倾向选更长的回答
A: "答案是 42。"(简洁)
B: "经过仔细思考,我认为这个问题需要多角度分析...最终答案是 42。"(啰嗦)
LLM Judge 经常选 B,即使 A 更优
防御:rubric 显式声明”长度不应影响评分”+ 控制回答长度近似。
4.3 ③ Self-Preference Bias(自我偏好)
GPT-4 当 Judge 时倾向选 GPT-4 写的回答;Claude 选 Claude 的
研究发现:模型 judge 自己输出时给分高 5-15%。
防御:用第三方 model 当 judge(评测 GPT 模型用 Claude judge,反之亦然)。
4.4 ④ Token Bias / Format Bias
Judge 对 markdown / code block / 列表等格式有偏好
A: "巴黎是法国首都。"
B: "**巴黎**:
- 法国首都
- 人口 200 万
- ..."
格式漂亮的 B 经常被选,即使内容相同
防御:rubric 说”不评格式只评内容”;或在 judge 前对两个回答统一 strip 格式。
4.5 综合校准前后对比
| Bias | 校准前误差 | 校准后(swap+rubric+third-party judge) |
|---|---|---|
| Position | 5-10% | < 1% |
| Verbosity | 10-20% | 2-3% |
| Self-preference | 5-15% | < 2% |
| Format | 5% | < 1% |
🌟 校准是 LLM Judge 必须的工程——直接用就是耍流氓。
5. 校准方法
5.1 校准 4 件套
① Position swap
每对评测都跑两次:(A, B) 和 (B, A),取一致的判断,不一致就标记 “tie”。
def swap_robust_judge(a, b):
score_ab = judge_pair(a, b)
score_ba = judge_pair(b, a)
if score_ab == "A" and score_ba == "B":
return "A wins (consistent)"
elif score_ab == "B" and score_ba == "A":
return "B wins (consistent)"
else:
return "Tie / Inconsistent"
② 多 judge 投票
用 3 个不同的 LLM(GPT / Claude / Gemini)各自评,多数票决定。
③ Human gold 校准
抽 50-100 题人工标注,作为 judge accuracy 基准:
Human label: [A, A, B, A, B, ...]
Judge output: [A, B, B, A, B, ...]
Agreement = 0.85
Cohen's Kappa = 0.7 (评估一致性,>0.6 算好)
如果 agreement < 0.7,改 prompt 或换 judge model。
④ Rubric + CoT
显式 rubric + chain-of-thought,而非”自由打分”。
5.2 校准代码示例
from deepeval.metrics import GEval
# 1. 定义 rubric + CoT
metric = GEval(
name="Helpfulness",
criteria="Score helpfulness 1-5",
evaluation_steps=[
"Identify user intent",
"Check if answer addresses intent",
"Penalize verbosity / format flair",
"Score based on substance only",
],
model="gpt-5", # 第三方 judge,不用被评的同一个 model
)
# 2. 跑 swap-robust pairwise(自己写)
def evaluate_pair(case_a, case_b):
score_a = metric.measure(case_a).score
score_b = metric.measure(case_b).score
return score_a - score_b # > 0 → A 好
# 3. 多 judge 投票
votes = [
evaluate_pair_with_model(a, b, "gpt-5"),
evaluate_pair_with_model(a, b, "claude-4-opus"),
evaluate_pair_with_model(a, b, "gemini-2.5-pro"),
]
final = majority(votes)
6. LLM Judge vs Verifier 边界
6.1 决策表
| 任务特征 | 选 Verifier | 选 LLM Judge |
|---|---|---|
| 答案唯一形式 | ✅ | |
| 可以执行验证(unit test、tool result) | ✅ | |
| 数值 / 字符串精确匹配 | ✅ | |
| 开放式回答 | ✅ | |
| 风格 / tone / 创意 | ✅ | |
| 多种正确答案 | ✅ | |
| 需要语义同义判断 | partial | ✅ |
| 高 stakes 决策 | ✅(更可靠) | (要多 judge 投票) |
6.2 混合策略(2026 推荐)
先 verifier,失败再 LLM Judge:
def hybrid_eval(response, ref):
# 1. 严格 verifier
if rule_based_verifier(response, ref):
return 1.0
# 2. 字符串近似(拼写、format 差异)
if fuzzy_match(response, ref):
return 0.9
# 3. LLM judge 兜底
return llm_judge(response, ref)
兼顾速度 + 准确度。
6.3 LLM Judge 的成本
| Judge model | $/eval(短回答) |
|---|---|
| GPT-4o-mini | ~$0.0005 |
| GPT-4o | ~$0.005 |
| Claude Sonnet 4.5 | ~$0.005 |
| Claude Opus 4.7 | ~$0.025 |
跑 1000 题 × 多 judge 投票 × swap = ~$50-300——比 verifier 贵 100 倍。
7. 实战:跑一组 ChatBot Arena 风格评测
7.1 完整脚本
"""
对比 GPT-5 和 Claude Sonnet 4.5 在客服任务上的胜率。
"""
import asyncio
from openai import AsyncOpenAI
from anthropic import AsyncAnthropic
oai = AsyncOpenAI()
anthr = AsyncAnthropic()
async def get_response(model, prompt):
if model.startswith("gpt"):
r = await oai.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
)
return r.choices[0].message.content
else:
r = await anthr.messages.create(
model=model,
max_tokens=2048,
messages=[{"role": "user", "content": prompt}],
)
return r.content[0].text
async def judge(question, answer_a, answer_b, judge_model):
"""Pairwise judge with rubric."""
prompt = f"""You are an expert evaluator. Compare the two answers.
Question: {question}
Answer A: {answer_a}
Answer B: {answer_b}
Rubric:
- Helpfulness (does it solve the user's problem?)
- Accuracy (factual correctness)
- Conciseness (no unnecessary verbosity — DO NOT prefer longer answers)
Step 1: Briefly evaluate Answer A
Step 2: Briefly evaluate Answer B
Step 3: Output exactly one of: "A wins" / "B wins" / "Tie"
"""
if judge_model.startswith("gpt"):
r = await oai.chat.completions.create(
model=judge_model,
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
)
text = r.choices[0].message.content
else:
r = await anthr.messages.create(
model=judge_model, max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
text = r.content[0].text
if "A wins" in text: return "A"
elif "B wins" in text: return "B"
else: return "Tie"
async def evaluate(prompts, model_a="gpt-5", model_b="claude-sonnet-4-5"):
judges = ["claude-opus-4-7", "gpt-5", "gemini-2.5-pro"]
results = []
for prompt in prompts:
# 1. 拿两个 model 的回答
ans_a = await get_response(model_a, prompt)
ans_b = await get_response(model_b, prompt)
# 2. 多 judge swap-robust
votes = []
for j in judges:
v_ab = await judge(prompt, ans_a, ans_b, j)
v_ba = await judge(prompt, ans_b, ans_a, j)
# swap consistent
if v_ab == "A" and v_ba == "B":
votes.append("A")
elif v_ab == "B" and v_ba == "A":
votes.append("B")
else:
votes.append("Tie")
# 3. 多数票
final = max(set(votes), key=votes.count)
results.append({"prompt": prompt, "winner": final, "votes": votes})
# 统计胜率
a_wins = sum(1 for r in results if r["winner"] == "A")
b_wins = sum(1 for r in results if r["winner"] == "B")
ties = sum(1 for r in results if r["winner"] == "Tie")
total = len(results)
print(f"{model_a} vs {model_b}:")
print(f" {model_a} 胜率: {a_wins/total:.1%}")
print(f" {model_b} 胜率: {b_wins/total:.1%}")
print(f" Ties: {ties/total:.1%}")
return results
# 运行
prompts = [...] # 100 条客服真实 prompt
asyncio.run(evaluate(prompts))
7.2 期望产出
gpt-5 vs claude-sonnet-4-5:
gpt-5 胜率: 38%
claude-sonnet-4-5 胜率: 42%
Ties: 20%
🌟 关键设计点(都已加入上面脚本):
- 多 judge 投票(3 个不同厂商)
- Swap robust(每对跑 AB 和 BA)
- Rubric 明确”不要偏好长回答”
- temperature=0.0 减少 judge 噪声
✅ 自我检验清单
- 何时用 LLM Judge:能列出 3 类必须 / 3 类不该的场景
- 三种范式:能默写 Pointwise / Pairwise / Reference-based,各适用场景
- G-Eval 思想:能解释”rubric + CoT + token prob”为什么比直接打分稳
- 4 类 Bias:能默写 Position / Verbosity / Self-preference / Format
- Position swap:能写代码做 swap-robust pairwise
- Self-preference 防御:能解释为什么”评测 GPT 用 Claude judge”
- Cohen’s Kappa:能解释为什么 agreement 0.85 加 kappa 才是好 judge
- 混合策略:能写”verifier 优先 + LLM Judge 兜底”代码
- 成本估算:能算 1000 题 × 多 judge × swap 的总 token cost
- 完整脚本:能默写一段含 4 件套校准的 LLM Judge 评测
📚 参考资料
论文
- Judging LLM-as-a-Judge with MT-Bench (Zheng et al., 2023):arXiv 2306.05685 — 经典开篇
- G-Eval (Liu et al., 2023):arXiv 2303.16634
- PandaLM:Reproducible LLM Evaluation:arXiv 2306.05087
- Length Bias in LLM Judges:多个 2024-2025 论文
框架
- DeepEval G-Eval:deepeval.com/docs/metrics-llm-evals
- MT-Bench:github.com/lm-sys/FastChat/tree/main/fastchat/llm_judge
- ChatBot Arena:chat.lmsys.org