提示词评估系统完整指南
原文参考:anthropic.skilljar.com/claude-with…
提示词评估系统完整指南
📖 引言
在 AI 应用开发中,提示词(Prompt)的质量直接影响模型的输出效果。本文介绍如何构建一个完整的提示词评估系统,实现自动化测试、AI 评分和可视化报告。
🏗️ 系统架构
整体流程
用户输入
↓
[任务描述 + 输入规范]
↓
generate_unique_ideas() → [测试想法列表]
↓
generate_test_case() → [测试用例]
↓
保存到 dataset.json
↓
run_prompt() → [模型输出]
↓
grade_output() → [评分结果]
↓
保存到 output.json + output.html
核心组件
- PromptEvaluator 类:核心评估引擎
- 测试数据集生成器:自动生成多样化测试用例
- 输出评分器:使用 AI 模型进行客观评分
- 报告生成器:生成 HTML 可视化报告
💻 分步实现
步骤 0:环境准备
说明:环境准备是整个系统的基础。我们需要导入必要的库(JSON 处理、并发执行、正则表达式等),初始化 Anthropic API 客户端,并定义核心的辅助函数。其中
chat()函数是与 AI 模型交互的核心,它封装了 API 调用逻辑,支持系统提示、温度控制和停止序列等参数,是后续所有步骤的基础。
实现代码
import json
import concurrent.futures
import re
from textwrap import dedent
from statistics import mean
from dotenv import load_dotenv
from anthropic import Anthropic
# 初始化客户端
load_dotenv()
client = Anthropic(api_key=api_key, base_url=base_url)
model = "claude-opus-4-5-thinking"
def add_user_message(messages, text):
messages.append({"role": "user", "content": text})
def add_assistant_message(messages, text):
messages.append({"role": "assistant", "content": text})
def chat(messages, system=None, temperature=1.0, stop_sequences=[], max_tokens=4000):
params = {
"model": model, "max_tokens": max_tokens, "messages": messages,
"temperature": temperature, "stop_sequences": stop_sequences,
}
if system:
params["system"] = system
text = client.messages.create(**params).content[0].text
# 客户端处理停止序列
for stop_seq in stop_sequences:
if stop_seq in text:
text = text.split(stop_seq)[0]
break
return text
步骤 1:生成测试想法 (generate_unique_ideas)
说明:这是评估系统的第一个核心步骤。该函数接收任务描述和输入规范,利用 AI 模型生成多样化的测试场景想法。使用
temperature=1.0确保生成的想法具有足够的多样性和创造性,覆盖各种边界情况和特殊场景。预填充"```json"并使用停止序列确保输出为规范的 JSON 格式,便于后续解析。
实现代码
def generate_unique_ideas(self, task_description, prompt_inputs_spec, num_cases):
"""基于任务描述生成测试用例的独特想法列表"""
example_prompt_inputs = ""
for key, value in prompt_inputs_spec.items():
example_prompt_inputs += f'"{key}": str # {value},'
prompt = f"""
生成 {num_cases} 个独特且多样化的想法,用于测试完成此任务的提示词:
<task_description>{task_description}</task_description>
<prompt_inputs>{example_prompt_inputs}</prompt_inputs>
输出格式:JSON 数组,每项是一个简短的场景描述。
"""
messages = []
add_user_message(messages, dedent(prompt))
add_assistant_message(messages, "```json")
text = chat(messages, stop_sequences=["```"], temperature=1.0,
system="你是一名测试场景设计师。")
return json.loads(text)
输入
task_description = "为一名运动员编写一份紧凑、简洁的一日膳食计划"
prompt_inputs_spec = {
"height": "运动员的身高(单位:厘米)",
"weight": "运动员的体重(单位:千克)",
"goal": "运动员的目标",
"restrictions": "运动员的饮食限制",
}
num_cases = 1
输出
[
"为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
]
步骤 2:生成测试用例 (generate_test_case)
说明:该函数将抽象的测试想法转化为具体的、可执行的测试用例。它生成两个关键内容:
prompt_inputs(具体的输入参数值)和solution_criteria(评估标准列表)。使用temperature=0.7平衡创造性和一致性,确保生成的测试用例既有变化又保持合理性。评估标准应简洁、可衡量,避免过度指定超出任务范围的要求。
实现代码
def generate_test_case(self, task_description, idea, prompt_inputs_spec={}):
"""基于任务描述和特定想法生成单个测试用例"""
example_prompt_inputs = ""
allowed_keys = []
for key, value in prompt_inputs_spec.items():
example_prompt_inputs += f'"{key}": "EXAMPLE_VALUE", // {value}\n'
allowed_keys.append(f'"{key}"')
prompt = f"""
基于以下信息生成一个详细的测试用例:
<task_description>{task_description}</task_description>
<specific_idea>{idea}</specific_idea>
<allowed_input_keys>{", ".join(allowed_keys)}</allowed_input_keys>
输出格式:
```json
{{
"prompt_inputs": {{ {example_prompt_inputs} }},
"solution_criteria": ["标准1", "标准2", ...]
}}
```
"""
messages = []
add_user_message(messages, dedent(prompt))
add_assistant_message(messages, "```json")
text = chat(messages, stop_sequences=["```"], temperature=0.7,
system="你是一名测试用例创建专家。")
test_case = json.loads(text)
test_case["task_description"] = task_description
test_case["scenario"] = idea
return test_case
输入
task_description = "为一名运动员编写一份紧凑、简洁的一日膳食计划"
idea = "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
prompt_inputs_spec = {
"height": "运动员的身高(单位:厘米)",
"weight": "运动员的体重(单位:千克)",
"goal": "运动员的目标",
"restrictions": "运动员的饮食限制",
}
输出
{
"prompt_inputs": {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
},
"solution_criteria": [
"膳食计划必须完全符合素食和无乳糖的要求",
"所有食材必须体现便携、脱水或高热量密度的特点",
"计划应包含针对极端寒冷环境的高热量配比",
"内容表达必须紧凑、简洁,涵盖一日三餐及零食"
],
"task_description": "为一名运动员编写一份紧凑、简洁的一日膳食计划",
"scenario": "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
}
步骤 3:批量生成数据集 (generate_dataset)
说明:该函数整合了前两个步骤,实现完整的数据集生成流程。首先调用
generate_unique_ideas()获取测试想法列表,然后使用ThreadPoolExecutor并发地为每个想法生成测试用例,大幅提高效率。生成的数据集保存为 JSON 文件,使用ensure_ascii=False确保中文正确显示。错误处理机制确保单个用例生成失败不会影响整体流程。
实现代码
def generate_dataset(self, task_description, prompt_inputs_spec={}, num_cases=1, output_file="dataset.json"):
"""基于任务描述生成测试数据集并保存到文件"""
# 1. 生成想法列表
ideas = self.generate_unique_ideas(task_description, prompt_inputs_spec, num_cases)
# 2. 并发生成测试用例
dataset = []
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_concurrent_tasks) as executor:
future_to_idea = {
executor.submit(self.generate_test_case, task_description, idea, prompt_inputs_spec): idea
for idea in ideas
}
for future in concurrent.futures.as_completed(future_to_idea):
try:
dataset.append(future.result())
except Exception as e:
print(f"生成测试用例时出错: {e}")
# 3. 保存到文件
with open(output_file, "w", encoding="utf-8") as f:
json.dump(dataset, f, indent=2, ensure_ascii=False)
return dataset
输入
evaluator.generate_dataset(
task_description="为一名运动员编写一份紧凑、简洁的一日膳食计划",
prompt_inputs_spec={
"height": "运动员的身高(单位:厘米)",
"weight": "运动员的体重(单位:千克)",
"goal": "运动员的目标",
"restrictions": "运动员的饮食限制",
},
output_file="dataset.json",
num_cases=1,
)
输出 (dataset.json)
[
{
"prompt_inputs": {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
},
"solution_criteria": [
"膳食计划必须完全符合素食和无乳糖的要求",
"所有食材必须体现便携、脱水或高热量密度的特点",
"计划应包含针对极端寒冷环境的高热量配比",
"内容表达必须紧凑、简洁,涵盖一日三餐及零食"
],
"task_description": "为一名运动员编写一份紧凑、简洁的一日膳食计划",
"scenario": "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
}
]
步骤 4:定义提示词函数 (run_prompt)
说明:这是用户需要自定义的核心函数,也是整个评估的对象。该函数接收测试用例中的
prompt_inputs,构建完整的提示词并调用 AI 模型获取输出。提示词的设计直接影响评估结果,建议包含清晰的任务说明、结构化的输入参数和具体的指导原则。使用较大的max_tokens(如 8000)确保复杂任务的输出不被截断。
实现代码
def run_prompt(prompt_inputs):
"""定义并执行要评估的提示词"""
prompt = f"""
为一名运动员生成一份符合其饮食限制的一日膳食计划。
<athlete_information>
- 身高: {prompt_inputs["height"]}
- 体重: {prompt_inputs["weight"]}
- 目标: {prompt_inputs["goal"]}
- 饮食限制: {prompt_inputs["restrictions"]}
</athlete_information>
指导原则:
1. 包含准确的每日卡路里数量
2. 显示蛋白质、脂肪和碳水化合物含量
3. 指定每餐的用餐时间
4. 仅使用符合限制的食物
5. 以克为单位列出所有食物份量
6. 如果提到预算,保持经济实惠
以下是一个示例输入和理想输出的例子:
<sample_input>
身高: 170
体重: 70
目标: 保持健康并改善胆固醇水平
限制: 高胆固醇
</sample_input>
<ideal_output>
以下是为一名旨在保持健康并改善胆固醇水平的运动员制定的一日膳食计划:
* **卡路里目标:** 约 2500 卡路里
* **宏量营养素分解:** 蛋白质 (140g),脂肪 (70g),碳水化合物 (340g)
**膳食计划:**
* **早餐 (7:00 AM):** 燕麦片(80g 干重)配浆果(100g)和核桃(15g)。脱脂牛奶(240g)。
* 蛋白质: 15g,脂肪: 15g,碳水化合物: 60g
...
</ideal_output>
此示例膳食计划结构良好,提供了食物选择和数量的详细信息,并与运动员的目标和限制保持一致。
"""
messages = []
add_user_message(messages, prompt)
return chat(messages, max_tokens=4000)
输入
prompt_inputs = {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
}
输出
以下是为一名在珠穆朗玛峰大本营进行高强度训练的运动员(180cm,75kg)量身定制的一日膳食计划。
该计划针对**极端寒冷**和**高海拔**环境,专注于**高热量密度**、**易于携带**、**脱水**且完全符合**素食(非肉类)**及**乳糖不耐受**要求的食物。
* **卡路里目标:** 约 4200 卡路里(以应对基础代谢、极端御寒产热及耐力消耗)
* **宏量营养素分解:** 蛋白质 (155g),脂肪 (150g),碳水化合物 (558g)
---
### **膳食计划:**
* **早餐 (6:30 AM) - 高能热食启动:** 强化营养速食燕麦粥。
* **食物:** 速食燕麦片(120g)、大豆蛋白粉(30g)、核桃碎(30g)、脱水蓝莓(20g)、椰浆粉(20g,提供优质脂肪)。
* **蛋白质:** 38g,**脂肪:** 35g,**碳水化合物:** 95g
* **上午加餐 / 训练持续补充 (10:00 AM) - 高密度能量:** 自制高能路粮(Trail Mix)。
* **食物:** 杏仁(40g)、腰果(30g)、脱乳制品黑巧克力豆(30g)、葡萄干(40g)。
* **蛋白质:** 18g,**脂肪:** 48g,**碳水化合物:** 65g
...
步骤 5:输出评分 (grade_output)
说明:该函数使用 AI 模型对提示词的输出进行客观评分。评分基于测试用例中定义的
solution_criteria和额外的强制性标准(extra_criteria)。使用temperature=0.0确保评分的一致性和可重复性。输出包含优点、缺点、详细推理和最终分数(1-10),便于分析提示词的具体问题并进行针对性优化。
实现代码
def grade_output(self, test_case, output, extra_criteria):
"""使用模型对测试用例的输出进行评分"""
prompt_inputs = ""
for key, value in test_case["prompt_inputs"].items():
prompt_inputs += f'"{key}":"{value}",\n'
extra_criteria_section = ""
if extra_criteria:
extra_criteria_section = f"""
强制性要求(违反则分数≤3):
<extra_criteria>{extra_criteria}</extra_criteria>
"""
eval_prompt = f"""
严格评估以下 AI 生成的解决方案。
<task_description>{test_case["task_description"]}</task_description>
<task_inputs>{{ {prompt_inputs} }}</task_inputs>
<solution>{output}</solution>
<criteria>{chr(10).join(test_case["solution_criteria"])}</criteria>
{extra_criteria_section}
评分指南:
* 1-3分:未满足强制性要求
* 4-6分:满足强制性要求,但次要标准有重大缺陷
* 7-8分:满足所有要求,有小问题
* 9-10分:完全满足所有标准
输出 JSON:{{"strengths": [], "weaknesses": [], "reasoning": "", "score": number}}
"""
messages = []
add_user_message(messages, eval_prompt)
add_assistant_message(messages, "```json")
return json.loads(chat(messages, stop_sequences=["```"], temperature=0.0))
输入
test_case = {
"prompt_inputs": {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
},
"solution_criteria": [
"膳食计划必须完全符合素食和无乳糖的要求",
"所有食材必须体现便携、脱水或高热量密度的特点",
"计划应包含针对极端寒冷环境的高热量配比",
"内容表达必须紧凑、简洁,涵盖一日三餐及零食"
]
}
output = "以下是为一名在珠穆朗玛峰大本营进行高强度训练的运动员..." # 模型生成的完整输出
extra_criteria = """
输出应包含:
- 每日卡路里总量
- 宏量营养素分解
- 包含确切食物、份量和时间的餐次
"""
输出
{
"strengths": [
"完全符合素食和乳糖不耐受的限制条件,选用了针对性的植物蛋白和油脂来源。",
"充分考虑了珠峰大本营的极端环境,选用了脱水、高热量密度的便携食材(如椰浆粉、TVP、坚果酱)。",
"宏量营养素计算详尽,逻辑清晰,包含了具体的食物份量和时间点。"
],
"weaknesses": [
"晚间加餐的碳水化合物数据计算存在明显逻辑错误(标称160g,但下方修正说明差异巨大且与食材不符)。",
"总热量计算与各项餐食累加值略有偏差,部分数据标注不够严谨(如晚间加餐的脂肪和碳水标注混乱)。"
],
"reasoning": "该解决方案完美符合所有强制性要求和次要标准。它针对特定环境(高海拔、极端寒冷)提供了极具实操性的脱水便携素食方案。虽然在晚间加餐的营养数据标注上出现了小范围的笔误或计算混乱,但整体框架、食材选择和热量配比均非常专业且符合任务描述。由于数据标注的小瑕疵,未能给满分。",
"score": 9
}
步骤 6:运行评估 (run_evaluation)
说明:这是评估系统的主入口函数,整合了执行和评分的完整流程。它读取数据集文件,并发地对每个测试用例执行提示词函数和评分,最后计算平均分数并生成报告。输出包括 JSON 格式的详细结果(便于程序处理)和 HTML 格式的可视化报告(便于人工查看)。通过分析评估结果,可以识别提示词的薄弱环节并进行迭代优化。
实现代码
def run_evaluation(self, run_prompt_function, dataset_file, extra_criteria=None,
json_output_file="output.json", html_output_file="output.html"):
"""对数据集中的所有测试用例运行评估"""
# 1. 读取数据集
with open(dataset_file, "r") as f:
dataset = json.load(f)
# 2. 并发执行评估
results = []
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_concurrent_tasks) as executor:
futures = {
executor.submit(self.run_test_case, tc, run_prompt_function, extra_criteria): tc
for tc in dataset
}
for future in concurrent.futures.as_completed(futures):
results.append(future.result())
# 3. 计算平均分数
average_score = mean([r["score"] for r in results])
print(f"平均分数: {average_score}")
# 4. 保存结果
with open(json_output_file, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
# 5. 生成 HTML 报告
html = generate_prompt_evaluation_report(results)
with open(html_output_file, "w", encoding="utf-8") as f:
f.write(html)
return results
输入
results = evaluator.run_evaluation(
run_prompt_function=run_prompt,
dataset_file="dataset.json",
extra_criteria="""
输出应包含:
- 每日卡路里总量
- 宏量营养素分解
- 包含确切食物、份量和时间的餐次
""",
)
输出
控制台输出:
已评分 1/1 个测试用例
平均分数: 9
output.json:
[
{
"output": "以下是为一名在珠穆朗玛峰大本营进行高强度训练的运动员(180cm,75kg)量身定制的一日膳食计划...",
"test_case": {
"prompt_inputs": {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
},
"solution_criteria": [
"膳食计划必须完全符合素食和无乳糖的要求",
"所有食材必须体现便携、脱水或高热量密度的特点",
"计划应包含针对极端寒冷环境的高热量配比",
"内容表达必须紧凑、简洁,涵盖一日三餐及零食"
],
"task_description": "为一名运动员编写一份紧凑、简洁的一日膳食计划",
"scenario": "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划..."
},
"score": 9,
"reasoning": "该解决方案完美符合所有强制性要求和次要标准。它针对特定环境(高海拔、极端寒冷)提供了极具实操性的脱水便携素食方案。虽然在晚间加餐的营养数据标注上出现了小范围的笔误或计算混乱,但整体框架、食材选择和热量配比均非常专业且符合任务描述。由于数据标注的小瑕疵,未能给满分。"
}
]
output.html:包含统计摘要和详细结果表格的 HTML 报告
📊 输入输出总结表
| 步骤 | 函数 | 输入 | 输出 |
|---|---|---|---|
| 1 | generate_unique_ideas() | 任务描述 + 输入规范 + 数量 | JSON 数组(测试场景列表) |
| 2 | generate_test_case() | 任务描述 + 场景 + 输入规范 | JSON(prompt_inputs + criteria) |
| 3 | generate_dataset() | 任务描述 + 输入规范 + 数量 | dataset.json 文件 |
| 4 | run_prompt() | prompt_inputs | 模型输出文本 |
| 5 | grade_output() | 测试用例 + 输出 + 额外标准 | JSON(score + reasoning) |
| 6 | run_evaluation() | 提示词函数 + 数据集 + 额外标准 | output.json + output.html |
⚠️ 关键注意事项
- Token 管理:Gemini 模型的思考过程会占用 tokens(如
thoughtsTokenCount: 957),需设置较大的max_tokens - 并发控制:合理设置
max_concurrent_tasks,避免 API 速率限制 - 评分一致性:评分使用
temperature=0.0确保结果稳定 - 中文支持:使用
ensure_ascii=False和encoding="utf-8" - 预填充技巧:使用
add_assistant_message(messages, "```json")引导模型输出 JSON
🔧 扩展功能
A/B 测试
def compare_prompts(prompt_versions, dataset_file):
"""对比多个提示词版本"""
return {name: evaluator.run_evaluation(func, dataset_file)
for name, func in prompt_versions.items()}
CI/CD 集成
def ci_evaluation(min_score_threshold=7.0):
"""如果平均分数低于阈值,返回失败"""
results = evaluator.run_evaluation(...)
avg_score = mean([r["score"] for r in results])
if avg_score < min_score_threshold:
raise ValueError(f"平均分数 {avg_score} 低于阈值 {min_score_threshold}")
return results
📝 总结
本系统实现了提示词的自动化评估:
- 自动化:自动生成测试用例和评估报告
- 客观性:使用 AI 模型进行客观评分
- 可扩展性:易于扩展和定制
- 实用性:提供可视化的评估报告
通过使用这个系统,你可以系统化地测试、评估和优化提示词质量。