Suxiong

Prompt 工程实践总结

工程师越来越依赖 AI 来加速我们的日常工作流程。这些 AI 工具可以自动补全、修复 bug,甚至生成整个模块或 MVP。然而 AI 输出的质量在很大程度上取决于你提供的 prompt 质量。换句话说,prompt engineering 已经成为了一项必备技能。本文从实用角度系统性地探讨如何为常见开发任务制作有效的 prompt。

工程师越来越依赖 AI 来加速我们的日常工作流程。这些 AI 工具可以自动补全、修复 bug,甚至生成整个模块或 MVP。然而 AI 输出的质量在很大程度上取决于你提供的 prompt 质量。换句话说,prompt engineering 已经成为了一项必备技能。本文从实用角度系统性地探讨如何为常见开发任务制作有效的 prompt。

AI 并非无所不能,因为他们对你的具体项目没有任何预先了解,只能依靠你提供的上下文。你提供的信息越多,输出的质量就越好。我总结了 10 种常见的 prompt 模式,以下是一份速查表:

技巧 (Technique)提示词模板 (Prompt template)目的 (Purpose)
1. 角色设定 (Role Prompting)"你是一名资深 {编程语言} 开发人员。请为了 {目标} 审查此函数。"模拟专家级的代码审查、调试或重构。
2. 明确上下文设置 (Explicit Context Setup)"问题如下:{摘要}。代码在下方。它本该执行 {预期行为},但实际上却在执行 {实际行为}。为什么?"清晰地描述问题,以避免通用的、肤浅的回答。
3. 输入/输出示例 (Input/Output Examples)"当给定 {输入} 时,此函数应返回 {预期输出}。你能编写或修复这段代码吗?"通过示例展示意图来引导助手。
4. 迭代链式提问 (Iterative Chaining)"首先,生成组件的骨架。接下来,我们将添加状态。然后处理 API 调用。"将大任务分解为步骤,以避免提示词过于庞大或模糊。
5. 模拟调试 (Debug with Simulation)"逐行遍历该函数。变量的值分别是什么?哪里可能会出错?"让助手模拟运行时行为并暴露隐藏的 Bug。
6. 功能蓝图规划 (Feature Blueprinting)"我正在构建 {功能}。需求如下:{要点列表}。使用的技术栈:{技术栈}。请搭建初始组件的脚手架并解释你的选择。"以 AI 主导的规划和脚手架搭建来启动功能开发。
7. 代码重构指导 (Code Refactor Guidance)"重构此代码以改进 {目标},例如 {例如:可读性、性能、惯用风格}。请使用注释解释所做的更改。"让 AI 的重构符合你的目标,而不是随意的更改。
8. 寻求替代方案 (Ask for Alternatives)"你能用函数式风格重写这个吗?递归版本会是什么样子?"探索多种实现路径并扩展你的工具箱(技能库)。
9. 橡皮鸭调试法 (Rubber Ducking)"这是我认为此函数的作用:{你的解释}。我有遗漏什么吗?这是否揭示了任何 Bug?"让 AI 挑战你的理解并发现不一致之处。
10. 约束锚定 (Constraint Anchoring)"请避免使用 {例如:递归} 并坚持使用 {例如:ES6 语法,无外部库}。针对 {例如:内存} 进行优化。函数如下:"防止 AI 越界或引入不兼容的模式。

好用 Prompt 的核心原则

向 AI 编程工具发送 prompt,有点像与一位只懂字面意思、但有时又很博学的合作者进行沟通。为了获得有用的结果,你需要清晰地设定背景,并引导 AI 了解你想要什么以及希望如何实现。

提供丰富的上下文

始终假设 AI 除了你提供的信息外,对你的项目一无所知。请包含相关的细节,例如编程语言、框架和库,以及涉及的具体函数或代码片段。如果遇到错误,请提供准确的报错信息,并描述代码的预期功能。具体性和上下文决定了你得到的是模糊的建议,还是精确、可执行的解决方案。

在实践中,这意味着你的提示词可能包含简短的背景设定,例如:"我有一个使用 Express 和 Mongoose 的 Node.js 函数,本应通过 ID 获取用户,但抛出了 TypeError。代码和报错信息如下……"。你提供的铺垫越多,AI 需要猜测的内容就越少。

