在构建大语言模型应用时,质量评判往往是最难自动化的环节。很多团队引入了 LLM Judge——一个用来给另一个模型输出打分的模型。做法看起来简单,但真正把评测跑起来之后,问题会立刻出现:分数忽高忽低,打分段位和人工判断不一致,同一个回答换一种评价提示词结果就反转。你开始怀疑,自己的 LLM Judge 到底在衡量什么?
这篇内容会把“LLM Judge 在衡量什么”这个问题拆成几条线:先确认评估对象和质量维度,再搭建最小可运行流程,然后设计评分提示词,接着验证 Judge 是否与人类判断一致,最后落到生产环境的评测体系。如果你正在做模型选型、提示词迭代、RAG 评估或评测平台建设,这套思路可以直接用。文章的最终目标不是帮你写出一个“看起来能跑”的打分脚本,而是帮你搞清楚每个分数背后到底反映了什么,以及分数不可信时该从哪一层开始查。
1. 先弄清 LLM Judge 的评估对象和评估意图
1.1 LLM Judge 的直观定义和工作位置
LLM Judge 指的是用一个语言模型去评判另一个模型或应用系统的输出。它可以是独立模型服务,也可以是一套包含评分规则、参考答案、输出格式约束的评估组件。更准确地说,Judge 是一个“评估代理”,它接收一组输入,然后输出一个结构化判断结果,例如分数、理由、是否通过等。
在评测链路中,Judge 通常位于最后一段:被评估对象产生输出,评测数据准备好上下文和参考信息,Judge 读取这些材料后打分。这个位置决定了它能看到什么,也决定了它看不到什么。如果评测环境没有把完整上下文传给 Judge,它就只能根据残缺信息猜测质量,分数自然不可靠。
常见误解是认为 Judge 和“人工标注”等价。人工标注者可以基于领域知识与经验做综合判断,而 Judge 只是“在给定提示词和输入条件下做出推断”。它既可能产生随机性,也可能产生系统性偏差。因此,任何 Judge 分数都不能不加验证地当作事实。
1.2 先区分评估对象:单轮回答、多轮对话、工具调用、检索结果
评估对象不同,衡量方式完全不同。用一个通用模板打天下,是评测体系最容易失败的原因。
- 单轮回答:关注“这个回答是否解决了用户的问题”,重点看正确性、完整性和相关性。
- 多轮对话:还要看上下文一致性、能否承接前面轮次的信息、任务完成度。
- 工具调用:重点看参数是否完整、调用目标是否正确、对工具结果的处理是否合理。
- RAG 检索结果:重点看回答是否忠实于检索上下文、引用是否准确、是否有编造。
以 RAG 为例,单纯问 Judge“这个回答质量高吗”,它可能根据流畅度和表面相关性给高分,却完全忽略回答是否引用了检索片段。这种评测结果看起来“挺合理”,实际衡量的是生成风格,而不是 RAG 系统的核心指标:忠实度。
1.3 给质量目标分类,避免“用一个分数衡量所有事”
先定义质量维度,再决定让 Judge 打哪个维度。常用维度包括:
| 维度 | 要回答的问题 | 典型适用场景 |
|---|---|---|
| 事实性 | 输出内容是否与已知事实一致 | 通用问答、知识库检索 |
| 相关性 | 输出是否回应了用户意图和上下文 | 对话系统、客服机器人 |
| 忠实度 | 输出是否严格基于给定参考资料 | RAG、文档摘要 |
| 完整性 | 输出是否覆盖问题关键方面 | 教程生成、需求分析 |
| 风格 | 语气、格式、专业性是否符合预期 | 内容生成、营销文案 |
| 安全性 | 是否有毒性、偏见、违规内容 | 内容审核、开放域对话 |
| 指令遵循 | 是否遵守约束格式和操作要求 | Agent、结构化输出 |
实际项目中,往往需要同时评估多个维度,但不建议让一个 Judge 一次性打所有维度的总分。多个维度混合在一个 prompt 里,Judge 会很难分配注意力。
推荐的实践是:每个维度独立设置一个评分任务,或者设计多维评分表,让 Judge 在结构化 JSON 里分别给出各维度分数。这样后续可以分析究竟是哪个维度导致整体质量下降,而不是得到一个无法拆解的 3 分。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从零搭一个最小可运行的 LLM Judge 评测流程
2.1 环境准备和依赖
在动手之前,先把环境固定下来。下面这些工具在常见评测项目中可以使用:
- Python 3.10 或更高版本。
- OpenAI 兼容 API 客户端,例如 OpenAI SDK、本地 vLLM 或 Ollama 提供的接口。
- pandas 用于汇总和分析评测结果。
- python-dotenv 管理模型服务的 API Key 和地址。
一个示例 requirements.txt 如下:
text复制openai>=1.0
python-dotenv>=1.0
pandas>=2.0
如果本地部署私有模型,可以通过 OpenAI 兼容接口访问,代码不需要大幅改动。但要注意不同后端的接口能力不同,例如 response_format 参数不一定都支持。落地前要确认客户端版本和服务端接口的兼容范围。
2.2 构造评测数据集:输入、期望输出、上下文
评测数据的最小单位是一条评测用例。它至少包含以下字段:
json复制[
{
"id": "case1",
"user_input": "解释一下什么是二阶段提交",
"reference": "二阶段提交是一种分布式事务协议,包含准备阶段和提交阶段。",
"retrieval_context": "二阶段提交由协调者和参与者构成,分为准备和提交两个阶段。",
"model_output": "二阶段提交是一种分布式一致性协议,分为准备阶段和提交阶段,协调者先收集参与者意见,再决定全局提交或回滚。"
}
]
字段含义需要提前约定:
user_input:用户触发请求的内容。reference:可选,参考答案或黄金答案。retrieval_context:可选,检索系统返回给模型的参考片段。model_output:被评估模型针对user_input生成的结果。
在生成 model_output 时,建议记录模型名、采样参数、响应延迟等信息。这些元数据对后续排查分数偏差非常关键。如果一条评测用例只保存了输出文本,没有保存当时的温度和版本,那么当 Judge 分数异常时,你很难判断是输出本身的问题还是评测设计的问题。
2.3 写一个 Judge 打分函数
下面是一个最小打分函数示例。它调用 OpenAI 兼容接口,要求模型返回 JSON 结构。
python复制import os
import json
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("LLM_API_KEY"),
base_url=os.getenv("LLM_BASE_URL"),
)
JUDGE_PROMPT = """
你是一个严格的评分员。请根据以下评分标准评价模型输出。
评分标准:
5分:准确、完整、结构清晰,完全满足用户意图。
3分:部分正确,存在少量错误或遗漏。
1分:严重错误或与问题无关。
只输出 JSON,不要输出其他解释。
{"score": 1到5的整数, "reason": "你的判断理由"}
"""
def judge_score(user_input, model_output, reference=None, model="gpt-4o-mini", temperature=0):
messages = [
{"role": "system", "content": JUDGE_PROMPT},
{"role": "user", "content": f"用户问题:{user_input}\n模型输出:{model_output}\n参考答案:{reference or '无'}"},
]
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
)
content = response.choices[0].message.content
# 实际使用时要加入 JSON 解析保护,示例只展示思路
return json.loads(content)
这段代码的关键点有三个。第一,temperature=0 降低了采样随机性,但这并不代表结果完全确定,因为后端模型可能仍有随机行为。第二,JUDGE_PROMPT 中明确写了“只输出 JSON”,这是为了让下游解析稳定。第三,参考答案作为可选字段传入,是否传入会改变评估语义。
要注意,如果使用本地模型,response_format 无法保证,服务端可能返回带解释的文本。更稳妥的做法是要求模型输出固定分隔符,或者在后端做一次 JSON 提取。
2.4 汇总结果并生成报告
拿到每一条用例的评分结果后,建议先做统计分析,再看个案:
python复制import pandas as pd
results = [
{"id": "case1", "score": 5, "reason": "内容准确且完整"},
{"id": "case2", "score": 2, "reason": "遗漏了原子性说明"},
]
df = pd.DataFrame(results)
print(df["score"].describe())
print("低分用例:")
print(df[df["score"] <= 2])
这一步的常见问题是只看平均值。平均值容易掩盖分布问题:如果一半用例得 5 分、一半得 1 分,平均分是 3,看起来“中等偏上”,实际上稳定性极差。因此要同时看分数分布、标准差和低分样本。
3. 评分设计决定 Judge 在测什么
3.1 三种评分方式:绝对评分、相对评分、多维评分
Prompt 里写“给回答打分”或“比较两个回答哪个更好”,二者衡量的是完全不同的东西。
| 评分方式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 绝对评分 | 按 1-5 等固定刻度给单条输出打分 | 可跨任务比较、易汇总 | 评分标准难统一,不同模型对刻度理解不同 |
| 相对评分 | 让 Judge 比较两个候选并选择/排序 | 更符合人类直觉、区分度高 | 不能得到绝对质量,只能知道相对好坏 |
| 多维评分 | 一次返回事实性、完整性等多个维度分数 | 信息丰富、便于定位问题 | Prompt 复杂,维度间可能互相干扰 |
如果你的目标是在多个模型之间选型,相对评分往往比绝对评分更可靠。因为人类也很难准确说“这个回答值 4 分还是 5 分”,但很容易判断“A 比 B 更适合用户”。
如果你的目标是持续监控线上质量变化,绝对评分更容易建立基线。此时必须把每个分值的行为描述写清楚。
3.2 把评分标准写入 Prompt 的两种做法
评分标准怎么写,直接决定 Judge 在对齐什么。两种常见写法如下。
第一种是简单描述型,只给一句“请从相关性打分”。这种写法的宽松度很高,Judge 会自行决定相关性是什么。
text复制请为模型输出打分,1-5 分,分数越高表示相关性越强。
第二种是行为化描述型,把每个分数对应到具体行为:
text复制评分标准:
5分:回答直接命中用户问题,包含必要的关键细节,没有多余或错误信息。
4分:回答基本命中用户问题,存在少量不必要信息,但没有错误。
3分:回答部分相关,关键信息有遗漏。
2分:回答与问题弱相关,大部分内容是泛泛而谈。
1分:回答与问题无关,或包含明显错误。
输出格式:JSON {"score": 整数, "reason": "简要理由"}
推荐使用第二种。描述越具体,Judge 的不确定性越小。但行为描述本身也是一种主观设计,需要和人工标注结果比对其有效性。
3.3 分数刻度选择:1-5 分还是 0-1 分
分数刻度不是越多越好。刻度越多,对不同分数之间的边界要求越高。1-5 分对大多数场景足够,因为 Judge 对“3 分和 4 分”的边界通常不敏感。如果评测目标只是“通过/不通过”,二分类反而更稳定。
关于刻度的一个常见坑:不要只定义最高分和最低分。中间档位最容易被忽略,导致 Judge 在犹豫时倾向于打 4 分或 5 分。建议至少把 1、3、5 分的行为描述写出来,必要时补充 2 分和 4 分。
3.4 是否提供参考答案和上下文
是否在 Judge 的输入中放入参考答案,会改变评估性质。
提供参考答案时,Judge 倾向于“看模型输出是否接近参考答案”。这适合评测标准答案明确的任务,例如代码生成、数学题、翻译题。但缺点是对“另一种同样正确的回答”可能误判。
不提供参考答案时,Judge 必须自己建立一份质量标准。这适合开放域问答、文案生成等场景,但代价是 Judge 的主观影响变大。
对于 RAG 场景,建议把检索上下文传给 Judge,并在 Prompt 中明确要求“只能依据给定参考资料判断”。否则 Judge 可能使用自身的世界知识去判断信源,这与 RAG 忠实度评估的初衷相违背。
4. 验证 Judge 本身是否合格:一致性、偏置与稳定性
4.1 为什么 Judge 的验证比业务代码测试更难
普通业务代码可以断言结果是否符合预期,而 Judge 返回的是概率性判断。同一个用例跑两次,可能得到不同分数;同一个用例在输入顺序改变后,结果也可能反转。因此,Judge 上线前必须先验证自身可靠性,否则后续所有质量数据都是不可解释的。
最简单的验证方式是准备一小批已经完成人工标注的样本,让 Judge 在同一批样本上运行,然后计算它与人工标注的对齐程度。
4.2 用准确性、Cohen's Kappa 衡量与人工标注的一致性
示例代码如下:
python复制from sklearn.metrics import accuracy_score, cohen_kappa_score
# 人工标注和 Judge 打分,数值分别对应分数等级
human_labels = [5, 4, 3, 2, 1, 4]
judge_labels = [5, 3, 3, 2, 2, 4]
print("accuracy:", accuracy_score(human_labels, judge_labels))
print("kappa:", cohen_kappa_score(human_labels, judge_labels))
accuracy_score 表示精确一致的比率,但它受类别分布影响。cohen_kappa_score 考虑了随机一致的可能性,更适合衡量两个评估者之间的真实一致性。
经验参考值:Kappa 在 0.6 以上可以接受,0.8 以上适合替代大部分人工评估。但这只是经验值,实际门槛要根据任务难度确定。如果任务本身在人工之间也只有 0.6 的一致性,那么 Judge 达到同样水平已经合理。
4.3 偏置检验:位置、长度、风格、自偏好
Judge 的偏置通常可以用控制变量法检验。构造一组固定答案,每次只改变一个变量,观察分数是否发生系统变化。
| 偏置类型 | 检验方法 | 典型现象 | 处理方式 |
|---|---|---|---|
| 位置偏置 | 交换两个候选回答的顺序 | 排在前面的回答得分更高 | 多次交换顺序取平均,或使用成对比较 |
| 冗长偏置 | 同一内容写成短版和长版 | 长回答总是分数更高 | 在 Prompt 中明确禁止按长度评分 |
| 风格偏置 | 同一内容用 Markdown 和纯文本 | 带格式的回答分更高 | 增加负例,强调只关注内容质量 |
| 自偏好 | 用不同模型生成风格各异的回答 | Judge 更偏好其所属风格的输出 | 使用多个不同模型作为 Judge,或混合厂商模型 |
自偏好问题尤其隐蔽。使用 GPT 系模型做 Judge 时,可能对 GPT 风格的长段落、小标题和总结式开头配方有天然偏好。要检测这一点,可以让同一种内容换一种风格和结构呈现给 Judge,看分数是否出现显著波动。
4.4 稳定性检验:温度、重复采样和案例顺序
稳定性检验是常被忽略的环节。把同一条用例连续送给 Judge 十次,统计分数分布。方差过大说明评测结果不可复现。
稳定性检验流程:
- 固定模型和 Prompt,只改变
temperature或随机种子。 - 对同一组用例运行 2-3 轮。
- 计算每一条用例的分数标准差。
- 标记标准差超过 1 分以上的用例,检查输入是否清晰、评分标准是否有歧义。
如果发现某些用例分数波动大,优先检查 Prompt 描述是否语义含糊,而不是加大采样次数掩盖问题。
5. 从分数异常反推根因:常见问题和排查链路
5.1 Judge 明明给出了分数,但和实际体验严重不符
现象:业务方人工查看一批样本,认为答案质量不错,但 Judge 打出的平均分只有 2 分。
排查顺序:
- 确认
model_output是否完整保存,是否被人为截断。 - 确认 Judge 的输入中是否包含足够上下文。如果模型输出来自 RAG 流程,但 Judge 只拿到问题和回答,它无法判断忠实度。
- 确认评分标准是否只定义了某个维度,比如只要求“相关性”,而业务方关注的是“完整性”。
- 确认人工评判和 Judge 的打分维度是否一致。
这类问题大概率不是模型能力问题,而是评测设计问题。建议先让业务方和算法同事一起对 10-20 条低分样本做分类,看低分原因是同一个维度还是分散在不同维度。
5.2 Judge 输出 JSON 解析失败
现象:评分脚本大量报 JSONDecodeError,或者 Judge 返回了多余的前缀后缀。
可能原因:
- 本地模型服务不支持
response_format。 - 输出长度超出模型最大 token 限制,被截断。
- Prompt 中没有明确“不要输出其他内容”。
解决方案:
- 提取文本中的第一个合法 JSON 子串。
- 如果不要求 JSON,使用固定分隔符,例如“SCORE:”和“REASON:”。
- 在解析失败时本试卷重试一次,重试仍然失败就标记为
invalid,不要静默丢弃。
示例提取函数:
python复制import json
import re
def extract_json(text):
try:
return json.loads(text)
except json.JSONDecodeError:
match = re.search(r"\{.*\}", text, re.DOTALL)
if match:
return json.loads(match.group())
return None
5.3 分数普遍偏高或偏低
如果所有用例的分数都集中在 4-5 分,或者集中在 1-2 分,说明评分刻度没有被正确使用。
常见原因:
- “5 分”被定义成“还不错”,而不是“完美”,Judge 自然会往高分打。
- “1 分”被定义成“完全不相关”,导致错误但相关的回答被认为不够差。
temperature设置过高时,评分的随机波动也会让分数向中间档位集中。
解决方案是调整 Prompt 中的行为描述,并加入中间档位。同时可以补充 few-shot 示例,给 Judge 展示“什么样的输出该打多少分”。
5.4 与人工评分不一致但无法定位
按以下顺序检查,每步都要输出中间结果,不要凭感觉下结论:
| 步骤 | 检查内容 | 工具或方法 |
|---|---|---|
| 1 | 人工标注本身的一致性 | 让两位标注员重复标同一批数据,计算 Kappa |
| 2 | 输入信息是否一致 | 检查人工标注时是否看到了额外背景 |
| 3 | 评分标准是否存在歧义 | 列出 Judge 的“reason”字段,看它的解释逻辑 |
| 4 | 是否存在偏置 | 做位置、长度、风格控制变量实验 |
| 5 | 是否偶尔随机错误 | 重复运行 3 次,看分数分布 |
实际项目中,最常被忽略的是第 1 步。很多团队先怀疑 Judge,最后发现人工标注本身就存在分歧。此时需要先定义“一致的标注规范”,再重新评估 Judge。
6. 生产环境的 LLM Judge 评测体系
6.1 学习环境与生产环境的差异
本地跑通几十条用例和生产环境持续评估数万条用例,完全是两个工程问题。
| 维度 | 学习/原型阶段 | 生产环境 |
|---|---|---|
| 数据量 | 几十到几百条 | 数千到数万条 |
| 成本 | 不敏感 | 需要预算估算和限流 |
| 结果可复现性 | 允许波动 | 需要固定版本、固定温度 |
| 审计要求 | 不做日志也能理解 | 需要保留每次评估的输入、输出、Prompt 版本 |
| 回归 | 一次性运行 | 需要有固定基线集和告警 |
在生产环境直接复用原型代码会带来两个后果:成本失控和结果不可追溯。应该把评测脚本改造成批处理任务,并将每次运行都记录为一条评测记录。
6.2 多 Judge 投票与分层评审
为了降低单一 Judge 的偏差,常见做法有三种。
第一种是双 Judge 独立打分。当两者分数一致时采用共同结果,不一致时由第三方仲裁或人工介入。
第二种是不同厂商模型混合评审,例如一个来自服务 API,一个来自本地模型。这样可以降低自偏好,但因为模型能力差异,两者对同一个输出的理解可能系统性不同,需要在正式使用前先做一致性验证。
第三种是分层评审。首先用规则过滤明显不合格结果,例如空回答、超长回答、格式错误;然后再用 LLM Judge 对剩余样本做质量打分。这样可以减少不必要的模型调用,也能避免规则和模型混在一起造成判断混乱。
6.3 评测集分级与回归机制
评测集只分“训练集”和“测试集”不够,生产环境还需要按变更频率和风险分级。
| 分级 | 内容 | 触发时机 |
|---|---|---|
| 红灯集 | 核心高危用例,例如安全、账号、支付 | 每次部署前后必须跑 |
| 回归集 | 覆盖主要任务类型的历史问题 | 每周或每版模型发布时跑 |
| 探索集 | 新场景、新数据、边界输入 | 季度性扩展和评估 |
红灯集的结果不需要做统计平均,只要有一条失败就应该阻止上线。回归集则用于观察质量变化趋势。探索集的目的是发现评测集的盲点,不能用于通过/不通过判断。
6.4 日志、版本和审计
每次评测运行至少要记录以下信息:
- 被评估模型的名称和版本。
- Judge 模型的名称和版本。
- Judge Prompt 的版本号。
- 采样参数,尤其是
temperature和max_tokens。 - 每条用例的原始输出、Judge 原始输出、解析后的分数和理由。
- 运行时间、数据文件版本、环境信息。
这样当分数出现异常时,可以先看“是不是某个环节的版本变了”,再决定是回滚还是调整。没有这些信息,任何趋势分析都很难定位真实原因。
7. 可复用清单和下一步扩展方向
7.1 评测目标定义清单
在写任何 Judge Prompt 之前,先确认这些项:
- [ ] 被评估对象是单轮回答、多轮对话、工具调用还是 RAG 检索结果。
- [ ] 本次评估只关注一个维度还是多个维度。
- [ ] 每个维度是否有明确的行为化描述,而不是“质量高”“效果好”这样的泛化词。
- [ ] 是否提供上下文或参考材料,提供后是否会影响评估语义。
- [ ] 最终决策依赖的是绝对分数、相对排序还是多维画像。
7.2 Judge Prompt 设计清单
- [ ] 评分刻度是否覆盖了中间的档位。
- [ ] 是否明确禁止基于长度、格式、风格进行打分。
- [ ] 是否要求输出结构化结果,并配置了解析失败兜底。
- [ ] 是否保存了 Judge 的原始返回,而不是只保存解析后的分数。
- [ ] 是否在 Prompt 中加入与当前任务相关的 few-shot 示例。
7.3 评测上线前检查清单
- [ ] 是否用小样本人工标注和 Judge 结果计算过一致性和 Kappa。
- [ ] 是否运行过位置、长度、风格等偏置检验。
- [ ] 是否对关键用例做过 3 次以上重复采样,确认方差在可接受范围。
- [ ] 是否估算过每次全量评测的 token 成本和耗时。
- [ ] 是否定义过失败判据,例如 JSON 解析失败率的上限。
7.4 扩展方向
LLM Judge 这条技术路线还有几个值得深入的方向。
第一个方向是低成本蒸馏打分。把大型模型的评分逻辑蒸馏成小型模型,在保留大部分一致性的同时降低成本和延迟。但需要注意,蒸馏会继承教师模型的偏置。
第二个方向是专业化 Judge 团队。让一个 Judge 只负责事实性,另一个只负责风格,最后用一个汇总器综合。这种方式在多维度评估场景下通常比单一 Judge 更可控。
第三个方向是动态基准集。根据真实用户反馈和线上错误样本,定期补充评测集,让评测能力跟上系统变化。评测集长期不更新,分数就会逐渐失去代表性。
第四个方向是多语言和多轮场景下的一致性验证。不同语言、不同语境下,Judge 的评分标准是否仍然有效,需要单独设计对照实验。
回到最初的问题:你的 LLM Judge 到底在衡量什么?答案不在模型本身,而在你设计的输入、评分标准、验证方式和审计流程里。只有把这些环节都落到可检查、可复现、可解释的程度,评测分数才有资格成为质量决策的依据。对刚接触评测体系的开发者,最值得先做的一件事,就是拿 20 条线下样本,分别用人工和 Judge 跑一遍,对比它们的理由和分数。这个动作的成本很低,却能最快暴露出你的评测设计究竟在哪里出了问题。
