大多数人优化 Prompt 的方式
“试试改个词?""加个例子?""换个说法?”
这种凭感觉的优化方式,就像蒙着眼睛调参——偶尔能碰对,但大多数时候在原地打转。
我们需要一套系统化的方法论,让 Prompt 优化变得可衡量、可复现、可持续。
优化方法论:四步循环
建立基线 → 定义指标 → 迭代优化 → 验证效果
↑ ↓
└────────────────────────────────────┘
第一步:建立基线
在优化之前,先搞清楚现在的 Prompt 表现如何。
import json
import time
def establish_baseline(prompt: str, test_cases: list[dict]) -> dict:
"""建立 Prompt 的基线表现"""
results = {
"prompt_version": "v1.0",
"timestamp": time.strftime("%Y-%m-%d %H:%M"),
"total_cases": len(test_cases),
"passed": 0,
"failed": 0,
"details": []
}
for case in test_cases:
output = call_llm(f"{prompt}\n\n输入:{case['input']}")
passed = case["evaluate"](output)
results["passed" if passed else "failed"] += 1
results["details"].append({
"input": case["input"],
"output": output[:200],
"expected": case.get("expected", ""),
"passed": passed,
"notes": ""
})
results["score"] = results["passed"] / results["total_cases"]
return results
测试用例设计
好的测试用例是优化的基础。覆盖以下几类:
| 类型 | 说明 | 占比建议 |
|---|---|---|
| 正常用例 | 典型的、预期的输入 | 40% |
| 边界用例 | 极端情况、空输入、超长输入 | 20% |
| 对抗用例 | 容易出错的、有歧义的输入 | 20% |
| 格式用例 | 验证输出格式是否正确 | 20% |
test_cases = [
# 正常用例
{
"input": "用户无法登录,提示密码错误",
"expected": "包含:复现步骤、可能原因、解决方案",
"evaluate": lambda x: all(k in x for k in ["步骤", "原因", "方案"])
},
# 边界用例
{
"input": "",
"expected": "应该提示输入为空",
"evaluate": lambda x: "请提供" in x or "输入" in x
},
# 对抗用例
{
"input": "这不是一个 Bug,我只是想聊天",
"expected": "应该礼貌引导回正题",
"evaluate": lambda x: "Bug" in x or "问题" in x
},
# 格式用例
{
"input": "页面加载很慢,大概要 10 秒",
"expected": "输出应该是结构化的",
"evaluate": lambda x: "##" in x or "1." in x
},
]
第二步:定义成功指标
不同任务需要不同的评估指标:
分类任务
def evaluate_classification(output: str, expected: str) -> dict:
"""评估分类任务"""
predicted = output.strip().lower()
expected = expected.strip().lower()
return {
"accuracy": predicted == expected,
"predicted": predicted,
"expected": expected
}
生成任务
生成任务的评估更复杂,通常需要多个维度:
def evaluate_generation(output: str, criteria: dict) -> dict:
"""评估生成任务"""
scores = {}
# 1. 完整性:是否包含所有必要部分
required_sections = criteria.get("required_sections", [])
present = sum(1 for s in required_sections if s in output)
scores["completeness"] = present / len(required_sections) if required_sections else 1.0
# 2. 长度:是否在合理范围内
word_count = len(output)
min_len = criteria.get("min_length", 100)
max_len = criteria.get("max_length", 2000)
scores["length"] = 1.0 if min_len <= word_count <= max_len else 0.5
# 3. 格式:是否符合要求的格式
format_checks = criteria.get("format_checks", [])
format_passed = sum(1 for check in format_checks if check(output))
scores["format"] = format_passed / len(format_checks) if format_checks else 1.0
# 4. 用 LLM 评估质量(1-10 分)
quality_score = llm_evaluate_quality(output, criteria.get("task_description", ""))
scores["quality"] = quality_score / 10
# 加权总分
weights = {"completeness": 0.3, "length": 0.1, "format": 0.2, "quality": 0.4}
scores["total"] = sum(scores[k] * weights[k] for k in weights)
return scores
def llm_evaluate_quality(output: str, task_description: str) -> int:
"""用 LLM 评估输出质量"""
response = call_llm(f"""请评估以下输出的质量(1-10 分)。
任务描述:{task_description}
输出内容:{output}
评分标准:
- 1-3 分:质量差,有明显错误或不相关
- 4-6 分:质量一般,基本完成但有改进空间
- 7-8 分:质量好,准确且有用
- 9-10 分:质量优秀,超出预期
请只输出一个数字。""")
import re
match = re.search(r'\d+', response)
return int(match.group()) if match else 5
第三步:迭代优化
有了基线和指标,就可以开始系统性优化了。
常见优化模式
| 优化模式 | 适用场景 | 效果 |
|---|---|---|
| 添加角色定义 | 输出风格不对 | 中 |
| 添加 Few-Shot 示例 | 格式不对或理解有偏差 | 高 |
| 细化约束条件 | 输出超出范围 | 中 |
| 添加 CoT 指令 | 推理类任务准确率低 | 高 |
| 拆分为多步 | 单个 Prompt 太复杂 | 高 |
| 调整输出格式 | 后续处理困难 | 中 |
| 添加负面示例 | 特定错误反复出现 | 高 |
A/B 测试框架
def ab_test(prompt_a: str, prompt_b: str, test_cases: list[dict]) -> dict:
"""A/B 测试两个 Prompt"""
results_a = {"scores": [], "total_time": 0}
results_b = {"scores": [], "total_time": 0}
for case in test_cases:
# 测试 Prompt A
start = time.time()
output_a = call_llm(f"{prompt_a}\n\n输入:{case['input']}")
results_a["total_time"] += time.time() - start
results_a["scores"].append(1 if case["evaluate"](output_a) else 0)
# 测试 Prompt B
start = time.time()
output_b = call_llm(f"{prompt_b}\n\n输入:{case['input']}")
results_b["total_time"] += time.time() - start
results_b["scores"].append(1 if case["evaluate"](output_b) else 0)
# 计算统计数据
avg_a = sum(results_a["scores"]) / len(results_a["scores"])
avg_b = sum(results_b["scores"]) / len(results_b["scores"])
return {
"prompt_a_score": avg_a,
"prompt_b_score": avg_b,
"winner": "A" if avg_a > avg_b else "B" if avg_b > avg_a else "平局",
"improvement": f"{(avg_b - avg_a) / avg_a * 100:+.1f}%" if avg_a > 0 else "N/A",
"prompt_a_avg_time": results_a["total_time"] / len(test_cases),
"prompt_b_avg_time": results_b["total_time"] / len(test_cases),
}
第四步:验证效果
优化后要在独立的测试集上验证,避免过拟合。
def validate_optimization(
old_prompt: str,
new_prompt: str,
validation_cases: list[dict] # 不同于优化时用的测试用例
) -> dict:
"""在独立验证集上验证优化效果"""
old_results = establish_baseline(old_prompt, validation_cases)
new_results = establish_baseline(new_prompt, validation_cases)
return {
"old_score": old_results["score"],
"new_score": new_results["score"],
"improvement": new_results["score"] - old_results["score"],
"regression_cases": [
d for d_old, d_new in zip(old_results["details"], new_results["details"])
if (d := d_old)["passed"] and not d_new["passed"]
]
}
注意 regression_cases——这些是优化后反而变差的用例。如果回归太多,说明优化方向有问题。
实战案例:优化一个 Bug 分类 Prompt
初始 Prompt(v1.0,得分 60%)
请将以下 Bug 报告分类为:UI、API、数据库、性能、安全。
Bug 报告:{input}
问题:分类不准确,经常把性能问题归为 API 问题。
优化 1:添加分类定义(v1.1,得分 72%)
请将以下 Bug 报告分类为以下类别之一:
- UI:界面显示、样式、交互相关的问题
- API:接口调用、请求响应、状态码相关的问题
- 数据库:数据存储、查询、迁移相关的问题
- 性能:加载速度、响应时间、资源占用相关的问题
- 安全:认证、授权、数据泄露相关的问题
Bug 报告:{input}
请只输出类别名称。
提升了 12%,但还不够。
优化 2:添加 Few-Shot 示例(v1.2,得分 85%)
请将 Bug 报告分类为:UI、API、数据库、性能、安全。
示例:
Bug:页面加载需要 15 秒 → 性能
Bug:按钮点击没反应 → UI
Bug:POST /users 返回 500 → API
Bug:用户密码明文存储 → 安全
Bug:查询超时,SQL 执行慢 → 数据库
Bug 报告:{input}
类别:
又提升了 13%。Few-Shot 示例的效果非常明显。
优化 3:添加 CoT + 边界处理(v1.3,得分 91%)
请将 Bug 报告分类为:UI、API、数据库、性能、安全。
分类规则:
- 如果涉及多个类别,选择根本原因所在的类别
- "慢"不一定是性能问题,要看原因(SQL 慢→数据库,接口慢→API)
- 如果无法确定,选择最可能的类别并说明原因
示例:
Bug:页面加载需要 15 秒 → 分析:加载慢是性能问题 → 性能
Bug:POST /users 返回 500 → 分析:接口错误 → API
Bug:查询用户列表很慢,SQL 执行了 30 秒 → 分析:虽然表现为慢,但根因是 SQL → 数据库
Bug 报告:{input}
请先简要分析,然后给出分类。
格式:
分析:[一句话分析]
类别:[类别名]
最终得分 91%,从 60% 提升了 31 个百分点。
优化过程总结
| 版本 | 改动 | 得分 | 提升 |
|---|---|---|---|
| v1.0 | 初始版本 | 60% | - |
| v1.1 | 添加分类定义 | 72% | +12% |
| v1.2 | 添加 Few-Shot | 85% | +13% |
| v1.3 | 添加 CoT + 边界规则 | 91% | +6% |
优化清单
当你的 Prompt 效果不好时,按这个清单逐项检查:
准确性问题
- 任务描述是否足够清晰?
- 是否提供了足够的上下文?
- 是否需要添加 Few-Shot 示例?
- 是否需要 CoT 推理?
- 是否有歧义的表述?
格式问题
- 输出格式是否明确定义?
- 是否提供了格式示例?
- 是否需要 JSON/XML 等结构化输出?
一致性问题
- 多次运行结果是否一致?
- 是否需要降低 temperature?
- 是否需要 Self-Consistency?
边界问题
- 空输入如何处理?
- 超长输入如何处理?
- 不相关输入如何处理?
- 多类别/多答案如何处理?
记录优化历史
每次优化都要记录,方便回溯和团队协作:
optimization_log = {
"task": "Bug 报告分类",
"versions": [
{
"version": "v1.0",
"date": "2026-03-01",
"prompt": "...",
"score": 0.60,
"changes": "初始版本",
"notes": "分类不准确,性能和 API 经常混淆"
},
{
"version": "v1.1",
"date": "2026-03-05",
"prompt": "...",
"score": 0.72,
"changes": "添加了每个分类的定义",
"notes": "定义帮助区分了大部分情况,但边界 case 仍有问题"
},
{
"version": "v1.2",
"date": "2026-03-08",
"prompt": "...",
"score": 0.85,
"changes": "添加了 5 个 Few-Shot 示例",
"notes": "示例效果显著,特别是对边界 case"
},
{
"version": "v1.3",
"date": "2026-03-10",
"prompt": "...",
"score": 0.91,
"changes": "添加 CoT 和边界处理规则",
"notes": "CoT 让模型先分析再分类,减少了冲动判断"
}
]
}
总结
Prompt 优化不是玄学,而是工程。关键是:
- 有基线:知道现在多少分
- 有指标:知道什么算”好”
- 有方法:知道往哪个方向改
- 有验证:知道改了之后是否真的变好了
大多数 Prompt 通过 3-5 轮迭代就能从 60 分提升到 85-90 分。剩下的 10 分往往需要更高级的技巧(Chaining、Self-Consistency 等)或者更多的测试数据。
优化 Prompt 就像优化代码:先让它跑起来,再让它跑得好,最后让它跑得快。不要试图一步到位,迭代才是王道。
相关文章
评论
加载中...
评论
加载中...