Prompt Engineering vs. Blind Prompting

Mitchell Hashimoto

提示词工程 vs. 盲目提示

“Prompt Engineering(提示词工程)”随着语言模型的发展而出现,用于描述通过应用 Prompting(提示)来有效地从语言模型中提取信息的过程,通常用于实际应用中。

许多自称在做 Prompt Engineering 的人实际上只是在进行 Blind Prompting(盲目提示)1 “Blind Prompting”是我用来描述一种通过粗糙的试错方法创建提示、几乎不进行或完全不进行测试、且对 Prompting 只有非常浅显了解的方法的术语。Blind Prompting 不是 Prompt Engineering。

对于 Prompt Engineering 是否真的能被称为“工程”,还是仅仅是追逐热点的人鼓吹的“巫术”,也存在很多质疑。我认为,大多数情况下,这种质疑源于我所看到的许多自称讨论 Prompt Engineering 的推文和博客文章,实际上充其量也只是比 Blind Prompting 稍好一点。

在这篇博客文章中,我将论证 Prompt Engineering 是一项基于真实实验方法论可以培养的真正技能。我将通过一个贴近实际的例子,逐步演示为某个能为应用带来实际价值的问题进行 Prompt Engineering 以构建解决方案的过程。

整篇文章都将偏向于期望文本输出,因为文本输出是我使用语言模型的主要用例。某些技术——例如测试技术——并不能一对一地适用于图像等其他类型的输出。不过,本文中的所有内容对于多模态输入都同样适用。


什么是 Prompting?

如果你已经非常熟悉“Prompting”或“Prompt”这两个术语,可以跳过本节。

对于语言模型(如果你不熟悉这个术语,可以想象成 ChatGPT),“Prompt”是用户向模型提供的输入。在 ChatGPT 中,它可以被有效地理解为你输入文字的文本框。随后语言模型会为你的 Prompt“推断”出一个“补全”。例如,如果我在 ChatGPT 中输入“4 + 3 = ”,它很可能会回复“7”(大概)。在这种情况下,“4 + 3 = ”就是 Prompt,而“7”就是补全。

“Prompting”是指利用 Prompt 作为从模型中提取所需信息的方式。它是一种颇具吸引力的信息提取方法,因为你不需要大型的离线训练集,不需要离线访问模型,而且即使对非工程师来说也感觉很直观。Prompting 只是调优模型的一种方法

最后,“Prompt Engineering”描述的是一门更为严谨的学科(如本文将展示的那样),它旨在将 Prompting 用作一种为实际应用构建可靠功能的方式。它与 ChatGPT 式的 Prompting 不同,因为通过 Prompt Engineering 生成的 Prompt 通常是为了在大量、多样的场景中反复使用,以可靠地为应用解决某个特定问题。


问题

首先,你必须有一个想要为之构建解决方案的问题。这个问题可用于评估 Prompting 是否是最佳解决方案,或者是否存在更合适的替代方案。工程化的起点不是为了使用某种方法而使用它,而是出于相信它是正确的方法

在这个例子中,我们假设自己是一家构建日历客户端的公司。我们希望用户能够使用自然语言输入日程。例如:

Dinner with Alice next Tuesday at Taco Bell

CorpConf on 11/4

1:1 with Bob tomorrow at 10 AM

语言模型可能是接收这种自然语言输入并提取结构化输出来描述日程的良好解决方案,这些结构化输出随后可在我们的应用中使用。

显然还有其他潜在的解决方案。我们可以利用一组正则表达式和字符串搜索来查找常见短语(on <Day of Week>tomorrowtodaynext week等)。

语言模型也有其自身的优势:它们可能提供一种能更好地处理其他语言的方法,或能更好地处理拼写错误或语法错误,或者至少在正则表达式失效时能作为一个不错的后备方案。无论如何,已经有足够的潜力让我们继续将 Prompting 作为一种潜在的解决方案进行探索。


演示集

接下来,我们必须整理一个演示集。演示集包含一个预期输入以及对应的预期输出。该集合将服务于多个目标:

  1. 它将用于衡量我们 Prompt 的准确率。通过使用单个演示的输入,我们可以断言是否收到了预期的输出。

  2. 它明确了我们期望的 Prompt 输入和输出的形式,使我们作为工程师能够判断其是否为我们的问题提供了合适的形态。

  3. 如果我们选择使用 Few-Shot Prompt,我们可以将该演示集的一个子集用作 Few-Shot(少样本)方法的范例。对于不熟悉“Few-Shot”这一术语的读者,Few-Shot 是一种除了 Prompt 之外还提供示例的 Prompt 风格。请参阅此处以了解 Few-Shot 与 Zero-Shot Prompting 的良好概述

