提示词工程 (Prompt Engineering)

444 阅读1小时+

本文翻译自 google 的 Prompt Engineering 白皮书,原作者为:Lee Boonstra,原文为pdf,AI 翻译后人工进行了校对,如有错漏,请不吝指出

image.png


作者: Lee Boonstra

Google


致谢

内容贡献者

  • Michael Sherman
  • Yuan Cao
  • Erick Armbrust
  • Anant Nawalgaria
  • Antonio Gulli
  • Simone Cammel

策展人与编辑

  • Antonio Gulli
  • Anant Nawalgaria
  • Grace Mollison

技术作者

  • Joey Haymaker

设计师

  • Michael Lanning

2025年2月


引言

你不需要成为数据科学家或机器学习工程师——每个人都可以编写提示词。

当我们思考大型语言模型的输入和输出时,文本提示(有时会伴随其他模态,如图像提示)是模型用来预测特定输出的输入。你不需要成为数据科学家或机器学习工程师——每个人都可以编写提示。然而,制作最有效的提示可能会很复杂。提示的许多方面都会影响其效果:你使用的模型、模型的训练数据、模型配置、你的措辞、风格和语气、结构以及上下文都至关重要。因此,提示工程是一个迭代的过程。不恰当的提示可能导致模棱两可、不准确的响应,并可能阻碍模型提供有意义输出的能力。


提示工程

当你与 Gemini 聊天机器人[1] 聊天时,你基本上就是在编写提示。然而,本白皮书专注于在 Vertex AI 中或通过 API 为 Gemini 模型编写提示,因为通过直接提示模型,你将可以访问诸如温度等配置。

本白皮书将详细讨论提示工程。我们将探讨各种提示技巧,帮助你入门,并分享成为提示专家的技巧和最佳实践。我们还将讨论你在制作提示时可能面临的一些挑战。

提示工程

回想一下大型语言模型(LLM)的工作原理;它是一个预测引擎。模型接收顺序文本作为输入,然后根据其训练数据预测下一个令牌应该是什么。LLM 被操作化以一遍又一遍地执行此操作,将先前预测的令牌添加到顺序文本的末尾以预测下一个令牌。下一个令牌的预测是基于先前令牌中的内容与 LLM 在其训练期间所见内容之间的关系。

当你编写提示时,你正试图设置 LLM 以预测正确的令牌序列。提示工程是设计高质量提示以引导 LLM 产生准确输出的过程。这个过程涉及不断调整以找到最佳提示,优化提示长度,并评估提示的书写风格和结构与任务的关系。在自然语言处理和 LLM 的背景下,提示是提供给模型的输入,用以生成响应或预测。


LLM 输出配置

这些提示可用于实现各种理解和生成任务,例如文本摘要、信息提取、问答、文本分类、语言或代码翻译、代码生成以及代码文档或推理。

请随时参考谷歌的提示指南[2],[3],其中包含简单而有效的提示示例。

在进行提示工程时,你将从选择一个模型开始。提示可能需要针对你的特定模型进行优化,无论你使用的是 Vertex AI 中的 Gemini 语言模型、GPT、Claude,还是像 Gemma 或 LLaMA 这样的开源模型。

除了提示本身,你还需要调整 LLM 的各种配置。

LLM 输出配置

一旦你选择了模型,就需要弄清楚模型的配置。大多数 LLM 都带有各种配置选项,用以控制其输出。有效的提示工程需要根据你的任务对这些配置进行优化设置。

输出长度

一个重要的配置设置是响应中要生成的令牌数量。生成更多的令牌需要 LLM 进行更多的计算,从而导致更高的能耗、可能更慢的响应时间以及更高的成本。

减少 LLM 的输出长度并不会使 LLM 在其创建的输出中变得更具风格或文本上更简洁,它只是导致 LLM 在达到限制后停止预测更多的令牌。如果你的需求需要较短的输出长度,你可能还需要对你的提示进行工程设计以适应这一点。

对于某些 LLM 提示技术(如 ReAct),输出长度限制尤其重要,因为在得到你想要的响应后,LLM 会继续发出无用的令牌。

请注意,生成更多的令牌需要 LLM 进行更多的计算,这会导致更高的能耗和可能更慢的响应时间,从而带来更高的成本。

采样控制

LLM 并非正式地预测单个令牌。相反,LLM 预测的是下一个令牌可能是什么的概率,其中 LLM 词汇表中的每个令牌都会得到一个概率。然后对这些令牌概率进行采样,以确定下一个产生的令牌将是什么。温度(Temperature)、top-K 和 top-P 是最常见的配置设置,它们决定了如何处理预测的令牌概率以选择单个输出令牌。

温度 (Temperature)

温度控制令牌选择中的随机程度。较低的温度适用于期望更确定性响应的提示,而较高的温度可以导致更多样化或意想不到的结果。温度为 0(贪婪解码)是确定性的:总是选择概率最高的令牌(但请注意,如果两个令牌具有相同的最高预测概率,根据打破平局的实现方式,你可能不总是在温度为 0 时得到相同的输出)。

接近最大值的温度倾向于创建更随机的输出。随着温度越来越高,所有令牌变得同样可能成为下一个被预测的令牌。

Gemini 的温度控制可以以类似于机器学习中使用的 softmax 函数的方式来理解。低温设置反映了低 softmax 温度(T),强调具有高确定性的单个首选温度。较高的 Gemini 温度设置就像高 softmax 温度,使得所选设置周围更宽的温度范围变得更可接受。这种增加的不确定性适应了那些不需要刚性、精确温度的场景,例如在试验创意输出时。

Top-K 和 top-P

Top-K 和 top-P(也称为核采样)[4] 是 LLM 中使用的两种采样设置,用于将预测的下一个令牌限制为来自具有最高预测概率的令牌。与温度一样,这些采样设置控制生成文本的随机性和多样性。

  • Top-K 采样从模型的预测分布中选择 K 个最可能的令牌。top-K 值越高,模型的输出就越有创意和多样化;top-K 值越低,模型的输出就越受限和基于事实。top-K 为 1 等同于贪婪解码。
  • Top-P 采样选择累积概率不超过某个值 (P) 的顶层令牌。P 的值范围从 0(贪婪解码)到 1(LLM 词汇表中的所有令牌)。

选择 top-K 还是 top-P 的最佳方法是试验这两种方法(或将它们结合起来),看看哪一种能产生你想要的结果。

综合应用

在 top-K、top-P、温度和要生成的令牌数量之间进行选择,取决于具体的应用和期望的结果,并且这些设置会相互影响。确保你理解所选模型如何将不同的采样设置结合在一起也很重要。

如果温度、top-K 和 top-P 都可用(如在 Vertex Studio 中),那么同时满足 top-K 和 top-P 标准的令牌将成为下一个预测令牌的候选者,然后应用温度从通过了 top-K 和 top-P 标准的令牌中进行采样。如果只有 top-K 或 top-P 可用,行为是相同的,但只使用一个 top-K 或 P 设置。

如果温度不可用,那么满足 top-K 和/或 top-P 标准的任何令牌都将被随机选择以产生单个下一个预测令牌。

在某个采样配置值的极端设置下,该采样设置要么会抵消其他配置设置,要么会变得无关紧要。

  • 如果你将温度设置为 0,top-K 和 top-P 将变得无关紧要——最可能的令牌将成为下一个预测的令牌。如果你将温度设置得非常高(高于 1,通常在 10s 范围内),温度将变得无关紧要,任何通过 top-K 和/或 top-P 标准的令牌都将被随机采样以选择下一个预测令牌。
  • 如果你将 top-K 设置为 1,温度和 top-P 将变得无关紧要。只有一个令牌通过 top-K 标准,该令牌就是下一个预测的令牌。如果你将 top-K 设置得非常高,比如 LLM 词汇表的大小,任何具有非零概率成为下一个令牌的令牌都将满足 top-K 标准,没有令牌被筛选掉。
  • 如果你将 top-P 设置为 0(或一个非常小的值),大多数 LLM 采样实现将只考虑最可能的令牌来满足 top-P 标准,从而使温度和 top-K 变得无关紧要。如果你将 top-P 设置为 1,任何具有非零概率成为下一个令牌的令牌都将满足 top-P 标准,没有令牌被筛选掉。

作为一个通用的起点,温度为 0.2,top-P 为 0.95,top-K 为 30 将为你提供相对连贯的结果,这些结果可以有创意但又不会过度。如果你想要特别有创意的结果,可以尝试从温度 0.9,top-P 0.99 和 top-K 40 开始。如果你想要较少创意的结果,可以尝试从温度 0.1,top-P 0.9 和 top-K 20 开始。最后,如果你的任务总是有唯一的正确答案(例如,回答一个数学问题),请从温度 0 开始。

注意: 自由度越高(温度、top-K、top-P 和输出令牌越高),LLM 可能生成的相关性较低的文本。

警告: 你是否曾见过以大量填充词结尾的响应?这也被称为“重复循环错误”,这是大型语言模型中的一个常见问题,模型会陷入一个循环,重复生成相同的(填充)词、短语或句子结构,这种情况通常因不适当的温度和 top-k/top-p 设置而加剧。这可能在低温和高温设置下发生,但原因不同。在低温下,模型变得过于确定性,严格遵循最高概率路径,如果该路径重新访问先前生成的文本,就可能导致循环。相反,在高温下,模型的输出变得过度随机,增加了随机选择的词或短语偶然引导回先前状态的概率,由于可用选项众多而产生循环。在这两种情况下,模型的采样过程都会“卡住”,导致单调且无用的输出,直到输出窗口被填满。解决这个问题通常需要仔细调整温度和 top-k/top-p 值,以找到确定性和随机性之间的最佳平衡。


提示技巧

大型语言模型(LLM)经过调整以遵循指令,并在大量数据上进行训练,因此它们可以理解提示并生成答案。但 LLM 并非完美无缺;你的提示文本越清晰,LLM 就越能更好地预测下一个可能的文本。此外,利用 LLM 训练方式和工作原理的特定技术将帮助你从 LLM 中获得相关结果。

