OpenAI API 提示词工程指南
2024年5月15日
通过提示工程策略提升结果。
本指南分享了从大型语言模型(有时称为GPT模型)如GPT-4o中获取更好结果的策略和战术。这里描述的方法有时可以结合使用,以获得更大的效果。我们鼓励进行实验,以找到最适合您的方法。
您还可以探索示例提示,这些提示展示了我们的模型能够实现的功能:
获取更好结果的六种策略
编写清晰的指令
这些模型无法读懂您的心思。如果输出内容过长,请求简短的回复。如果输出内容过于简单,请求专家级的写作。如果您不喜欢格式,请展示您希望看到的格式。模型越少需要猜测您想要什么,您获得所需结果的可能性就越大。
战术:
提供参考文本
语言模型在被问及深奥主题或要求引用和URL时,可能会自信地编造虚假的答案。就像一张笔记可以帮助学生在考试中表现更好一样,向这些模型提供参考文本可以帮助其以更少的虚构内容进行回答。
策略:
将复杂任务拆分为更简单的子任务
就像在软件工程中将复杂系统分解为一组模块化组件是一种良好实践一样,提交给语言模型的任务也是如此。复杂任务的错误率往往高于简单任务。此外,复杂任务通常可以重新定义为一系列简单任务的工作流程,其中早期任务的输出用于构建后续任务的输入。
战术:
给模型时间“思考”
如果被问到17乘以28,你可能不会立即知道答案,但可以花时间算出来。同样,模型在试图立即回答时更容易出现推理错误,而不是花时间推导出答案。在给出答案之前请求“思维链”可以帮助模型更可靠地推理出正确答案。
战术:
使用外部工具
通过将其他工具的输出提供给模型来弥补模型的弱点。例如,文本检索系统(有时称为RAG或检索增强生成)可以告诉模型相关文档。像OpenAI的代码解释器这样的代码执行引擎可以帮助模型进行数学运算和运行代码。如果某项任务可以通过工具比通过语言模型更可靠或更高效地完成,则将其外包,以获得两者的最佳效果。
战术:
系统性测试变更
如果能够测量性能,提升性能会更容易。在某些情况下,对提示的修改可能在少数孤立示例上取得更好的性能,但在更具代表性的示例集上却导致整体性能下降。因此,为了确保变更对性能是净正面的,可能需要定义一个全面的测试套件(也称为“评估”)。
战术:
战术
每个上述策略都可以通过具体的战术来实现。这些战术旨在提供一些尝试的想法。它们绝不是全面的,您可以自由尝试这里未列出的创意。
策略:编写清晰的指令
战术:在查询中包含细节以获得更相关的答案
为了获得高度相关的响应,请确保请求提供任何重要的细节或上下文。否则,您就把猜测留给模型,模型可能无法准确理解您的意思。
更差 | 更好 |
如何在Excel中添加数字? | 如何在Excel中自动为整张表的每一行美元金额求和,并将所有总和放在名为“总计”的列的右侧? |
谁是总统? | 2021年墨西哥的总统是谁,选举的频率是多少? |
编写代码以计算斐波那契数列。 | 编写一个TypeScript函数以高效计算斐波那契数列。对代码进行充分注释,以解释每个部分的功能及其编写原因。 |
总结会议记录。 | 用一个段落总结会议记录。然后写一个Markdown列表,列出发言者及其每个人的要点。最后,列出发言者建议的下一步或行动项(如果有的话)。 |
战术:要求模型采用某种角色
系统消息可用于指定模型在回复中使用的人物角色。
当我请求帮助写某些内容时,你将回复一份文档,其中每段至少包含一个笑话或俏皮的评论。
给我的钢螺栓供应商写一封感谢信,感谢他们在短时间内准时交货。这使我们能够交付一个重要的订单。
根据内部评估,gpt-4.5-preview
模型具有特定的系统消息,能够带来更好的性能。在此添加你自己的系统消息内容:
你是一位高效能、深思熟虑且精确的助手。你的目标是深入理解用户的意图,在需要时提出澄清问题,逐步思考复杂问题,提供清晰准确的答案,并主动预见有用的后续信息。始终优先考虑真实、细致、深刻和高效,特别针对用户的需求和偏好定制你的回复。
策略:使用分隔符清晰指示输入的不同部分
分隔符如三重引号、XML标签、章节标题等可以帮助划分文本的不同部分,以便进行不同的处理。
用俳句总结由三重引号分隔的文本。 """insert text here""" 您将获得一对关于同一主题的文章(用XML标签分隔)。首先总结每篇文章的论点。然后指出哪篇文章的论点更好,并解释原因。
您将获得一篇论文的摘要和建议标题。论文标题应让读者对论文的主题有一个清晰的了解,同时也要引人注目。如果标题未能满足这些标准,请建议5个替代标题。
摘要:在此插入摘要 标题:在此插入标题
对于诸如这些简单任务,使用分隔符可能不会影响输出质量。然而,任务越复杂,澄清任务细节的重要性就越高。不要让模型费力去理解您所要求的内容。
策略:指定完成任务所需的步骤
某些任务最好以步骤序列的形式指定。明确写出步骤可以使模型更容易遵循它们。
使用以下逐步说明来响应用户输入。步骤1 - 用户将提供您带有三重引号的文本。将此文本总结为一句话,并以“摘要:”作为前缀。步骤2 - 将步骤1中的摘要翻译成西班牙语,并以“翻译:”作为前缀。
策略:提供示例
提供适用于所有示例的一般指令通常比通过示例演示任务的所有排列组合更有效,但在某些情况下,提供示例可能更容易。例如,如果您希望模型复制一种特定的响应用户查询的风格,而这种风格难以明确描述。这被称为“少量示例”提示。
以一致的风格回答。
雕刻出最深谷地的河流源自一处谦逊的泉水;最宏伟的交响乐源自一个音符;最复杂的挂毯始于一根孤独的线。
教我关于海洋的知识。
策略:指定输出的期望长度
您可以要求模型生成特定目标长度的输出。目标输出长度可以通过字数、句子数、段落数、要点数等来指定。然而,请注意,指示模型生成特定数量的单词并不能高精度地实现。模型更可靠地生成特定数量的段落或要点。
将三重引号内的文本总结为大约50个单词。 """insert text here"""
将三重引号内的文本总结为2个段落。 """insert text here"""
将三重引号内的文本总结为3个要点。 """insert text here"""
策略:提供参考文本
战术:指示模型使用参考文本回答
如果我们能够为模型提供与当前查询相关的可信信息,那么我们可以指示模型使用提供的信息来构建它的答案。
使用提供的文章(以三重引号分隔)来回答问题。如果在文章中找不到答案,请写“我找不到答案。”
<insert articles, each delimited by triple quotes> 问题:
鉴于所有模型都有有限的上下文窗口,我们需要某种方式动态查找与所提问题相关的信息。嵌入 可用于实现高效的知识检索。有关如何实现这一点的更多细节,请参见策略 “使用基于嵌入的搜索来实现高效的知识检索”。
策略:指示模型用引用的参考文本回答
如果输入已补充相关知识,要求模型在其答案中添加引用是很简单的,方法是引用提供文档中的段落。请注意,输出中的引用可以通过在提供的文档中进行字符串匹配来进行程序验证。 Insufficient information.
策略:将复杂任务拆分为更简单的子任务
对于需要大量独立指令集来处理不同情况的任务,首先对查询类型进行分类并利用该分类来确定所需指令是非常有益的。这可以通过定义固定类别并硬编码与处理给定类别任务相关的指令来实现。这个过程也可以递归应用,将任务分解为一系列阶段。这种方法的优点在于,每个查询将仅包含执行任务下一阶段所需的指令,这可能导致比使用单个查询执行整个任务更低的错误率。这也可能导致更低的成本,因为较大的提示运行成本更高(查看定价信息)。
例如,假设在客户服务应用中,查询可以有效地分类如下:
{
"primary": "Technical Support",
"secondary": "Troubleshooting"
}
首先,请检查所有连接到路由器的电缆是否都已连接。请注意,随着时间的推移,电缆可能会松动。
如果所有电缆都已连接,但问题仍然存在,请告诉我您使用的路由器型号。
如果您的路由器型号是 MTD-327J,请按下红色按钮并保持 5 秒钟,然后等待 5 分钟再测试连接。
如果您的路由器型号是 MTD-327S,请拔掉电源插头,然后重新插上,等待 5 分钟再测试连接。
如果在重启设备并等待 5 分钟后,您的问题仍然存在,我将为您联系 IT 支持。{"IT support requested"}
如果您开始询问与此主题无关的问题,请确认您是否希望结束当前的故障排除聊天,并根据以下方案对您的请求进行分类:<insert primary/secondary classification scheme from above here> 注意到模型已被指示发出特殊字符串,以指示对话状态的变化。这使我们能够将系统转变为状态机,其中状态决定了注入哪些指令。通过跟踪状态、在该状态下相关的指令,以及可选的从该状态允许的状态转换,我们可以为用户体验设置护栏,这在结构较少的方法中是难以实现的。
策略:对于需要非常长对话的对话应用,摘要或过滤先前的对话
由于模型具有固定的上下文长度,用户与助手之间的对话如果将整个对话包含在上下文窗口中,则无法无限期继续。
对此问题有多种解决方法,其中之一是对先前的对话轮次进行摘要。当输入的大小达到预定的阈值长度时,这可能会触发一个查询,摘要部分对话,并将先前对话的摘要作为系统消息的一部分包含在内。或者,先前的对话可以在整个对话过程中异步地在后台进行摘要。
一个替代方案是动态选择与当前查询最相关的对话之前的部分。请参见策略 "使用基于嵌入的搜索来实现高效的知识检索"。
策略:逐步总结长文档并递归构建完整摘要
由于模型具有固定的上下文长度,因此无法在单个查询中总结超过上下文长度减去生成摘要长度的文本。
为了总结一本非常长的文档,例如一本书,我们可以使用一系列查询来总结文档的每个部分。部分摘要可以连接在一起并进行总结,从而产生摘要的摘要。这个过程可以递归进行,直到整个文档被总结。如果需要使用早期部分的信息来理解后面的部分,那么一个有用的技巧是,在总结该点的内容时,包含在书中任何给定点之前的文本的持续摘要。OpenAI 在之前的 研究 中研究了这种总结书籍的程序,使用了 GPT-3 的变体。
策略:给模型时间“思考”
策略:指示模型在急于得出结论之前先自行解决问题
策略:给模型时间“思考”
有时候,当我们明确指示模型从第一原则推理后再得出结论时,能够获得更好的结果。例如,假设我们想让一个模型评估学生对数学问题的解答。最明显的方法是简单地问模型学生的解答是否正确。
确定学生的解答是否正确。
问题陈述:我正在建立一个太阳能发电设施,我需要帮助计算财务。
- 土地成本为每平方英尺100美元
- 我可以以每平方英尺250美元的价格购买太阳能电池板
- 我谈判了一份维护合同,每年固定费用为10万美元,另外每平方英尺10美元
第一年运营的总成本是平方英尺数量的函数。
学生的解答:设 x 为设施的大小(平方英尺)。
- 土地成本:100x
- 太阳能电池板成本:250x
- 维护成本:100,000 + 100x
总成本:100x + 250x + 100,000 + 100x = 450x + 100,000
学生的解答是正确的。
但学生的解答实际上并不正确!我们可以通过提示模型先生成自己的解答来成功让模型注意到这一点。
首先,自己解决这个问题。然后将你的解答与学生的解答进行比较,并评估学生的解答是否正确。在你自己解决问题之前,不要决定学生的解答是否正确。
问题陈述
我正在建立一个太阳能发电安装项目,需要帮助计算财务情况。
- 土地成本为每平方英尺 $100
- 我可以以每平方英尺 $250 的价格购买太阳能电池板
- 我谈判了一份维护合同,每年固定费用为 $100,000,另外每平方英尺 $10
第一年运营的总成本作为平方英尺数量的函数是什么?
学生的解决方案
设 x 为安装的面积(平方英尺)。
- 土地成本:100x
- 太阳能电池板成本:250x
- 维护成本:100,000 + 100x
总成本:
100x + 250x + 100,000 + 100x = 450x + 100,000
设 x 为安装的面积(平方英尺)。
- 土地成本:100x
- 太阳能电池板成本:250x
- 维护成本:100,000 + 10x
总成本:
100x + 250x + 100,000 + 10x = 360x + 100,000
学生的解决方案是错误的。他们在维护成本计算中犯了错误,使用了 100x 而不是 10x。第一年运营的正确总成本是 360x + 100,000。
策略:使用内心独白或一系列查询来隐藏模型的推理过程
策略:给模型时间“思考”
之前的战术表明,有时模型在回答特定问题之前,详细推理问题是很重要的。对于某些应用,模型用来得出最终答案的推理过程可能不适合与用户分享。例如,在辅导应用中,我们可能希望鼓励学生自己得出答案,但模型对学生解决方案的推理过程可能会向学生透露答案。
内心独白是一种可以用来缓解这一问题的战术。内心独白的想法是指示模型将那些旨在对用户隐藏的输出部分放入一个结构化格式中,以便于解析。然后,在向用户呈现输出之前,输出会被解析,只有部分输出会被显示。
按照以下步骤回答用户查询。
步骤 1 - 首先自己解决问题。不要依赖学生的解决方案,因为它可能是错误的。将此步骤的所有工作放在三重引号(""")内。
步骤 2 - 将你的解决方案与学生的解决方案进行比较,并评估学生的解决方案是否正确。将此步骤的所有工作放在三重引号(""")内。
步骤 3 - 如果学生犯了错误,确定你可以给学生什么提示,而不直接给出答案。将此步骤的所有工作放在三重引号(""")内。
步骤 4 - 如果学生犯了错误,将上一步的提示提供给学生(在三重引号外)。
不要写“步骤 4 - ...”,而是写“提示:”。
问题陈述:
另外,这可以通过一系列查询来实现,其中除了最后一个查询外,所有查询的输出都对最终用户隐藏。
首先,我们可以让模型独立解决问题。由于这个初始查询不需要学生的解决方案,因此可以省略。这提供了额外的好处,即模型的解决方案不会受到学生尝试解决方案的偏见。
接下来,我们可以让模型利用所有可用信息来评估学生解决方案的正确性。
比较你的解答与学生的解答,并评估学生的解答是否正确。
问题陈述: """
最后,我们可以让模型利用自己的分析,以一个乐于助人的辅导员的身份构建回复。
你是一位数学辅导员。如果学生犯了错误,请以不揭示答案的方式给学生提供提示。如果学生没有犯错,只需给他们一个鼓励的评论。
问题陈述: """
策略:询问模型在之前的过程中是否遗漏了什么
假设我们正在使用一个模型列出与特定问题相关的来源摘录。在列出每个摘录后,模型需要确定是否应该开始写另一个摘录,或者是否应该停止。如果源文档很大,模型常常会过早停止,未能列出所有相关摘录。在这种情况下,通过提示模型进行后续查询以查找它在之前的过程中遗漏的任何摘录,通常可以获得更好的性能。
示例实现可以在 OpenAI Cookbook 中找到。请参阅战术 “指示模型使用检索到的知识回答查询”,了解如何使用知识检索来最小化模型编造错误事实的可能性。
策略:使用外部工具
语言模型不能单独依赖于准确地执行算术或长计算。在需要这种情况下,可以指示模型编写并运行代码,而不是进行自己的计算。特别是,可以指示模型将要运行的代码放入指定格式,例如三重反引号。生成输出后,可以提取并运行代码。最后,如果需要,可以将代码执行引擎(即 Python 解释器)的输出作为输入提供给模型,以便进行下一个查询。
您可以通过将 Python 代码放在三重反引号中来编写和执行代码,例如 ```code goes here```。使用此方法进行计算。
找到以下多项式的所有实值根:3*x**5 - 5*x**4 - 3*x**3 - 7*x - 10。
代码执行的另一个良好用例是调用外部 API。如果模型被指示正确使用 API,它可以编写利用该 API 的代码。可以通过提供文档和/或代码示例来指示模型如何使用 API,以展示如何使用该 API。
您可以通过将 Python 代码放在三重反引号中来编写和执行代码。还要注意,您可以访问以下模块,以帮助用户向他们的朋友发送消息:
import message
message.write(to="John", message="Hey, want to meetup after work?")
警告:执行模型生成的代码本身并不安全,在任何寻求这样做的应用中都应采取预防措施。特别是,需要一个沙箱代码执行环境来限制不受信任代码可能造成的危害。
策略:让模型访问特定函数
聊天完成 API 允许在请求中传递函数描述的列表。这使得模型能够根据提供的模式生成函数参数。生成的函数参数以 JSON 格式由 API 返回,并可以用于执行函数调用。函数调用提供的输出可以在随后的请求中反馈给模型,以闭合循环。这是使用 OpenAI 模型调用外部函数的推荐方式。要了解更多信息,请参见我们的入门文本生成指南中的 函数调用部分 和 OpenAI Cookbook 中的更多 函数调用示例。
策略:系统地测试更改
有时很难判断一个更改——例如,一个新指令或新设计——是否使您的系统变得更好或更糟。查看几个示例可能会暗示哪一个更好,但样本量小的时候,很难区分真正的改进和随机运气。也许这个更改在某些输入上有助于性能,但在其他输入上却有害。 评估程序(或称“评估”)对于优化系统设计非常有用。好的评估应具备以下特点:
- 代表真实世界的使用情况(或至少具有多样性)
- 包含多个测试案例以增强统计能力(请参见下表的指南)
- 易于自动化或重复
需要检测的差异 | 95% 置信度所需样本量 |
---|---|
30% | ~10 |
10% | ~100 |
3% | ~1,000 |
1% | ~10,000 |
输出的评估可以由计算机、人类或两者结合进行。计算机可以使用客观标准(例如,只有一个正确答案的问题)自动化评估,也可以使用一些主观或模糊标准,在这些标准中,模型输出通过其他模型查询进行评估。OpenAI Evals 是一个开源软件框架,提供创建自动化评估的工具。
基于模型的评估在存在一系列可能被认为质量同样高的输出时非常有用(例如,对于长答案的问题)。可以通过基于模型的评估现实地评估的内容与需要人类评估的内容之间的界限是模糊的,并且随着模型能力的提升而不断变化。我们鼓励进行实验,以了解基于模型的评估在您的用例中能发挥多大作用。
策略:根据黄金标准答案评估模型输出
假设已知某个问题的正确答案应该参考一组特定的已知事实。那么我们可以使用模型查询来计算答案中包含了多少个所需的事实。
例如,使用以下系统消息:
您将获得由三重引号分隔的文本,该文本应为问题的答案。检查以下信息是否直接包含在答案中:
- 尼尔·阿姆斯特朗是第一个在月球上行走的人。
- 尼尔·阿姆斯特朗第一次在月球上行走的日期是1969年7月21日。
对于每一点,执行以下步骤: 1 - 重述该点。 2 - 提供答案中与该点最接近的引用。 3 - 考虑如果有人阅读该引用而不知道该主题,是否可以直接推断出该点。解释原因,然后再做决定。 4 - 如果第3步的答案是“是”,则写“yes”,否则写“no”。
最后,提供“yes”答案的数量。将此数量提供为 {"count":
以下是一个满足两个要点的示例输入:
"""尼尔·阿姆斯特朗因成为第一个踏上月球的人而闻名。这一历史事件发生在1969年7月21日,期间进行的是阿波罗11号任务。"""
以下是一个仅满足一个要点的示例输入:
策略:系统性测试变更
"""尼尔·阿姆斯特朗在登月舱上迈出一步,创造了历史,成为第一个在月球上行走的人。"""
这是一个示例输入,其中没有任何条件被满足:
"""在'69年夏天,一次宏伟的航行,阿波罗11号,勇敢如传奇之手。阿姆斯特朗迈出一步,历史展开,“一小步,”他说,为了一个新世界。"""
对于这种基于模型的评估类型,有许多可能的变体。考虑以下变体,它跟踪候选答案与标准答案之间的重叠类型,并跟踪候选答案是否与标准答案的任何部分相矛盾。
请按照以下步骤响应用户输入。每一步都要完整重述,然后再继续。即“步骤1:推理……”。
步骤1:逐步推理提交的答案与专家答案相比,信息是:不相交、相等、子集、超集,还是重叠(即有一些交集但不是子集/超集)。
步骤2:逐步推理提交的答案是否与专家答案的任何方面相矛盾。
步骤3:输出一个JSON对象,结构如下:{"type_of_overlap": "disjoint" 或 "equal" 或 "subset" 或 "superset" 或 "overlapping", "contradiction": true 或 false}
这是一个包含不合格答案的示例输入,但它与专家答案并不矛盾:
其他资源
Question: """尼尔·阿姆斯特朗最著名的事件是什么,发生在什么日期?假设为UTC时间。""" Submitted Answer: """他不是在月球上走过吗?""" Expert Answer: """尼尔·阿姆斯特朗最著名的事件是成为第一个在月球上行走的人。这个历史事件发生在1969年7月21日。"""
这是一个直接与专家答案相矛盾的示例输入:
Question: """尼尔·阿姆斯特朗最著名的事件是什么,发生在什么日期?假设为UTC时间。""" Submitted Answer: """在1969年7月21日,尼尔·阿姆斯特朗成为第二个在月球上行走的人,紧随其后的是巴兹·奥尔德林。""" Expert Answer: """尼尔·阿姆斯特朗最著名的事件是成为第一个在月球上行走的人。这个历史事件发生在1969年7月21日。"""
这是一个正确答案的示例输入,提供了比必要更多的细节:
Question: """尼尔·阿姆斯特朗最著名的事件是什么,发生在什么日期?假设为UTC时间。""" Submitted Answer: """在1969年7月21日02:56 UTC,尼尔·阿姆斯特朗成为第一个踏上月球表面的人,标志着人类历史上的一个重大成就。""" Expert Answer: """尼尔·阿姆斯特朗最著名的事件是成为第一个在月球上行走的人。这个历史事件发生在1969年7月21日。""" 为了获得更多灵感,请访问 OpenAI Cookbook,其中包含示例代码以及指向第三方资源的链接,例如: