提示词工程

提示词是你在模型进行预测之前放在它面前的文本。这正是本章要深化的观点:提示词不是你给一个心智下达的命令,而是你安排的**上下文**,使你想要的答案成为最可能的后续内容。
提示词的真正含义
同一个模型,两个提示词。"写关于气候变化的文章"给你一篇你没有要求的漫无目的的论文。"为12岁的孩子写三句关于气候变化主要成因的总结,不要统计数据"给你一些实际可用的内容。模型在这两次调用之间没有改变。改变的是上下文,输出也随之改变。
前一章介绍了模型如何通过预测最可能的下一个token来生成文本,由它面前的所有内容引导。提示词就是那个"面前的所有内容"。这是值得坚持的定义:提示词不是你对一个人提出的请求,而是你设置的**上下文**,使你希望得到的后续内容成为模型最可能找到的东西。
这个重新定位改变了你的写作方式。你不是在劝服或指示某个人。你是在安排文本,使你想要的答案是可能的下一件事。本章中的每项技巧都是实现这一目标的不同方式,一旦你能看到底层的预测机制,每一项都会变得更有意义。
三种角色
当你通过代码与模型交谈时,你不是发送一块文本。你发送一个**消息的列表,每条消息都带有一个角色**,标记它来自哪里。有三种:
- system:你的常设指令。模型应该充当谁、它遵循的规则、你想要的格式。设置一次,适用于整个对话。
- user:来自使用你的应用的人的消息。实际的请求。
- assistant:模型之前生成的消息。这是对话如何保持其历史的方式。
messages = [
{"role": "system", "content": "你是一个烹饪应用的简洁助手。用一句话回答。"},
{"role": "user", "content": "在饼干中我可以用什么代替黄油?"},
]在底层,这些角色不是进入模型的单独通道。它们被扁平化为一个文本流,在每条消息周围有特殊的标记token,这整个流就是模型预测的内容。角色很重要,因为模型是在这样排列的对话上训练的,所以它学会了将system内容视为权威的常设规则,user内容视为要回答的请求。
系统消息是初学者最不充分利用的。这是你在每一轮上设置所需行为的地方:语气、受众、格式、模型应该永远不做的事情。把持久的规则放在那里,把具体的请求放在用户消息中。
因为模型在请求之间没有记忆,assistant角色也是如何重放之前说过的内容的:要继续对话,你把之前的交换作为assistant和user消息发送回去。当你开始从代码调用模型时会有更多相关信息。
system、user或assistant的消息列表引导模型。这些标记被扁平化为模型预测的一个文本流,但它被训练为将system视为权威规则,user视为请求。把持久的指令放在system中,具体的请求放在user中,重放之前的assistant和user消息来承载对话的历史。 具体优于模糊
两个提示词,一个模型。"写关于气候变化的文章"返回一篇长度和语气不可预测的通用论文。"为12岁的孩子写三句关于气候变化主要成因的总结,用简单的语言,没有统计数据"返回接近你所想的东西。同一个机器,输出完全不同,唯一的差异是你确定了多少。
原因可以追溯到预测循环。在每一步,模型都在从一个合理后续的范围中选择。一个模糊的提示词将那个范围保持宽阔,所以模型用一般中最常见的东西填充空白,这很少是你心中确切的东西。一个**具体的**提示词在模型写第一个token之前把范围缩小到你想要的答案。
# 模糊:模型决定长度、语气、受众、格式
"写关于气候变化的文章。"
# 具体:你决定
(
"为12岁的孩子写三句关于气候变化主要成因的总结,"
"用简单的语言,没有统计数据。"
)模糊版本可以继续千种合理的方式,你得到其中的随机一个。具体版本提前排除了几乎所有这些。当你写提示词时,说出你真正想要的:
- 格式:一个句子、一个列表、JSON、一个表格。
- 长度:一个段落、三个要点、50个词以下。
- 受众:初学者、专家、12岁的孩子。
- 不确定时做什么:"如果文本没有说,回复'未指定'而不是猜测。"
最后一个比看起来的承载更多的重量。模型会用一个自信的猜测填充一个间隙,因为一个流畅的猜测是可能的后续。显式给它说"我不知道"的选项使那成为可能的路径,这是少数几个提示词级的动作之一,可以减少上一章的幻觉问题。具体性在模型写一个token之前就缩小了它的选项。
展示示例
有时,设置正确后续的最清晰方式就是展示它。在提示词中放入几个完成的例子称为**少样本提示词(没有例子是零样本**)。它工作的原因是,在心底,模型是一个模式继续器:给它两三个输入然后正确输出的例子,最可能出现的下一件事是应用于你新输入的相同模式。
messages = [
{"role": "system", "content": "将每个评论标记为正面、负面或中立。仅回复标签。"},
{"role": "user", "content": "食物很冷,送来很慢。"},
{"role": "assistant", "content": "负面"},
{"role": "user", "content": "不错的饭,没什么特别的。"},
{"role": "assistant", "content": "中立"},
{"role": "user", "content": "这是我多年来吃过最好的汉堡!"},
]
# 模型现在回答:正面这两个完成的例子建立了确切的模式:一个词,全大写,来自一个固定的集合。为第三个评论继续那个模式现在远比写一段分析段落可能得多,所以这就是你得到的。这是为什么几个清晰的例子经常击败一段描述,特别是对于分类和格式:你在演示输出的形状,而不是描述它并希望。选择涵盖棘手情况的例子,因为模型按你展示的来模式化自己,包括你的例子中的任何错误。
请求它推理
这是最有用的提示词想法之一,也是最不直观的之一,它直接来自模型如何工作。模型一次生成一个token,它拥有的唯一工作空间是它已经写的文本。它没有隐藏的草稿纸来安静地解决一个硬问题,然后再回答。它的思考(如果有的话)在文本中大声发生,在它生成的token中。
所以如果你问一个多步骤问题并要求仅最后的答案,你强迫模型在一个有地方工作的地方一跃到尾部,在任何涉及推理、数学或逻辑的东西上,它经常跃跃错误。修复是让它在开放中工作。请求它在答案之前铺排它的步骤:
messages = [
{"role": "system", "content": "一步步地解决问题,然后在自己的行上给出最后的答案。"},
{"role": "user", "content": "一家商店以3支笔2美元的价格出售笔。12支笔要多少钱?"},
]
# 没有"一步步",模型经常大声说一个错误的数字
# 有了它,模型写出工作,每一步使下一个更可靠这被称为**思维链**提示词,它工作的原因是机械的,不是魔法的。模型写的每一步都变成它为下一个token读的上下文的一部分,所以写"12支笔是3组4"使"4组乘以2美元是8美元"成为自然的、可能的继续。你在页面上而不是在一个不可能的跳跃中给模型空间来计算。"一步步思考"或"展示你的工作"是日常版本。
两个警告。写的推理仍然是生成的文本,所以它可以看起来合理并达到错误的答案,你不应该把它看作一个保证的证明。而且一些较新的模型在没有被问的情况下在内部进行这种工作。但是底层原理在无论什么模型接下来出现都坚持:一个模型当它有空间来写出步骤时推理更好,因为它自己的输出是它仅有的思考地方。
构建提示词
当提示词增长时,结构使上下文清晰,所以模型正确权衡每个部分。几个习惯帮助:
- **把指令放在第一位,数据放在最后。**说明要做什么,然后提供要在其上进行的文本。
- **用分隔符分离指令和数据。**在任何提供的文本中包装清晰的标记,所以你的规则和输入之间的边界是明确的。
- **明确要求输出形状。**如果你想要JSON,说好,并描述字段。
prompt = f'''总结下面的客户消息为一句话。
然后列出它提到的任何产品名称。
客户消息:
"""
{user_message}
"""'''三引号是一个**分隔符**。它们标记"这里面的所有东西都是要处理的数据,而不是要遵循的指令"。这不仅仅是整洁,这里预测视图显示为什么它是一个真实的防护。
对于模型,它是一个文本流,所以如果用户输入直接流入你的指令,没有边界,用户可以写"忽略以上并做这个代替"并且模型有没有可靠的方式来告诉那条线与你真实的指令分开。它可能继续通过服从它。
那个攻击称为提示词注入,并保持指令和不信任的数据清晰地围起是第一道防线,在安全和限制中涵盖。对于模型它是一个文本流,所以围起不信任的输入。
在实践中
这里是一个堆积这些想法中几个的提示词:一个系统消息为持久规则,一个具体请求,一个例子固定格式,和清晰的分隔符数据。
messages = [
{
"role": "system",
"content": (
"你从会议笔记中提取行动项。"
"回复为一个编号列表,每行一个行动,每个以一个动词开始。"
"如果没有行动项,回复'无'。"
),
},
{"role": "user", "content": '笔记:"""我们同意小王会到周五发送预算,李四会预订场地。"""'},
{"role": "assistant", "content": "1. 到周五发送预算(小王)\n2. 预订场地(李四)"},
{"role": "user", "content": f'笔记:"""{meeting_notes}"""'},
]系统消息一次设置规则,例子固定确切格式,每个请求传递围起的数据。每个块安排上下文,所以你想要的输出是可能的继续。那是整个工艺:这是一个**可靠的提示词**,而不是一个幸运的。接下来你从真实代码在从代码调用模型中发送消息,比如这些。
system消息为持久规则,一个具体请求,一个例子固定格式,和清晰的分隔符数据。每个块安排上下文,所以你想要的输出是可能的下一个文本。那是工艺,一个通过设计而不是通过幸运工作的提示词。 