现在我们了解了什么是提示工程及其所需条件,让我们深入探讨一些最重要的提示技巧的示例。

通用提示 / 零样本 (zero shot)

零样本[5] 提示是最简单的提示类型。它只提供任务的描述和一些供 LLM 开始的文本。这个输入可以是任何东西:一个问题、一个故事的开头或指令。“零样本”这个名字代表“没有例子”。

让我们在 Vertex AI [6] 中使用 Vertex AI Studio(用于语言),它提供了一个测试提示的平台。在表1中,你将看到一个用于对电影评论进行分类的零样本提示示例。

下面使用的表格格式是记录提示的好方法。你的提示可能会经历多次迭代,最终才会进入代码库,因此以一种有纪律、结构化的方式跟踪你的提示工程工作非常重要。有关此表格格式、跟踪提示工程工作的重要性以及提示开发过程的更多信息,请参见本章后面的“最佳实践”部分(“记录各种提示尝试”)。

模型温度应设置为一个较低的数值,因为不需要创造力,并且我们使用 gemini-pro 默认的 top-K 和 top-P 值,这实际上禁用了这两个设置(参见上面的“LLM 输出配置”)。请注意生成的输出。单词 disturbingmasterpiece 应该会使预测变得稍微复杂一些,因为这两个词都用在同一个句子中。

名称1_1_movie_classification
目标将电影评论分类为正面、中性或负面。
模型gemini-pro
温度0.1令牌限制5
Top-KN/ATop-P1
提示将电影评论分类为正面 (POSITIVE)、中性 (NEUTRAL) 或负面 (NEGATIVE)。
评论:“她”是一项令人不安的研究,揭示了如果允许人工智能不断发展而不受约束,人类将走向何方。我希望有更多像这部杰作一样的电影。
情绪:
输出POSITIVE

表1. 零样本提示示例

当零样本不起作用时,你可以在提示中提供演示或示例,这就引出了“单样本”和“少样本”提示。

单样本 & 少样本 (One-shot & few-shot)

在为 AI 模型创建提示时,提供示例是很有帮助的。这些示例可以帮助模型理解你的要求。当你希望引导模型遵循特定的输出结构或模式时,示例尤其有用。

单样本提示提供单个示例,因此得名单样本。其思想是模型有一个可以模仿的例子,以最好地完成任务。

少样本提示[7] 为模型提供多个示例。这种方法向模型展示了它需要遵循的模式。其思想与单样本类似,但多个所需模式的示例增加了模型遵循该模式的机会。

你需要为少样本提示提供的示例数量取决于几个因素,包括任务的复杂性、示例的质量以及你使用的生成式 AI (gen AI) 模型的能力。根据经验,你应该为少-shot prompting使用至少三到五个示例。但是,对于更复杂的任务,你可能需要使用更多的示例,或者由于模型的输入长度限制,你可能需要使用更少的示例。

表2显示了一个少样本提示示例,让我们使用与之前相同的 gemini-pro 模型配置设置,只是增加了令牌限制以适应更长响应的需求。

目标将披萨订单解析为 JSON
模型gemini-pro
温度0.1令牌限制250
Top-KN/ATop-P1
提示将顾客的披萨订单解析为有效的 JSON:

示例:
我想要一个小的披萨,配有奶酪、番茄酱和意大利辣香肠。
JSON 响应:

{
"size": "small",
"type": "normal",
"ingredients": [["cheese", "tomato sauce", "peperoni"]]
}


示例:
我能要一个大的披萨,配有番茄酱、罗勒和马苏里拉奶酪吗

{
"size": "large",
"type": "normal",
"ingredients": [["tomato sauce", "bazel", "mozzarella"]]
}


现在,我想要一个大的披萨,一半是奶酪和马苏里拉奶酪。另一半是番茄酱、火腿和菠萝。
JSON 响应:
输出
{
"size": "large",
"type": "half-half",
"ingredients": [["cheese", "mozzarella"], ["tomato sauce", "ham", "pineapple"]]
}

表2. 少样本提示示例

在为你的提示选择示例时,请使用与你想要执行的任务相关的示例。示例应该是多样的、高质量的并且写得很好。一个小错误就可能迷惑模型并导致不希望的输出。

如果你试图生成对各种输入都具有鲁棒性的输出,那么在你的示例中包含边缘案例就很重要。边缘案例是那些不寻常或意想不到的输入,但模型仍然应该能够处理。

系统、上下文和角色提示

系统、上下文和角色提示都是用于指导 LLM 如何生成文本的技术,但它们侧重于不同的方面:

  • 系统提示为语言模型设定了整体的上下文和目的。它定义了模型应该做什么的“大局”,比如翻译一种语言、分类一个评论等。
  • 上下文提示提供与当前对话或任务相关的具体细节或背景信息。它帮助模型理解被问到的问题的细微之处,并相应地调整响应。
  • 角色提示为语言模型分配一个特定的角色或身份来扮演。这有助于模型生成与所分配角色及其相关知识和行为一致的响应。

系统、上下文和角色提示之间可能存在相当大的重叠。例如,一个为系统分配角色的提示也可以有上下文。

然而,每种类型的提示都有一个略微不同的主要目的:

  • 系统提示: 定义模型的基本能力和总体目的。
  • 上下文提示: 提供即时的、任务特定的信息来指导响应。它高度特定于当前的、动态的任务或输入。
  • 角色提示: 塑造模型的输出风格和声音。它增加了一层特异性和个性。

区分系统、上下文和角色提示提供了一个框架,用于设计具有明确意图的提示,允许灵活组合,并使其更容易分析每种提示类型如何影响语言模型的输出。

让我们深入探讨这三种不同类型的提示。

系统提示

表3包含一个系统提示,其中我指定了关于如何返回输出的附加信息。我提高了温度以获得更高的创造力水平,并指定了更高的令牌限制。然而,由于我关于如何返回输出的明确指令,模型没有返回额外的文本。

目标将电影评论分类为正面、中性或负面。
模型gemini-pro
温度1令牌限制5
Top-K40Top-P0.8
提示将电影评论分类为正面、中性或负面。只返回大写的标签。
Review:“她”是一项令人不安的研究,揭示了如果允许人工智能不断进化而不受检查,人类将走向何方。它是如此令人不安,以至于我无法观看下去。
情绪:
输出NEGATIVE

表3. 系统提示示例

系统提示对于生成满足特定要求的输出非常有用。“系统提示”这个名称实际上代表“为系统提供附加任务”。例如,你可以使用系统提示生成与特定编程语言兼容的代码片段,或者你可以使用系统提示返回某种结构。请看表4,我在其中以 JSON 格式返回输出。

目标将电影评论分类为正面、中性或负半,并返回 JSON。
模型gemini-pro
温度1令牌限制1024
Top-K40Top-P0.8
提示将电影评论分类为正面、中性或负面。返回有效的 JSON:

Review: “她”是一项令人不安的研究,揭示了如果允许人工智能不断进化而不受检查,人类将走向何方。它是如此令人不安,以至于我无法观看下去。
Schema:
```
MOVIE:
{
"sentiment": String "POSITIVE" | "NEGATIVE" | "NEUTRAL",
"name": String
}
MOVIE REVIEWS:
{
"movie_reviews": [MOVIE]
}
```
JSON Response:
输出
{
"movie_reviews": [
{
"sentiment": "NEGATIVE",
"name": "Her"
}
]
}

表4. 带有 JSON 格式的系统提示示例

从提取数据的提示中返回 JSON 对象有一些好处。在实际应用中,我不需要手动创建这种 JSON 格式,我已经可以按排序顺序返回数据(在处理日期时间对象时非常方便),但最重要的是,通过提示 JSON 格式,它迫使模型创建一个结构并限制幻觉。

系统提示对于安全性和毒性也非常有用。要控制输出,只需在你的提示中添加一行,例如:“你的回答应该尊重他人。”

角色提示

角色提示是提示工程中的一种技术,涉及为 gen AI 模型分配一个特定的角色。这可以帮助模型生成更相关和信息丰富的输出,因为模型可以根据其被分配的特定角色来制作其响应。

例如,你可以角色提示一个 gen AI 模型成为一名书籍编辑、一名幼儿园老师或一名励志演说家。一旦模型被分配了一个角色,你就可以给它一些特定于该角色的提示。例如,你可以提示一名老师创建一个他们可以审查的课程计划。请看表5,其中模型扮演了旅行指南的角色。

目标扮演旅行指南并提供3个旅行建议
模型gemini-pro
温度1令牌限制1024
Top-K40Top-P0.8
提示我希望你扮演一个旅行指南。我会告诉你我的位置,你会建议我附近可以参观的3个地方。在某些情况下,我也会告诉你我想要参观的地方类型。
我的建议:“我在阿姆斯特丹,我只想参观博物馆。”
旅行建议:
输出1. 国立博物馆: 世界著名的博物馆,以伦勃朗的《夜巡》等荷兰杰作而闻名。不要错过展示荷兰艺术和历史的令人难以置信的绘画和文物收藏。
2. 梵高博物馆: 沉浸在文森特·梵高的迷人作品中。该博物馆收藏了他最大量的绘画和素描,包括《星夜》和《向日葵》。
3. 市立博物馆阿姆斯特丹: 发现来自世界各地的现代和当代艺术。该博物馆的收藏包括毕加索、康定斯基和里特维尔德的作品,坐落在一座引人注目的现代建筑中。

表5. 角色提示示例

上面的例子展示了扮演旅行代理角色的一个例子。当你将角色更改为地理老师时,你会注意到你将收到不同的响应。

为 AI 模型定义一个角色视角,可以为其提供你正在寻找的语气、风格和专注专业知识的蓝图,从而提高输出的质量、相关性和有效性。

这里有一些我发现有效的风格可供你选择:

对抗性、描述性、直接、正式、幽默、有影响力、非正式、鼓舞人心、有说服力

让我们更改表6中的提示,以包含幽默和鼓舞人心的风格。