明确你的目标或问题

模糊的提问只会导致模糊的回答。不要问"为什么我的代码跑不通?"这类问题,而要精准指出你需要什么样的分析。例如:"这个 JavaScript 函数返回了 undefined,而不是预期的结果。基于下方的代码,你能帮我找出原因并修复它吗?"这样的提问更有可能获得有用的答案。

一个用于调试的提示词公式是:"当输入为 [示例输入] 时,预期行为是 [预期行为],但实际行为却是 [当前行为]。Bug 出在哪里?"同样,如果你需要优化代码,请指明具体的优化方向(例如:"我该如何提升这个排序函数处理 1 万条数据时的运行时性能?")。具体性能够引导 AI 的关注点。

拆解复杂任务

当实现新功能或解决多步骤问题时,不要把整个问题塞进一个巨大的提示词里。通常更有效的做法是将工作拆分成更小的块,并进行迭代。例如,"首先,生成一个产品列表页面的 React 组件骨架。接下来,我们将添加状态管理。然后,我们将集成 API 调用。" 每个提示词都建立在前一个的基础上。

通常不建议一次性要求生成整个庞大的功能;相反,应从一个高层目标开始,然后迭代地请求每个部分。这种方法不仅能让 AI 的回复保持专注和可控,而且也反映了人类增量构建解决方案的方式。

包含输入/输出示例或预期行为

如果你能用例子说明你想要什么,那就这么做。例如,"给定数组 [3,1,4],此函数应返回 [1,3,4]。" 在提示词中提供具体的例子有助于 AI 理解你的意图并减少歧义。这就好比给初级开发人员一个快速测试用例——它明确了需求。

在提示工程术语中,这有时被称为"少样本提示(few-shot prompting)",即你向 AI 展示一个可遵循的模式。哪怕只有一个正确行为的例子,也能显著引导模型的回复。

利用角色或人设

一个在许多广为流传的提示词案例中普及的强大技巧是,要求 AI "扮演(act as)"特定的角色或人设。这可以影响回答的风格和深度。例如,"扮演一名资深 React 开发人员,审查我的代码中潜在的 Bug"或"你是一名 JavaScript 性能专家。优化以下函数。"

通过设定角色,你引导(prime)助手采用相关的基调——无论是作为严格的代码审查员、面向初级开发者的热心老师,还是寻找漏洞的安全分析师。社区分享的提示词已经证明了这种方法的成功,例如"扮演 JavaScript 错误处理程序并帮我调试此函数。数据未能从 API 调用中正确渲染。" 在我们自己的使用中,我们仍然必须提供代码和问题细节,但角色扮演提示词可以产生更结构化和专家级的指导。

迭代并完善对话

提示工程是一个交互过程,而非一锤子买卖。开发者通常需要审视 AI 的第一次回答,然后提出后续问题或进行修正。如果解决方案不太对,你可以说:"那个方案使用了递归,但我更倾向于迭代方法——你能不用递归再试一次吗?"或者,"很好,现在你能改进变量命名并添加注释吗?"

AI 会记住聊天会话中的上下文,因此你可以逐步引导它达到预期的结果。关键在于将 AI 视为一个你可以指导的伙伴——在第一次尝试时,进展比完美更重要。

保持代码清晰和一致

最后一个原则虽然有点间接,但对于基于你的代码上下文工作的工具来说非常重要。即使在 AI 介入之前,也要编写干净、结构良好的代码和注释。有意义的函数和变量名、一致的格式以及文档字符串(docstrings),不仅让你的代码更容易被人类理解,也给了 AI 更强的线索来了解你在做什么。

如果你展示了一致的模式或风格,AI 将会延续它。把这些工具当作极其专注的初级开发人员——它们会从你的代码和注释中捕捉每一个暗示。

牢记这些核心原则,让我们深入探讨具体的场景。我们将从调试开始,这也许是最直接的用例:你有行为异常的代码,你希望 AI 帮你找出原因。

DEBUG 代码的 PROMPT 指南

以下是如何系统地编写 prompt,以便寻求帮助来发现并修复 Bug:

清晰地描述问题和症状

在提示词的开头,先描述出了什么问题以及代码本该实现什么功能。务必包含确切的报错信息或异常行为表现。

