谷歌第七版Prompt Engineering—最佳实践

12 阅读14分钟

Prompt Engineering

Author: Lee Boonstra

第三章:最佳实践

www.kaggle.com/whitepaper-…

github.com/Vocal-map/G…

本书主要是以Google自己的平台为主进行介绍,但将这些操作迁移到其他平台上可存在可行性。

最佳实践

寻找合适的提示需要不断尝试。以下是一些Google总结的最佳实践。

提供示例

最重要的最佳实践之一是在提示中提供one-shotfew-shot。这是一种非常有效的方法,它可以告诉 LLM 如何正确的处理问题。

这些示例可以展示所期望的输出结果或类似的回答,使模型能够从这些例子中学习,并相应地调整自己的生成内容。这就像是为模型提供了一个参考点或目标方向,可以提升其回答的准确性、风格和语气,使其更符合你的预期。

简洁设计

提示语应当简洁、清晰,并且对你和模型来说都易于理解。一个基本的原则是:如果连你自己都觉得困惑,那么模型也很可能会感到困惑。因此,尽量避免使用复杂的提示词,也不要提供多余或不相关的信息。

Before

我现在正在访问纽约,我想了解更多关于好地方的情况。我和两个3岁的孩子在一起。假期我们应该去哪里?

这段提示信息虽然完整,但表达方式冗长,模型可能需要更多处理步骤才能理解核心需求。

After

扮演旅游者的导游。描述一个3岁的孩子在纽约曼哈顿的好去处。

这个版本更加简洁明了,使用了明确的动词,直接告诉模型要做什么,让输出更准确高效。这种方式不仅提高了模型的理解速度,也更容易获得符合预期的回答。

尝试使用能够描述具体操作的动词。例如: 扮演、分析、分类、归类、对比等 。这些动词可以帮助你更清晰、准确地向模型传达任务要求,使提示更加高效和专业。

对输出内容要有明确具体的要求

在编写提示时,要对你希望得到的输出结果有清晰而具体的描述。一条过于简短或笼统的指令可能不足以有效引导大语言模型(LLM),导致输出不够相关或泛泛而谈。通过在提示中提供具体细节(可以通过系统指令或上下文进行补充),可以帮助模型聚焦于关键信息,从而提升输出的准确性和相关性。

示例:

应该这样做(DO):

Generate a 3 paragraph blog post about the top 5 video game consoles. The blog post should be informative and engaging, and it should be written in a conversational style.

请生成一篇关于前五大视频游戏主机的三段式博客文章。该文章应具有信息性和吸引力,并以一种口语化的风格撰写。

不应该这样做(DO NOT):

Generate a blog post about video game consoles.

请写一篇关于视频游戏主机的博客文章。

这个提示太模糊,没有说明文章长度、语气、风格或重点内容,可能导致输出质量不稳定或不符合预期。

多用指令,而非限制

在提示工程中,指令和限制都被用来引导大语言模型(LLM)的输出。

  • 指令(Instruction) 是对响应的格式、风格或内容提供的明确说明。它指导模型应该做什么或生成什么样的内容。
  • 限制(Constraint) 是对响应所设定的一系列限制或边界。它规定了模型不应该做什么或需要避免的内容。

越来越多的研究表明,在提示中注重正向指令往往比过度依赖限制条件更加有效。这种方法与人类更倾向于接受正面指导而非一堆“不能做什么”的限制是一致的。

指令直接传达了期望的结果,而限制可能会让模型去猜测哪些内容是被允许的。指令给予模型一定的灵活性,并在设定的边界内鼓励其发挥创造力;而过多的限制则可能抑制模型的潜力。此外,一组限制条件之间还可能发生冲突。

虽然限制不如指令,但限制条件仍然是有价值的。例如,当你需要防止模型生成有害或带有偏见的内容时,或者对输出格式或风格有严格要求时。

如果可以的话,优先使用正向指令:与其告诉模型不要做什么,不如告诉它应该做什么。这样可以避免混淆,并提高输出的准确性。

应该这样做(DO):

请生成一篇关于前五大视频游戏主机的一段式博客文章。
只讨论游戏主机本身、制造它的公司、发布年份以及总销量。

不应该这样做(DO NOT):

请生成一篇关于前五大视频游戏主机的一段式博客文章。
不要列出具体的游戏名称。

开始时优先使用指令,清楚地说明你希望模型做什么,只有在必要时才加入限制条件,比如出于安全性、清晰性或特定需求的考虑。进行指令迭代,尝试不同的指令与限制组合,找到最适合你具体任务的方式,并记录这些经验。