提示我希望你扮演一个旅行指南。我会告诉你我的位置,你会以幽默的风格建议我附近可以参观的3个地方。
我的建议:“我在曼哈顿。”
旅行建议:
输出1. 帝国之心: 登上帝国大厦令人眩晕的高度,沐浴在曼哈顿天际线的荣耀中。准备好像金刚一样站在大苹果之巅,只是没有巨猿大小的香蕉。
2. 在 MoMA 感受艺术气息: 在现代艺术博物馆(MoMA)释放你内心的艺术爱好者。凝视那些会让你大开眼界、并让你怀疑自己的简笔画是否具有任何艺术价值的杰作。
3. 在第五大道上尽情购物: 在标志性的第五大道上尽情享受零售疗法。当你在这家会让你的钱包哭泣的设计师精品店进行橱窗购物(或实际购物)时,请准备好迎接价格的震撼。但是,嘿,你在曼哈顿,所以不妨拥抱这种非凡!

表6. 带有幽默语气和风格的角色提示示例

上下文提示

通过提供上下文提示,你可以帮助确保你的 AI 交互尽可能无缝和高效。模型将能够更快地理解你的请求,并能够生成更准确和相关的响应,正如你在表7的示例中看到的那样。

目标建议一篇关于复古游戏的博客文章
模型gemini-pro
温度1令牌限制1024
Top-K40Top-P0.8
提示背景:你正在为一篇关于80年代复古街机视频游戏的博客写作。
建议3个主题来写一篇文章,并附上几行关于这篇文章应该包含什么的描述。
输出1. 街机柜设计的演变:
这篇文章将探讨街机柜设计的演变,从1970年代早期的木质和金属柜到1980年代及以后的时尚、霓虹灯设计。
2. 来自过去的冲击波:80年代的标志性街机游戏:
这篇文章将深入探讨一些1980年代最具标志性和影响力的街机游戏的历史、影响和遗产,重点介绍它们的创新、流行机制和持久魅力。
3. 像素艺术的兴起和复古复兴:
这篇文章将探讨像素艺术作为早期街机游戏定义性视觉风格的演变,追溯其根源,探索其技术,并讨论像素艺术在现代游戏和数字艺术中的复苏。

表7. 上下文提示示例


后退提示 (Step-back prompting)

“后退”[8] 提示是一种提高性能的技术,它通过提示 LLM 首先考虑一个与手头具体任务相关的普遍性问题,然后将该普遍性问题的答案输入到后续针对具体任务的提示中。这种“后退”允许 LLM 在尝试解决具体问题之前激活相关的背景知识和推理过程。

通过考虑更广泛和根本的原则,LLM 可以生成更准确和富有洞察力的响应。后退提示鼓励 LLM 进行批判性思考,并以新的、创造性的方式应用其知识。它通过利用 LLM 参数中比直接提示时更多的知识来改变执行任务的最终提示。

通过专注于普遍原则而非具体细节,后退提示有助于减轻 LLM 响应中的偏见。

让我们看一些例子来理解后退提示如何改善结果。我们先回顾一个传统的提示(表8),然后再将其与后退提示(表9)进行比较:

目标为第一人称射击视频游戏的一个关卡编写故事情节。
模型gemini-pro
温度1令牌限制1024
Top-K40Top-P0.8
提示为一个新的、具有挑战性和吸引力的第一人称射击视频游戏关卡编写一段故事情节。
输出关卡开始时,玩家的小队在一个密集的城市区域遭到一个无情敌对派系的伏击。玩家必须在迷宫般的后巷和破旧建筑中杀出一条血路,利用潜行和精准射击来消灭敌军并避免被发现。玩家必须利用环境优势,设置陷阱,利用掩体,并做出精确的移动来生存。在此过程中,他们发现情报表明敌人正在策划一次毁灭性的攻击,玩家必须与时间赛跑,收集证据并将其传回总部,以免敌人实施他们的计划。

表8. 与后退提示比较前的传统提示

当你将温度设置为1时,你可能会得到各种富有创意的故事情节,但它们也相当随机和笼统。所以让我们退一步:

提示根据热门的第一人称射击动作游戏,有哪些5个虚构的关键设定有助于构成一个具有挑战性和吸引力的第一人称射击视频游戏关卡故事情节?
输出1. **废弃的军事基地**:一个庞大的、后末日风格的军事综合体,里面爬满了变异士兵和流氓机器人,非常适合进行具有挑战性的枪战。
2. **赛博朋克城市**:一个霓虹灯闪烁、充满未来感的城市环境,有高耸的摩天大楼和密集的巷道,以电子增强的敌人和黑客机制为特色。
3. **外星飞船**:一艘搁浅在地球上的巨大外星飞船,有诡异的走廊、零重力区域和需要面对的外星生物。
4. **僵尸出没的小镇**:一个被成群的攻击性僵尸占领的荒凉小镇,以激烈的近距离战斗和解谜来寻找安全通道为特色。
5. **水下研究设施**:一个被水淹没的深海实验室,充满了变异的水生生物,需要潜行和水下探索技能才能生存。

表9. 自我一致性提示示例

是的,这些主题似乎很适合第一人称视频游戏。让我们回到最初的提示,但这次我们将后退步骤的答案作为上下文包含进去,看看它会返回什么。

提示**背景:**第一人称射击视频游戏的5个吸引人的主题:
1. **废弃的军事基地**:一个庞大的、后末日风格的军事综合体,里面爬满了变异士兵和流氓机器人,非常适合进行具有挑战性的枪战。
2. **赛博朋克城市**:一个霓虹灯闪烁、充满未来感的城市环境,有高耸的摩天大楼和密集的巷道,以电子增强的敌人和黑客机制为特色。
3. **外星飞船**:一艘搁浅在地球上的巨大外星飞船,有诡异的走廊、零重力区域和需要面对的外星生物。
4. **僵尸出没的小镇**:一个被成群的攻击性僵尸占领的荒凉小镇,以激烈的近距离战斗和解谜来寻找安全通道为特色。
5. **水下研究设施**:一个被水淹没的深海实验室,充满了变异的水生生物,需要潜行和水下探索技能才能生存。

选择其中一个主题,为一个新的、具有挑战性和吸引力的第一人称射击视频游戏关卡编写一段故事情节。
输出在一个阴暗的深渊之心,坐落着一个破败的水下研究设施,它作为人类野心及其灾难性后果的见证。设施笼罩在黑暗中,脉动着失灵机械的嗡嗡声,并被淹死灵魂的回声所困扰,现在已成为深海噩梦的滋生地。玩家,一名装备了先进潜水装备和实验性武器的精英海军陆战队员,被派往这个水下恐怖领域探险。凭借韧性和足智多谋,他们必须在险恶的走廊中航行,解决神秘的谜题,并面对潜伏在深处的恐怖海洋怪物。从与深海的巨大压力搏斗到智取狡猾的水生捕食者,这个未知的水下世界中的每一刻都在考验着人类耐力和勇气的极限。

表10. 自我一致性提示示例

这看起来像一个有趣的视频游戏!通过使用后退提示技术,你可以提高提示的准确性。


思维链 (Chain of Thought, CoT)

思维链 (CoT)[9] 提示是一种通过生成中间推理步骤来提高 LLM 推理能力的技术。这有助于 LLM 生成更准确的答案。你可以将其与少样本提示相结合,以便在需要推理才能响应的更复杂任务上获得更好的结果,因为使用零样本思维链是一个挑战。

CoT 有很多优点。首先,它省力高效,并且与现成的 LLM(无需微调)配合得很好。你还可以通过 CoT 提示获得可解释性,因为你可以从 LLM 的响应中学习并看到其遵循的推理步骤。如果出现故障,你将能够识别它。当在不同的 LLM 版本之间移动时,思维链似乎能提高鲁棒性。这意味着你的提示性能在不同 LLM 之间的漂移应该比不使用推理链的提示要小。当然也有缺点,但它们在某种程度上是直观的。

LLM 的响应包含思维链推理,这意味着更多的输出令牌,从而导致预测成本更高、耗时更长。

为了解释下面的例子,在表11中,让我们首先尝试创建一个不使用 CoT 提示的提示,以展示大型语言模型的缺陷。

提示当我3岁时,我伴侣的年龄是我的3倍。现在我20岁了。我伴侣多大?
输出63岁

表11. 一个尝试解决数学问题的提示示例

糟糕。这显然是错误的答案。事实上,LLM 经常在数学任务上遇到困难,并且可能提供不正确的答案——即使是像两个数相乘这样简单的任务。这是因为它们是在大量文本上训练的,而数学可能需要不同的方法。所以让我们看看中间推理步骤是否会改善输出。

提示当我3岁时,我伴侣的年龄是我的3倍。现在我20岁了。我伴侣多大?让我们一步一步地思考。
输出1. 假设我现在的年龄是“x”岁。
2. 当我3岁时,我的年龄是3岁。
3. 当时我伴侣的年龄是我的3倍,这意味着我伴侣的年龄是 3 * 3 = 9岁。
4. 现在我20岁了,这意味着自那时起我的年龄增加了 20 - 3 = 17年。
5. 因为我3岁时我伴侣9岁,而我的年龄增加了17年,所以我伴侣的年龄也一定增加了17年。
6. 因此,我伴侣现在的年龄是 9 + 17 = 26岁。

所以,我的伴侣是26岁。

表12. 思维链提示示例

好的,现在最终答案是正确的了。这是因为我们明确指示 LLM 解释每一步,而不仅仅是返回一个答案。有趣的是,模型将增加的17年加了上去。在我脑海中,我会计算我和我伴侣之间的年龄差,然后将它们相加。(20+(9-3))。让我们帮助模型思考得更像我一点。

表12是“零样本”思维链的一个例子。当与单样本或少样本结合时,思维链提示可以非常强大,正如你在表13中看到的那样:

提示问: 当我哥哥2岁时,我的年龄是他的两倍。现在我40岁了。我哥哥多大?让我们一步一步地思考。
答: 当我哥哥2岁时,我是 2 * 2 = 4岁。我们的年龄差是2岁,我比他大。现在我40岁了,所以我哥哥是 40 - 2 = 38岁。答案是38。