例如,不要只是笼统地说"我的代码跑不通",你可以这样写提示词:

"我有一个 JavaScript 函数,本该计算数组中数字的总和,但它返回的是 NaN(非数字),而不是实际的总和。代码如下:[附上代码]。对于像 [1,2,3] 这样的数字数组,它应该输出一个数字(总和),但我得到的却是 NaN。导致这个 Bug 的原因可能是什么?"

这条提示词明确了编程语言、预期行为、观察到的错误输出,并提供了代码上下文——这些都是至关重要的信息。提供结构化的背景信息(代码 + 报错 + 预期结果 + 已尝试的方法)能为 AI 提供一个坚实的分析起点。相比之下,像"为什么我的函数不起作用?"这样笼统的提问效果甚微——在缺乏背景信息的情况下,模型只能给出最宽泛的猜测。

针对棘手的 Bug,采用逐步或逐行分析的方法

对于更复杂的逻辑 Bug(即没有抛出明显的错误信息,但输出结果是错的),你可以提示 AI 逐步检查代码的执行过程。

例如:"请逐行检查这个函数,并追踪每一步 total 变量的值。它的累加不正确——逻辑哪里出错了?"

这是一个典型的"橡皮鸭调试"提示词——你本质上是在要求 AI 模拟人类使用打印语句(print)或调试器进行调试的过程。这类提示词通常能揭示一些微妙的问题,比如变量未重置或条件逻辑错误,因为 AI 会详细列出每一步的状态。如果你怀疑代码的某个特定部分,可以聚焦提问:"解释一下这里的 filter 调用在做什么,它是否可能排除了不该排除的项目。"让 AI 扮演解释者的角色,往往能在解释的过程中让 Bug 浮出水面。

"橡皮鸭" 这个概念最早出现在经典编程书籍《程序员修炼之道》中。书中讲述了一个程序员随身携带一只橡皮鸭,每当代码调试不通时,就会强迫自己向这只鸭子详细解释每一行代码。

尽可能提供最小可复现示例

有时你的实际代码库很大,但 Bug 可以通过一小段代码来演示。如果你能提取或简化出仍然能复现问题的代码,请这样做并将其提供给 AI。这不仅能让 AI 更容易集中注意力,还能迫使你理清问题(这本身通常就是一个有益的练习)。

例如,如果你在一个深层嵌套的函数调用中遇到 TypeError,试着用几行可以分享的代码来复现它。目标是用最少的代码隔离 Bug,假设哪里出了问题,进行测试,然后迭代。你可以通过这样说来让 AI 参与其中:"这是一个精简后的示例,它仍然会触发错误 [附上代码片段]。为什么会出现这个错误?"通过简化,你消除了干扰,帮助 AI 准确定位问题。(这个技巧反映了许多资深工程师的建议:如果你无法立即找到 Bug,就简化问题空间。如果你向 AI 展示一个更小的案例,它就能协助进行分析。)

提出聚焦的问题和追问

在提供了背景信息后,直接提出你的需求往往非常有效。例如:"导致这个问题的原因可能是什么,我该如何修复它?" 这能引导 AI 既进行诊断又提出解决方案。如果 AI 的初次回答不清楚或帮助有限,请毫不犹豫地进行追问。你可以说:"这个解释很有道理。你能演示一下如何修复代码吗?请提供修正后的代码。"

在聊天场景中,AI 拥有对话历史记录,因此它可以直接输出修改后的代码。如果你使用的是像 VS Code 中的 Copilot 或 Cursor 这样的行内工具(而非聊天界面),你可以在代码上方写一行注释,比如 // BUG: 返回 NaN,修复此函数,然后看它如何自动补全——但通常来说,交互式聊天能提供更透彻的解释。

另一种追问模式是:如果 AI 给出了修复方案但你不明白其中的原理,可以问:"你能解释一下为什么这个改动能解决问题吗?" 这样你不仅能为下次积累经验,还能再次核实 AI 的推理是否站得住脚。

重构代码的 PROMPT 指南

代码重构就在不改变功能的前提下,让代码更整洁、更高效或更符合语言习惯。但"更好"具体意味着什么。以下是如何编写代码重构提示词的方法:

明确陈述你的重构目标