上述第 (2) 点极其重要。我们需要对期望的输入和期望的输出有一个大致的理解,因为在这两端[通常]都有软件需要确保数据符合某种格式,并期望以某种格式返回。这与典型的软件工程并无不同,在软件工程中我们将问题分解为一组具有输入/输出预期的函数。

我们可以利用之前的示例并将其扩展为完整的演示:

Q: Dinner with Alice next Tuesday at Taco Bell
A: next Tuesday

Q: CorpConf on 11/4
A: 11/4

Q: 1:1 with Bob tomorrow at 10 AM
A: tomorrow

关于演示集大小的说明:在这篇博客文章中,我们只有三个演示。实际上,你可能至少需要十几个。演示越多,你能做的测试就越充分,但由于 token 的使用,成本也会越高。当达到一定规模时,微调语言模型往往会更经济。

上述演示中做了两个重要的决定。对于任何 Prompting 问题,你都必须做出类似的决定。

第一,我们只提取一条信息。试图让模型提取整个日程的所有信息,如事件名称、参与者、时间、地点等,并以某种精美的、可直接使用的 JSON 或其他格式输出,这可能很诱人。模型或许能够做到这一点。但在处理一个新问题时,我建议首先将其分解为单一问题。这会使问题更易于处理,并且最终也会为你提供一个基准准确率,你可以用它来衡量多输出方法是否真的值得。

第二,我们的输出未经转换。我们并不试图将所有内容都转换为日期,或确保所有内容的大小写都正确等等。我们做的是字面文本提取。有一些非常出色的确定性库可以将像“next Tuesday”这样的字符串以极高的准确率转换为时间戳。这不是我们需要语言模型来为我们做的事情。因此,我们只需提取出粗略的日期格式(“next Tuesday”、“11/4”、“tomorrow”),然后使用传统的编程方法来解决将其转换为时间戳的问题,因为这是一个简单的问题。输出越简单,就越容易获得更高的准确率。

最后,关于输出解码的简要说明:LLM 会以各种方式补全你的 Prompt:它可能是一个完整的句子,可能会添加一个句号,可能会大写等等。你应该确定你希望来自 LLM 的输出有多完美,以及在验证演示集之前你愿意进行多少规范化处理。

例如,如果我在做文本提取,我通常认为对整个输出进行去除首尾空白和句号并转为小写是合理的。如果我在做更高级的事情,比如生成 JSON,我可能会以某种确定性的顺序和样式解析并重新编码 JSON,以便比较是确定性的。依此类推。

我的建议是:让来自 LLM 的输出尽可能简单和灵活,并在你的应用中执行一些规范化操作。一开始不要试图强迫 LLM 输出完全完美的格式。过早地在 LLM 中进行过多的“输出塑形”会使人难以区分 LLM 执行某项核心任务(在此例中为信息提取)的能力与其结构化输出的能力。


候选 Prompt

现在我们来提出一些候选 Prompt。候选 Prompt 是我们认为可能会从语言模型中引发我们所期望行为的 Prompt。我们提出多个候选是因为我们不太可能一下子就选出最佳的 Prompt。

为了保持入门级文本的性质,我们将手动构思 Prompt。为了有效,提示词工程师在构建 Prompt 时应掌握一些基本知识。例如,肯定式的表述比防御式的表述更好。清晰简洁通常比重复冗长更好。在构建 Few-Shot Prompt 时,标签的均匀分布很重要,展示完整的标签集合也很重要,等等。在选择范例时,LLM 很可能答错的范例通常表现最好,已有研究表明范例按从短到长的顺序排列时往往表现最好,等等。

需要引用!抱歉,我没有引用支持这些建议的实验研究。老实说,我太懒了,不想去查找我读过的相关论文(通常每个观点都有多篇)。如果你选择不相信我,没关系,更重要的一点是,关于提示技术及其有效性的实验研究是存在的。但是,我保证这些不是我编造的,尽管其中一些可能已经随着现代模型的发展而过时。