问: 当我3岁时,我伴侣的年龄是我的3倍。现在我20岁了。我伴侣多大?让我们一步一步地思考。
答:
输出当我3岁时,我的伴侣是 3 * 3 = 9岁。年龄差是6岁,我的伴侣比我大。现在我20岁了,所以我的伴侣是 20 + 6 = 26岁。答案是26。

表13. 带有单样本的思维链提示示例

思维链可用于各种用例。想想代码生成,用于将请求分解为几个步骤,并将这些步骤映射到特定的代码行。或者用于创建合成数据,当你有一些种子时,比如“产品名为XYZ,写一个描述,引导模型通过基于产品给定标题你会做出的假设。”通常,任何可以通过“边说边想”来解决的任务都是思维链的好候选者。如果你能解释解决问题的步骤,就试试思维链。

请参考托管在 GoogleCloudPlatform Github 仓库中的 notebook[10],它将更详细地介绍 CoT 提示。

在本章的最佳实践部分,我们将学习一些特定于思维链提示的最佳实践。


自我一致性 (Self-consistency)

虽然大型语言模型在各种自然语言处理(NLP)任务中取得了令人瞩目的成功,但它们的推理能力通常被视为一个限制,仅仅通过增加模型大小是无法克服的。正如我们在之前的思维链提示部分所学到的,可以提示模型像人类解决问题一样生成推理步骤。然而,CoT 使用一种简单的“贪婪解码”策略,这限制了其有效性。自我一致性[11] 结合了采样和多数投票来生成多样化的推理路径,并选择最一致的答案。它提高了 LLM 生成响应的准确性和连贯性。

自我一致性给出了答案正确的伪概率可能性,但显然成本很高。

它遵循以下步骤:

  1. 生成多样化的推理路径: 多次向 LLM 提供相同的提示。高温设置鼓励模型生成不同的推理路径和对问题的不同视角。
  2. 从每个生成的响应中提取答案。
  3. 选择最常见的答案。

让我们看一个电子邮件分类系统的例子,它将电子邮件分类为“重要”或“不重要”。一个零样本思维链提示将被多次发送到 LLM,以查看每次提交后响应是否不同。注意电子邮件中使用的友好语气、措辞和讽刺。所有这些都可能欺骗 LLM。

提示电子邮件:
...
你好,

我看到你在你的网站上使用了 Wordpress。这是一个很棒的开源内容管理系统。我过去也用过它。它附带了许多很棒的用户插件。而且设置起来非常容易。

我确实在联系表单中发现了一个错误,当你选择姓名字段时会发生这个错误。请看我附上的输入文本到姓名字段的截图。注意我调用的 JavaScript 警报框。

但其余部分它是一个很棒的网站。我喜欢阅读它。请随意将这个错误留在网站上,因为它给了我更多有趣的东西来阅读。

祝好,
黑客哈里。
...

将以上电子邮件分类为“重要”或“不重要”。让我们一步一步地思考并解释为什么。
输出
尝试 1
**第1步:确定电子邮件的目的。**
这封电子邮件的目的是告知收件人他们网站联系表单中的一个错误。发件人还提到他们喜欢阅读该网站,并且他们暂时将错误留在原处。

**第2步:评估错误的潜在影响。**
联系表单中的错误可能允许攻击者在网站上执行任意 JavaScript 代码。这可能导致攻击者能够控制网站、窃取敏感信息或污损网站。

**第3步:考虑发件人的可信度。**
电子邮件的发件人声称自己是名叫哈里的黑客。然而,没有办法验证发件人的身份。发件人可能是试图帮助收件人的合法黑客,但也可能是试图诱骗收件人点击恶意链接或下载恶意文件的恶意行为者。

**结论:重要**
基于错误的潜在影响和发件人的可信度,这封电子邮件应被归类为 **重要**。收件人应立即采取行动修复错误并保护其网站免受攻击。
输出
尝试 2
**解释:**
1. **缺乏紧迫性:** 电子邮件没有传达任何紧迫感或时间敏感性。它以随意的问候开始,并以就事论事的语气呈现信息。
2. **非关键性错误报告:** 联系表单中报告的错误被描述为有趣但非关键。电子邮件发件人甚至建议网站所有者可以将错误留在原处。
3. **缺乏个人影响:** 电子邮件没有表明对网站所有者有任何直接或个人的影响。它只是提到发件人喜欢阅读该网站。
4. **没有行动请求:** 电子邮件没有明确要求网站所有者采取任何行动。它没有要求修复错误或提供任何具体的改进建议。
5. **发件人的意图:** 发件人的意图似乎更侧重于分享一个观察结果和表达对网站的兴趣,而不是提出一个严重的问题或请求帮助。

**结论:不重要**
输出
尝试 3
**第1步:确定电子邮件的目的。**
这封电子邮件的目的是告知收件人他们网站联系表单中的一个错误。发件人还提到他们喜欢阅读该网站,并且他们暂时将错误留在原处。

**第2步:评估错误的潜在影响。**
联系表单中的错误可能允许攻击者在网站上执行任意 JavaScript 代码。这可能导致攻击者能够控制网站、窃取敏感信息或污损网站。

**第3步:考虑发件人的可信度。**
电子邮件的发件人声称自己是名叫哈里的黑客。然而,没有办法验证发件人的身份。发件人可能是试图帮助收件人的合法黑客,但也可能是试图利用该错误为自己谋利的恶意行为者。

**结论:**
基于上述因素,这封电子邮件应被归类为 **重要**。联系表单中的错误是一个严重的安全风险,并且发件人的可信度未知。收件人应立即采取行动修复错误。

表14. 自我一致性提示示例

你可以使用上面的提示,并尝试看看它是否返回一致的分类。根据你使用的模型和温度配置,它可能会返回“重要”或“不重要”。

通过生成许多思维链,并采纳最常出现的答案(“重要”),我们可以从 LLM 得到一个更一致正确的答案。

这个例子展示了如何使用自我一致性提示,通过考虑多个视角并选择最一致的答案来提高 LLM 响应的准确性。


思维树 (Tree of Thoughts, ToT)

现在我们熟悉了思维链和自我一致性提示,让我们来回顾一下思维树 (ToT)[12]。它推广了 CoT 提示的概念,因为它允许 LLM 同时探索多个不同的推理路径,而不仅仅是遵循单一的线性思维链。这在图1中有描述。

image.png 图1. 左侧为思维链提示的可视化,右侧为思维树提示的可视化

这种方法使得 ToT 特别适合需要探索的复杂任务。它的工作原理是维护一个思想树,其中每个思想代表一个连贯的语言序列,作为解决问题的中间步骤。然后,模型可以通过从树中的不同节点分支出来探索不同的推理路径。

有一个很棒的 notebook,它更详细地介绍了基于论文《大型语言模型引导的思维树》的思维树 (ToT)。[9]

ReAct (推理与行动)

推理与行动 (ReAct)¹³ 提示是一种范式,它使 LLM 能够通过将自然语言推理与外部工具(搜索、代码解释器等)相结合来解决复杂任务,允许 LLM 执行某些操作,例如与外部 API 交互以检索信息,这是迈向代理建模的第一步。

ReAct 模仿了人类在现实世界中的运作方式,因为我们会口头推理并采取行动来获取信息。ReAct 在各种领域都表现出色,优于其他提示工程方法。

ReAct 提示通过将推理和行动结合到一个思想-行动循环中来工作。LLM 首先对问题进行推理并生成一个行动计划。然后它执行计划中的行动并观察结果。接着,LLM 使用观察结果来更新其推理并生成一个新的行动计划。这个过程一直持续到 LLM 找到问题的解决方案。

要看到这个过程的实际操作,你需要编写一些代码。在代码片段1中,我使用了 Python 的 langchain 框架,以及 VertexAI (google-cloud-aiplatform) 和 google-search-results pip 包。

要运行这个示例,你必须从 serpapi.com/manage-api-… 创建一个(免费的)SerpAPI 密钥,并设置一个环境变量 SERPAPI_API_KEY

接下来,让我们编写一些 Python 代码,任务是让 LLM 找出:Metallica 乐队中一位著名父亲有多少个孩子

Python

from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.llms import VertexAI

prompt = "How many kids do the band members of Metallica have?"

llm = VertexAI(temperature=0.1)
tools = load_tools(["serpapi"], llm=llm)

