一、为什么需要LLM当裁判
Agent迭代最痛的点是评测:改了一版Prompt,到底变好还是变差?人工评测一天只能看几十条,样本少、标准飘、成本高。LLM-as-Judge(用大模型当裁判) 是目前业界主流解法:让一个独立的LLM按既定标准给Agent输出打分,把评测变成可批量、可回归的流水线。
二、Judge提示词设计:先定标准再打分
裁判最怕"凭感觉"。评分前必须把标准写死,维度、分值、锚点一个都不能少:
JUDGE_PROMPT = """你是一名严格的AI Agent质量评审员。请对下面的【模型回答】按维度打分,每项1-5分。
评分锚点:
5分=完全正确且信息充分;4分=正确但略有遗漏;3分=部分正确;
2分=存在明显错误;1分=完全不相关或严重幻觉。
【用户问题】
{question}
【标准答案(如有)】
{reference}
【模型回答】
{answer}
请严格按以下JSON格式输出,不要输出其他内容:
{{"准确率": 5, "完整性": 4, "格式合规": 5, "理由": "一句简短理由"}}"""
def judge_score(question, answer, reference="", judge_llm=None):
"""调用裁判模型,解析出结构化评分"""
prompt = JUDGE_PROMPT.format(
question=question, reference=reference or "无", answer=answer)
raw = judge_llm.chat(prompt, max_tokens=200, temperature=0)
try:
return json.loads(extract_json(raw)) # 解析JSON部分
except Exception:
return {"准确率": 1, "完整性": 1, "格式合规": 1, "理由": "裁判输出解析失败"}
裁判与选手必须隔离:评测用的Judge模型要和Agent主模型区分开,避免"自己给自己打分"的自夸偏差。
三、两种评测模式:成对比较 vs 单点打分
| 模式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 成对比较 | 让Judge选A/B哪个更好 | 区分度高,适合Prompt A/B | 只能排序,说不出绝对质量 |
| 单点打分 | 按rubric逐维度打分 | 可量化、可设阈值 | 分数漂移,需锚点校准 |
生产建议:调优阶段用成对比较,发布前用单点打分。成对比较实现:
def pairwise_compare(question, answer_a, answer_b, judge_llm):
"""成对比较,自动轮换顺序消除位置偏差"""
votes = {"A": 0, "B": 0, "tie": 0}
for order in [(answer_a, answer_b), (answer_b, answer_a)]: # 正反各评一次
prompt = f"""以下是针对同一问题的两个回答,请判断哪个更好。
只输出 A / B / 平局。
问题:{question}
回答1:{order[0]}
回答2:{order[1]}
更好的回答是:"""
choice = judge_llm.chat(prompt, max_tokens=5, temperature=0).strip().upper()
if choice == "A":
votes["A" if order[0] is answer_a else "B"] += 1
elif choice == "B":
votes["B" if order[1] is answer_b else "A"] += 1
else:
votes["tie"] += 1
return "A" if votes["A"] > votes["B"] else ("B" if votes["B"] > votes["A"] else "tie")
位置偏差是成对比较的头号陷阱:同一个回答放前面赢、放后面输,说明Judge不可信。轮换顺序后若两次结论矛盾,记为平局并告警。
四、一致性校验:裁判本身也要被考核
Judge不是神。上线前必须验证裁判的自一致性:同一条样本跑3次,结果一致率低于80%的裁判配置不能用。
def judge_consistency(samples, judge_llm, runs=3):
"""自一致性校验:同一批样本重复打分,统计一致率"""
consistent = 0
for q, a in samples:
scores = set()
for _ in range(runs):
s = judge_score(q, a, judge_llm=judge_llm)
# 以总分作为一致性比较基准
scores.add(sum(v for k, v in s.items() if isinstance(v, (int, float))))
if len(scores) == 1: # 三次总分完全一致
consistent += 1
return consistent / len(samples)
一致率≥0.8说明裁判稳定;0.6~0.8需要检查评分标准是否模糊;低于0.6直接换Judge模型或重写评分锚点。同时建议人工抽检10%与Judge结果对账,人工一致率也应≥85%。
五、接入CI回归流水线
评测体系最大的价值是挡住回归。每次改Prompt、换模型、动检索参数,自动跑一遍评测:
# eval_pipeline.py 示例:与CI集成的评测入口
def run_regression(dataset_path="golden_set.jsonl", judge_llm=None, agent=None):
"""黄金数据集回归:通过率低于阈值则CI失败"""
passed, total = 0, 0
fails = []
with open(dataset_path, encoding="utf-8") as f:
for line in f:
case = json.loads(line)
answer = agent.run(case["question"])
scores = judge_score(case["question"], answer,
case.get("reference", ""), judge_llm)
total += 1
if scores["准确率"] >= 4 and scores["格式合规"] >= 4:
passed += 1
else:
fails.append({"question": case["question"], "scores": scores})
pass_rate = passed / total
print(f"通过率: {pass_rate:.1%} ({passed}/{total})")
if pass_rate < 0.9: # 阈值可配置
print("回归失败,最近失败样本:")
for f in fails[:5]:
print(" -", f["question"][:50], f["scores"])
return 1 # 非零退出码,CI报红
return 0
黄金数据集要持续扩充:每次线上发现的新失败案例,人工确认后追加进数据集,让评测体系跟着业务一起成长。
六、Judge的局限与最佳实践
| 场景 | 适合Judge | 必须人工 |
|---|---|---|
| 事实准确性 | 有标准答案时可 | 无标准答案的开放问题 |
| 格式/结构合规 | 非常适合 | - |
| 代码正确性 | 只能看风格 | 必须跑测试用例 |
| 安全/伦理内容 | 辅助筛查 | 最终人工裁定 |
**三条铁律**:第一,Judge输出必须结构化(JSON),解析失败一律按低分处理并告警;第二,所有Judge调用temperature=0,杜绝随机波动;第三,评测报告要有"裁判置信度"字段——当Judge给分与历史分布偏差过大时,标记该样本转人工复核。LLM-as-Judge不是取代人工,而是把人工从重复劳动中解放出来,集中在真正困难的样本上。