控制最大 Token 长度

要控制大语言模型(LLM)生成响应的长度,你可以选择在配置中设置最大 Token 限制,或是在提示中明确要求特定的长度。例如: "用一条推文的长度解释量子物理。"

在提示中使用变量

为了重复使用提示并使其更具动态性,可以在提示中使用变量,以便根据不同输入更改内容。例如,如下表所示,有一个用于提供某城市事实信息的提示。与其在提示中硬编码城市名称,不如使用一个变量。

使用变量可以节省你的时间和精力,避免重复编写相同的内容。如果你需要在多个提示中使用相同的信息,你可以将该信息存储在一个变量中,然后在每个提示中引用这个变量。当你将提示集成到自己的应用程序中时,这种方法特别有意义。

PromptVARIABLES
{city} = "Amsterdam"

PROMPT
You are a travel guide. Tell me a fact about the city: {city}
OutputAmsterdam is a beautiful city full of canals, bridges, and narrow streets. It’s a great place to visit for its rich history, culture, and nightlife.

尝试不同的输入格式和写作风格

不同的模型、模型配置、提示格式、用词选择以及提交方式都可能导致不同的输出结果。因此,重要的是要对提示进行实验,例如风格、用词和提示类型(零样本、少样本、系统提示)等。

例如,对于生成关于革命性游戏主机 Sega Dreamcast 的文章这一问题,可以用不同的方式来构造提示,比如作为问题陈述指令,从而得到不同的输出:

问题(Question):

What was the Sega Dreamcast and why was it such a revolutionary console?

Sega Dreamcast 是什么?为什么它是一款如此革命性的游戏主机?

陈述(Statement):

The Sega Dreamcast was a sixth-generation video game console released by Sega in 1999. It...

Sega Dreamcast 是世嘉于1999年发布的一款第六代视频游戏主机。它...

指令(Instruction):

Write a single paragraph that describes the Sega Dreamcast console and explains why it was so revolutionary.

请写一段话,描述 Sega Dreamcast 游戏主机并解释它为何如此具有革命性。

在分类任务中使用few-shot时,要混合不同的类别

一般来说,few-shot的顺序不应该对结果产生太大影响。然而,在进行分类任务时,尽可能在few-shot中混合不同的可能类别。这是因为如果不这样做,模型可能会过拟合示例的具体顺序。

通过混合不同的响应类别,可以确保模型是在学习识别每个类别的关键特征,而不是简单地记住示例的顺序。这将使模型在面对未见过的数据时表现出更强的鲁棒性和泛化能力。

适应模型的更新

需要及时了解模型架构的变化以及新功能等。尝试使用更新版本的模型,并调整你的提示,以更好地利用模型的新特性。

输出格式

除了提示词的输入格式外,还可以尝试不同的输出格式。对于如提取、选择、解析、排序或分类数据等非创造性任务,可以考虑将输出返回为结构化的格式,比如JSON

从用于提取数据的提示中返回 JSON 对象有一些好处。在实际应用中,不需要手动创建这种JSON格式,因为数据可以直接以处理好的顺序返回。更重要的是,通过指定要求JSON格式作为输出,可以迫使模型创建结构化响应并减少幻觉的产生。

总结使用JSON作为输出格式的好处包括:

  • 输出风格一致;
  • 专注于希望接收的数据;
  • 减少了出现幻觉的可能;
  • 增强了对关系的认识;
  • 获得数据类型;
  • 可以对其进行排序。

JSON Repair

尽管以JSON格式返回数据有许多优点,但这并非没有缺点。JSON的结构化特性虽然有利于解析和在应用程序中的使用,但它相比纯文本需要更多的 token,这导致处理时间增加和成本上升。此外,JSON的冗长很容易耗尽整个输出窗口,特别是在生成的内容因 token 限制而被终中断时,该问题尤为突出。这种截断通常会导致JSON无效,缺少关键的闭合大括号或方括号,从而使输出不可用。

不过该问题也不是不能解决,json-repair 这样的库在这些情况下非常有用。它能够智能地尝试自动修复那些不完整或格式有误的 JSON 对象,使其成为 LLM 生成内容时的重要工具。尤其是在应对可能发生的输出截断问题时,这类工具显得尤为关键。通过使用 json-repair,可以更有效地避免因 JSON 被截断而导致的数据损坏问题,从而提升数据处理的稳定性和效率。

使用 Schema

正如我们在本文中多次看到的那样,将结构化的 JSON 作为输出是一种非常有效的解决方案。那么输入呢?JSON 不仅非常适合用于结构化 LLM 所生成的输出,而且它同样也能很好地结构化输入内容。

