洛雪音乐
72.63M · 2026-02-05
原文参考:anthropic.skilljar.com/claude-with…
在 AI 应用开发中,提示词(Prompt)的质量直接影响模型的输出效果。本文介绍如何构建一个完整的提示词评估系统,实现自动化测试、AI 评分和可视化报告。
用户输入
↓
[任务描述 + 输入规范]
↓
generate_unique_ideas() → [测试想法列表]
↓
generate_test_case() → [测试用例]
↓
保存到 dataset.json
↓
run_prompt() → [模型输出]
↓
grade_output() → [评分结果]
↓
保存到 output.json + output.html
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
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
[
"为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
]
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": "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
}
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,
)
[
{
"prompt_inputs": {
"height": "180cm",
"weight": "75kg",
"goal": "在珠穆朗玛峰大本营(极端寒冷)进行高强度耐力训练,需高热量密度、脱水且便携的膳食以维持体能。",
"restrictions": "素食(不含肉类)、乳糖不耐受(不含奶制品)。"
},
"solution_criteria": [
"膳食计划必须完全符合素食和无乳糖的要求",
"所有食材必须体现便携、脱水或高热量密度的特点",
"计划应包含针对极端寒冷环境的高热量配比",
"内容表达必须紧凑、简洁,涵盖一日三餐及零食"
],
"task_description": "为一名运动员编写一份紧凑、简洁的一日膳食计划",
"scenario": "为一名在极端寒冷环境下(如珠穆朗玛峰大本营)进行高强度耐力训练、且患有乳糖不耐受的素食登山运动员设计膳食计划,要求所有食材必须是便携、脱水或高热量密度的,以应对恶劣环境和体力极度消耗。"
}
]
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
...
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
}
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 |
thoughtsTokenCount: 957),需设置较大的 max_tokensmax_concurrent_tasks,避免 API 速率限制temperature=0.0 确保结果稳定ensure_ascii=False 和 encoding="utf-8"add_assistant_message(messages, "```json") 引导模型输出 JSONdef compare_prompts(prompt_versions, dataset_file):
"""对比多个提示词版本"""
return {name: evaluator.run_evaluation(func, dataset_file)
for name, func in prompt_versions.items()}
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
本系统实现了提示词的自动化评估:
通过使用这个系统,你可以系统化地测试、评估和优化提示词质量。