如何把 LLM as a Judge 对齐
在本指南中,你将学习如何用 Ragas 系统地评测 LLM-as-judge 指标,并把它与人类专家判断对齐。
- 构建可复用的评测流水线,用于 judge 对齐
- 分析 judge 与人类标签之间的分歧模式
- 迭代 judge prompts,以改进与专家决策的对齐
为什么要先对齐你的 LLM judge?
在运行评测实验之前,重要的是把 LLM judge 对齐到你的具体用例。一个不对齐的 judge 就像指向错误方向的指南针——你基于它的指导所做的每一次改进,都会让你离目标更远。把 judge 对齐到匹配专家判断,能确保你在改进真正重要的东西。这一对齐步骤是可靠评测的基础。
真正的价值:看你的数据
虽然构建一个对齐的 LLM judge 很有用,真正的业务价值来自系统地分析数据并理解失败模式。judge 对齐过程迫使你深入检查边界用例、澄清评测标准,并揭示什么让回复好或坏。把 judge 看作放大你分析能力的工具,而不是替代它。
设置你的环境
我们创建了一个你可以安装并运行的简单模块,这样你可以专注于理解评测过程,而不是创建应用。
uv pip install "ragas[examples]"
export OPENAI_API_KEY="your-api-key-here"
完整代码
你可以在这里查看 judge 对齐评测流水线的完整代码。
理解数据集
我们将使用 EvalsBench 数据集,其中包含对业务问题 LLM 回复的专家标注样例。每一行包括:
question:原始提出的问题grading_notes:好的回复应覆盖的关键点response:LLM 生成的回复target:人类专家的二元判断(pass/fail)
下载数据集:
# Create datasets folder and download the dataset
mkdir -p datasets
curl -o datasets/benchmark_df.csv https://raw.githubusercontent.com/vibrantlabsai/EvalsBench/main/data/benchmark_df.csv
加载并检查数据集:
import pandas as pd
from ragas import Dataset
def load_dataset(csv_path: str = None) -> Dataset:
"""Load annotated dataset with human judgments.
Expected columns: question, grading_notes, response, target (pass/fail)
"""
path = csv_path or "datasets/benchmark_df.csv"
df = pd.read_csv(path)
dataset = Dataset(name="llm_judge_alignment", backend="local/csv")
for _, row in df.iterrows():
dataset.append({
"question": row["question"],
"grading_notes": row["grading_notes"],
"response": row["response"],
"target": (row["target"]),
})
return dataset
# Load the dataset
dataset = load_dataset()
print(f"Dataset loaded with {len(dataset)} samples")
数据集中的示例行:
| question | grading_notes | response | target |
|---|---|---|---|
| What are the key methods for determining the pre-money valuation of a tech startup before a Series A investment round, and how do they differ? | DCF method: !future cash flows!, requires projections; Comp. analysis: similar co. multiples; VC method: rev x multiple - post-$; Founder's share matter; strategic buyers pay more. | Determining the pre-money valuation of a tech startup before a Series A investment round is a critical step... (covers DCF, comparable analysis, VC method) | pass |
| What key metrics and strategies should a startup prioritize to effectively manage and reduce churn rate in a subscription-based business model? | Churn:! monitor monthly, \<5% ideal. Retention strategies: engage users, improve onboarding. CAC & LTV: balance 3:1+. Feedback loops: implement early. Customer support: proactive & responsive, critical. | Managing and reducing churn rate in a subscription-based business model is crucial... (missing specific metrics and strategies) | fail |
数据集包含对同一问题的多个回复——有些 pass,有些 fail。这帮助 judge 学习可接受与不可接受回复之间的细微区别。
理解你的 ground truth
judge 对齐的质量完全取决于 ground truth 标签的质量。在生产场景中,请让一位 核心领域专家 参与——其判断对你的用例最关键的人(例如心理健康 AI 的心理学家、法律 AI 的律师,或支持聊天机器人的客服总监)。他们一致的判断成为你的 judge 对齐的黄金标准。你不需要给每个例子都打标签——一个有代表性的样本(100-200 个覆盖多样场景的例子)就足以进行可靠对齐。
理解评测方法
在本指南中,我们评测数据集中预先存在的回复,而不是生成新回复。这种方法确保跨评测运行的结果可复现,让我们聚焦于 judge 对齐而不是回复生成。
评测工作流是:数据集行(question + response)→ Judge → 与人类 target 比较
定义评测指标
对于 judge 对齐,我们需要两个指标:
主指标:accuracy(LLM judge) - 评测回复并返回带 reason 的 pass/fail 决策。
对齐指标:judge_alignment - 检查 judge 的决策是否与人类专家的 verdict 匹配。
设置 judge 指标
定义一个简单的基线 judge 指标,对照 grading notes 评测回复:
from ragas.metrics import DiscreteMetric
# Define the judge metric with a simple baseline prompt
accuracy_metric = DiscreteMetric(
name="accuracy",
prompt="Check if the response contains points mentioned from the grading notes and return 'pass' or 'fail'.\n\nResponse: {response}\nGrading Notes: {grading_notes}",
allowed_values=["pass", "fail"],
)
对齐指标
对齐指标把 judge 的决策与人类 verdict 比较:
from ragas.metrics.discrete import discrete_metric
from ragas.metrics.result import MetricResult
@discrete_metric(name="judge_alignment", allowed_values=["pass", "fail"])
def judge_alignment(judge_label: str, human_label: str) -> MetricResult:
"""Compare judge decision with human label."""
judge = judge_label.strip().lower()
human = human_label.strip().lower()
if judge == human:
return MetricResult(value="pass", reason=f"Judge={judge}; Human={human}")
return MetricResult(value="fail", reason=f"Judge={judge}; Human={human}")
实验函数
实验函数 编排完整评测流水线——用 judge 评测回复并测量对齐:
from typing import Dict, Any
from ragas import experiment
from ragas.metrics import DiscreteMetric
from ragas_examples.judge_alignment import judge_alignment # The metric we created above
@experiment()
async def judge_experiment(
row: Dict[str, Any],
accuracy_metric: DiscreteMetric,
llm,
):
"""Run complete evaluation: Judge → Compare with human."""
# Step 1: Get response (in production, this is where you'd call your LLM app)
# For this evaluation, we use pre-existing responses from the dataset
app_response = row["response"]
# Step 2: Judge evaluates the response
judge_score = await accuracy_metric.ascore(
question=row["question"],
grading_notes=row["grading_notes"],
response=app_response,
llm=llm,
)
# Step 3: Compare judge decision with human target
alignment = judge_alignment.score(
judge_label=judge_score.value,
human_label=row["target"]
)
return {
**row,
"judge_label": judge_score.value,
"judge_reason": judge_score.reason,
"alignment": alignment.value,
"alignment_reason": alignment.reason,
}
运行基线评测
执行评测流水线并收集结果
import os
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas_examples.judge_alignment import load_dataset
# Load dataset
dataset = load_dataset()
print(f"Dataset loaded with {len(dataset)} samples")
# Initialize LLM client
openai_client = AsyncOpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
llm = llm_factory("gpt-4o-mini", client=openai_client)
# Run the experiment
results = await judge_experiment.arun(
dataset,
name="judge_baseline_v1_gpt-4o-mini",
accuracy_metric=accuracy_metric,
llm=llm,
)
# Calculate alignment rate
passed = sum(1 for r in results if r["alignment"] == "pass")
total = len(results)
print(f"✅ Baseline alignment: {passed}/{total} passed ({passed/total:.1%})")
📋 输出(baseline v1)
2025-10-08 22:40:00,334 - Loaded dataset with 160 samples
2025-10-08 22:40:00,334 - Initializing LLM client with model: gpt-4o-mini
2025-10-08 22:40:01,858 - Running baseline evaluation...
Running experiment: 100%|████████████████████████| 160/160 [04:35<00:00, 1.72s/it]
2025-10-08 22:44:37,149 - ✅ Baseline alignment: 121/160 passed (75.6%)
初始表现分析
评测会生成全面的 CSV 结果,包含所有输入(question、grading_notes、response)、人类 targets、带推理的 judge 决策,以及对齐比较。
分析错误与失败模式
运行基线评测后,我们可以分析不对齐模式,了解 judge 在何处与人类专家不一致。
基线表现:75.6% 对齐(121/160 正确)
让我们检查错误分布
📋 代码
import pandas as pd
# Load results
df = pd.read_csv('experiments/judge_baseline_v1_gpt-4o-mini.csv')
# Analyze misalignments
false_positives = len(df[(df['judge_label'] == 'pass') & (df['target'] == 'fail')])
false_negatives = len(df[(df['judge_label'] == 'fail') & (df['target'] == 'pass')])
print(f"False positives (judge too lenient): {false_positives}")
print(f"False negatives (judge too strict): {false_negatives}")
📋 输出
False positives (judge too lenient): 39
False negatives (judge too strict): 0
关键观察: 全部 39 个不对齐(24.4%)都是 false positives——judge 说 "pass" 但人类专家说 "fail" 的情况。基线 judge 过于宽松,漏掉了省略 grading notes 中关键概念的回复。
失败用例样本
下面是 judge 错误地通过了缺失关键概念的回复的例子:
| Grading Notes | Human Label | Judge Label | What's Missing |
|---|---|---|---|
*Valuation caps*, $, post-$ val key. Liquidation prefs: 1x+ common. Anti-dilution: *full vs. weighted*. Board seats: 1-2 investor reps. ESOP: 10-20%. |
fail | pass | Response discusses all points comprehensively but human annotators marked it as fail for subtle omissions |
*Impact on valuation*: scalability potential, dev costs, integration ease. !Open-source vs proprietary issues. !Tech debt risks. Discuss AWS/GCP/Azure... |
fail | pass | Missing specific discussion of post-money valuation impact |
Historical vs. forecasted rev; top-down & bottom-up methods; *traction evidence*; !unbiased assumptions; 12-24mo project... |
fail | pass | Missing explicit mention of traction evidence |
错误中的常见模式:
- 缺失 grading notes 中的 1-2 个具体概念,同时覆盖了其他概念
- 隐含 vs 显式覆盖 - judge 接受隐含概念,我们希望显式提及
- 缩写术语 没有被正确解码(例如 "mkt demand" = market demand,"post-$" = post-money valuation)
- 忽略关键标记 - 标有
*或!的点往往是必要的
改进 judge prompt
基于错误分析,我们需要创建一个改进后的 prompt,它:
- 理解 grading notes 中使用的 缩写
- 识别关键标记(
*、!、具体数字) - 要求所有概念 都存在,而不仅仅是大多数
- 接受语义等价物(同一概念的不同措辞)
- 平衡严格程度 - 既不太宽松也不太严格
创建改进后的 v2 prompt
用全面的评测标准定义增强的 judge 指标:
from ragas.metrics import DiscreteMetric
# Define improved judge metric with enhanced evaluation criteria
accuracy_metric_v2 = DiscreteMetric(
name="accuracy",
prompt="""Evaluate if the response covers ALL the key concepts from the grading notes. Accept semantic equivalents but carefully check for missing concepts.
ABBREVIATION GUIDE - decode these correctly:
• Financial: val=valuation, post-$=post-money, rev=revenue, ARR/MRR=Annual/Monthly Recurring Revenue, COGS=Cost of Goods Sold, Opex=Operating Expenses, LTV=Lifetime Value, CAC=Customer Acquisition Cost
• Business: mkt=market, reg/regs=regulation/regulatory, corp gov=corporate governance, integr=integration, S&M=Sales & Marketing, R&D=Research & Development, acq=acquisition
• Technical: sys=system, elim=elimination, IP=Intellectual Property, TAM=Total Addressable Market, diff=differentiation
• Metrics: NPS=Net Promoter Score, SROI=Social Return on Investment, proj=projection, cert=certification
EVALUATION APPROACH:
Step 1 - Parse grading notes into distinct concepts:
- Separate by commas, semicolons, or line breaks
- Each item is a concept that must be verified
- Example: "*Gross Margin* >40%, CAC, LTV:CAC >3:1" = 3 concepts
Step 2 - For each concept, check if it's addressed:
- Accept semantic equivalents (e.g., "customer acquisition cost" = "CAC")
- Accept implicit coverage when it's clear (e.g., "revenue forecasting" covers "historical vs forecasted rev")
- Be flexible on exact numbers (e.g., "around 40%" acceptable for ">40%")
Step 3 - Count missing concepts:
- Missing 0 concepts = PASS
- Missing 1+ concepts = FAIL (even one genuinely missing concept should fail)
- Exception: If a long list (10+ items) has 1 very minor detail missing but all major points covered, use judgment
CRITICAL RULES:
1. Do NOT require exact wording - "market demand" = "mkt demand" = "demand analysis"
2. Markers (* or !) mean important, not mandatory exact phrases:
- "*traction evidence*" can be satisfied by discussing metrics, growth, or validation
- "!unbiased assumptions" can be satisfied by discussing assumption methodology
3. Numbers should be mentioned but accept approximations:
- "$47B to $10B" can be "$47 billion dropped to around $10 billion"
- "LTV:CAC >3:1" can be "LTV to CAC ratio of at least 3 to 1" or "3x or higher"
4. FAIL only when concepts are genuinely absent:
- If notes mention "liquidation prefs, anti-dilution, board seats" but response only has board seats → FAIL
- If notes mention "scalability, tech debt, IP" but response never discusses technical risks → FAIL
- If notes mention "GDPR compliance" and response never mentions GDPR or EU regulations → FAIL
5. PASS when ALL concepts present:
- All concepts covered, even with different wording → PASS
- Concepts addressed implicitly when clearly implied → PASS
- Minor phrasing differences → PASS
- One or more concepts genuinely absent → FAIL
Response: {response}
Grading Notes: {grading_notes}
Are ALL distinct concepts from the grading notes covered in the response (accepting semantic equivalents and implicit coverage)?""",
allowed_values=["pass", "fail"],
)
用 LLM 优化 prompts
在你清晰识别错误模式之后,可以用 LLM 优化 prompts。你也可以用 LLM 识别错误,但务必审阅它们,确保与 ground truth 标签对齐。你还可以使用像 Cursor、Claude Code 这样的编码 agent,或像 DSPy 这样的框架,系统地优化 judge prompts。
用改进后的 prompt 重新运行评测
用增强的 v2 prompt 再次运行评测(设置与基线相同,只替换指标):
# Use the same dataset and LLM setup from the baseline evaluation above
results = await judge_experiment.arun(
dataset,
name="judge_accuracy_v2_gpt-4o-mini",
accuracy_metric=accuracy_metric_v2, # ← Using improved v2 prompt
llm=llm,
)
passed = sum(1 for r in results if r["alignment"] == "pass")
total = len(results)
print(f"✅ V2 alignment: {passed}/{total} passed ({passed/total:.1%})")
📋 输出(improved v2)
2025-10-08 23:42:11,650 - Loaded dataset with 160 samples
2025-10-08 23:42:11,650 - Initializing LLM client with model: gpt-4o-mini
2025-10-08 23:42:12,730 - Running v2 evaluation with improved prompt...
Running experiment: 100%|██████████| 160/160 [04:39<00:00, 1.75s/it]
2025-10-08 23:46:52,740 - ✅ V2 alignment: 139/160 passed (86.9%)
显著改进! 对齐率从 75.6% 提升到 86.9%。
如果你需要进一步迭代:
- 分析剩余错误以识别模式(它们是 false positives 还是 false negatives?)
- 连同标签一起标注你的推理——这在改进 LLM Judge 时会有帮助,你也可以把这些作为 few shot 例子加入。
- 使用更聪明的模型 - 更有能力的模型如 GPT-5 或 Claude 4.5 Sonnet 通常作为 judges 表现更好
- 利用 AI 助手 - 本指南是用 Cursor AI agents 分析失败并迭代 prompts 创建的。你可以使用 AI 编码 agents(Cursor、Claude 等)或像 DSPy 这样的框架,系统地优化 judge prompts
- 当对齐在 2-3 次迭代中趋于平稳,或达到你的业务阈值时停止
你已经完成了什么
你已经用 Ragas 构建了一个系统化的评测流水线,它:
- 用清晰指标对照专家判断测量 judge 对齐
- 通过结构化错误分析识别失败模式
- 用可复现实验跟踪跨评测运行的改进
这个对齐后的 judge 成为可靠 AI 评测的基础。有了一个你可以信任的 judge,你现在可以自信地评测 RAG 流水线、agent 工作流或任何 LLM 应用——并知道指标上的改进会转化为质量上的真实改进。