光说"重构这段代码"太过于宽泛了。你是想提高可读性?降低复杂度?优化性能?还是想换一种编程范式或类库?AI 需要一个明确的目标。一个好的提示词会界定任务范围,例如:"重构以下函数以提高其可读性和可维护性(减少重复代码,使用更清晰的变量名)。" 或者"优化此算法的速度——在处理大量输入时它太慢了。"

通过阐明具体目标,你能帮助模型决定采用哪种转换方式。例如,告诉它你关注性能,可能会引导它使用更高效的排序算法或缓存机制;而关注可读性,则可能引导它将函数拆分为更小的模块或添加注释。如果你有多个目标,请一一列出。

Strapi 指南中的一个提示词模板甚至建议列举出具体问题:"我希望解决的问题:1) [性能问题],2) [代码重复],3) [使用了过时的 API]。" 这样一来,AI 就确切知道需要修复什么。请记住,它并不会天生就知道你认为代码中的哪些部分是问题——你必须告诉它。

提供必要的代码上下文

在进行重构时,你通常需要在提示词中包含需要改进的代码片段。重要的是要包含你想要重构的完整函数或部分,有时如果相关的话,还需要包含一些周围的上下文(比如该函数的用法或相关代码,这可能会影响重构的方式)。

此外,务必提及编程语言和框架,因为所谓的"地道(idiomatic)"代码在不同环境下差异很大,比如 Node.js 与 Deno 的风格不同,React 类组件与函数式组件也不同。

例如:"我有一个写成类的 React 组件。请把它重构为使用 Hooks 的函数式组件。" 这样 AI 就会应用典型的步骤(使用 useState, useEffect 等)。如果你只是说"重构这个 React 组件"而不阐明风格,AI 可能不知道你特指想要用 Hooks。

如果相关,请包含版本或环境细节。例如,"这是一个 Node.js v14 代码库"或"我们使用的是 ES6 模块"。这会影响 AI 是否使用特定的语法(如 import/export 对比 require),这是正确重构的一部分。如果你想确保它不引入不兼容的内容,请明确提及你的约束条件。

鼓励在提供代码的同时给出解释

从 AI 主导的重构中学习(并验证其正确性)的一个好方法是要求它解释所做的更改。

例如:"请建议代码的重构版本,并解释你所做的改进。" 这甚至被内置在我们引用的提示词模板中:"……建议重构后的代码并解释你的改动。"

当 AI 提供解释时,你可以评估它是否理解了代码并达到了你的目标。解释可能会说:"我将两个类似的循环合并为一个以减少重复,并使用字典来加快查找速度"等等。如果解释中有哪里听起来不对劲,那就是一个需要仔细检查代码的警示信号(red flag)。简而言之,利用 AI 的解释能力作为一种安全保障——这就像是让 AI 对它自己的重构工作进行了一次代码审查。

使用角色扮演来设定高标准

如前所述,要求 AI 扮演代码审查员或资深工程师的角色非常有效。对于重构,你可以说:"扮演一位经验丰富的 TypeScript 专家,重构这段代码以符合最佳实践和现代标准。"

这通常不仅能带来表面的修改,还能带来更有深度的改进,因为 AI 会努力符合"专家"的人设。一个来自提示词指南的流行案例是让 AI 扮演导师:"扮演一位指导初级开发者的经验丰富的 Python 开发人员。提供解释并编写文档字符串(docstrings)。重写代码以对其进行优化。"

那个案例的结果是,AI 使用了更高效的数据结构(集合 set 来去重),并为一个原本使用循环的函数提供了一行代码的解决方案。角色扮演不仅帮助它进行了重构,还帮助它解释了为什么新方法更好(在该案例中,使用 set 是众所周知的去重优化手段)。

实现新功能的 PROMPT 指南

这里的挑战通常在于,这些任务往往是开放式的——实现同一个功能有许多种方法。针对代码生成的提示词工程,其核心在于引导 AI 生成符合你需求和代码风格的代码。以下是实现这一目标的策略:

从高层指令入手,然后逐层深入

首先,用通俗易懂的语言概述你想构建的内容,最好将其拆解为更小的任务(这类似于我们之前提到的拆解复杂任务的建议)。

