Ragas Ragas
stable · 中文译文
中文译文 · 原文:https://docs.ragas.io/en/stable/howtos/applications/iterate_prompt/ · 许可证 Apache-2.0

如何评测你的 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

识别出的模式:

  1. 过度打上 Billing:即使主要不是账单相关,也添加 "Billing"
  2. HowTo vs Account 混淆:把信息性问题误分类为账户管理操作
  3. 过度打上 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。