单独用一篇专门的文章来讲述这些技术中的一些才合适,这并非本文的目标。本文的目标是展示高层次的端到端流程,并表明从 LLM 中提取价值是存在工程化方法的

在此阶段,目标是提出一些不错的 Zero-Shot Prompt。Zero-Shot Prompt 可以转化为 Few-Shot,而这些又可以进一步转化为 Chain-of-Thought(思维链)。而这些中的每一个又都可以进一步转化为批量 Prompt 等等。因此,由于 Zero-Shot 是需要尝试的基础,我们将重点放在它上面。

以下是我提出的三个候选 Prompt:

Identify the date or day mentioned in the given text and provide it as the output.

Identify the date or day mentioned in the given event description.

Determine the date or day from each input and provide the output accordingly as a single word or date.

这些都是合理的 Prompt。对于任何受过教育的人来说,每个 Prompt 都可能产生非常高的准确率。但语言模型不是人,所以我们不能自动期望有同等的表现。我之前已经展示过非常合理的 Prompt 也可能表现得极其糟糕。因此,我们的下一步是通过进行一些测试和测量来为我们的决策提供依据。


Prompt 测试

有了一组候选 Prompt 以及一个演示集,我们现在可以衡量准确率了。迄今为止,我发现做到这一点的最佳方法是使用像 LangChain 这样的库构建一个简单的 Python 脚本。在我的测试中,我通常会遍历每个演示并执行以下 Prompt 模板:

{{prompt}}. Q: {{input}} A:

我总是首先测试 Zero-Shot。我想获得一个基准准确率指标。在此之后,你就可以测试 Few-Shot,并不仅比较不同的候选 Prompt,还比较不同的 Prompt 类型。依此类推。

这必须针对每个模型分别进行。即使在更强大的模型上,相同的 Prompt 也不保证具有相同或更好的准确率;准确率可能会下降2

Prompt 测试最基本的结果应该是一张如下所示的表格。你可能还会有其他维度,例如模型(例如 GPT-3.5 vs GPT-4)。

           | Zero-Shot | Few-Shot | ... |
-----------------------------------------
Prompt 1   | 64        | 68       | ... |
-----------------------------------------
Prompt ... | 44        | 52       | ... |
-----------------------------------------
Prompt N   | 23        | 22       | ... |

该表格在 Y 轴上显示候选 Prompt,在 X 轴上显示使用这些 Prompt 的 Prompt 类型。数值是以正确答案百分比表示的准确率。延续日历应用的例子,“Prompt 1”作为带有单个演示的 Zero-Shot Prompt 可能如下所示:

Identify the date or day mentioned in the given text and provide it as the output. Q: CorpConf on 11/4 A:

我们期望答案是“11/4”。Few-Shot 版本可能如下所示:

Identify the date or day mentioned in the given text and provide it as the output.

Q: Dinner with Alice next Tuesday at Taco Bell. A: next Tuesday

Q: 1:1 with Bob tomorrow at 10 AM. A: tomorrow

Q: CorpConf on 11/4. A:

给有经验的 Prompt 使用者的一个说明:上述 Few-Shot 示例并没有说类似“模仿下面的示例”这样的话。实验研究表明这并不能可靠地提高准确率,所以我喜欢先在不加这句话的情况下进行测试以节省 token。其次,Few-Shot 示例中的范例从未展示“MM/DD”这种提取方式作为示例,这是不规范的做法。在真实的 Few-Shot 设置中,展示所有提取样式可能很重要(Zhao, et al 2021)。

对于某些类型的问题,例如分类问题,你可以使用混淆矩阵(Strobelt, et al 2022)来可视化其他标签的概率,并据此判断你的标签集是否可以得到更好的调整。

除了准确率之外,你还想衡量所使用的 token 数量、请求次数等。所有这些在选择最终 Prompt 时都必须纳入考虑。


选择 Prompt

最后,你选择其中一个候选 Prompt 集成到你的应用中。这不一定是最准确的 Prompt。这是一项基于所用模型、所需 token 和所呈现准确率的成本与准确率分析。

例如,你可能会发现 Few-Shot 变体表现最好,但在测试集上仅准确率高 4%,却需要多 200% 的 token(对于当前基于 API 的模型来说,实际上成本翻倍)。你可能会为你的业务判断,以一半的成本换取低 4% 的准确率是值得的。

