如何评测你的 Prompt 并改进它
在本指南中,你将学习如何用 Ragas 评测并迭代改进一个 prompt。
你将完成什么
- 基于评测的错误分析迭代并改进 prompt
- 建立清晰的决策标准,以便在 prompts 之间做选择
- 为你的数据集构建可复用的评测流水线
- 学习如何利用 Ragas 构建评测流水线
完整代码
- 数据集和脚本位于仓库中的
examples/iterate_prompt/ - 完整代码可在 GitHub 上获取
任务定义
在本例中,我们考虑客服工单分类任务。
- Labels(多标签):
Billing、Account、ProductIssue、HowTo、Feature、RefundCancel - Priority(恰好一个):
P0、P1或P2
数据集
我们为用例创建了一个合成数据集。每一行有 id, text, labels, priority。数据集中的示例行:
| id | text | labels | priority |
|---|---|---|---|
| 1 | Upgraded to Plus… bank shows two charges the same day; want the duplicate reversed. | Billing;RefundCancel | P1 |
| 2 | SSO via Okta succeeds then bounces back to /login; colleagues can sign in; state mismatch; blocked from boards. | Account;ProductIssue | P0 |
| 3 | Need to export a board to PDF with comments and page numbers for audit; deadline next week. | HowTo | P2 |
要为你的用例定制数据集,请创建一个 datasets/ 目录并添加你自己的 CSV 文件。你也可以连接到不同的 backends。更多信息请参阅 Core Concepts - Evaluation Dataset。
最好从你的应用中采样真实数据来创建数据集。如果没有,你可以用 LLM 生成合成数据。我们建议使用像 gpt-5 high-reasoning 这样的推理模型,它可以生成更准确、更复杂的数据。务必手动审阅并核验你使用的数据。
在数据集上评测你的 prompt
Prompt runner
首先,我们在一个用例上运行 prompt,测试一切是否正常。
在这里查看完整 prompt v1
You categorize a short customer support ticket into (a) one or more labels and (b) a single priority.
Allowed labels (multi-label):
- Billing: charges, taxes (GST/VAT), invoices, plans, credits.
- Account: login/SSO, password reset, identity/email/account merges.
- ProductIssue: malfunction (crash, error code, won't load, data loss, loops, outages).
- HowTo: usage questions ("where/how do I…", "where to find…").
- Feature: new capability or improvement request.
- RefundCancel: cancel/terminate and/or refund requests.
- AbuseSpam: insults/profanity/spam (not mild frustration).
Priority (exactly one):
- P0 (High): blocked from core action or money/data at risk.
- P1 (Normal): degraded/needs timely help, not fully blocked.
- P2 (Low): minor/info/how-to/feature.
Return exactly in JSON:
{"labels":[<labels>], "priority":"P0"|"P1"|"P2"}
cd examples/iterate_prompt
export OPENAI_API_KEY=your_openai_api_key
uv run run_prompt.py
这将在示例用例上运行 prompt 并打印结果。
示例输出
$ uv run run_prompt.py
Test ticket:
"SSO via Okta succeeds then bounces me back to /login with no session. Colleagues can sign in. I tried clearing cookies; same result. Error in devtools: state mismatch. I'm blocked from our boards."
Response:
{"labels":["Account","ProductIssue"], "priority":"P0"}
用于打分的指标
通常最好使用更简单的指标,而不是复杂的指标。你应该使用与用例相关的指标。关于指标的更多信息见 Core Concepts - Metrics。这里我们使用两个离散指标:labels_exact_match 和 priority_accuracy。把它们分开有助于分析和修复不同的失败模式。
priority_accuracy:检查预测的 priority 是否与期望的 priority 匹配;对正确的紧急程度分流很重要。labels_exact_match:检查预测标签集合是否与期望标签完全匹配;对避免过度/不足打标签很重要,并帮助我们测量系统在给用例打标签上的准确率。
# examples/iterate_prompt/evals.py
import json
from ragas.metrics.discrete import discrete_metric
from ragas.metrics.result import MetricResult
@discrete_metric(name="labels_exact_match", allowed_values=["correct", "incorrect"])
def labels_exact_match(prediction: str, expected_labels: str):
try:
predicted = set(json.loads(prediction).get("labels", []))
expected = set(expected_labels.split(";")) if expected_labels else set()
return MetricResult(
value="correct" if predicted == expected else "incorrect",
reason=f"Expected={sorted(expected)}; Got={sorted(predicted)}",
)
except Exception as e:
return MetricResult(value="incorrect", reason=f"Parse error: {e}")
@discrete_metric(name="priority_accuracy", allowed_values=["correct", "incorrect"])
def priority_accuracy(prediction: str, expected_priority: str):
try:
predicted = json.loads(prediction).get("priority")
return MetricResult(
value="correct" if predicted == expected_priority else "incorrect",
reason=f"Expected={expected_priority}; Got={predicted}",
)
except Exception as e:
return MetricResult(value="incorrect", reason=f"Parse error: {e}")
实验函数
实验函数用于在数据集上运行 prompt。关于 experimentation 的更多信息见 Core Concepts - Experimentation。
注意我们把 prompt_file 作为参数传入,以便用不同 prompts 运行实验。你也可以把其他参数传给实验函数,例如 model、temperature 等,并用不同配置做实验。建议在做实验时一次只改变 1 个参数。
# examples/iterate_prompt/evals.py
import asyncio, json
from ragas import experiment
from run_prompt import run_prompt
@experiment()
async def support_triage_experiment(row, prompt_file: str, experiment_name: str):
response = await asyncio.to_thread(run_prompt, row["text"], prompt_file=prompt_file)
try:
parsed = json.loads(response)
predicted_labels = ";".join(parsed.get("labels", [])) or ""
predicted_priority = parsed.get("priority")
except Exception:
predicted_labels, predicted_priority = "", None
return {
"id": row["id"],
"text": row["text"],
"response": response,
"experiment_name": experiment_name,
"expected_labels": row["labels"],
"predicted_labels": predicted_labels,
"expected_priority": row["priority"],
"predicted_priority": predicted_priority,
"labels_score": labels_exact_match.score(prediction=response, expected_labels=row["labels"]).value,
"priority_score": priority_accuracy.score(prediction=response, expected_priority=row["priority"]).value,
}
数据集加载器(CSV)
数据集加载器用于把数据集加载到 Ragas dataset 对象中。关于 datasets 的更多信息见 Core Concepts - Evaluation Dataset。
# examples/iterate_prompt/evals.py
import os, pandas as pd
from ragas import Dataset
def load_dataset():
current_dir = os.path.dirname(os.path.abspath(__file__))
df = pd.read_csv(os.path.join(current_dir, "datasets", "support_triage.csv"))
dataset = Dataset(name="support_triage", backend="local/csv", root_dir=".")
for _, row in df.iterrows():
dataset.append({
"id": str(row["id"]),
"text": row["text"],
"labels": row["labels"],
"priority": row["priority"],
})
return dataset
用当前 prompt 运行实验
uv run evals.py run --prompt_file promptv1.txt
这将在数据集上运行给定 prompt,并把结果保存到 experiments/ 目录。
示例输出
$ uv run evals.py run --prompt_file promptv1.txt
Loading dataset...
Dataset loaded with 20 samples
Running evaluation with prompt file: promptv1.txt
Running experiment: 100%|██████████████████████████████████████████████████████████████████| 20/20 [00:11<00:00, 1.79it/s]
✅ promptv1: 20 cases evaluated
Results saved to: experiments/20250826-041332-promptv1.csv
promptv1 Labels Accuracy: 80.00%
promptv1 Priority Accuracy: 75.00%
改进 prompt
从结果中分析错误
在你喜欢的电子表格编辑器中打开 experiments/{timestamp}-promptv1.csv 并分析结果。查找 labels_score 或 priority_score 不正确的用例。
从我们的 promptv1 实验中,我们可以识别出若干错误模式:
Priority 错误:过度提高优先级(P1 → P0)
模型持续把本应为 P1 的账单相关问题标为 P0(最高优先级):
| Case | Issue | Expected | Got | Pattern |
|---|---|---|---|---|
| ID 19 | Auto-charge after pausing workspace | P1 | P0 | Billing dispute treated as urgent |
| ID 1 | Duplicate charge on same day | P1 | P0 | Billing dispute treated as urgent |
| ID 5 | Cancellation with refund request | P1 | P0 | Routine cancellation treated as urgent |
| ID 13 | Follow-up on cancellation | P1 | P0 | Follow-up treated as urgent |
模式:模型把任何账单/退款/取消都当作紧急(P0),而大多数其实是常规业务操作(P1)。
Label 错误:过度打标签与混淆
| Case | Issue | Expected | Got | Pattern |
|---|---|---|---|---|
| ID 9 | GST tax question from US user | Billing;HowTo |
Billing;Account |
Confuses informational questions with account actions |
| ID 10 | Account ownership transfer | Account |
Account;Billing |
Adds Billing when money/plans mentioned |
| ID 20 | API rate limit question | ProductIssue;HowTo |
ProductIssue;Billing;HowTo |
Adds Billing when plans mentioned |
| ID 16 | Feature request for offline mode | Feature |
Feature;HowTo |
Adds HowTo for feature requests |
识别出的模式:
- 过度打上 Billing:即使主要不是账单相关,也添加 "Billing"
- HowTo vs Account 混淆:把信息性问题误分类为账户管理操作
- 过度打上 HowTo:当用户问 "how" 但意思是 "can you build this" 时,给功能请求添加 "HowTo"
改进 prompt
基于错误分析,我们将创建带有针对性改进的 promptv2_fewshot.txt。你可以用 LLM 生成 prompt,或手动编辑。在本例中,我们把错误模式和原始 prompt 传给 LLM,生成带 few-shot 例子的修订 prompt。
promptv2_fewshot 中的关键新增:
1. 增强的 Priority 指南,聚焦业务影响:
- P0: Blocked from core functionality OR money/data at risk OR business operations halted
- P1: Degraded experience OR needs timely help BUT has workarounds OR not fully blocked
- P2: Minor issues OR information requests OR feature requests OR non-urgent how-to
2. 保守的多标签规则,防止过度打标签:
## Multi-label Guidelines
Use single label for PRIMARY issue unless both aspects are equally important:
- Billing + RefundCancel: Always co-label. Cancellation/refund requests must include Billing.
- Account + ProductIssue: For auth/login malfunctions (loops, "invalid_token", state mismatch, bounce-backs)
- Avoid adding Billing to account-only administration unless there is an explicit billing operation
Avoid over-tagging: Focus on which department should handle this ticket first.
3. 带具体场景的详细 Priority 指南:
## Priority Guidelines
- Ignore emotional tone - focus on business impact and available workarounds
- Billing disputes/adjustments (refunds, duplicate charges, incorrect taxes/pricing) = P1 unless causing an operational block
- Login workarounds: If Incognito/another account works, prefer P1; if cannot access at all, P0
- Core business functions failing (webhooks, API, sync) = P0
4. 带推理的全面例子: 添加了 7 个覆盖不同场景的例子,并带有显式推理,以演示正确分类。
## Examples with Reasoning
Input: "My colleague left and I need to change the team lead role to my email address."
Output: {"labels":["Account"], "priority":"P1"}
Reasoning: Administrative role change; avoid adding Billing unless a concrete billing action is requested.
Input: "Dashboard crashes when I click reports tab, but works fine in mobile app."
Output: {"labels":["ProductIssue"], "priority":"P1"}
Reasoning: Malfunction exists but workaround available (mobile app works); single label since primary issue is product malfunction.
尽量不要直接加入数据集中的例子,那可能导致对数据集过拟合,你的 prompt 可能在其他用例上失败。
评测新 prompt
创建带有改进的 promptv2_fewshot.txt 后,用新 prompt 运行实验:
uv run evals.py run --prompt_file promptv2_fewshot.txt
这将在同一数据集上评测改进后的 prompt,并把结果保存到一个新的带时间戳的文件。
示例输出
$ uv run evals.py run --prompt_file promptv2_fewshot.txt
Loading dataset...
Dataset loaded with 20 samples
Running evaluation with prompt file: promptv2_fewshot.txt
Running experiment: 100%|██████████████████████████████████████████████████████████████| 20/20 [00:11<00:00, 1.75it/s]
✅ promptv2_fewshot: 20 cases evaluated
Results saved to: experiments/20250826-231414-promptv2_fewshot.csv
promptv2_fewshot Labels Accuracy: 90.00%
promptv2_fewshot Priority Accuracy: 95.00%
实验会在 experiments/ 目录中创建一个新的 CSV 文件,结构与第一次运行相同,便于直接比较。
分析并比较结果
我们创建了一个简单的工具函数,接收多个 CSV 并合并,以便轻松比较:
uv run evals.py compare --inputs experiments/20250826-041332-promptv1.csv experiments/20250826-231414-promptv2_fewshot.csv
这会打印每个实验的准确率,并在 experiments/ 目录中保存一个合并后的 CSV 文件。
示例
$ uv run evals.py compare --inputs experiments/20250826-041332-promptv1.csv experiments/20250826-231414-promptv2_fewshot.csv
promptv1 Labels Accuracy: 80.00%
promptv1 Priority Accuracy: 75.00%
promptv2_fewshot Labels Accuracy: 90.00%
promptv2_fewshot Priority Accuracy: 95.00%
Combined comparison saved to: experiments/20250826-231545-comparison.csv
在这里,我们可以看到 promptv2_fewshot 改进了 labels 和 priority 的准确率。但我们也可以看到仍有一些用例失败。我们可以分析错误并进一步改进 prompt。
当改进趋于平稳,或准确率满足业务要求时停止迭代。
如果你仅靠 prompt 改进就把准确率顶到了天花板,可以尝试用更好的模型做实验。
把这个循环应用到你的用例
- 为你的用例创建 dataset、metrics、experiment
- 运行评测并分析错误
- 基于错误分析改进 prompt
- 重新运行评测并比较结果
- 当改进趋于平稳,或准确率满足业务要求时停止
一旦你有了数据集和评测循环,就可以把它扩展到测试更多参数,例如 model 等。
Ragas 框架会自动处理编排、并行执行和结果汇总,帮助你评测并聚焦于自己的用例!
进阶:对齐 LLM judges
如果你使用基于 LLM 的指标做评测,请考虑先把你的 judge 与人类专家判断对齐,以确保评测可靠。参见 How to Align an LLM as a Judge。