OpenAI Prompt Engineering 完全指南
来源: OpenAI Platform Docs整理日期: 2025-01
概述
Prompt Engineering(提示工程)是与大语言模型(LLM)有效交互的核心技能。它不仅仅是"写好问题",而是一套系统的方法论,包括理解模型能力、优化输入结构、提升输出质量,以及与外部工具集成等多个维度。
本指南整理自 OpenAI 官方文档,涵盖六大核心策略和 Prompt Caching(提示缓存)优化技术。
第一部分:六大核心策略
策略一:Write Clear Instructions(编写清晰的指令)
模型无法读取你的想法。如果输出太长,要求简洁回复;如果输出太简单,要求专家级写作。如果你不喜欢某种格式,展示你想要的格式。模型猜测你意图的次数越少,你获得满意结果的可能性就越大。
Tactic 1.1:在查询中包含详细信息
提供足够的上下文和细节,让模型理解你的需求。
❌ Bad prompt:
如何在 Excel 中添加数字?✅ Good prompt:
如何在 Excel 中将一行美元金额相加?我想对整个工作表的所有行自动执行此操作,所有总计最终显示在右侧名为"Total"的列中。Tactic 1.2:要求模型采用特定角色(Persona)
使用 System Message 为模型设定角色和行为方式。
{
"role": "system",
"content": "你是一位资深的软件架构师,擅长解释复杂的技术概念。请用简洁易懂的语言回答问题,必要时提供代码示例。"
}Tactic 1.3:使用分隔符清晰标记输入的不同部分
使用三重引号 """、XML 标签 <tag>、Markdown 标题等分隔符,帮助模型理解输入结构。
请将以下文本总结为一个要点列表:
"""
[在此处插入长文本]
"""Tactic 1.4:指定完成任务所需的步骤
将复杂任务分解为明确的步骤序列。
请按以下步骤处理用户输入:
步骤 1 - 用户会用三重引号提供文本。将这段文本总结成一句话,前缀为"摘要:"
步骤 2 - 将步骤 1 的摘要翻译成英语,前缀为"Translation:"Tactic 1.5:提供示例(Few-Shot Prompting)
通过提供示例来展示期望的输出格式和风格。
以一致的风格回答问题。
Q: 教我什么是耐心。
A: 雕刻最深峡谷的河流源自一处普通的泉水;最宏伟的交响乐始于一个音符;最复杂的织锦始于一根线。
Q: 教我什么是海洋。
A:Tactic 1.6:指定所需的输出长度
明确指定输出的长度要求,可以使用字数、句子数、段落数或项目符号数。
请用大约 100 字总结以下文本。请用 3 个要点总结以下文本。策略二:Provide Reference Text(提供参考文本)
即使是最强大的模型也可能会生成虚假信息(Hallucination),尤其是在回答冷门话题或被要求提供引用和 URL 时。提供参考文本可以帮助模型生成更准确、更可靠的回复。
Tactic 2.1:指示模型使用参考文本回答
使用以下用三重引号括起来的文章来回答问题。如果在文章中找不到答案,请回复"我无法找到答案"。
"""
[在此处插入参考文章]
"""
问题:[在此处插入问题]Tactic 2.2:指示模型从参考文本中引用
要求模型在回答中标注引用来源,便于验证。
你将收到一份用三重引号括起来的文档和一个问题。你的任务是仅使用提供的文档来回答问题,并引用用于回答问题的文档段落。如果文档不包含回答此问题所需的信息,请简单地写:"信息不足"。如果提供了问题的答案,必须用引用进行注释。使用以下格式引用相关段落 ({"citation": …})。策略三:Split Complex Tasks into Simpler Subtasks(将复杂任务拆分为更简单的子任务)
就像软件工程中将复杂系统分解为模块一样,提交给模型的任务也可以进行类似的分解。复杂任务往往比简单任务有更高的错误率,而且复杂任务通常可以重新定义为一系列简单任务的工作流程。
Tactic 3.1:使用意图分类来识别用户查询的最相关指令
对于需要大量独立指令集来处理不同情况的任务,首先对查询类型进行分类,然后根据分类结果确定需要哪些指令是有益的。
你将收到客户服务查询。将每个查询分类为主要类别和次要类别。以 JSON 格式提供输出,键为:primary 和 secondary。
主要类别:计费、技术支持、账户管理、一般咨询
计费的次要类别:
- 取消订阅或升级
- 添加付款方式
- 收费说明
- 争议收费
技术支持的次要类别:
- 故障排除
- 设备兼容性
- 软件更新
... [继续其他类别]Tactic 3.2:对于需要很长对话的对话应用,总结或过滤之前的对话
由于模型具有固定的 Context Window(上下文窗口),即模型在任何时候能够"看到"的文本量有限,用户和助手之间的对话不能无限期地持续。
解决方案:
- 递归总结:定期总结之前的对话内容
- 后台异步处理:在对话进行时,后台总结历史内容
- 动态选择:根据当前查询选择最相关的历史片段
Tactic 3.3:分段总结长文档,递归构建完整摘要
对于超出单次处理能力的长文档:
- 将文档分成多个部分
- 分别总结每个部分
- 将各部分摘要连接起来再次总结
- 递归进行,直到整个文档被总结
策略四:Give the Model Time to "Think"(给模型时间"思考")
当模型在推理过程中出错时,通常是因为它试图立即给出答案,而不是花时间计算出正确答案。在得出最终答案之前,要求模型进行推理链(Chain of Reasoning)可以帮助模型更可靠地推理出正确答案。
Tactic 4.1:指示模型在得出结论之前先制定自己的解决方案
先让模型独立思考,然后再评估用户的答案。
❌ 直接判断(容易出错):
判断学生的解答是否正确。
问题:[数学问题]
学生的解答:[学生答案]✅ 先思考再判断:
首先自己解决这个问题。然后将你的解决方案与学生的解决方案进行比较,评估学生的解决方案是否正确。在自己解决问题之前,不要决定学生的解决方案是否正确。
问题:[数学问题]
学生的解答:[学生答案]Tactic 4.2:使用内心独白(Inner Monologue)或查询序列来隐藏模型的推理过程
在某些应用中,模型用于得出最终答案的推理过程可能不适合与用户共享。可以指示模型将想要隐藏的输出部分放入结构化格式中,便于解析时隐藏。
请按照以下步骤回答用户问题:
步骤 1 - 首先自己解决问题。不要依赖学生的解答,因为它可能是错误的。将此步骤的所有工作放在三重引号 """ 内。
步骤 2 - 将你的解答与学生的解答进行比较,评估学生的解答是否正确。将此步骤的所有工作放在三重引号 """ 内。
步骤 3 - 如果学生犯了错误,确定你可以给学生什么提示而不透露答案。将此步骤的所有工作放在三重引号 """ 内。
步骤 4 - 如果学生犯了错误,向学生提供上一步的提示(三重引号之外)。不要写"步骤 4",而是写"提示:"。Tactic 4.3:询问模型是否遗漏了之前的内容
对于复杂任务,让模型进行自我检查:
在上述分析中,你是否遗漏了任何重要的点?请回顾并补充。策略五:Use External Tools(使用外部工具)
通过将其他工具的输出提供给模型,来弥补模型的弱点。例如,文本检索系统可以告诉模型相关文档的信息;代码执行引擎可以帮助模型进行数学计算和运行代码。
Tactic 5.1:使用基于嵌入的搜索实现高效知识检索
RAG(Retrieval-Augmented Generation,检索增强生成) 是一种强大的模式:
- 将文档分割成小块(Chunks)
- 使用 Embedding 模型将每个块转换为向量
- 将向量存储在向量数据库中
- 查询时,将用户问题转换为向量
- 搜索最相似的文档块
- 将相关文档作为上下文提供给模型
# 伪代码示例
query_embedding = embed(user_query)
relevant_docs = vector_db.search(query_embedding, top_k=5)
context = "\n".join(relevant_docs)
response = llm.chat(
system=f"使用以下上下文回答问题:\n{context}",
user=user_query
)Tactic 5.2:使用代码执行来进行更准确的计算或调用外部 API
模型进行数学计算时可能会出错。与其让模型自己计算,不如让它生成代码来执行计算。
你可以通过将 Python 代码放在三重反引号中来执行代码,例如 ```code goes here```。使用此功能执行计算。
问题:找出以下多项式的所有实根:3x^5 - 5x^4 - 3x^3 - 7x - 10⚠️ 安全提示:模型生成的代码不保证安全,应该只在沙箱环境中执行。
Tactic 5.3:授予模型访问特定 Function 的权限
Function Calling(函数调用) 允许模型调用你定义的外部函数:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"}
},
"required": ["city"]
}
}
}
]策略六:Test Changes Systematically(系统地测试更改)
在某些情况下,对 Prompt 的修改可能会在少数孤立示例上取得更好的性能,但在更具代表性的示例集上导致更差的整体性能。因此,为了确保更改对性能产生净正面影响,可能需要定义一个全面的测试套件(Evaluation Suite)。
Tactic 6.1:参照黄金标准答案评估模型输出
构建评估系统来衡量 Prompt 更改的效果:
# 评估框架示例
test_cases = [
{"input": "问题1", "expected": "预期答案1"},
{"input": "问题2", "expected": "预期答案2"},
# ... 更多测试用例
]
def evaluate_prompt(prompt_template, test_cases):
results = []
for case in test_cases:
actual = call_model(prompt_template, case["input"])
score = compare(actual, case["expected"])
results.append(score)
return sum(results) / len(results)有时很难确定某个更改是使性能变好还是变差。使用模型本身来评判可能是一个有效的方法。
第二部分:核心 Prompting 技术
Zero-Shot Prompting(零样本提示)
直接向模型提问,不提供任何示例。依赖模型的预训练知识。
将以下文本分类为正面、负面或中性:
"这部电影的剧情很精彩,但节奏有点慢。"适用场景:简单任务、模型已有足够知识的领域
Few-Shot Prompting(少样本提示)
在 Prompt 中提供几个示例,帮助模型理解期望的输入输出模式。
将句子分类为情感:
句子:"我今天心情很好!" -> 情感:正面
句子:"天气真糟糕。" -> 情感:负面
句子:"明天是周五。" -> 情感:中性
句子:"这次考试考得不错!" -> 情感:最佳实践:
- 提供 2-5 个高质量示例
- 示例应覆盖不同的边界情况
- 保持示例格式的一致性
Chain of Thought (CoT) Prompting(思维链提示)
引导模型展示推理过程,逐步得出结论。这种方法对于数学、逻辑推理和复杂问题特别有效。
Zero-Shot CoT
只需在 Prompt 末尾添加 "Let's think step by step"(让我们一步步思考)。
Roger 有 5 个网球。他又买了 2 罐网球。每罐有 3 个网球。Roger 现在有多少个网球?
让我们一步步思考。Few-Shot CoT
在示例中展示完整的推理过程:
问:一家餐厅有 23 个苹果。如果他们用了 20 个做午餐,又买了 6 个,他们有多少个苹果?
答:餐厅最初有 23 个苹果。他们用了 20 个做午餐,所以剩下 23 - 20 = 3 个。他们又买了 6 个,所以现在有 3 + 6 = 9 个苹果。答案是 9。
问:Roger 有 5 个网球。他又买了 2 罐网球。每罐有 3 个网球。Roger 现在有多少个网球?
答:CoT 最有效的场景:
- 数学计算
- 常识推理
- 符号推理
- 多步骤逻辑问题
第三部分:Prompt Caching(提示缓存)
什么是 Prompt Caching?
Prompt Caching 是 OpenAI 在 2024 年推出的自动优化功能,可以显著降低 API 调用成本。它通过缓存 Prompt 中重复使用的前缀部分,避免重复计算。
工作原理
- 自动检测:系统自动识别请求中的公共前缀
- 缓存存储:首次请求时缓存该前缀
- 复用缓存:后续请求如果具有相同前缀,直接使用缓存
- 生命周期:缓存通常在 5-10 分钟无访问后过期
技术规格
| 规格 | 说明 |
|---|---|
| 最小缓存长度 | 1,024 tokens |
| 缓存增量 | 128 tokens |
| 典型生命周期 | 5-10 分钟无活动 |
| 支持模型 | GPT-4o, GPT-4o-mini, o1-preview, o1-mini 等 |
定价优势
缓存命中的输入 tokens 按标准价格的 50% 收费
例如,对于 GPT-4o:
- 标准输入价格:$2.50 / 1M tokens
- 缓存输入价格:$1.25 / 1M tokens
最佳使用场景
- 长系统提示:固定的角色定义、规则说明
- 多轮对话:保持上下文的会话场景
- 批量处理:使用相同指令处理多个输入
- 代码助手:需要携带大量代码上下文
优化技巧
# 优化前:系统提示在每次请求中完整发送
messages = [
{"role": "system", "content": very_long_system_prompt},
{"role": "user", "content": user_input}
]
# 优化后:保持系统提示不变,利用缓存
# 确保 system prompt 放在最前面,且内容稳定不变关键原则:
- 将不变的内容(系统提示)放在 Prompt 的最前面
- 保持前缀的稳定性,避免频繁修改
- 合并多个小请求为批量请求
监控缓存效果
API 响应中的 usage 对象会包含缓存信息:
{
"usage": {
"prompt_tokens": 2048,
"completion_tokens": 150,
"cached_tokens": 1792
}
}cached_tokens 表示从缓存中读取的 token 数量。
第四部分:实战建议
Prompt 设计清单
在设计 Prompt 时,检查以下要点:
- [ ] 任务清晰:模型是否明确知道要做什么?
- [ ] 格式明确:是否指定了输出格式?
- [ ] 角色设定:是否需要特定的角色或风格?
- [ ] 示例充分:复杂任务是否提供了示例?
- [ ] 约束条件:长度、语言、边界条件是否说明?
- [ ] 参考材料:需要准确性时是否提供了参考文本?
常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 输出不符合预期格式 | 格式说明不够明确 | 提供输出示例或使用 JSON Schema |
| 模型"编造"信息 | 缺少参考文本 | 提供 Reference Text 并要求引用 |
| 复杂任务失败 | 任务过于复杂 | 拆分为多个子任务 |
| 数学计算错误 | 模型不擅长计算 | 使用 Code Interpreter 或 Function Calling |
| 回答过于笼统 | 上下文不足 | 增加具体细节和背景信息 |
模型选择建议
| 场景 | 推荐模型 | 原因 |
|---|---|---|
| 日常对话、简单任务 | GPT-4o-mini | 性价比高、速度快 |
| 复杂推理、代码生成 | GPT-4o | 强大的综合能力 |
| 数学/科学推理 | o1-preview / o1 | 内置 CoT,推理更强 |
| 大规模生产应用 | GPT-4o-mini + Caching | 成本优化 |