1.6 提示词工程(二)
目录
3.2 提供参考文本
3.2.1 使用参考文本来构建答案
3.2.2 指导模型用引用的文本回答问题
3.3 把复杂的任务拆分成简单的子任务
3.3.1 利用意图分类确定与用户查询最相关的指令
3.3.2 针对需要长时间对话的应用程序,应概括或过滤之前的对话内容
3.3.3 逐段归纳长文档并递归地构建完整摘要
3.4 给模型更多时间“思考”
3.4.1 在模型急于得出结论之前,指导模型自己寻找解决方案
3.4.2 运用内心独白或连续提问来隐藏模型的推理过程
3.4.3 询问模型是否有遗漏
3.2 提供参考文本
语言模型可能会自信地编造出虚假答案,特别是当回应一些深奥主题或被要求提供引文和 URLs 时。就像学生在考试中借助笔记能够帮助其取得更好的成绩一样,为这类模型提供参考文本也可减少其制造虚假信息的情况。
3.2.1 使用参考文本来构建答案
如果我们能向模型提供与提问内容相关的可靠信息,我们就可以指导模型利用这些信息来构建答案。
系统 | 当你被提供特定文章,并需要回答问题时,请依据这些文章中的内容来作答。如果这些文章中没有包含答案,你只需表明“无法找到答案”。 |
用户 | <插入文章内容,每篇文章之间用三个引号隔开> 问题:<插入问题> |
由于所有模型都受到上下文窗口大小的限制,我们需要一种方法来动态地查询与提出的问题相关的信息。可以使用Embeddings (嵌入式技术)来实现有效的知识检索。
3.2.2 指导模型用引用的文本回答问题
如果输入信息中已经包含了相关知识,就可以直接要求模型在回答问题时引用所提供的文件中的段落。值得注意的是,输出中的引用可以通过在所提供的文件中匹配字符串来进行验证。
系统 | 你将会收到一个用三个引号标记的文档和一个问题。你的任务是仅使用所提供的文档来回答这个问题,并引用文档中用来回答问题的部分。如果文档中没有包含足够的信息来回答这个问题,就简单地写“信息不足”。如果提供了问题的答案,那么必须用引用标记。引用相关段落时,请使用以下格式({"citation": …})。 |
用户 | """<插入文档>""" 问题:<插入问题> |
3.3 把复杂的任务拆分成简单的子任务
就像在软件工程中,我们会习惯于把复杂的系统分解成一套模块化的组件,对于提交给语言模型的任务也是同样的道理。相较于简单的任务,复杂任务的错误率往往会更高。而更进一步,我们常常可以把这些复杂任务重新设定为一系列的工作流程,每一个流程就是一个更简单的任务,而且这些任务之间是相互联系的,前一个任务的输出会作为后一个任务的输入。
3.3.1 利用意图分类确定与用户查询最相关的指令
在一些需要处理众多独立指令集的任务中,先对查询的类型进行分类,然后根据这个分类来确定需要的指令,这样做往往是有益的。具体方法是,先定义固定的类别,并为处理这些类别中的任务硬编码相关指令。这个过程还可以递归地应用于把一个任务分解成多个阶段。这种方法的好处是,每个查询只包含执行下一个任务阶段所需的指令,相比于用一个查询来完成整个任务,这样可以降低错误率。同时,这也可能降低成本,因为较长的提示的运行成本更高(更多详情请参见 定价信息)。
比如,对于客户服务应用来说,可以有效地把查询分为以下几类:
系统 | 你会收到客户服务的查询。请把每个查询分为一个主要类别和一个次要类别,并以 json 格式提供结果,键值分别为:primary 和 secondary。 主要类别包括:账单、技术支持、账户管理或一般咨询。 账单的次要类别包括: - 取消订阅或升级 - 添加支付方式 - 解释收费 - 争议收费。 技术支持的次要类别包括: - 故障排除 - 设备兼容性 - 软件更新。 账户管理的次要类别包括: - 密码重置 - 更新个人信息 - 关闭账户 - 账户安全。 一般咨询的次要类别包括: - 产品信息 - 定价 - 反馈 - 与人交谈。 |
用户 | 我需要重新恢复我的互联网连接。 |
基于客户查询的分类,我们可以为模型提供一系列更具体的指令来处理下一步。例如,如果客户需要“故障排除”的帮助。
系统 | 你将负责处理技术支持中的客户服务咨询,需要进行故障排查。帮助用户的步骤如下: - 首先,请用户检查路由器的所有电缆连接是否牢固。需要注意的是,电缆有可能随着时间推移而松动。 - 如果确认电缆连接无误但问题依旧,请询问他们使用的路由器型号。 - 接下来,根据路由器型号提供重启设备的具体指导: -- 若型号为 MTD-327J,建议按住红色按钮 5 秒,然后等待 5 分钟后再检查网络连接。 -- 若型号为 MTD-327S,建议拔掉电源后重新插上,同样等待 5 分钟后检查网络连接。 - 如果重启设备并等待 5 分钟后用户的问题仍未解决,请将他们转接至 IT 支持,并显示 IT 支持请求 的信息。 - 如果用户开始询问与本次故障排查无关的问题,请确认他们是否愿意结束当前的故障排查话题,并按照给定的分类方案处理他们的请求。 <在此处插入上面的主要/次要分类方案> |
用户 | 我需要重新恢复我的互联网连接。 |
请注意,模型被设定为在对话状态发生变化时发出特殊指令。这使我们的系统能够转变为一个状态机,根据当前的状态来决定注入哪些指令,以及从该状态出发可以转换到哪些状态。通过监控对话状态、相关指令以及状态转换,我们能更好地控制用户体验,这在非结构化的交流方式中难以实现。
3.3.2 针对需要长时间对话的应用程序,应概括或过滤之前的对话内容
由于模型的上下文长度是固定的,用户与助手的对话不能无限延续,尤其是当整个对话内容都包含在上下文窗口中时。
解决这一问题的方法之一是概括之前的对话。当输入内容达到一定长度时,可以触发对部分对话内容进行概括的查询,这样的概括可以作为系统消息的一部分。或者,也可以在整个对话过程中不断后台概括之前的对话内容。
另一种方法是动态地挑选对话中与当前问题最相关的部分。
3.3.3 逐段归纳长文档并递归地构建完整摘要
因为模型的上下文长度是固定的,所以它们无法一次性总结超过上下文长度减去所生成摘要长度的文本。
例如,要总结一本很长的书,我们可以使用一系列的查询来分别总结书中的每个章节。这些部分的摘要可以被连结并进一步总结,形成摘要的摘要。这个过程可以递归地进行,直至整本书被总结完毕。如果在理解书中后续部分时需要前面章节的信息,那么在总结当前部分内容时附加一个前面内容的连续摘要会是一个实用的技巧。OpenAI 之前利用 GPT-3 的变种对这种总结书籍的方法进行了研究。
3.4 给模型更多时间“思考”
如果被要求计算 17 乘以 28,我们可能不能立即给出答案,但可以花一些时间逐步计算出结果。同样,在 AI 模型试图立刻回答问题时,往往比理性思考后再做出回答更容易出错。所以,在模型给出答案之前,要求其展示一下"思考过程",有助于模型更可靠地推导出正确的答案。
3.4.1 在模型急于得出结论之前,指导模型自己寻找解决方案
有时,在明确指导模型根据基本原理进行推理之前,我们可能会得到更好的结果。假设我们希望模型评估一个学生对数学问题的解答。最直观的方式是直接询问模型学生的解答是否正确。
系统 | 判断学生的解答是否正确。 |
用户 | 问题概述:我正在搭建一个太阳能发电项目,需要协助计算财务相关的事项。 - 土地的价格是每平方英尺 100 美元 - 我可以以每平方英尺 250 美元的价格购买太阳能板 - 我已经签订了一份保养合同,按照这份合同,我需要支付每年固定的 10 万美元以及每平方英尺额外的 10 美元的费用。 那么,第一年的运营总成本是多少呢?这个成本应该以安装面积(平方英尺数)为变量来计算。 学生的解答:假设安装面积为 x 平方英尺。 1. 土地成本为 100x 2. 太阳能板成本为 250x 3. 维护成本为 100,000 + 10x 总成本为 100x(土地)+ 250x(太阳能板)+ 100,000(固定维护费)+ 10x(额外维护费)= 360x + 100,000 |
助手 | 学生的解答正确。 |
然而,学生的解答其实并不正确!我们可以通过引导模型首先产生它自己的解答,从而让它成功地发现这个问题。
系统 | 首先,你需要自行找出问题的解答,然后把你的解答与学生的解答进行比较,评定学生的解答是否准确。在你没有自行解决这个问题之前,请不要对学生的解答进行判断。 |
用户 | 问题概述:我正在搭建一个太阳能发电项目,需要协助计算财务相关的事项。 - 土地的价格是每平方英尺 100 美元 - 我可以以每平方英尺 250 美元的价格购买太阳能板 - 我已经签订了一份保养合同,按照这份合同,我需要支付每年固定的 10 万美元以及每平方英尺额外的 10 美元的费用。 那么,第一年的运营总成本是多少呢?这个成本应该以安装面积(平方英尺数)为变量来计算。 学生的解答:假设安装面积为 x 平方英尺。 1. 土地成本为 100x 2. 太阳能板成本为 250x 3. 维护成本为 100,000 + 10x 总成本为 100x(土地)+ 250x(太阳能板)+ 100,000(固定维护费)+ 10x(额外维护费)= 360x + 100,000 |
助手 | 让我们设定 x 为太阳能发电装置的面积,单位为平方英尺。 1. 土地成本:100x 2. 太阳能板成本:250x 3. 维护成本:100,000 + 10x 总成本:100x + 250x + 100,000 + 10x = 360x + 100,000 学生的解答存在错误。他们在计算维护成本时,错误地以 100x 替代了正确的 10x。第一年运行的正确总成本就是 360x + 100,000 美元。 |
3.4.2 运用内心独白或连续提问来隐藏模型的推理过程
先前的策略证明,在回答特定问题之前,模型有时需要深入地推理问题的过程。然而,在某些应用环境中,模型为得出最终答案的推理过程不宜与用户分享。比如,在教导应用里,我们或许希望鼓励学生自行思考答案,但模型对于学生解答的推理过程可能会无意间透露出答案。
内心独白就是能有效应对这种情况的策略。内心独白的主要概念是指导模型将那些需要被用户隐藏的输出部分以一种结构化的形式表现,从而容易进行解析。然后在将输出结果展示给用户之前,将其进行解析处理,最后只呈现部分的解析结果给用户看。
系统 | 按以下步骤回答用户问题。 第 1 步 - 首先独立解决问题。不要依赖学生的答案,因为可能有误。将此步骤的所有内容用三重引号 (""") 包围。 第 2 步 - 将你的解答与学生的答案比较,判断学生的答案是否正确。将此步骤的所有内容用三重引号 (""") 包围。 第 3 步 - 如果学生答案有误,想出一个不直接透露答案的提示。将此步骤的所有内容用三重引号 (""") 包围。 第 4 步 - 如果学生答案有误,给出第 3 步的提示(不用三重引号)。用“提示:”替代“第 4 步 - ...”。 |
用户 | 问题陈述:<插入问题陈述> 学生解答:<插入学生解答> |
另一种方式是通过一系列的查询来实现,除了最后一个外,所有查询的结果都不展示给用户。
首先,我们可以让模型独立解决这个问题。因为这个初始步骤不需要学生的答案,所以可以省略。这样可以确保模型的答案不会受到学生答案的影响。
用户 | <插入问题陈述> |
然后,模型将利用所有信息来判断学生的答案是否正确。
系统 | 比较并评估你的解答与学生的解答,确定学生的解答是否正确。 |
用户 | 问题陈述:"""<插入问题陈述>""" 你的解答:"""<插入模型生成的解答>""" 学生的解答:"""<插入学生的解答>""" |
最后,让模型根据自己的分析以有帮助的家教身份给出回复。
系统 | 作为一个数学家教,如果学生有误,给出一个提示,但不透露答案。如果学生答案正确,就给予鼓励性的评论。 |
用户 | 问题陈述:""<插入问题陈述>""" 你的解答:"""<插入模型生成的解答>""" 学生的解答:"""<插入学生的解答>""" 分析:"""<插入模型从前一步生成的分析>""" |
3.4.3 询问模型是否有遗漏
比如我们在用模型列出与特定问题相关的摘录时,每列出一段后,模型需要决定是继续写下一个还是停止。如果原文很长,模型可能会过早结束,错过一些相关摘录。此时,通过后续查询来寻找之前遗漏的摘录,通常可以获得更好的效果。
系统 | 你将收到一个文档,其内容由三个引号界定。你的任务是选择与这个问题相关的部分:“在人工智能的发展历史中,发生了哪些重大的范式转变?”请确保你选取的摘录能够完整地提供足够的背景信息,以便理解它们的含义 — 换言之,不要选择那些缺乏重要背景信息的片段。请按照以下 JSON 格式输出结果:[{"excerpt": "..."}, ... {"excerpt": "..."}] |
用户 | """<在此处插入文档>""" |
助手 | [{"excerpt": "模型在此处写下一个摘录"}, ... {"excerpt": "模型在此处写下另一个摘录"}] |
用户 | 还有其他相关的摘录吗?请注意不要重复之前的摘录。同时确保所选摘录包含充分的背景信息,使得它们易于理解 — 也就是说,避免选择那些缺少关键背景的片段。 |