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 可能会给你一个分步计划:
- 添加一个用于搜索查询的输入框。
- 添加状态(state)来保存查询内容。
- 根据查询内容过滤产品列表。
- 确保过滤不区分大小写,等等。
一旦你有了这个计划(你也可以在 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)可以参考对话的先前部分,但再次给予明确的上下文有助于避免混淆。如果距离上次展示代码已经过了一段时间(或隔了几条消息),这点尤为重要。
重写提示词的战术方法
最后,这是当事情出错时重写提示词的战术方法:
-
确定 AI 回复中缺失或错误的地方。它是否解决了一个错误的问题?它是否产生了报错或不合适的解决方案?例如,也许你要求用 TypeScript 解决,但它给了纯 JavaScript。或者当你明确想要迭代解法时,它写了一个递归解法。精准定位差异所在。
-
在新的提示词中添加或强调该要求。你可以说,"解决方案应该是 TypeScript,而不是 JavaScript。请包含类型注解。"或者,"我提到我想要一个迭代解法——请避免递归,改用循环。"有时,在提示词中字面意义地使用"注意:"或"重要:"这样的短语来强调关键约束很有帮助(模型没有情感,但它确实会将某些措辞视为重要性的指示)。例如:"重要:不要为此使用任何外部库。"或"注意:代码必须在浏览器中运行,所以不要使用 Node 特有的 API。"
-
如果需要,进一步拆解请求。如果 AI 在复杂的请求上反复失败,试着先要求一小部分。或者问一个可能澄清局面的问题:"你明白我说的 X 是什么意思吗?"模型可能会复述它认为你的意思,如果错了,你可以纠正它。这就是元提示 (meta-prompting)——讨论提示词本身——有时可以解决误解。
-
如果对话陷入僵局,考虑重新开始。有时经过多次尝试后,对话可能会进入一种混乱状态。开始一个新的会话(或暂时清除聊天历史),并根据之前的失败经验,用更完善的请求从头开始提示,这通常会有所帮助。模型不介意重复,而全新的上下文可以消除之前消息中积累的任何混淆。
通过意识到这些坏味道及其解决方案,你将能更快地在操作过程中调整提示词。针对开发者的提示词工程在很大程度上是一个迭代的、反馈驱动的过程。
结尾
提示词工程(Prompt Engineering)既是一门艺术,也是一门科学——它正迅速成为开发者的必备技能。通过编写清晰、富含上下文的提示词,你本质上是在教 AI 你需要什么,就像向同事解释问题一样。
Happy prompting, and happy coding!