10.3 LLM-as-Judge
上一节我们建立了 Benchmark 和测试集——有了"考卷"。但开放式问答题怎么打分?精确匹配太死板,人工评分太昂贵。这一节我们介绍一种"用 AI 当裁判"的评估方法——LLM-as-Judge。
生活类比:LLM-as-Judge 就像是请了一个资深教师来批阅作文——他不是死板地核对标准答案,而是从立意、结构、语言等多个维度给出评分和评语。而这位"教师"不知疲倦、成本可控、还能同时批改一万份试卷。
为什么需要 LLM-as-Judge?
传统评估方法在面对 Agent 的开放式输出时捉襟见肘:
┌──────────────────────────────────────────────────────────────────┐
│ 传统评估 vs LLM-as-Judge │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 传统方法: │
│ ├─ 精确匹配(Exact Match):只有完全一致才算对 │
│ │ └─ 问题:对于开放式回答,语义相同但表达不同被判错 │
│ ├─ 规则匹配(Regex/Keywords):检查关键词是否出现 │
│ │ └─ 问题:规则难以覆盖所有变体,维护成本高 │
│ └─ 人工评估(Human Evaluation):最准确但最昂贵 │
│ └─ 问题:成本高、速度慢、不可扩展 │
│ │
│ LLM-as-Judge: │
│ ├─ 语义理解:能判断语义等价而非字面匹配 │
│ ├─ 灵活评分:可给出多维度、多粒度的评分 │
│ ├─ 可解释性:能给出评分理由和改进建议 │
│ └─ 可扩展:一次 API 调用即可评估,成本可控 │
│ │
│ 核心理念:用更强的 LLM 来评估较弱 LLM / Agent 的输出 │
│ │
└──────────────────────────────────────────────────────────────────┘生活类比:精确匹配就像让机器按"标准答案一字不差"来阅卷——"今天天气晴"和"今天是晴天"会被判为不同答案。LLM-as-Judge 则像人类老师,理解两者表达的是同一个意思。
LLM-as-Judge 的典型应用场景:
| 场景 | 评估内容 | 示例 |
|---|---|---|
| 回答质量 | 正确性、完整性、相关性 | "这个回答是否准确回答了用户问题?" |
| 安全性 | 有害内容、偏见、违规 | "这个回答是否包含不安全的建议?" |
| Agent 轨迹 | 规划合理性、工具选择 | "Agent 的工具调用顺序是否合理?" |
| RAG 质量 | 忠实度、上下文利用 | "生成内容是否忠实于检索到的文档?" |
| 对话质量 | 流畅度、共情、一致性 | "这个回复是否自然且符合上下文?" |
Judge 模型的选型策略
选 Judge 模型就像选裁判——不同级别的比赛需要不同水平的裁判。社区篮球赛找个懂规则的就行,NBA 总决赛得请最顶级的裁判。
┌──────────────────────────────────────────────────────────────────┐
│ Judge 模型选型决策树 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 需要评估什么? │
│ ├─ 简单事实正确性 ──► 中等模型即可(GPT-4o-mini / Claude Haiku) │
│ ├─ 复杂推理质量 ──► 强推理模型(GPT-4o / Claude Sonnet) │
│ ├─ 代码质量 ──► 代码专用模型或强模型(Claude 3.5 / GPT-4o) │
│ ├─ 创意/主观质量 ──► 多 Judge 投票 + 人工校准 │
│ └─ 安全/合规 ──► 专用安全模型 + 人工审核 │
│ │
│ 关键原则: │
│ 1. Judge 模型能力 > 被评估模型能力(通常) │
│ 2. 成本与效果平衡:高频评估用轻量模型,关键评估用强模型 │
│ 3. 多模型交叉验证:减少单一模型的系统性偏差 │
│ │
└──────────────────────────────────────────────────────────────────┘常用 Judge 模型对比:
| 模型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| GPT-4o | 综合能力强,指令遵循好 | 成本较高 | 通用评估 |
| GPT-4o-mini | 成本低,速度快 | 推理能力有限 | 简单评估、大规模 |
| Claude 3.5 Sonnet | 推理细腻,长文本好 | 有时过于谨慎 | 代码、复杂推理 |
| Gemini 1.5 Pro | 长上下文,多模态 | 一致性略低 | 多模态评估 |
| DeepSeek-V3 | 性价比高 | 生态不如 OpenAI | 中文场景 |
核心原则:用博士生改本科生的作业没问题,但反过来就会出错。Judge 模型的能力必须强于被评估的模型。
Judge Prompt 设计方法论
Judge Prompt 的设计直接决定了评估质量——就像考试评分标准写得越清楚,阅卷越准确。以下是核心设计框架:
┌──────────────────────────────────────────────────────────────────┐
│ Judge Prompt 设计框架 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 1. 角色定义(Role) │
│ "你是一个专业的 AI 输出质量评估专家..." │
│ │
│ 2. 评估维度(Dimensions) │
│ 明确列出评估的各个维度及权重 │
│ │
│ 3. 评分标准(Rubric) │
│ 每个维度的具体评分标准,用示例说明 │
│ │
│ 4. 输出格式(Output Format) │
│ 严格的 JSON 格式要求,便于自动化解析 │
│ │
│ 5. 示例(Few-shot Examples) │
│ 提供 2-3 个标注好的评估示例 │
│ │
│ 6. 约束条件(Constraints) │
│ 避免常见偏差的指令(如位置偏差、长度偏差) │
│ │
└──────────────────────────────────────────────────────────────────┘Judge Prompt 模板示例:
# Judge Prompt 模板——用于评估 Agent 回答质量
# 使用 {user_query}、{agent_response}、{expected_response} 作为占位符
JUDGE_PROMPT_TEMPLATE = """
你是一个专业的 AI Agent 输出质量评估专家。请根据以下标准评估 Agent 的回答质量。
## 评估维度
请从以下 5 个维度对回答进行评分(每个维度 1-5 分):
1. **正确性(Correctness)**:回答的事实是否正确?信息是否准确无误?
- 5分:完全正确,所有信息准确
- 3分:大部分正确,有少量不准确
- 1分:严重错误,误导用户
2. **完整性(Completeness)**:回答是否完整覆盖了用户问题的所有方面?
- 5分:全面覆盖,无遗漏
- 3分:覆盖了主要方面,有少量遗漏
- 1分:严重不完整,遗漏关键信息
3. **相关性(Relevance)**:回答是否与用户问题紧密相关?有无无关内容?
- 5分:高度相关,直击要点
- 3分:基本相关,有少量偏离
- 1分:答非所问
4. **清晰度(Clarity)**:回答的表达是否清晰易懂?结构是否合理?
- 5分:表达清晰,结构合理
- 3分:基本清晰,结构可优化
- 1分:混乱难懂
5. **有用性(Helpfulness)**:回答是否真正解决了用户的问题?
- 5分:完美解决用户问题
- 3分:部分解决,用户仍需补充信息
- 1分:毫无帮助
## 用户问题
{user_query}
## Agent 回答
{agent_response}
## 期望回答(参考)
{expected_response}
## 输出格式
请严格按照以下 JSON 格式输出评估结果,不要包含任何其他内容:
```json
{{
"scores": {{
"correctness": <1-5>,
"completeness": <1-5>,
"relevance": <1-5>,
"clarity": <1-5>,
"helpfulness": <1-5>
}},
"overall_score": <1-5>,
"strengths": ["<优点1>", "<优点2>"],
"weaknesses": ["<不足1>", "<不足2>"],
"suggestion": "<改进建议>"
}}重要提醒
- 请客观公正,不要因为回答长度而影响评分
- 考虑回答是否真正解决了用户的问题,而非形式上的完美
- 如果期望回答标记为"无",请仅基于用户问题判断 """
#### 实现 LLM-as-Judge 评估器
下面是完整的 LLM-as-Judge 评估器实现,代码逐行注释:
```python
import json # JSON 解析
import asyncio # 异步处理
from typing import Dict, List, Optional # 类型注解
from dataclasses import dataclass, field # 数据类
from openai import AsyncOpenAI # OpenAI 异步客户端
@dataclass
class EvalDimension:
"""
评估维度定义——描述一个评估维度及其评分标准
每个维度有名称、描述、权重和评分指南。
权重决定了该维度在总分中的占比。
"""
name: str # 维度名称,如"正确性"
description: str # 维度描述
weight: float # 权重(0.0-1.0,所有维度权重之和为 1.0)
scoring_guide: Dict[int, str] # 评分指南:{5: "完全正确", 3: "大部分正确", 1: "严重错误"}
@dataclass
class EvalResult:
"""单次评估结果——一个 Judge 对一次评估的完整输出"""
scores: Dict[str, float] # 各维度得分,如 {"正确性": 4.5}
overall_score: float # 总分(加权平均)
strengths: List[str] # 优点列表
weaknesses: List[str] # 不足列表
suggestion: str # 改进建议
judge_model: str # 使用的 Judge 模型名称
raw_response: str # Judge 的原始 JSON 响应
class LLMJudge:
"""
LLM-as-Judge 评估器——使用 LLM 自动评估 Agent 输出质量
核心流程:
1. 构建 System Prompt(包含评估维度和评分标准)
2. 构建 User Prompt(包含用户问题和 Agent 回答)
3. 调用 LLM 获取评估结果(JSON 格式)
4. 解析结果并返回 EvalResult
"""
# 默认评估维度——5 个维度的权重之和为 1.0
DEFAULT_DIMENSIONS = [
EvalDimension(
name="正确性",
description="回答的事实准确性",
weight=0.25, # 正确性权重最高
scoring_guide={
5: "完全正确,所有信息准确无误",
3: "大部分正确,有少量不准确信息",
1: "严重错误,包含误导性信息",
}
),
EvalDimension(
name="完整性",
description="回答是否覆盖了问题的所有方面",
weight=0.20,
scoring_guide={
5: "全面覆盖,无遗漏",
3: "覆盖主要方面,有少量遗漏",
1: "严重不完整,遗漏关键信息",
}
),
EvalDimension(
name="相关性",
description="回答与用户问题的相关程度",
weight=0.20,
scoring_guide={
5: "高度相关,直击要点",
3: "基本相关,有少量偏离",
1: "答非所问",
}
),
EvalDimension(
name="清晰度",
description="回答的表达清晰度和结构合理性",
weight=0.15,
scoring_guide={
5: "表达清晰,结构合理,易于理解",
3: "基本清晰,结构可优化",
1: "混乱难懂,逻辑不清",
}
),
EvalDimension(
name="有用性",
description="回答是否真正解决了用户问题",
weight=0.20,
scoring_guide={
5: "完美解决用户问题,可直接使用",
3: "部分解决,用户仍需补充信息",
1: "毫无帮助,未解决用户问题",
}
),
]
def __init__(
self,
model: str = "gpt-4o", # Judge 模型,默认 GPT-4o
api_key: Optional[str] = None, # API Key
base_url: Optional[str] = None, # 自定义 API 地址(可选)
):
self.model = model # 保存模型名称
self.client = AsyncOpenAI( # 初始化异步 OpenAI 客户端
api_key=api_key,
base_url=base_url,
)
self.dimensions = self.DEFAULT_DIMENSIONS # 使用默认评估维度
def _build_system_prompt(self) -> str:
"""构建系统提示词——包含评估维度、评分规则和输出格式"""
# 拼接所有维度的描述
dims_desc = "\n".join([
f" - {d.name}(权重 {d.weight}):{d.description}\n"
f" {d.scoring_guide}"
for d in self.dimensions
])
return f"""你是一个专业的 AI 输出质量评估专家。请严格按照以下标准评估回答质量。
## 评估维度
{dims_desc}
## 评分规则
- 每个维度给出 1-5 分的整数评分
- 总分 = sum(各维度分数 × 权重)
- 必须给出具体的优点和不足
- 必须给出可操作的改进建议
## 输出格式
必须是合法的 JSON,格式如下:
{{
"scores": {{"维度名": 分数, ...}},
"overall_score": 总分(保留 1 位小数),
"strengths": ["优点1", "优点2"],
"weaknesses": ["不足1", "不足2"],
"suggestion": "改进建议"
}}
## 重要提醒
- 客观公正,不受回答长度影响
- 基于回答内容本身判断,不为缺失的信息编造理由
- 如果回答确实优秀,大方给高分;如果确实差,果断给低分
"""
async def evaluate(
self,
user_query: str, # 用户问题
agent_response: str, # Agent 的回答
expected_response: Optional[str] = None, # 期望回答(可选)
) -> EvalResult:
"""执行评估——核心方法"""
# 构建 User Prompt
user_prompt = f"""## 用户问题
{user_query}
## Agent 回答
{agent_response}
"""
# 如果有期望回答,添加到 Prompt 中
if expected_response:
user_prompt += f"""
## 参考回答
{expected_response}
"""
# 调用 LLM API
response = await self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": self._build_system_prompt()},
{"role": "user", "content": user_prompt},
],
temperature=0.1, # 低温度以获得一致的评估结果
response_format={"type": "json_object"}, # 强制 JSON 输出
)
raw = response.choices[0].message.content # 获取原始响应文本
parsed = json.loads(raw) # 解析 JSON
return EvalResult(
scores=parsed["scores"],
overall_score=parsed["overall_score"],
strengths=parsed["strengths"],
weaknesses=parsed["weaknesses"],
suggestion=parsed["suggestion"],
judge_model=self.model,
raw_response=raw,
)
# 使用示例
async def main():
"""演示 LLM-as-Judge 的使用"""
judge = LLMJudge(model="gpt-4o") # 初始化 Judge
# 测试用例
test_cases = [
{
"query": "Python 中如何读取 CSV 文件?",
"response": "用 pandas 的 read_csv 函数即可,例如 pd.read_csv('file.csv')",
"expected": "可以使用 csv 模块或 pandas 库。csv 模块:csv.reader();pandas:pd.read_csv()。",
},
{
"query": "解释一下什么是递归?",
"response": "递归就是函数自己调用自己。",
"expected": "递归是一种编程技巧,函数通过调用自身来解决子问题。需要基准条件和递归条件。",
},
]
for i, case in enumerate(test_cases):
result = await judge.evaluate(
user_query=case["query"],
agent_response=case["response"],
expected_response=case["expected"],
)
print(f"\n{'='*50}")
print(f"测试用例 {i+1}: {case['query'][:30]}...")
print(f"总分: {result.overall_score}")
print(f"各维度: {json.dumps(result.scores, ensure_ascii=False)}")
print(f"优点: {result.strengths}")
print(f"不足: {result.weaknesses}")
print(f"建议: {result.suggestion}")
if __name__ == "__main__":
asyncio.run(main()) # 运行异步主函数多 Judge 投票机制
单一 Judge 可能存在系统性偏差——就像只有一个裁判的比赛,裁判的主观倾向会影响结果。多 Judge 投票可以显著提升评估的可靠性。
┌──────────────────────────────────────────────────────────────────┐
│ 多 Judge 投票架构 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ 待评估输出 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │ Judge A │ │ Judge B │ │ Judge C │ │
│ │(GPT-4o) │ │(Claude) │ │(Gemini) │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ │ 评分 A │ 评分 B │ 评分 C │
│ └───────────────┼───────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ 投票/聚合 │ │
│ │ - 多数投票 │ │
│ │ - 加权平均 │ │
│ │ - 一致性检查│ │
│ └──────┬──────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 最终评分 │ │
│ └───────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘生活类比:多 Judge 投票就像选秀节目的评委团——三个评委各自打分,取平均或多数意见。单个评委可能有偏好(比如偏爱某种风格),但三个不同风格的评委一起评,偏好就被中和了。
多 Judge 聚合策略:
| 策略 | 方法 | 适用场景 |
|---|---|---|
| 多数投票 | 取多数 Judge 一致的结果 | 分类任务(通过/不通过) |
| 加权平均 | 按 Judge 可靠性加权取平均 | 连续评分任务 |
| 中位数 | 取中位数,抵抗极端值 | 存在 outlier Judge 时 |
| 一致性过滤 | 仅保留一致性高的评估 | 需要高置信度时 |
多 Judge 投票系统实现
import asyncio # 异步并发
import json
import numpy as np # 数值计算
from typing import Dict, List, Optional
from dataclasses import dataclass
@dataclass
class JudgeConfig:
"""
Judge 配置——描述一个 Judge 实例的配置
weight 用于加权平均:权重越高,该 Judge 的评分影响力越大。
"""
name: str # Judge 名称,如 "judge_gpt4o"
model: str # 模型名称,如 "gpt-4o"
api_key: str # API Key
base_url: str # API 地址
weight: float = 1.0 # 权重(默认 1.0,等权)
class MultiJudgeSystem:
"""
多 Judge 投票系统——并行调用多个 Judge 并聚合结果
优势:
- 减少单一模型的系统性偏差
- 不同模型家族交叉验证
- 一致性检查可识别分歧较大的评估项
"""
def __init__(self, judges: List[JudgeConfig]):
"""初始化多 Judge 系统"""
self.judges = judges # Judge 配置列表
self.judge_instances = {} # Judge 实例字典
for config in judges:
self.judge_instances[config.name] = LLMJudge(
model=config.model,
api_key=config.api_key,
base_url=config.base_url,
)
async def evaluate(
self,
user_query: str,
agent_response: str,
expected_response: Optional[str] = None,
) -> Dict:
"""多 Judge 并行评估——同时调用所有 Judge"""
tasks = [] # 异步任务列表
for name, judge in self.judge_instances.items():
tasks.append(
judge.evaluate(user_query, agent_response, expected_response)
)
# 并行执行所有 Judge 评估
results: List[EvalResult] = await asyncio.gather(*tasks)
aggregated = self._aggregate_results(results) # 聚合结果
consensus = self._check_consensus(results) # 一致性检查
return {
"aggregated": aggregated, # 聚合后的评分
"consensus": consensus, # 一致性分析
"individual_results": [ # 各 Judge 的详细结果
{
"judge": config.name,
"model": config.model,
"overall_score": result.overall_score,
"scores": result.scores,
}
for config, result in zip(self.judges, results)
],
}
def _aggregate_results(self, results: List[EvalResult]) -> Dict:
"""聚合多个 Judge 的结果——加权平均"""
weights = [j.weight for j in self.judges] # 提取权重列表
# 加权平均总分
overall_scores = [r.overall_score for r in results]
weighted_avg = np.average(overall_scores, weights=weights)
# 各维度加权平均
dim_scores = {} # 维度 -> 分数列表
all_dims = set() # 所有出现的维度
for r in results:
all_dims.update(r.scores.keys())
for dim in all_dims:
dim_values = [r.scores.get(dim, 0) for r in results]
dim_scores[dim] = np.average(dim_values, weights=weights)
std_dev = np.std(overall_scores) # 标准差(衡量一致性)
median = np.median(overall_scores) # 中位数
return {
"weighted_avg": round(weighted_avg, 1),
"median": round(median, 1),
"std_dev": round(std_dev, 2), # 标准差越小,一致性越高
"dimension_scores": {
dim: round(score, 1) for dim, score in dim_scores.items()
},
"min_score": min(overall_scores),
"max_score": max(overall_scores),
}
def _check_consensus(
self, results: List[EvalResult], threshold: float = 0.5
) -> Dict:
"""
检查 Judges 之间的一致性
- 高一致性:std_dev < 0.5(可直接采纳)
- 中一致性:std_dev < 1.0(建议人工抽检)
- 低一致性:std_dev >= 1.0(需要人工评估)
"""
scores = [r.overall_score for r in results]
std_dev = np.std(scores) # 计算标准差
if std_dev < 0.5:
level = "高"
action = "可以直接采纳聚合结果"
elif std_dev < 1.0:
level = "中"
action = "建议人工抽检低分项"
else:
level = "低"
action = "需要进行人工评估,或检查 Judge Prompt"
# 找出分歧较大的维度
disagreements = []
if std_dev >= 0.5:
dim_scores = {}
for r in results:
for dim, score in r.scores.items():
if dim not in dim_scores:
dim_scores[dim] = []
dim_scores[dim].append(score)
for dim, values in dim_scores.items():
if np.std(values) >= 1.0: # 维度内标准差 >= 1.0
disagreements.append(dim)
return {
"level": level,
"std_dev": round(std_dev, 2),
"action": action,
"disagreed_dimensions": disagreements, # 分歧较大的维度
}
# 使用示例
async def multi_judge_demo():
"""多 Judge 评估演示"""
judges = [
JudgeConfig(
name="judge_gpt4o",
model="gpt-4o",
api_key="your-openai-key",
base_url="https://api.openai.com/v1",
weight=1.0, # 等权
),
JudgeConfig(
name="judge_claude",
model="claude-3-5-sonnet-20241022",
api_key="your-anthropic-key",
base_url="https://api.anthropic.com/v1",
weight=1.0,
),
JudgeConfig(
name="judge_gpt4o_mini",
model="gpt-4o-mini",
api_key="your-openai-key",
base_url="https://api.openai.com/v1",
weight=0.5, # 轻量模型权重较低
),
]
system = MultiJudgeSystem(judges)
result = await system.evaluate(
user_query="解释 Kubernetes 中 Pod 和 Service 的关系",
agent_response="Pod 是 K8s 中运行容器的基本单元,Service 是为 Pod 提供稳定网络访问的抽象。",
expected_response="Pod 是部署和运行容器的最小单元。Service 是为一组 Pod 提供统一访问入口的抽象层。",
)
print("多 Judge 评估结果")
print("=" * 50)
print(f"\n聚合结果:")
print(f" 加权平均: {result['aggregated']['weighted_avg']}")
print(f" 中位数: {result['aggregated']['median']}")
print(f" 标准差: {result['aggregated']['std_dev']}")
print(f"\n一致性:")
print(f" 级别: {result['consensus']['level']}")
print(f" 建议: {result['consensus']['action']}")
print(f"\n各 Judge 详细结果:")
for r in result['individual_results']:
print(f" {r['judge']} ({r['model']}): {r['overall_score']}")
if __name__ == "__main__":
asyncio.run(multi_judge_demo())LLM-as-Judge 的常见偏差与缓解
LLM 作为 Judge 并非完美,它也有自己的"偏见"。了解这些偏差并采取缓解策略,是保证评估质量的关键:
┌──────────────────────────────────────────────────────────────────┐
│ 常见偏差类型及缓解策略 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 1. 位置偏差(Position Bias) │
│ 问题:Judge 倾向于给靠前的选项更高分 │
│ 缓解:随机打乱选项顺序,多次评估取平均 │
│ │
│ 2. 长度偏差(Length Bias) │
│ 问题:Judge 倾向于给更长的回答更高分 │
│ 缓解:在 Prompt 中明确要求忽略长度,控制输出长度 │
│ │
│ 3. 自我增强偏差(Self-enhancement Bias) │
│ 问题:Judge 倾向于给风格与自己相似的输出更高分 │
│ 缓解:使用不同家族的模型作为 Judge │
│ │
│ 4. 顺序效应(Order Effect) │
│ 问题:评估顺序影响评分(疲劳效应或对比效应) │
│ 缓解:随机化评估顺序,控制批次大小 │
│ │
│ 5. 锚定效应(Anchoring Effect) │
│ 问题:先看到的评分标准或示例会影响后续判断 │
│ 缓解:使用明确的 Rubric,减少模糊空间 │
│ │
│ 6. 幻觉(Hallucination in Judge) │
│ 问题:Judge 自己编造不存在的错误或优点 │
│ 缓解:要求 Judge 引用原文中的具体内容来支持判断 │
│ │
└──────────────────────────────────────────────────────────────────┘生活类比:
- 位置偏差:就像老师阅卷时,先看的几份卷子普遍给分宽松,后面的越改越严。
- 长度偏差:就像"作文写得长就给高分"——内容空洞但字数多的反而比精炼的好文得分高。
- 自我增强偏差:就像文科老师给文科风格的文章打分更高,理科风格被低估。
- 幻觉:Judge 说"第三段引用了错误的数据",但其实原文根本没有第三段——这就是 Judge 自己"编"出来的。
常见误区
误区一:Judge 模型和被评估模型用同一个
用 GPT-4o 评估 GPT-4o 的输出,Judge 会天然偏好与自己风格相似的回答——这就是"自我增强偏差"。正确做法是使用不同家族的模型,或至少用更强版本的模型评估较弱版本的输出。
误区二:Prompt 写得太简单,Judge 自由发挥
如果只告诉 Judge "请评估这个回答的好坏",它可能给出"挺好的"这样模糊的评语。必须明确评估维度、评分标准(Rubric)和输出格式,才能得到可量化、可自动解析的结果。
误区三:不校准 Judge 的一致性
LLM-as-Judge 不是"设好就忘"的。随着模型版本更新和业务变化,Judge 的评估标准可能漂移。建议定期用人工标注的样本校准 Judge,确保评估质量不退化。
误区四:忽视 JSON 解析失败的情况
LLM 有时不会严格按 JSON 格式输出——可能多了说明文字、嵌套了代码块、或者格式错误。生产代码必须包含 JSON 解析失败的重试逻辑和容错处理。
本节小结
| 概念 | 关键点 | 实践建议 |
|---|---|---|
| Judge 模型选择 | 能力 > 被评估模型 | 用 GPT-4o 评估 GPT-4o-mini,不要反向 |
| Prompt 设计 | 明确维度 + Rubric + 示例 | 至少提供 2-3 个标注示例 |
| 输出格式 | 强制 JSON | 使用 response_format={"type": "json_object"} |
| 多 Judge | 不同模型家族交叉验证 | 3 个不同模型家族的 Judge |
| 偏差缓解 | 位置/长度/自我增强 | 随机化、Rubric 约束、多模型 |
| 成本控制 | 高频评估用轻量模型 | GPT-4o-mini 做初筛,GPT-4o 做复核 |
关键原则:
- LLM-as-Judge 是目前评估开放式 Agent 输出的最实用方案,但需要精心设计 Prompt
- Judge 模型的选择应遵循"用更强的模型评估较弱模型"的原则
- 多 Judge 投票可以显著降低单一模型的系统性偏差,代价是成本和延迟增加
- 定期用人工评估校准 Judge 的一致性,确保评估质量不退化
- 结构化输出(JSON)是自动化评估流水线的基础
有了自动评分的"阅卷老师",下一步需要把评估流程嵌入开发流水线——这正是 10.4 节评估自动化要解决的问题。