或者,你可能决定回过头去测试其他提高准确率的方法。例如,你可以在成本较低的模型上尝试自一致性解码策略(Wang, et al 2022),看看是否能足够地提升准确率。有时,在低成本模型上使用更多 token 比在高成本模型上使用较少 token 能显著节省费用。例如,如今 GPT-4 比 GPT-3.5 贵约 15 倍。这意味着你实际上拥有 15 倍的 token 预算来提升 GPT-3.5 Prompt 的准确率(需注意速率限制等前提)。

对于本博客文章中的例子,我们可能会选择表格中 Prompt 1 的 Zero-Shot 版本,因为它在可能显著更少的 token 下达到了 64% 的准确率。也许我们认为 64% 的准确率至少足以填充我们日历应用的事件模板。对于这个特定问题,我认为我们可以做得比 64% 好得多,但这只是我在本文中使用的数字。

最重要的是,你拥有了做出明智决策所需的数据。


信任但验证与持续改进

由于生成式 AI 的概率性,你的 Prompt 很可能存在一些问题。即使你在测试集上的准确率为 100%,也可能存在会产生错误输出的未知输入。因此,你应该信任但验证,并将验证失败的案例添加到你的演示集中,以便开发新的 Prompt 并提高准确率。

验证在很大程度上取决于具体问题。对于我们的日历应用示例,我们可能想明确地询问用户:“这个日程正确吗?”如果他们回答“否”,则将自然语言输入记录下来以供人工审查。或者,我们也许可以做得更好,自动追踪用户在我们自动信息提取后手动更改的任何日程3

再举一个不同的例子,如果我们的 Prompt 正在生成代码(例如正则表达式或编程语言文本),我们至少可以尝试对其进行解析。解析绝不应该成为安全顾虑4,而且它至少提供了最基本的验证,即至少语法是正确的。同样,如果此验证失败,我们可以记录输入和输出,扩充我们的演示集,并开发更好的 Prompt。

验证还有助于防范对抗性提示。对抗性提示本身就是一个完整的话题,我不会在本文中展开。


继续前行……

这篇博客文章展示了我认为开发 Prompt 如何能够成为一种工程实践。它描述了一种系统化的方法,用于识别问题、形成解决方案、验证这些解决方案,并通过持续改进来完善这些解决方案。

将本文中的方法与“Blind Prompting”进行对比,后者依赖轶事经验和无处不在的试错来得出某种提议的解决方案,并且往往不会构建适当的系统化基础设施来随着时间的推移可靠地迭代 Prompt。

我想指出的是,这篇博客文章非常基础。本文中有多处可以使用已经广为人知的更高级技术来改进。此外,我没有涵盖诸如对抗性提示等重要主题。作为一个具体的例子,对于为 Few-Shot Prompt 选择最佳示例有更科学的方法,但我希望尽可能让本文保持基础。如果你想学习一些更高级的技术,Lilian Weng(莉莉安·翁)的 Prompt Engineering(《提示词工程》)提供了一个极佳的概述。

此外,每个人都在迅速转向更高阶的 LLM 集成:Prompt Chaining(提示链)、Agent(智能体)等。有些人认为,未来诸如此类及更多的创新将使人工 Prompting 变得过时。无论这是否属实,我是一个相信从“第一性原理”5中学习是有价值的人,我认为学习像这样的 Prompting 技术只会提升我利用更高阶语言模型技术的能力。我也相信,像这样的基础 Prompting 仍然能够让更高阶的概念表现得更好。

脚注

  1. 也许是很多“聒噪”的人。我遇到并向许多非常出色的提示词工程师学习过,他们真正在该领域应用了良好的工程实践。不幸的是,我在 Twitter 和其他平台上看到的很多噪音往往并非如此。

  2. 又需要一处引用。同样,我有点懒,但这基于我在多篇论文中读到的实验研究。你可以选择不相信我,自己去测试一下就好。

  3. 这里显然存在隐私方面的影响。我只是在分享一个例子,该技术是否合适取决于实际情况。

  4. YAML。😐

  5. 我意识到真正的“第一性原理”会是更底层的概念。我在这里使用“第一性原理”这一短语是取其常见的比喻义,用来指代一个任意的、足够底层的起点,在此之上可以构建更多的知识。

原文由 Mitchell Hashimoto 发布

本文章由 muse-spark-1.2-contributor 进行翻译