如何为你的用例评测一个新的 LLM
当一个新的 LLM 发布时,你可能想判断它在你的特定用例上是否优于当前模型。本指南展示如何用 Ragas 框架对两个模型运行准确率比较。
你将完成什么
学完本指南后,你将:
- 搭建比较两个 LLM 的结构化评测
- 在一个真实的业务任务上评测模型表现
- 生成详细结果,为模型选择决策提供依据
- 得到一个可复用的评测循环,每当有新模型出现时都可以重新运行
评测场景
我们将用折扣计算作为测试用例:给定客户画像,计算合适的折扣百分比并解释推理过程。该任务需要规则应用与推理——这些技能能区分模型能力。
注意:你可以把这种方法适配到对你的应用重要的任何用例。
📁 完整代码:本示例的完整源代码可在 Github 上获取
设置你的环境与 API 访问
首先,安装包含 benchmark LLM 示例代码的 ragas-examples 包:
pip install ragas[examples]
接下来,确保你已配置好 API 凭证:
export OPENAI_API_KEY=your_actual_api_key
LLM 应用
我们已在 examples 包中为你设置了一个简单的 LLM 应用,这样你可以专注于评测,而不是构建应用本身。该应用根据业务规则计算客户折扣。
下面是定义折扣计算逻辑的系统 prompt:
SYSTEM_PROMPT = """
You are a discount calculation assistant. I will provide a customer profile and you must calculate their discount percentage and explain your reasoning.
Discount rules:
- Age 65+ OR student status: 15% discount
- Annual income < $30,000: 20% discount
- Premium member for 2+ years: 10% discount
- New customer (< 6 months): 5% discount
Rules can stack up to a maximum of 35% discount.
Respond in JSON format only:
{
"discount_percentage": number,
"reason": "clear explanation of which rules apply and calculations",
"applied_rules": ["list", "of", "applied", "rule", "names"]
}
"""
你可以用一个示例客户画像测试该应用:
from ragas_examples.benchmark_llm.prompt import run_prompt
# Test with a sample customer profile
customer_profile = """
Customer Profile:
- Name: Sarah Johnson
- Age: 67
- Student: No
- Annual Income: $45,000
- Premium Member: Yes, for 3 years
- Account Age: 3 years
"""
result = await run_prompt(customer_profile)
print(result)
📋 输出
{
"discount_percentage": 25,
"reason": "Sarah qualifies for a 15% discount due to age (67). She also gets a 10% discount for being a premium member for over 2 years. The total stacking of 15% and 10% discounts results in 25%. No other discounts apply based on income or account age.",
"applied_rules": ["Age 65+", "Premium member for 2+ years"]
}
查看评测数据集
为这次评测我们构建了一个合成数据集,测试用例包括:
- 结果清晰的简单用例
- 规则边界上的边界用例
- 信息含糊的复杂场景
每个用例指定:
customer_profile:输入数据expected_discount:期望的折扣百分比description:用例复杂度指示
示例数据集结构(添加一列 id 以便比较):
| ID | Customer Profile | Expected Discount | Description |
|---|---|---|---|
| 1 | Martha is a 70-year-old retiree who enjoys gardening. She has never enrolled in any academic course recently, has an annual pension of 50,000 dollars, signed up for our service nine years ago and never upgraded to premium. | 15 | Senior only |
| 2 | Arjun, aged 19, is a full-time computer-science undergraduate. His part-time job brings in about 45,000 dollars per year. He opened his account a year ago and has no premium membership. | 15 | Student only |
| 3 | Cynthia, a 40-year-old freelance artist, earns roughly 25,000 dollars a year. She is not studying anywhere, subscribed to our basic plan five years back and never upgraded to premium. | 20 | Low income only |
要为你的用例定制数据集,请创建一个 datasets/ 目录并添加你自己的 CSV 文件。更多信息请参阅 Core Concepts - Evaluation Dataset。
最好从你的应用中采样真实数据来创建数据集。如果没有,你可以用 LLM 生成合成数据。由于我们的用例稍复杂,我们建议使用像 gpt-5-high 这样的模型,它可以生成更准确的数据。务必手动审阅并核验你使用的数据。
注意
这里的示例数据集大约有 10 个用例,以便指南保持紧凑;真实评测可以先从小规模的 20-30 个样本开始,但务必逐步迭代改进到 50-100 个样本范围,以从评测中获得更可信的结果。确保广泛覆盖你的 agent 可能面对的不同场景(包括边界用例和复杂问题)。准确率一开始不需要 100%——用结果做错误分析,迭代 prompt、数据和工具,并持续改进。
加载数据集
def load_dataset():
"""Load the dataset from CSV file. Downloads from GitHub if not found locally."""
import urllib.request
current_dir = os.path.dirname(os.path.abspath(__file__))
dataset_path = os.path.join(current_dir, "datasets", "discount_benchmark.csv")
# Download dataset from GitHub if it doesn't exist locally
if not os.path.exists(dataset_path):
os.makedirs(os.path.dirname(dataset_path), exist_ok=True)
urllib.request.urlretrieve("https://raw.githubusercontent.com/vibrantlabsai/ragas/main/examples/ragas_examples/benchmark_llm/datasets/discount_benchmark.csv", dataset_path)
return Dataset.load(name="discount_benchmark", backend="local/csv", root_dir=current_dir)
数据集加载器会检查 CSV 文件是否在本地存在。如果找不到,它会自动从 GitHub 下载。
指标函数
通常最好使用简单指标。你应该使用与用例相关的指标。关于指标的更多信息见 Core Concepts - Metrics。评测使用这个准确率指标给每个回复打分:
@discrete_metric(name="discount_accuracy", allowed_values=["correct", "incorrect"])
def discount_accuracy(prediction: str, expected_discount):
"""Check if the discount prediction is correct."""
import json
parsed_json = json.loads(prediction)
predicted_discount = parsed_json.get("discount_percentage")
expected_discount_int = int(expected_discount)
if predicted_discount == expected_discount_int:
return MetricResult(
value="correct",
reason=f"Correctly calculated discount={expected_discount_int}%"
)
else:
return MetricResult(
value="incorrect",
reason=f"Expected discount={expected_discount_int}%; Got discount={predicted_discount}%"
)
实验结构
每个模型评测遵循这个实验模式:
@experiment()
async def benchmark_experiment(row, model_name: str):
# Get model response
response = await run_prompt(row["customer_profile"], model=model_name)
# Parse response (strict JSON mode expected)
try:
parsed_json = json.loads(response)
predicted_discount = parsed_json.get('discount_percentage')
except Exception:
predicted_discount = None
# Score the response
score = discount_accuracy.score(
prediction=response,
expected_discount=row["expected_discount"]
)
return {
**row,
"model": model_name,
"response": response,
"predicted_discount": predicted_discount,
"score": score.value,
"score_reason": score.reason
}
运行实验
用基线模型和候选模型运行评测实验。我们将比较这些示例模型:
- 基线:"gpt-4.1-nano-2025-04-14"
- 候选:"gpt-5-nano-2025-08-07"
from ragas_examples.benchmark_llm.evals import benchmark_experiment, load_dataset
# Load dataset
dataset = load_dataset()
print(f"Dataset loaded with {len(dataset)} samples")
# Run baseline experiment
baseline_results = await benchmark_experiment.arun(
dataset,
name="gpt-4.1-nano-2025-04-14",
model_name="gpt-4.1-nano-2025-04-14"
)
# Calculate and display accuracy
baseline_accuracy = sum(1 for r in baseline_results if r["score"] == "correct") / len(baseline_results)
print(f"Baseline Accuracy: {baseline_accuracy:.2%}")
# Run candidate experiment
candidate_results = await benchmark_experiment.arun(
dataset,
name="gpt-5-nano-2025-08-07",
model_name="gpt-5-nano-2025-08-07"
)
# Calculate and display accuracy
candidate_accuracy = sum(1 for r in candidate_results if r["score"] == "correct") / len(candidate_results)
print(f"Candidate Accuracy: {candidate_accuracy:.2%}")
每个实验会在 experiments/ 下保存一个 CSV,包含每行结果,包括:
- id, model, response, predicted_discount, score, score_reason
示例实验输出(为可读性只展示部分列)
| ID | Description | Expected | Predicted | Score | Score Reason |
|---|---|---|---|---|---|
| 1 | Senior only | 15 | 15 | correct | Correctly calculated discount=15% |
| 2 | Student only | 15 | 5 | incorrect | Expected discount=15%; Got discount=5% |
| 3 | Low income only | 20 | 20 | correct | Correctly calculated discount=20% |
| 4 | Senior, low income, new customer (capped) | 35 | 35 | correct | Correctly calculated discount=35% |
| 6 | Premium 2+ yrs only | 10 | 15 | incorrect | Expected discount=10%; Got discount=15% |
注意
尽可能固定并记录确切的模型快照/版本(例如用 "gpt-4o-2024-08-06" 而不是只写 "gpt-4o")。提供商会定期更新别名,不同快照之间的表现可能变化。你可以在提供商的模型文档中找到可用快照(参见 OpenAI 的 model catalog 作为示例)。在结果中包含快照,能让未来比较公平且可复现。
比较结果
用不同模型运行实验后,并排比较它们的表现:
from ragas_examples.benchmark_llm.evals import compare_inputs_to_output
# Compare the two experiment results
# Update these paths to match your actual experiment output files
output_path = compare_inputs_to_output(
inputs=[
"experiments/gpt-4.1-nano-2025-04-14.csv",
"experiments/gpt-5-nano-2025-08-07.csv"
]
)
print(f"Comparison saved to: {output_path}")
这次比较会:
- 读取两个实验文件
- 打印每个模型的准确率
- 创建一个新的 CSV,把结果并排放在一起
比较文件展示:
- 测试用例细节(客户画像、期望折扣)
- 对每个模型:它的回复、是否正确,以及原因
📋 输出
gpt-4.1-nano-2025-04-14 Accuracy: 50.00%
gpt-5-nano-2025-08-07 Accuracy: 90.00%
Comparison saved to: experiments/20250820-150548-comparison.csv
用合并后的 CSV 分析结果
在这次示例运行中:
- 过滤一个模型优于另一个模型的用例,会浮现这些情况:"Senior and new customer"、"Student and new customer"、"Student only"、"Premium 2+ yrs only"。
- 每个模型回复中的 reason 字段显示它为何给出该输出。
比较 CSV 中的示例行(为可读性只展示部分列)
| id | customer_profile | description | expected_discount | gpt-4.1-nano-2025-04-14_score | gpt-5-nano-2025-08-07_score | gpt-4.1-nano-2025-04-14_score_reason | gpt-5-nano-2025-08-07_score_reason | gpt-4.1-nano-2025-04-14_response | gpt-5-nano-2025-08-07_response |
|---|---|---|---|---|---|---|---|---|---|
| 2 | Arjun, aged 19, is a full-time computer-science undergraduate. His part-time job brings in about 45,000 dollars per year. He opened his account a year ago and has no premium membership. | Student only | 15 | incorrect | correct | Expected discount=15%; Got discount=0% | Correctly calculated discount=15% | ...reason="Arjun is 19 years old, so he does not qualify for age-based or senior discounts. His annual income of $45,000 exceeds the $30,000 threshold, so no income-based discount applies. He opened his account a year ago, which is more than 6 months, so he is not a new customer. He has no premium membership and no other applicable discounts."... | ...reason="Eligible for 15% discount due to student status (Arjun is 19 and an undergraduate)."... |
| 6 | Leonardo is 64, turning 65 next month. His salary is exactly 30,000 dollars. He has maintained a premium subscription for two years and seven months and has been with us for five years. | Premium 2+ yrs only | 10 | incorrect | correct | Expected discount=10%; Got discount=25% | Correctly calculated discount=10% | ...reason="Leonardo is about to turn 65, so he qualifies for the age discount of 15%. Premium 2+ years noted"... | ...reason="Leonardo is 64, turning 65 next month. premium 2+ years: 10%"... |
有新模型发布时重新运行
一旦这个评测与你的项目放在一起,它就变成可重复的检查。当新的 LLM 发布时(如今常常每周都有),把它作为候选接入,并重新运行同一评测,与当前基线比较。
解读结果并做出决策
看什么
- 基线准确率 vs 候选准确率 以及 差值。
- 本次运行的例子:基线 50%(5/10),候选 90%(9/10),差值 +40%。
如何读这些行
- 浏览两个模型不一致的行。
- 用每行的 score_reason 查看它为何被标为 correct/incorrect。
- 寻找模式(例如错过规则叠加、像 "almost 65" 这样的边界用例、精确收入阈值)。
超越准确率
- 检查 成本 和 延迟。如果对你的用例来说太慢或太贵,更高的准确率可能不值得。
决策
- 如果新模型在你的重要用例上明显更准确,并且符合成本/延迟需求,就切换。
- 如果收益很小、失败击中关键用例,或成本/延迟不可接受,就留下。
在本例中:
- 我们会切换到 "gpt-5-nano-2025-08-07"。它把准确率从 50% 提升到 90%(+40%),并修复了关键失败模式(错过规则叠加、边界条件)。如果其延迟/成本符合你的约束,它就是更好的默认选择。
适配到你的用例
要为你的具体应用评测模型,你可以把 GitHub 代码 当作模板,并适配到你的用例。
Ragas 框架会自动处理编排、并行执行和结果汇总,帮助你评测并聚焦于自己的用例!