例如,假设你想在现有的 Web 应用中添加一个搜索栏功能。你可以首先这样编写提示词:

"请列出一个计划,在我的 React 应用中添加搜索功能,用于按名称过滤产品列表。产品数据是从 API 获取的。"

AI 可能会给你一个分步计划:

  1. 添加一个用于搜索查询的输入框。
  2. 添加状态(state)来保存查询内容。
  3. 根据查询内容过滤产品列表。
  4. 确保过滤不区分大小写,等等。

一旦你有了这个计划(你也可以在 AI 的帮助下完善它),就可以通过针对性的提示词来逐个攻克这些要点。

例如:

"好的,实现第一步:创建一个 SearchBar 组件,包含一个能更新 searchQuery 状态的输入框。"

接着是:

"实现第三步:给定 searchQuery 和一个产品数组,过滤产品(按名称进行不区分大小写的匹配)。"

通过拆分功能,你可以确保每个提示词都具体明确,且 AI 的回复易于管理。这也契合了迭代式开发的理念——你可以在构建过程中逐一测试每个部分。

提供相关的上下文或参考代码

如果你要在现有项目中添加功能,向 AI 展示该项目中类似功能的实现方式会有极大的帮助。例如,如果你已经有一个与你想要构建的组件类似的组件,你可以说:

"这是一个现有的 UserList 组件(附上代码……)。现在创建一个类似的 ProductList 组件,但要包含一个搜索栏。"

AI 会识别出这些模式(比如你使用的特定库或代码风格惯例)并加以应用。打开相关文件或在提示词中引用它们,能提供上下文,从而获得更符合项目具体情况且风格一致的代码建议。

另一个技巧是:如果你的项目使用特定的编码风格或架构(比如用 Redux 管理状态,或某种 CSS 框架),请提出来。

"我们使用 Redux 进行状态管理——请将搜索状态集成到 Redux store 中。"

训练有素的模型随后会生成符合 Redux 模式等的代码。本质上,你是在教 AI 了解你的项目环境,以便它能定制输出结果。有些助手甚至可以利用你的整个代码仓库作为上下文;如果使用这类工具,请确保将它指向仓库中类似的模块或文档。

如果是开始新项目但你有偏好的方法,你也可以提出来:

"我想用函数式编程风格来实现这个功能(无外部状态,使用数组方法)。"

或者:

"确保遵循 MVC 模式,将逻辑放在控制器中,而不是视图中。"

这些通常是资深工程师会提醒初级工程师的细节,而在这里,你就是那个指导 AI 的资深工程师。

使用注释和 TODO 作为行内提示词

当直接在 IDE 中使用 Copilot 工作时,一个有效的工作流是写一条注释来描述你需要的一段代码,然后让 AI 自动补全它。

例如,在 Node.js 后端中,你可以写:

// TODO: 验证请求负载(确保提供了姓名和电子邮件)

然后开始写下一行。Copilot 通常能捕捉到你的意图,并生成执行该验证的代码块。这之所以有效,是因为你的注释实际上就是一个自然语言提示词。不过,如果 AI 误解了意图,要做好编辑生成代码的准备——一如既往,务必验证其正确性。

提供预期的输入/输出或用法示例

这就好比我们之前讨论过的,如果你要求 AI 实现一个新函数,附带一个简短的使用示例或简单的测试用例会非常有帮助。

例如:"在 JavaScript 中实现一个 formatPrice(amount) 函数,它接收一个数字(如 2.5)并返回格式化为美元的字符串(如 $2.50)。例如,formatPrice(2.5) 应该返回 '$2.50'。"

通过提供这个示例,你限制了 AI 生成与之逻辑一致的函数。如果没有这个示例,AI 可能会假设其他的格式或货币单位。这种差异虽然细微,但可能至关重要。

在 Web 开发背景下的另一个例子:"实现一个 Express 中间件来记录请求。例如,对 /users 发起 GET 请求时,应该在控制台打印 'GET /users'。" 这清楚地表明了输出应该是什么样子。在提示词中包含预期行为,就像是设定了一个测试用例,AI 会努力去满足它。

当结果不符合预期时,增加细节或约束条件重写提示词

生成新功能的第一次尝试往往不会一步到位,这很常见。也许代码能运行,但写法不地道(not idiomatic),或者遗漏了某个需求。