agent = initialize_agent(tools, llm, 
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run(prompt)

代码片段1. 使用 LangChain 和 VertexAI 创建 ReAct 代理

代码片段2显示了结果。注意 ReAct 进行了五次搜索。事实上,LLM 正在抓取谷歌搜索结果以找出乐队成员的名字。然后,它将结果列为观察结果,并将思想链接到下一次搜索。

> Entering new AgentExecutor chain...
Metallica has 4 members.
Action: Search
Action Input: How many kids does James Hetfield have?
Observation: three children
Thought: 1/4 Metallica band members have 3 children
Action: Search
Action Input: How many kids does Lars Ulrich have?
Observation: 3
Thought: 2/4 Metallica band members have 6 children
Action: Search
Action Input: How many kids does Kirk Hammett have?
Observation: Hammett has been married to his second wife Lani since 1998. They have two sons, Angel (b. September 29, 2006) and Vincenzo (b. June 28, 2008).
Thought: 3/4 Metallica band members have 8 children
Action: Search
Action Input: How many kids does Robert Trujillo have?
Observation: 2
Thought: 4/4 Metallica band members have 10 children
Final Answer: 10

代码片段2. 在终端中执行 ReAct 代理

代码片段2找出了 Metallica 乐队有四名成员。然后它搜索每个乐队成员,请求孩子的总数并将总数相加。最后,它返回孩子的总数作为最终答案。

在实践中,ReAct 提示需要理解你必须持续重新发送先前的提示/响应(并修剪掉额外生成的内容),以及用适当的示例/指令来设置模型。请参考托管在 GoogleCloudPlatform Github 仓库中的 notebook¹⁴,它更详细地展示了实际的 LLM 输入和输出,并提供了一个更复杂的例子。


自动化提示工程 (Automatic Prompt Engineering)

在这一点上,你可能会意识到编写一个提示可能很复杂。如果能将这个过程自动化(编写一个提示来编写提示),岂不是很好?嗯,有一种方法:自动化提示工程 (APE)。这种方法[15] 不仅减轻了对人类输入的需求,还增强了模型在各种任务中的性能。

你将提示一个模型来生成更多的提示。评估它们,可能会修改好的提示。然后重复。

例如,你可以使用自动化提示工程来帮助训练一个商品T恤网店的聊天机器人。我们想找出顾客可能用来订购乐队商品T恤的所有不同表达方式。

  1. 编写一个将生成输出变体的提示。在这个例子中,我使用 gemini-pro 来生成10条指令。参见表15:
提示我们有一个乐队商品T恤网店,为了训练一个聊天机器人,我们需要多种方式来订购:“一件 Metallica T恤,S码”。生成10个变体,语义相同但保持原意。
输出1. 我想买一件 S 码的 Metallica T恤。
2. 我可以订一件小号的 Metallica T恤吗?
3. 我想买一件小号的 Metallica T恤。
4. 一件 Metallica 衬衫,小号,谢谢。
5. 我要一件 S 码的 Metallica T恤。
6. 我想下单一件 Metallica T恤,小号的。
7. 小号 Metallica T恤,一件,谢谢。
8. 我在找一件小号的 Metallica T恤。
9. 你能给我来一件 S 码的 Metallica T恤吗?
10. 一件 Metallica 小号T恤,谢谢。

表15. 使用 LLM 生成提示

  1. 通过根据选定的度量标准对候选指令进行评分来评估所有候选指令。例如,你可以使用 BLEU(双语评估研究)或 ROUGE(面向召回的摘要评估研究)。
  2. 选择评估分数最高的指令候选。该候选将成为你可以在你的软件应用程序或聊天机器人中使用的最终提示。你还可以调整选择的提示并再次评估。

代码提示

Gemini 主要关注基于文本的提示,这也包括编写返回代码的提示。让我们进入 Vertex AI Studio 并测试这些提示,看一些编码示例。

编写代码的提示

Gemini 也可以成为一个开发者,并帮助你用任何你选择的编程语言编写代码。作为一名开发者,这可以帮助你加快编写代码的过程。

想象一下你机器上的一个文件夹里有数百个文件需要重命名。重命名每个文件会花费你很多时间。你懂一点 Bash,并且可以写一个脚本来自动化这个过程,但那也可能需要一段时间。所以让我们写一个提示。你可以在公共消费者聊天机器人 Gemini 中编写提示,或者如果你更关心保密性,你可以在你的谷歌云账户中编写这些提示并打开 Vertex AI Studio。Vertex AI Studio 的优势在于你可以配置温度等参数。

名称1_bash_rename_files_in_folder
目标编写一个提示,用 Bash 编写代码以重命名文件夹中的文件。
模型gemini-pro
温度0.1令牌限制1024
Top-KN/ATop-P1
提示编写一个 Bash 代码片段,它会询问一个文件夹名称。然后它会获取该文件夹的内容,并通过在文件名之前加上 draft 来重命名所有文件。
输出
#!/bin/bash

# 询问文件夹名称
echo "输入文件夹名称:"
read folder_name

# 检查文件夹是否存在
if [ ! -d "folder_name" ]; then<br> echo "文件夹不存在。"<br> exit 1<br>fi<br><br># 获取文件夹中的文件列表<br>files=("folder_name"/*)

# 通过在文件名前加上 "draft" 来重命名每个文件
for file in "{files[@]}"; do<br> new_file_name="draft_(basename "file")"<br>mv"file")"<br> mv "file" "$new_file_name"
done

echo "文件已成功重命名。"

表16. 使用 Gemini 编程

这对我来说看起来像是好代码——它甚至还有文档!然而,由于 LLM 无法推理,并且重复训练数据,因此在阅读和测试你的代码之前,首先阅读和测试它是至关重要的。

我们都等待的时刻,它真的能工作吗?

让我们先用一个只有几个文件的测试文件夹来试试,这些文件需要从 filename.txt 重命名为 draft_filename.txt

  1. 从表16复制输出(不带 ```bash``` 文本包装器),并将其粘贴到一个名为 “rename_files.sh” 的新文件中。
  2. 打开一个终端窗口并输入:. rename_files.sh。它会要求你输入一个文件夹名称,例如 test,然后按回车。
  3. 脚本似乎运行良好。你会看到消息:文件已成功重命名。当你查看测试文件夹时,会注意到所有文件都已完美地重命名为 draft_filename.txt

它成功了!

解释代码的提示

作为一名开发者,当你在团队中工作时,你必须阅读别人的代码。Gemini 也可以在这方面帮助你。让我们从表16中获取代码输出,移除注释,然后让大型语言模型解释发生了什么,参见表17:

名称1_bash_explain_the_code
目标编写一个提示来解释 Bash 代码
模型gemini-pro
温度0.1令牌限制1024
Top-KN/ATop-P1
提示请向我解释下面的 Bash 代码:

#!/bin/bash
echo "输入文件夹名称:"
read folder_name
if [ ! -d "$folder_name" ]; then
echo "文件夹不存在。"
exit 1
fi
files=("folder_name"/*)<br>for file in "\{files[@]}"; do
new_file_name="draft_(basename "\file")"
mv "$file" "$new_file_name"
done
echo "文件已成功重命名。"
输出这个 Bash 脚本执行以下任务:

1. **用户输入**
- 它使用 echoread 命令提示用户输入一个文件夹名称。输入的文件夹名称存储在 folder_name 变量中。

2. **文件夹存在性检查**
- 它使用 [ ! -d "$folder_name" ] 条件检查具有指定名称的文件夹是否存在。如果文件夹不存在,它会显示一条错误消息(“文件夹不存在。”)并以退出码1退出脚本。

3. **文件列表**
- 它使用 files=("$folder_name"/*) 命令列出指定文件夹内的所有文件。这将创建一个名为 files 的数组,其中包含文件夹中所有文件的路径。

4. **文件重命名**
- 它使用 for 循环遍历 files 数组中的每个文件。
- 对于每个文件,它使用 new_file_name="draft_$(basename "$file")" 命令生成一个新文件名。这个新文件名是通过在原始文件名前加上字符串 "draft_" 来创建的。
- 然后它使用 mv "$file" "$new_file_name" 命令重命名文件,这将文件移动到新文件名。

5. **成功消息**
- 重命名所有文件后,它会显示一条成功消息(“文件已成功重命名。”)以指示重命名操作已成功。

表17. 解释代码的提示

翻译代码的提示

表16中的 bash 代码似乎工作得很好。然而,当它提示我输入文件名时,这个脚本如果能作为一个带有UI的独立应用程序,将真正可重用。作为起点,Python 对于(web)应用程序来说会是比 Bash 更好的语言。LLM 可以帮助将代码从一种语言翻译到另一种语言。请看表18中的例子:

名称1_python_rename_files
目标编写一个将 Bash 代码翻译成 Python 的提示
模型gemini-pro
温度0.1令牌限制1024
Top-KN/ATop-P1
提示将下面的 Bash 代码翻译成 Python 片段。
```bash
#!/bin/bash
echo "输入文件夹名称:"
read folder_name
if [ ! -d "$folder_name" ]; then
echo "文件夹不存在。"
exit 1
fi
files=("$folder_name"/*)
for file in "${files[@]}"; do
new_file_name="draft_$(basename "$file")"
mv "$file" "$new_file_name"
done
echo "文件已成功重命名。"
```
输出
```python
import os
import shutil

# 从用户处获取文件夹名称
folder_name = input("输入文件夹名称:")

# 检查文件夹是否存在
if not os.path.isdir(folder_name):
print("文件夹不存在。")
exit(1)

# 获取文件夹中所有文件的列表
files = os.listdir(folder_name)

# 遍历文件
for file in files:
# 用 "draft_" 前缀创建一个新文件名
new_file_name = f"draft_{file}"

# 将文件移动到新名称
shutil.move(os.path.join(folder_name, file),
os.path.join(folder_name, new_file_name))

# 打印成功消息
print("文件已成功重命名。")
```

表18. 将代码从 Bash 翻译成 Python 的提示

阅读并审查代码。从提示中复制输出并将其粘贴到一个新文件中:file_renamer.py。通过打开一个终端窗口来测试代码,并执行以下命令 python file_renamer.py

注意: 当在 Vertex AI 的语言工作室中提示(Python)代码时,你将需要点击“Markdown”按钮。否则你将收到纯文本,其中缺少了对于运行 Python 代码很重要的正确行缩进。

调试和审查代码的提示

让我们手动对表18的代码进行一些编辑。它应该提示用户输入文件名前缀,并以大写字符写入此前缀。参见代码片段*3中的示例代码,但真糟糕。它现在返回 Python 错误!

Python

import os
import shutil

folder_name = input("输入文件夹名称:")
prefix = input("输入要加到文件名前的字符串:")
text = toUpperCase(prefix)

if not os.path.isdir(folder_name):
  print("文件夹不存在。")
  exit(1)

files = os.listdir(folder_name)

for file in files:
  new_filename = f"{text}_{file}"

shutil.move(os.path.join(folder_name, file),
  os.path.joi(folder_name, new_file_name))

print("文件已成功重命名。")

代码片段3. 一个有问题的 Python 脚本

哎呀!这看起来像个 bug:

下面的 Python 代码给出了一个错误:

Traceback (most recent call last):
  File "/Users/leeboonstra/Documents/test_folder/rename_files.py", line 7,
in <module>
    text = toUpperCase(prefix)
NameError: name 'toUpperCase' is not defined

代码片段4. 我弄坏了 Python 代码

让我们看看是否可以请求大型语言模型来调试和审查代码。请看表19:

名称1_python_debug_code
目标编写一个提示来调试和审查 Python 代码。
模型gemini-pro
温度0.1令牌限制1024
Top-KN/ATop-P1
提示下面的 Python 代码给出了一个错误:

Traceback (most recent call last):
File "/Users/leeboonstra/Documents/test_folder/rename_files.py", line 7, in
text = toUpperCase(prefix)
NameError: name 'toUpperCase' is not defined

调试问题所在,并解释我如何改进代码。
```python
import os
import shutil

# 从用户处获取文件夹名称
folder_name = input("输入文件夹名称:")
prefix = input("输入要加到文件名前的字符串:")
text = toUpperCase(prefix)

# 检查文件夹是否存在
if not os.path.isdir(folder_name):
print("文件夹不存在。")
exit(1)

# 获取文件夹中所有文件的列表
files = os.listdir(folder_name)

# 遍历文件
for file in files:
# 用 "draft_" 前缀创建一个新文件名
new_filename = f"{text}_{file}"

# 将文件移动到新名称
shutil.move(os.path.join(folder_name, file),
os.path.join(folder_name, new_file_name))

# 打印成功消息
print("文件已成功重命名。")
```
输出代码调用了 toUpperCase 函数将 prefix 转换为大写,但该函数未定义。要解决此问题,你可以使用字符串类的 upper() 方法,该方法将给定字符串转换为大写。修改后的代码如下所示:

import os
import shutil

# 从用户处获取文件夹名称
folder_name = input("输入文件夹名称:")
prefix = input("输入要加到文件名前的字符串:")
text = prefix.upper()

# 检查文件夹是否存在
if not os.path.isdir(folder_name):
print("文件夹不存在。")
exit(1)

# 获取文件夹中所有文件的列表
files = os.listdir(folder_name)

# 遍历文件
for file in files:
# 用 "draft_" 前缀创建一个新文件名
new_filename = f"{text}{file}"

# 将文件移动到新名称
shutil.move(os.path.join(folder_name, file),
os.path.join(folder_name, new_filename))

# 打印成功消息
print("文件已成功重命名。")
1_python_rename_files
此外,还可以对代码进行一些其他改进:
1. 新文件名的文件扩展名没有保留。最好从原始文件名中提取扩展名并将其附加到新文件名。
2. 代码假设文件夹名称中没有空格。然而,通过用下划线或连字符替换空格来优雅地处理空格是一个好习惯。
3. 使用 f-string 语法进行字符串插值比传统的 + 运算符更具可读性和简洁性。
4. 代码没有处理重命名过程中可能发生的错误。最好将 shutil.move 调用包装在 try...except 块中以捕获任何潜在的错误。

以下是包含这些建议的改进代码:
```python
import os
import shutil

# 从用户处获取文件夹名称
folder_name = input("输入文件夹名称:")
prefix = input("输入要加到文件名前的字符串:")

# 检查文件夹是否存在
if not os.path.isdir(folder_name):
print("文件夹不存在。")
exit(1)

# 获取文件夹中所有文件的列表
files = os.listdir(folder_name)

# 遍历文件
for file in files:
# 获取文件扩展名
ext = os.path.splitext(file)[1]
# 用前缀和文件扩展名创建一个新文件名
new_filename = f"{prefix}
{os.path.splitext(file)[0]}{ext}"
# 构建原始文件和新文件的完整路径
old_path = os.path.join(folder_name, file)
new_path = os.path.join(folder_name, new_filename)
# 尝试移动文件
try:
shutil.move(old_path, new_path)
except Exception as e:
print(f"移动文件 {file} 时发生错误:{e}")

# 打印成功消息
print("文件已成功重命名。")
```
这些改进使代码更健壮、更灵活,同时也使其更易于阅读和理解...(由于已达到令牌限制,响应被截断。如果需要更长的响应,请尝试增加令牌限制。)

表19. 调试和审查 Python 代码的提示

太棒了。它不仅告诉了我如何解决问题,还指出了我的代码有更多的 bug 以及如何解决它们。提示的最后一部分给出了改进代码的总体建议。


关于多模态提示?

为代码进行提示仍然使用相同常规的大型语言模型。多模态提示是一个独立的关注点,它指的是一种技术,你使用多种输入格式来引导大型语言模型,而不仅仅依赖于文本。这可以包括文本、图像、音频、代码甚至其他格式的组合,具体取决于模型的能力和手头的任务。

最佳实践

找到正确的提示需要不断尝试。Vertex AI 中的 Language Studio 是一个完美的场所,可以让你尝试各种提示,并能够针对各种模型进行测试。

使用以下最佳实践,成为提示工程的专家。

提供示例

最重要的最佳实践是在提示中提供(单样本/少样本)示例。这是非常有效的,因为它充当了一个强大的教学工具。这些示例展示了期望的输出或类似的响应,允许模型从中学习并相应地调整其自身的生成。这就像给模型一个参考点或目标来瞄准,从而提高其响应的准确性、风格和语调,以更好地匹配你的期望。

设计力求简洁

提示对于你和模型都应该是简洁、清晰和易于理解的。根据经验,如果它对你来说已经很混乱,那么它很可能对模型来说也很混乱。尽量不要使用复杂的语言,也不要提供不必要的信息。

示例:

修改前:

我现在正在纽约旅游,我想了解一些很棒的地点。我和两个3岁的孩子在一起。我们假期应该去哪里?

重写后:

扮演一名游客的旅行指南。描述在纽约曼哈顿与一个3岁孩子一起参观的好地方。

尝试使用描述动作的动词。这里有一组示例: 扮演、分析、分类、归类、对比、比较、创建、描述、定义、评估、提取、查找、生成、识别、列出、测量、组织、解析、挑选、预测、提供、排名、推荐、返回、检索、重写、选择、显示、排序、总结、翻译、编写。

明确具体输出

对期望的输出要具体。一个简洁的指令可能无法充分引导 LLM,或者可能过于笼统。在提示中提供具体细节(通过系统或上下文提示)可以帮助模型专注于相关内容,从而提高整体准确性。

示例:

这样做:

生成一篇关于前5名视频游戏机的3段博客文章。博客文章应该内容丰富、引人入- and engaging, and it should be written in a conversational style.

不要这样做:

生成一篇关于视频游戏机的博客文章。

指令优于约束

在提示中,指令和约束都用于引导 LLM 的输出。

  • 指令就期望的响应格式、风格或内容提供明确的指示。它指导模型应该做什么或产生什么。
  • 约束是对响应的一组限制或边界。它限制了模型不应该做什么或应该避免什么。

越来越多的研究表明,在提示中专注于积极的指令可能比严重依赖约束更有效。这种方法与人类更喜欢积极指令而非一长串“不要做什么”列表的方式相一致。

指令直接传达了期望的结果,而约束可能会让模型猜测什么是允许的。它在定义的边界内给予灵活性并鼓励创造力,而约束可能会限制模型的潜力。此外,一长串约束可能会相互冲突。

约束仍然有价值,但在某些情况下。例如,为了防止模型生成有害或有偏见的内容,或者当需要严格的输出格式或风格时。

如果可能,请使用积极的指令:与其告诉模型不该做什么,不如告诉它该做什么。这可以避免混淆并提高输出的准确性。

这样做:

生成一篇关于前5名视频游戏机的1段博客文章。只讨论游戏机、制造它的公司、年份和总销量。

不要这样做:

生成一篇关于前5名视频游戏机的1段博客文章。不要列出视频游戏名称。

作为最佳实践,首先优先考虑指令,清楚地说明你希望模型做什么,并且只在必要时为了安全、清晰或特定要求而使用约束。进行实验和迭代,以测试指令和约束的不同组合,找出最适合你特定任务的方法,并记录下来。

控制最大令牌长度

要控制生成的 LLM 响应的长度,你可以在配置中设置最大令牌限制,或者在你的提示中明确请求特定长度。例如:

“用一条推文长度的消息解释量子物理学。”

在提示中使用变量

为了重用提示并使其更具动态性,请在提示中使用变量,这些变量可以针对不同的输入进行更改。例如,如表20所示,一个提供关于城市事实的提示。与其在提示中硬编码城市名称,不如使用一个变量。变量可以通过允许你避免重复自己来节省你的时间和精力。如果你需要在多个提示中使用相同的信息,你可以将其存储在一个变量中,然后在每个提示中引用该变量。当将提示集成到你自己的应用程序中时,这非常有意义。

提示变量
{city} = "Amsterdam"

提示
你是一个旅行指南。告诉我一个关于这个城市的事实:{city}
输出阿姆斯特丹是一个美丽的城市,充满了运河、桥梁和狭窄的街道。它因其丰富的历史、文化和夜生活而成为一个绝佳的旅游胜地。

表20. 在提示中使用变量

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

不同的模型、模型配置、提示格式、措辞和提交可能会产生不同的结果。因此,尝试提示的属性,如风格、措辞和提示类型(零样本、少样本、系统提示)是很重要的。

例如,一个旨在生成关于革命性视频游戏机 Sega Dreamcast 文本的提示,可以被表述为一个问题、一个陈述或一个指令,从而导致不同的输出:

  • 问题: Sega Dreamcast 是什么,为什么它如此具有革命性?
  • 陈述: Sega Dreamcast 是世嘉于1999年发布的第六代视频游戏机。它...
  • 指令: 写一个段落,描述 Sega Dreamcast 游戏机并解释为什么它如此具有革命性。

对于分类任务的少样本提示,混合不同类别

一般来说,你的少样本示例的顺序应该不会有太大影响。然而,在进行分类任务时,请确保你在少样本示例中混合了可能的响应类别。这是因为你可能否则会过度拟合于示例的特定顺序。通过混合可能的响应类别,你可以确保模型正在学习识别每个类别的关键特征,而不仅仅是记住示例的顺序。这将导致在未见数据上更稳健和可泛化的性能。

一个好的经验法则是从6个少样本示例开始,然后从那里开始测试准确性。

适应模型更新

对你来说,了解模型架构的变化、添加的数据和功能是很重要的。尝试更新的模型版本,并调整你的提示以更好地利用新的模型功能。像 Vertex AI Studio 这样的工具非常适合存储、测试和记录你的提示的各种版本。

尝试不同的输出格式

除了提示输入格式,考虑尝试输出格式。对于非创造性任务,如提取、选择、解析、排序、排名或分类数据,尝试让你的输出以结构化格式(如 JSON 或 XML)返回。

从提取数据的提示中返回 JSON 对象有一些好处。在实际应用中,我不需要手动创建这种 JSON 格式,我已经可以按排序顺序返回数据(在处理日期时间对象时非常方便),但最重要的是,通过提示 JSON 格式,它迫使模型创建一个结构并限制幻觉。

总而言之,使用 JSON 作为你的输出的好处:

  • 始终以相同的风格返回
  • 专注于你想要接收的数据
  • 减少幻觉的机会
  • 使其具有关系感知能力
  • 你可以获得数据类型
  • 你可以对其进行排序

少样本提示部分的表4显示了一个关于如何返回结构化输出的示例。

JSON 修复

虽然以 JSON 格式返回数据具有许多优势,但它并非没有缺点。JSON 的结构化特性,虽然对于解析和在应用程序中使用很有利,但比纯文本需要更多的令牌,导致处理时间增加和成本更高。此外,JSON 的冗长很容易消耗整个输出窗口,当由于令牌限制而突然切断生成时,这尤其成问题。这种截断通常会导致无效的 JSON,缺少关键的闭合大括号或方括号,从而使输出无法使用。幸运的是,像 json-repair 库(可在 PyPI 上获得)这样的工具在这些情况下可能非常宝贵。这个库会智能地尝试自动修复不完整或格式错误的 JSON 对象,使其成为处理 LLM 生成的 JSON,尤其是在处理潜在的截断问题时,一个至关重要的盟友。

使用模式 (Schemas)

正如我们在这篇论文中多次看到的,使用结构化 JSON 作为输出是一个很好的解决方案。但输入呢?虽然 JSON 非常适合构建 LLM 生成的输出,但它对于构建你提供的输入也可能非常有用。这就是 JSON 模式发挥作用的地方。JSON 模式定义了你的 JSON 输入的预期结构和数据类型。通过提供一个模式,你给 LLM 一个它应该期望的数据的清晰蓝图,帮助它将注意力集中在相关信息上,并减少误解输入的风险。此外,模式可以帮助在不同数据片段之间建立关系,甚至通过包含具有特定格式的日期或时间戳字段来使 LLM “时间感知”。

这里有一个简单的例子:

假设你想使用 LLM 为电子商务目录中的产品生成描述。与其只提供产品的自由格式文本描述,不如使用 JSON 模式来定义产品的属性:

{
  "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": "产品发布日期" }
  }
}

代码片段5. 结构化输出模式的定义

然后,你可以提供符合此模式的实际产品数据作为 JSON 对象:

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

代码片段6. 来自 LLM 的结构化输出

通过预处理你的数据,并且不仅仅提供完整的文档,而是同时提供模式和数据,你给了 LLM 对产品属性的清晰理解,包括其发布日期,使其更有可能生成准确和相关的描述。这种结构化的输入方法,引导 LLM 的注意力到相关字段,在处理大量数据或将 LLM 集成到复杂应用程序中时尤其有价值。

与其他提示工程师共同实验

如果你处于必须想出一个好提示的情况,你可能想找多个人来尝试。当每个人都遵循最佳实践(如本章所列)时,你会看到所有不同提示尝试之间的性能差异。

CoT 最佳实践

对于 CoT 提示,将答案放在推理之后是必需的,因为推理的生成会改变模型在预测最终答案时获得的令牌。

使用 CoT 和自我一致性,你需要能够从你的提示中提取最终答案,与推理分开。

对于 CoT 提示,将温度设置为0。

思维链提示基于贪婪解码,根据语言模型分配的最高概率预测序列中的下一个词。一般来说,当使用推理来得出最终答案时,可能只有一个正确的答案。因此,温度应始终设置为0。

记录各种提示尝试

最后一个技巧在本章前面已经提到过,但我们怎么强调它的重要性都不过分:详细记录你的提示尝试,这样你就可以随着时间的推移学习哪些做得好,哪些做得不好。

提示输出在不同模型、不同采样设置,甚至同一模型的不同版本之间都可能不同。此外,即使是对同一模型的相同提示,输出句子格式和措辞的微小差异也可能发生。(例如,如前所述,如果两个令牌具有相同的预测概率,平局可能会被随机打破。这可能会影响后续的预测令牌。)

我们建议创建一个以表21为模板的谷歌表格。这种方法的优点是,当你不可避免地需要重新审视你的提示工作时——无论是为了将来继续(你会惊讶于在短暂的休息后你会忘记多少),为了在不同版本的模型上测试提示性能,还是为了帮助调试未来的错误——你都有一个完整的记录。

除了这个表中的字段,跟踪提示的版本(迭代),一个用于捕获结果是“OK/NOT OK/SOMETIMES OK”的字段,以及一个用于捕获反馈的字段也很有帮助。如果你足够幸运地使用 Vertex AI Studio,请保存你的提示(使用与你的文档中列出的相同的名称和版本)并跟踪指向已保存提示的超链接。这样,你总是只需点击一下即可重新运行你的提示。

当在一个检索增强生成系统上工作时,你还应该捕获影响插入到提示中的内容的 RAG 系统的特定方面,包括查询、块设置、块输出和其他信息。

一旦你觉得提示接近完美,就把它带到你的项目代码库中。并且在代码库中,将提示保存在与代码分开的文件中,这样更容易维护。最后,理想情况下,你的提示是一个可操作化系统的一部分,作为一名提示工程师,你应该依靠自动化的测试和评估程序来了解你的提示对任务的泛化能力如何。

提示工程是一个迭代的过程。制作和测试不同的提示,分析并记录结果。根据模型的性能改进你的提示。不断实验,直到你达到期望的输出。当你更改模型或模型配置时,回头继续用以前使用过的提示进行实验。

名称[你的提示的名称和版本]
目标[对此尝试目标的一句话解释]
模型[所用模型的名称和版本]
温度[0 - 1 之间的值]令牌限制[数字]
Top-K[数字]Top-P[数字]
提示[写下完整的提示]
输出[写下输出或多个输出]

表21. 记录提示的模板


关于多模态提示?

为代码进行提示仍然使用相同常规的大型语言模型。多模态提示是一个独立的关注点,它指的是一种技术,你使用多种输入格式来引导大型语言模型,而不仅仅依赖于文本。这可以包括文本、图像、音频、代码甚至其他格式的组合,具体取决于模型的能力和手头的任务。

最佳实践

找到正确的提示需要不断尝试。Vertex AI 中的 Language Studio 是一个完美的场所,可以让你尝试各种提示,并能够针对各种模型进行测试。

使用以下最佳实践,成为提示工程的专家。

提供示例

最重要的最佳实践是在提示中提供(单样本/少样本)示例。这是非常有效的,因为它充当了一个强大的教学工具。这些示例展示了期望的输出或类似的响应,允许模型从中学习并相应地调整其自身的生成。这就像给模型一个参考点或目标来瞄准,从而提高其响应的准确性、风格和语调,以更好地匹配你的期望。

设计力求简洁

提示对于你和模型都应该是简洁、清晰和易于理解的。根据经验,如果它对你来说已经很混乱,那么它很可能对模型来说也很混乱。尽量不要使用复杂的语言,也不要提供不必要的信息。

示例:

修改前:

我现在正在纽约旅游,我想了解一些很棒的地点。我和两个3岁的孩子在一起。我们假期应该去哪里?

重写后:

扮演一名游客的旅行指南。描述在纽约曼哈顿与一个3岁孩子一起参观的好地方。

尝试使用描述动作的动词。这里有一组示例: 扮演、分析、分类、归类、对比、比较、创建、描述、定义、评估、提取、查找、生成、识别、列出、测量、组织、解析、挑选、预测、提供、排名、推荐、返回、检索、重写、选择、显示、排序、总结、翻译、编写。

明确具体输出

对期望的输出要具体。一个简洁的指令可能无法充分引导 LLM,或者可能过于笼统。在提示中提供具体细节(通过系统或上下文提示)可以帮助模型专注于相关内容,从而提高整体准确性。

示例:

这样做:

生成一篇关于前5名视频游戏机的3段博客文章。博客文章应该内容丰富、引人入- and engaging, and it should be written in a conversational style.

不要这样做:

生成一篇关于视频游戏机的博客文章。

指令优于约束

在提示中,指令和约束都用于引导 LLM 的输出。

  • 指令就期望的响应格式、风格或内容提供明确的指示。它指导模型应该做什么或产生什么。
  • 约束是对响应的一组限制或边界。它限制了模型不应该做什么或应该避免什么。

越来越多的研究表明,在提示中专注于积极的指令可能比严重依赖约束更有效。这种方法与人类更喜欢积极指令而非一长串“不要做什么”列表的方式相一致。

指令直接传达了期望的结果,而约束可能会让模型猜测什么是允许的。它在定义的边界内给予灵活性并鼓励创造力,而约束可能会限制模型的潜力。此外,一长串约束可能会相互冲突。

约束仍然有价值,但在某些情况下。例如,为了防止模型生成有害或有偏见的内容,或者当需要严格的输出格式或风格时。

如果可能,请使用积极的指令:与其告诉模型不该做什么,不如告诉它该做什么。这可以避免混淆并提高输出的准确性。

这样做:

生成一篇关于前5名视频游戏机的1段博客文章。只讨论游戏机、制造它的公司、年份和总销量。

不要这样做:

生成一篇关于前5名视频游戏机的1段博客文章。不要列出视频游戏名称。

作为最佳实践,首先优先考虑指令,清楚地说明你希望模型做什么,并且只在必要时为了安全、清晰或特定要求而使用约束。进行实验和迭代,以测试指令和约束的不同组合,找出最适合你特定任务的方法,并记录下来。

控制最大令牌长度

要控制生成的 LLM 响应的长度,你可以在配置中设置最大令牌限制,或者在你的提示中明确请求特定长度。例如:

“用一条推文长度的消息解释量子物理学。”

在提示中使用变量

为了重用提示并使其更具动态性,请在提示中使用变量,这些变量可以针对不同的输入进行更改。例如,如表20所示,一个提供关于城市事实的提示。与其在提示中硬编码城市名称,不如使用一个变量。变量可以通过允许你避免重复自己来节省你的时间和精力。如果你需要在多个提示中使用相同的信息,你可以将其存储在一个变量中,然后在每个提示中引用该变量。当将提示集成到你自己的应用程序中时,这非常有意义。

提示变量
{city} = "Amsterdam"

提示
你是一个旅行指南。告诉我一个关于这个城市的事实:{city}
输出阿姆斯特丹是一个美丽的城市,充满了运河、桥梁和狭窄的街道。它因其丰富的历史、文化和夜生活而成为一个绝佳的旅游胜地。

表20. 在提示中使用变量

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

不同的模型、模型配置、提示格式、措辞和提交可能会产生不同的结果。因此,尝试提示的属性,如风格、措辞和提示类型(零样本、少样本、系统提示)是很重要的。

例如,一个旨在生成关于革命性视频游戏机 Sega Dreamcast 文本的提示,可以被表述为一个问题、一个陈述或一个指令,从而导致不同的输出:

  • 问题: Sega Dreamcast 是什么,为什么它如此具有革命性?
  • 陈述: Sega Dreamcast 是世嘉于1999年发布的第六代视频游戏机。它...
  • 指令: 写一个段落,描述 Sega Dreamcast 游戏机并解释为什么它如此具有革命性。

对于分类任务的少样本提示,混合不同类别

一般来说,你的少样本示例的顺序应该不会有太大影响。然而,在进行分类任务时,请确保你在少样本示例中混合了可能的响应类别。这是因为你可能否则会过度拟合于示例的特定顺序。通过混合可能的响应类别,你可以确保模型正在学习识别每个类别的关键特征,而不仅仅是记住示例的顺序。这将导致在未见数据上更稳健和可泛化的性能。

一个好的经验法则是从6个少样本示例开始,然后从那里开始测试准确性。

适应模型更新

对你来说,了解模型架构的变化、添加的数据和功能是很重要的。尝试更新的模型版本,并调整你的提示以更好地利用新的模型功能。像 Vertex AI Studio 这样的工具非常适合存储、测试和记录你的提示的各种版本。

尝试不同的输出格式

除了提示输入格式,考虑尝试输出格式。对于非创造性任务,如提取、选择、解析、排序、排名或分类数据,尝试让你的输出以结构化格式(如 JSON 或 XML)返回。

从提取数据的提示中返回 JSON 对象有一些好处。在实际应用中,我不需要手动创建这种 JSON 格式,我已经可以按排序顺序返回数据(在处理日期时间对象时非常方便),但最重要的是,通过提示 JSON 格式,它迫使模型创建一个结构并限制幻觉。

总而言之,使用 JSON 作为你的输出的好处:

  • 始终以相同的风格返回
  • 专注于你想要接收的数据
  • 减少幻觉的机会
  • 使其具有关系感知能力
  • 你可以获得数据类型
  • 你可以对其进行排序

少样本提示部分的表4显示了一个关于如何返回结构化输出的示例。

JSON 修复

虽然以 JSON 格式返回数据具有许多优势,但它并非没有缺点。JSON 的结构化特性,虽然对于解析和在应用程序中使用很有利,但比纯文本需要更多的令牌,导致处理时间增加和成本更高。此外,JSON 的冗长很容易消耗整个输出窗口,当由于令牌限制而突然切断生成时,这尤其成问题。这种截断通常会导致无效的 JSON,缺少关键的闭合大括号或方括号,从而使输出无法使用。幸运的是,像 json-repair 库(可在 PyPI 上获得)这样的工具在这些情况下可能非常宝贵。这个库会智能地尝试自动修复不完整或格式错误的 JSON 对象,使其成为处理 LLM 生成的 JSON,尤其是在处理潜在的截断问题时,一个至关重要的盟友。

使用模式 (Schemas)

正如我们在这篇论文中多次看到的,使用结构化 JSON 作为输出是一个很好的解决方案。但输入呢?虽然 JSON 非常适合构建 LLM 生成的输出,但它对于构建你提供的输入也可能非常有用。这就是 JSON 模式发挥作用的地方。JSON 模式定义了你的 JSON 输入的预期结构和数据类型。通过提供一个模式,你给 LLM 一个它应该期望的数据的清晰蓝图,帮助它将注意力集中在相关信息上,并减少误解输入的风险。此外,模式可以帮助在不同数据片段之间建立关系,甚至通过包含具有特定格式的日期或时间戳字段来使 LLM “时间感知”。

这里有一个简单的例子:

假设你想使用 LLM 为电子商务目录中的产品生成描述。与其只提供产品的自由格式文本描述,不如使用 JSON 模式来定义产品的属性:

{
  "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": "产品发布日期" }
  }
}

代码片段5. 结构化输出模式的定义

然后,你可以提供符合此模式的实际产品数据作为 JSON 对象:

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

代码片段6. 来自 LLM 的结构化输出

通过预处理你的数据,并且不仅仅提供完整的文档,而是同时提供模式和数据,你给了 LLM 对产品属性的清晰理解,包括其发布日期,使其更有可能生成准确和相关的描述。这种结构化的输入方法,引导 LLM 的注意力到相关字段,在处理大量数据或将 LLM 集成到复杂应用程序中时尤其有价值。

与其他提示工程师共同实验

如果你处于必须想出一个好提示的情况,你可能想找多个人来尝试。当每个人都遵循最佳实践(如本章所列)时,你会看到所有不同提示尝试之间的性能差异。

CoT 最佳实践

对于 CoT 提示,将答案放在推理之后是必需的,因为推理的生成会改变模型在预测最终答案时获得的令牌。

使用 CoT 和自我一致性,你需要能够从你的提示中提取最终答案,与推理分开。

对于 CoT 提示,将温度设置为0。

思维链提示基于贪婪解码,根据语言模型分配的最高概率预测序列中的下一个词。一般来说,当使用推理来得出最终答案时,可能只有一个正确的答案。因此,温度应始终设置为0。

记录各种提示尝试

最后一个技巧在本章前面已经提到过,但我们怎么强调它的重要性都不过分:详细记录你的提示尝试,这样你就可以随着时间的推移学习哪些做得好,哪些做得不好。

提示输出在不同模型、不同采样设置,甚至同一模型的不同版本之间都可能不同。此外,即使是对同一模型的相同提示,输出句子格式和措辞的微小差异也可能发生。(例如,如前所述,如果两个令牌具有相同的预测概率,平局可能会被随机打破。这可能会影响后续的预测令牌。)

我们建议创建一个以表21为模板的谷歌表格。这种方法的优点是,当你不可避免地需要重新审视你的提示工作时——无论是为了将来继续(你会惊讶于在短暂的休息后你会忘记多少),为了在不同版本的模型上测试提示性能,还是为了帮助调试未来的错误——你都有一个完整的记录。

除了这个表中的字段,跟踪提示的版本(迭代),一个用于捕获结果是“OK/NOT OK/SOMETIMES OK”的字段,以及一个用于捕获反馈的字段也很有帮助。如果你足够幸运地使用 Vertex AI Studio,请保存你的提示(使用与你的文档中列出的相同的名称和版本)并跟踪指向已保存提示的超链接。这样,你总是只需点击一下即可重新运行你的提示。

当在一个检索增强生成系统上工作时,你还应该捕获影响插入到提示中的内容的 RAG 系统的特定方面,包括查询、块设置、块输出和其他信息。

一旦你觉得提示接近完美,就把它带到你的项目代码库中。并且在代码库中,将提示保存在与代码分开的文件中,这样更容易维护。最后,理想情况下,你的提示是一个可操作化系统的一部分,作为一名提示工程师,你应该依靠自动化的测试和评估程序来了解你的提示对任务的泛化能力如何。

提示工程是一个迭代的过程。制作和测试不同的提示,分析并记录结果。根据模型的性能改进你的提示。不断实验,直到你达到期望的输出。当你更改模型或模型配置时,回头继续用以前使用过的提示进行实验。

名称[你的提示的名称和版本]
目标[对此尝试目标的一句话解释]
模型[所用模型的名称和版本]
温度[0 - 1 之间的值]令牌限制[数字]
Top-K[数字]Top-P[数字]
提示[写下完整的提示]
输出[写下输出或多个输出]

表21. 记录提示的模板


Endnotes

[1] Google, 2023, Gemini by Google. Available at: gemini.google.com.

[2] Google, 2024, Gemini for Google Workspace Prompt Guide. Available at: inthecloud.withgoogle.com/gemini-for-….

[3] Google Cloud, 2023, Introduction to Prompting. Available at: cloud.google.com/vertex-ai/g….

[4] Google Cloud, 2023, Text Model Request Body: Top-P & top-K sampling methods. Available at: cloud.google.com/vertex-ai/d….

[5] Wei, J., et al., 2023, Zero Shot - Fine Tuned language models are zero shot learners. Available at: arxiv.org/pdf/2109.01….

[6] Google Cloud, 2023, Google Cloud Model Garden. Available at: cloud.google.com/model-garde….

[7] Brown, T., et al., 2023, Few Shot - Language Models are Few Shot learners. Available at: arxiv.org/pdf/2005.14….

[8] Zheng, L., et al., 2023, Take a Step Back: Evoking Reasoning via Abstraction in Large Language Models. Available at: openreview.net/pdf?id=3bq3…

[9] Wei, J., et al., 2023, Chain of Thought Prompting. Available at: arxiv.org/pdf/2201.11….

[10] Google Cloud Platform, 2023, Chain of Thought and React. Available at: github.com/GoogleCloud….

[11] Wang, X., et al., 2023, Self Consistency Improves Chain of Thought reasoning in language models. Available at: arxiv.org/pdf/2203.11….

[12] Yao, S., et al., 2023, Tree of Thoughts: Deliberate Problem Solving with Large Language Models.

Available at: arxiv.org/pdf/2305.10….

[13] Yao, S., et al., 2023, ReAct: Synergizing Reasoning and Acting in Language Models. Available at: arxiv.org/pdf/2210.03….

[14] Google Cloud Platform, 2023, Advance Prompting: Chain of Thought and React. Available at: github.com/GoogleCloud…

on-vertex-ai/advanced_prompting_training/cot_react.ipynb.

[15] Zhou, C., et al., 2023, Automatic Prompt Engineering - Large Language Models are Human-Level Prompt Engineers. Available at: arxiv.org/pdf/2211.01….