提示词评估指标与方法
构建高质量的提示词不能只靠"感觉好不好"——你需要系统化的评估方法来量化提示词的效果,发现问题,并指导优化方向。本节将建立一套完整的评估框架。
为什么需要评估
graph LR
A[凭感觉写提示词] --> B[效果不稳定]
B --> C[不知道如何改进]
C --> D[浪费时间和成本]
E[系统化评估] --> F[量化效果]
F --> G[定位问题]
G --> H[精准优化]
style A fill:#ffcdd2,stroke:#c62828,stroke-width:2px
style D fill:#ffcdd2,stroke:#c62828,stroke-width:2px
style E fill:#c8e6c9,stroke:#43a047,stroke-width:3px
style H fill:#c8e6c9,stroke:#43a047,stroke-width:2px
评估维度与指标
五维评估模型
graph TB
A[提示词评估] --> B[准确性 Accuracy]
A --> C[相关性 Relevance]
A --> D[一致性 Consistency]
A --> E[安全性 Safety]
A --> F[效率 Efficiency]
B --> B1[事实正确率]
B --> B2[逻辑一致性]
C --> C1[任务完成度]
C --> C2[信息充分度]
D --> D1[多次运行一致]
D --> D2[跨模型一致]
E --> E1[无有害输出]
E --> E2[无PII泄露]
F --> F1[Token消耗]
F --> F2[响应延迟]
style A fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style B fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style C fill:#c8e6c9,stroke:#43a047,stroke-width:2px
style D fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style E fill:#ffcdd2,stroke:#c62828,stroke-width:2px
style F fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
各维度定义与指标
| 维度 | 核心问题 | 量化指标 | 评估方法 |
|---|---|---|---|
| 准确性 | 回答是否正确? | 精确率、召回率、F1 | 与标准答案对比 |
| 相关性 | 回答是否切题? | BLEU、ROUGE、BERTScore | 语义相似度计算 |
| 一致性 | 多次回答是否稳定? | 标准差、一致率 | 多次运行统计 |
| 安全性 | 是否产生有害内容? | 违规率、拒绝率 | 安全测试用例 |
| 效率 | 成本和速度如何? | Token数、延迟(ms) | 直接测量 |
自动化评估实现
评估框架代码
"""
prompt_evaluator.py - 提示词评估框架
用途:量化评估不同提示词的效果,支持对比实验
"""
import time
import json
import statistics
from dataclasses import dataclass, field
from typing import Callable, Optional
from openai import OpenAI
client = OpenAI()
@dataclass
class EvalCase:
"""单个评估用例"""
input_text: str
expected_output: str
category: str = "general"
metadata: dict = field(default_factory=dict)
@dataclass
class EvalResult:
"""单次评估结果"""
input_text: str
expected: str
actual: str
scores: dict # 各维度得分(0-1)
latency_ms: float
token_usage: dict
passed: bool
class PromptEvaluator:
"""
提示词评估器。
使用方法:
evaluator = PromptEvaluator(model="gpt-4o-mini")
# 添加测试用例
evaluator.add_case(EvalCase(
input_text="法国的首都是哪里?",
expected_output="巴黎",
category="factual"
))
# 运行评估
report = evaluator.evaluate(prompt_template="请简洁回答:{input}")
print(report.summary())
"""
def __init__(self, model: str = "gpt-4o-mini"):
self.model = model
self.cases: list[EvalCase] = []
self.results: list[EvalResult] = []
def add_case(self, case: EvalCase):
"""添加评估用例"""
self.cases.append(case)
def add_cases_from_json(self, filepath: str):
"""从JSON文件加载评估用例"""
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
for item in data:
self.cases.append(EvalCase(**item))
def evaluate(
self,
prompt_template: str,
system_prompt: str = "",
runs_per_case: int = 1,
temperature: float = 0.0,
custom_scorer: Optional[Callable] = None
) -> "EvalReport":
"""
运行评估。
Args:
prompt_template: 提示词模板,用 {input} 占位
system_prompt: 系统提示词
runs_per_case: 每个用例运行次数(用于一致性测试)
temperature: 温度参数
custom_scorer: 自定义评分函数 (expected, actual) -> dict[str, float]
Returns:
EvalReport: 评估报告
"""
self.results = []
for case in self.cases:
case_results = []
for run in range(runs_per_case):
user_message = prompt_template.replace("{input}", case.input_text)
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": user_message})
# 计时
start = time.time()
response = client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature
)
latency = (time.time() - start) * 1000
actual = response.choices[0].message.content.strip()
usage = {
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens
}
# 评分
if custom_scorer:
scores = custom_scorer(case.expected_output, actual)
else:
scores = self._default_scoring(case.expected_output, actual)
passed = all(v >= 0.5 for v in scores.values())
result = EvalResult(
input_text=case.input_text,
expected=case.expected_output,
actual=actual,
scores=scores,
latency_ms=latency,
token_usage=usage,
passed=passed
)
case_results.append(result)
self.results.extend(case_results)
return EvalReport(self.results, prompt_template, self.model)
def _default_scoring(self, expected: str, actual: str) -> dict:
"""
默认评分方法(基于字符串匹配)。
对于生产环境,建议替换为 LLM-as-Judge 或 BERTScore。
"""
expected_lower = expected.lower().strip()
actual_lower = actual.lower().strip()
# 精确匹配
exact_match = 1.0 if expected_lower == actual_lower else 0.0
# 包含匹配(expected是否包含在actual中)
contains_match = 1.0 if expected_lower in actual_lower else 0.0
# 词重叠度(简单版F1)
expected_words = set(expected_lower.split())
actual_words = set(actual_lower.split())
if len(expected_words) == 0 or len(actual_words) == 0:
word_overlap = 0.0
else:
intersection = expected_words & actual_words
precision = len(intersection) / len(actual_words) if actual_words else 0
recall = len(intersection) / len(expected_words) if expected_words else 0
word_overlap = (2 * precision * recall / (precision + recall)) if (precision + recall) > 0 else 0.0
return {
"exact_match": exact_match,
"contains_match": contains_match,
"word_overlap": word_overlap
}
class EvalReport:
"""评估报告"""
def __init__(self, results: list[EvalResult], prompt_template: str, model: str):
self.results = results
self.prompt_template = prompt_template
self.model = model
def summary(self) -> str:
"""生成评估摘要"""
total = len(self.results)
passed = sum(1 for r in self.results if r.passed)
# 汇总各维度得分
score_keys = self.results[0].scores.keys() if self.results else []
avg_scores = {}
for key in score_keys:
values = [r.scores[key] for r in self.results]
avg_scores[key] = statistics.mean(values)
# 延迟统计
latencies = [r.latency_ms for r in self.results]
avg_latency = statistics.mean(latencies)
p95_latency = sorted(latencies)[int(len(latencies) * 0.95)] if latencies else 0
# Token统计
total_tokens = sum(r.token_usage["total_tokens"] for r in self.results)
lines = [
"=" * 50,
f"评估报告 | 模型: {self.model}",
f"提示词: {self.prompt_template[:60]}...",
"=" * 50,
f"通过率: {passed}/{total} ({passed/total*100:.1f}%)",
"",
"各维度平均得分:",
]
for key, val in avg_scores.items():
bar = "█" * int(val * 20) + "░" * (20 - int(val * 20))
lines.append(f" {key:20s} {bar} {val:.3f}")
lines.extend([
"",
f"平均延迟: {avg_latency:.0f}ms | P95延迟: {p95_latency:.0f}ms",
f"总Token消耗: {total_tokens:,}",
"=" * 50,
])
return "\n".join(lines)
def failed_cases(self) -> list[EvalResult]:
"""返回未通过的用例"""
return [r for r in self.results if not r.passed]
def to_json(self) -> str:
"""导出为JSON"""
data = {
"model": self.model,
"prompt_template": self.prompt_template,
"total": len(self.results),
"passed": sum(1 for r in self.results if r.passed),
"results": [
{
"input": r.input_text,
"expected": r.expected,
"actual": r.actual,
"scores": r.scores,
"passed": r.passed,
"latency_ms": r.latency_ms,
"tokens": r.token_usage["total_tokens"]
}
for r in self.results
]
}
return json.dumps(data, ensure_ascii=False, indent=2)
运行评估示例
evaluator = PromptEvaluator(model="gpt-4o-mini")
# 构建测试集
test_cases = [
EvalCase("法国的首都是哪里?", "巴黎", "factual"),
EvalCase("2+3等于多少?", "5", "math"),
EvalCase("HTTP状态码404表示什么?", "未找到", "technical"),
EvalCase("Python中用什么关键词定义函数?", "def", "coding"),
EvalCase("地球上最大的海洋是什么?", "太平洋", "factual"),
]
for case in test_cases:
evaluator.add_case(case)
# 对比两个提示词
prompt_v1 = "回答问题:{input}"
prompt_v2 = "请用一到两个词简洁回答以下问题。\n问题:{input}\n答案:"
report_v1 = evaluator.evaluate(prompt_v1)
report_v2 = evaluator.evaluate(prompt_v2)
print("=== 提示词 V1 ===")
print(report_v1.summary())
print("\n=== 提示词 V2 ===")
print(report_v2.summary())
LLM-as-Judge 评估法
对于开放式任务(如写作、翻译、总结),传统的文本匹配指标不够用。可以用另一个 LLM 来担任"评委":
graph LR
A[待评估的提示词] --> B[生成回答]
B --> C[LLM评委]
D[评分标准] --> C
E[参考答案/标准] --> C
C --> F[结构化评分]
style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style C fill:#ede7f6,stroke:#5e35b1,stroke-width:3px
style D fill:#fff9c4,stroke:#f9a825,stroke-width:2px
style F fill:#c8e6c9,stroke:#43a047,stroke-width:2px
实现
def llm_judge(
question: str,
answer: str,
reference: str = "",
criteria: list[str] = None
) -> dict:
"""
使用LLM作为评委进行评分。
Args:
question: 原始问题
answer: 待评估的回答
reference: 参考答案(可选)
criteria: 评估标准列表
Returns:
{"scores": {...}, "reasoning": str, "overall": float}
"""
if criteria is None:
criteria = ["准确性", "完整性", "清晰度", "相关性"]
criteria_text = "\n".join(f"- {c}(1-5分)" for c in criteria)
ref_section = ""
if reference:
ref_section = f"\n## 参考答案\n{reference}\n"
judge_prompt = f"""你是一个严格的AI评估专家。请根据以下标准对回答进行评分。
## 评分标准
{criteria_text}
## 问题
{question}
{ref_section}
## 待评估回答
{answer}
## 要求
1. 对每个标准给出1-5分的评分
2. 简要说明评分理由(每项一句话)
3. 给出整体评分(1-5分)
请以JSON格式输出:
{{
"scores": {{"标准名": 分数, ...}},
"reasoning": "评分理由",
"overall": 整体分数
}}"""
response = client.chat.completions.create(
model="gpt-4o", # 评委建议使用更强的模型
messages=[{"role": "user", "content": judge_prompt}],
temperature=0.0,
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
# 使用示例
result = llm_judge(
question="解释什么是机器学习",
answer="机器学习是AI的一个分支,它让计算机通过数据学习规律,而不需要明确编程。",
criteria=["准确性", "完整性", "通俗易懂", "专业深度"]
)
print(f"总分: {result['overall']}/5")
for criterion, score in result['scores'].items():
print(f" {criterion}: {score}/5")
评估最佳实践
构建测试集的原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 覆盖性 | 覆盖所有预期使用场景 | 简单问题、复杂问题、边缘情况 |
| 平衡性 | 正反例数量均衡 | 正常输入 + 对抗输入各50% |
| 代表性 | 反映真实用户分布 | 根据日志统计选取典型查询 |
| 独立性 | 用例间互不依赖 | 每个用例可独立执行 |
| 可维护 | 随业务变化更新 | 版本化管理测试集 |
常见评估陷阱
⚠️ 陷阱1: 过拟合测试集 — 不断针对测试用例优化提示词,导致在真实场景中表现下降。解决:留出验证集(Hold-out Set)。
⚠️ 陷阱2: 忽略一致性 — 只看单次结果,忽略多次运行的波动。解决:每个用例运行3-5次,计算标准差。
⚠️ 陷阱3: 指标不对齐 — 评估指标与业务目标不一致。解决:先定义业务成功标准,再选择对应指标。
动手练习
练习:建立你的评估流程
- 选择一个你正在开发的提示词任务(如文本分类、问答、翻译)
- 构建至少 10 个测试用例(覆盖正常和边缘场景)
- 使用上面的
PromptEvaluator对比两个不同的提示词版本 - 加入 LLM-as-Judge 来评估开放式输出的质量
- 生成评估报告并分析差异
本节要点
- ✅ 提示词评估包括五个维度:准确性、相关性、一致性、安全性、效率
- ✅ 量化评估需要构建标准测试集和自动化评估框架
- ✅ 简单任务可用字符串匹配(精确匹配、包含匹配、F1)
- ✅ 开放式任务推荐 LLM-as-Judge 评估法
- ✅ 构建测试集需遵循覆盖性、平衡性、代表性、独立性、可维护性原则
- ✅ 避免过拟合测试集、忽略一致性、指标不对齐三大陷阱
下一步:测试、迭代与版本管理 🚀