与其感到沮丧,不如把 AI 当作一个交了初稿的初级开发者——现在你需要给出反馈。

例如:"这个方案可行,但我更希望你使用内置的数组 filter 方法,而不是 for 循环。" 或者,"你能把生成的组件重构为使用 React Hooks 的形式吗?不要用类组件,我们的代码库全是函数式组件。"

你也可以添加新的约束条件:"另外,确保函数在 O(n) 或更好的时间复杂度下运行,因为 n 的值可能很大。"

这种迭代式的提示非常强大。有一个真实案例:一位开发者要求大语言模型(LLM)生成代码,用 JS Canvas 库画一个冰淇淋筒,但模型一直输出不相关的内容,直到开发者用更具体的细节和上下文完善了提示词才成功。

经验教训是,不要尝试一次失败后就放弃。找出提示词中缺少了什么或哪里被误解了,并加以澄清。这就是提示词工程的精髓——每一次微调都能引导模型更接近你的构想。

常见的坏味道 PROMPT 并如何避免

模糊的提示词 (The Vague Prompt)

这是典型的"它跑不通,请修好它"或"写点能实现 X 功能的东西",但缺乏足够的细节。我们之前见过一个例子,问"为什么我的函数不起作用?"只能得到毫无用处的回答。模糊的提示词迫使 AI 去猜测上下文,结果往往是通用的建议或不相关的代码。

解决方法:增加上下文和细节。如果你发现自己提出的问题得到的回答像"神奇八号球"(Magic 8-ball,指模棱两可、碰运气的回答)一样(比如"你检查过 X 吗?"),请停下来,补充更多细节(报错信息、代码片段、预期结果与实际结果的对比等)重新组织你的问题。一个好的做法是读一遍你的提示词并自问:"这个问题是否适用于几十种不同的场景?"如果是,那就太模糊了。要把它写得足够具体,以至于它只能适用于你的场景。

过载的提示词 (The Overloaded Prompt)

这是相反的问题:要求 AI 一次性做太多事情。例如,"生成一个完整的 Node.js 应用,包含身份验证、React 前端和部署脚本。"或者即使在较小的范围内,"一次性修复这 5 个 Bug 并添加这 3 个功能。" AI 可能会尝试去做,但你很可能会得到一个混乱或不完整的结果,或者它可能会忽略请求中的某些部分。即使它处理了所有内容,回复也会很长,难以验证。

解决方法:拆分任务,分清轻重缓急。正如我们之前强调的,一次只做一件事。这使得发现错误变得更容易,并确保模型保持专注。如果你发现自己在指令中写了一大段话,里面包含多个"并且 (and)",请考虑将其拆分为单独的提示词或按顺序执行的步骤。

缺失问题 (Missing the Question)

有时用户会提供大量信息,但从未明确提出问题或说明他们需要什么。例如,扔出一大段代码片段,然后只说"这是我的代码"。这会让 AI 感到困惑——它不知道你想要什么。

务必包含一个清晰的请求,例如"找出上述代码中的所有 Bug"、"解释这段代码的作用"或"完成代码中的 TODO"。提示词应该有明确的目的。如果你只提供文本而没有问题或指令,AI 可能会做出错误的假设(比如总结代码而不是修复代码)。确保 AI 知道你为什么要给它看这段代码。即使只是简单地加上"这段代码有什么问题?"或"请继续实现这个函数",也能给它指明方向。

模糊的成功标准 (Vague Success Criteria)

这是一个微妙的问题——有时你可能要求优化或改进,但你没有定义"成功"是什么样子的。例如,"让这个函数更快。"以什么指标衡量更快?如果 AI 不知道你的性能约束,它可能会进行无关紧要的微优化,或者使用一种理论上更快但实际上差别不大的方法。或者"让代码更整洁"——"整洁"是主观的。我们之前的处理方式是明确陈述目标,如"减少重复"或"改进变量命名"等。

解决方法:量化或限定改进目标。例如,"优化此函数以在线性时间内运行(当前版本是二次方时间)"或"重构此代码以移除全局变量并改用类"。基本上,要明确你通过重构或新功能要解决什么问题。如果你把问题留得太开放,AI 可能会去解决一个你并不关心的问题。