这就引入了 JSON Schema(JSON 模式) 的概念。

一个 JSON Schema 定义了你的 JSON 输入所应具备的结构和数据类型。通过提供一个 schema,你可以为模型提供一份清晰的数据结果,让它知道应该关注哪些信息,并降低误解输入的风险。

此外,schema 还可以帮助建立不同数据字段之间的关系,甚至可以通过包含特定格式的日期或时间戳字段,让 LLM 具备“时间感知”能力。

假设你想使用一个 LLM 来为电商平台的产品目录生成产品描述。与其仅仅提供一段自由格式的文本描述,不如使用 JSON Schema 明确地定义产品的属性:

{
  "type": "object",
  "properties": {
    "name": { "type": "string", "description": "产品名称" },
    "category": { "type": "string", "description": "产品类别" },
    "price": { "type": "number", "format": "float", "description": "产品价格" },
    "features": {
      "type": "array",
      "items": { "type": "string" },
      "description": "产品的关键功能"
    },
    "release_date": {
      "type": "string",
      "format": "date",
      "description": "产品发布日期"
    }
  }
}

接着,可以提供符合该 schema 的实际产品数据:

{
  "name": "Wireless Headphones",
  "category": "Electronics",
  "price": 99.99,
  "features": ["Noise cancellation", "Bluetooth 5.0", "20-hour battery life"],
  "release_date": "2023-10-27"
}

通过预处理数据,并以 JSON Schema 加上数据的方式提供输入,你可以让模型更清楚地理解每个字段的含义,包括产品的发布时间,从而大大提升它生成准确且相关描述的可能性。

这种结构化输入的方法引导 LLM 将注意力集中在关键字段上,尤其在处理大量数据或将 LLM 集成到复杂应用中时,这种方法尤为有效。

一起实验

如果你需要设计出一个好的 prompt ,或许可以找多人一起来参与尝试。当每个人都遵循最佳实践时,你会发现所有这些不同提示之间在表现上会存在差异。

CoT 最佳实践

在使用 CoT prompting 时,需要将推理过程放在前面,答案放在最后。这是因为模型在生成推理内容后,预测最终答案时所接收到的 token 会随之改变。

在使用 CoT 和 self-consistency 方法时,你需要能够从提示中提取出最终的答案,并将其与推理过程分开。

在进行 CoT 时,应将温度值(temperature)设置为 0。CoT 基于 greedy decoding,即语言模型根据对下一个词的概率预测,选择概率最高的词来逐步生成推理过程。通常来说,在进行推理并得出最终答案时,很可能只有一个正确答案。因此,温度值应始终设为 0,以获得更稳定和准确的输出。

记录各种尝试

请详细记录你每一次提示的尝试,这样可以随着时间的推移不断学习哪些方法有效、哪些方法无效。

不同模型之间、不同采样设置下,甚至是同一模型的不同版本之间,提示的输出结果都可能存在差异。此外,即使对同一模型使用完全相同的提示,输出句子的格式和用词也可能出现细微差别。例如,如果两个token具有相同的预测概率,系统可能会随机选择其中一个作为结果,这可能会影响后续预测。

在构建一个检索增强生成系统(RAG系统) 时,还需要记录那些会影响插入到提示词中的具体内容的相关因素。这些因素包括:查询语句的构造方式、文本块的设置、返回的文本块数量,以及其他可能影响最终生成结果的信息。

当你觉得某个提示已经接近理想状态时,就可以将它导入到你的项目代码库中。建议将提示内容单独保存在一个独立的文件里,而不是和代码混在一起,这样更方便后续维护和更新。最终,你希望这些提示能够成为一套可落地、可操作的系统的一部分。作为一名提示工程师,你应该依靠自动化测试和评估流程来持续验证提示在不同任务上的表现,了解它的泛化能力和实际效果。

提示工程是一个不断迭代的过程。需要设计不同的提示,进行测试,分析结果,并根据模型的表现持续优化提示内容。不断尝试,直到获得你期望的输出效果。当更换了模型或者调整了模型配置后,也要用之前表现良好的提示继续实验和优化,以确保在新的环境下依然能获得理想的结果。

Name[name and version of your prompt]
Goal[One sentence explanation of the goal of this attempt]
Model[name and version of the used model]
Temperature[value between 0 - 1]
Token Limit[number]
Top-K[number]
Top-P[number]
Prompt[Write all the full prompt]
Output[Write out the output or multiple outputs]