一、为什么需要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不是取代人工,而是把人工从重复劳动中解放出来,集中在真正困难的样本上。