忽略 AI 的澄清或输出 (Ignoring AI's Clarification or Output)

有时 AI 可能会回复一个澄清性的问题或假设。例如:"你是使用 React 类组件还是函数式组件?"或"我假设输入是一个字符串——请确认。"如果你忽略这些并只是重申你的请求,你就错过了一个改进提示词的机会。AI 正在发出信号,表明它需要更多信息。务必回答它的问题或完善你的提示词以包含这些细节。

此外,如果 AI 的输出明显偏离(比如它误解了问题),不要只是原封不动地重试相同的提示词。花点时间调整你的措辞。也许你的提示词中有一个模棱两可的短语或遗漏了某些关键内容。把它当作一次对话——如果人类误解了,你会换一种方式解释;对 AI 也要这样做。

风格多变或不一致 (Varying Style or Inconsistency)

如果你不断改变提问方式或一次性混合不同的格式,模型可能会感到困惑。例如,在指令中在第一人称和第三人称之间切换,或者以令人困惑的方式混合伪代码和实际代码。

尽量在单个提示词中保持一致的风格。如果你提供示例,请确保它们清晰地划分开来(使用 Markdown 的三重反引号标记代码,使用引号标记输入/输出示例等)。一致性有助于模型正确解析你的意图。另外,如果你有偏好的风格(比如 ES6 对比 ES5 语法),请始终如一地提及它,否则模型可能会在一个提示词中建议一种方式,而在稍后的提示词中建议另一种方式。

像"上面的代码"这样模糊的引用 (Vague references like "above code")

在使用聊天界面时,如果你说"上面的函数"或"之前的输出",请确保引用是清晰的。如果对话很长,你说"重构上面的代码",AI 可能会跟丢,或者选择了错误的代码片段进行重构。

更稳妥的做法是再次引用(粘贴)代码,或者具体指名你想要重构的函数。模型的注意力窗口是有限的,虽然许多大语言模型(LLM)可以参考对话的先前部分,但再次给予明确的上下文有助于避免混淆。如果距离上次展示代码已经过了一段时间(或隔了几条消息),这点尤为重要。

重写提示词的战术方法

最后,这是当事情出错时重写提示词的战术方法:

  1. 确定 AI 回复中缺失或错误的地方。它是否解决了一个错误的问题?它是否产生了报错或不合适的解决方案?例如,也许你要求用 TypeScript 解决,但它给了纯 JavaScript。或者当你明确想要迭代解法时,它写了一个递归解法。精准定位差异所在。

  2. 在新的提示词中添加或强调该要求。你可以说,"解决方案应该是 TypeScript,而不是 JavaScript。请包含类型注解。"或者,"我提到我想要一个迭代解法——请避免递归,改用循环。"有时,在提示词中字面意义地使用"注意:"或"重要:"这样的短语来强调关键约束很有帮助(模型没有情感,但它确实会将某些措辞视为重要性的指示)。例如:"重要:不要为此使用任何外部库。"或"注意:代码必须在浏览器中运行,所以不要使用 Node 特有的 API。"

  3. 如果需要,进一步拆解请求。如果 AI 在复杂的请求上反复失败,试着先要求一小部分。或者问一个可能澄清局面的问题:"你明白我说的 X 是什么意思吗?"模型可能会复述它认为你的意思,如果错了,你可以纠正它。这就是元提示 (meta-prompting)——讨论提示词本身——有时可以解决误解。

  4. 如果对话陷入僵局,考虑重新开始。有时经过多次尝试后,对话可能会进入一种混乱状态。开始一个新的会话(或暂时清除聊天历史),并根据之前的失败经验,用更完善的请求从头开始提示,这通常会有所帮助。模型不介意重复,而全新的上下文可以消除之前消息中积累的任何混淆。

通过意识到这些坏味道及其解决方案,你将能更快地在操作过程中调整提示词。针对开发者的提示词工程在很大程度上是一个迭代的、反馈驱动的过程。

结尾

提示词工程(Prompt Engineering)既是一门艺术,也是一门科学——它正迅速成为开发者的必备技能。通过编写清晰、富含上下文的提示词,你本质上是在教 AI 你需要什么,就像向同事解释问题一样。

Happy prompting, and happy coding!

On this page