程序员的提示工程实战手册
将 AI 编程助手转变为更可靠的开发伙伴
开发者们正日益依赖 AI 编程助手来加速日常工作流程。这些工具可以自动补全函数、建议错误修复,甚至能生成整个模块或最小可行产品(MVP)。然而,我们中许多人已经体会到,AI 输出的质量在很大程度上取决于你提供的提示词质量。换言之,提示工程已成为一项必备技能。一句措辞不当的请求可能只会得到无关或泛泛的答案,而一个精心设计的提示则能产出深思熟虑、准确甚至富有创意的代码解决方案。本文将从实践角度出发,探讨如何系统地为常见的开发任务构建有效的提示。
AI 结对程序员虽然强大,但并非无所不能——除了你告知或作为上下文包含的信息外,它们对你的具体项目或意图一无所知。你提供的信息越多,输出的效果就越好。我们将提炼出关键的提示模式、可复用的框架以及那些在开发者中引起共鸣且易于记忆的例子。你将看到好提示与坏提示的并排对比,以及它们得到的真实 AI 回应,并附有评论,以理解为何一个成功而另一个失败。这是一份快速入门的备忘单:
给 AI 代码助手的提示词技巧 - @addyosmani
| 技巧 | 提示词模板 | 目的 |
|---|---|---|
| 1. 角色提示 (Role Prompting) | "你是一位资深的 某种语言 开发者。请为了 某个目标 来审查这个函数。" | 模拟专家级的代码审查、调试或重构,获得更高质量的建议。 |
| 2. 明确上下文 (Explicit Context Setup) | "问题是:问题摘要。代码如下。它本应 预期行为,但现在却 实际行为。这是为什么?" | 清晰地框定问题,避免 AI 给出泛泛而谈、流于表面的回答。 |
| 3. 输入/输出示例 (Input/Output Examples) | "当输入为 某个输入 时,这个函数应该返回 预期输出。你能编写或修复这段代码吗?" | 通过具体示例来展示你的意图,引导 AI 智能体准确理解需求。 |
| 4. 迭代式链条 (Iterative Chaining) | "第一步,生成组件的骨架代码。下一步,我们来添加状态管理。最后,处理 API 调用。" | 将复杂的大任务分解成连续的小步骤,避免因提示过于庞大或含糊而导致 AI 混乱。 |
| 5. 模拟调试 (Debug with Simulation) | "请逐行过一遍这个函数。每个变量的值是什么?代码最有可能在哪里出错?" | 让 AI 智能体模拟代码的运行时行为,从而帮助你发现 那些隐藏很深的 bug。 |
| 6. 功能蓝图 (Feature Blueprinting) | "我正在构建 某个功能。需求是:几点核心需求。技术栈是:所用技术。请搭建出初始组件的脚手架,并解释你的选择。" | 借助 AI 主导的规划和脚手架能力,高效启动新功能的开发。 |
| 7. 重构指导 (Code Refactor Guidance) | "请重构这段代码以提升 某个目标,例如 可读性、性能、代码风格等。请用注释来解释你做了哪些更改。" | 确保 AI 的代码重构与你的核心目标对齐,而不是进行随意的、不必要的修改。 |
| 8. 寻求替代方案 (Ask for Alternatives) | "你能用函数式风格重写这段代码吗?如果用递归版本会是什么样子?" | 探索多种不同的实现路径,开阔思路,并丰富你的技术工具箱。 |
| 9. 小黄鸭调试法 (Rubber Ducking) | "我是这样理解这个函数功能的:你的解释。我有什么遗漏吗?这个解释能暴露出什么 bug 吗?" | 让 AI 扮演“小黄鸭”的角色,通过向它解释来挑战你自己的理解,并发现逻辑上的矛盾之处。 |
| 10. 约束锚定 (Constraint Anchoring) | "请避免使用 例如:递归,并严格遵守 例如:ES6 语法,不使用外部库。请为 例如:内存占用 进行优化。函数如下:" | 给 AI 设定明确的边界和限制,防止它“自由发挥”过度,或引入与项目不兼容的代码模式。 |
高效代码提示的基础
向 AI 编码工具提问,有点像与一个极其刻板、但有时知识渊博的合作者沟通。为了得到有用的结果,你需要清晰地设定场景,并引导 AI 明白你想要什么以及你希望它如何做。
以下是贯穿本手册所有示例的基础原则:
- 提供丰富的上下文。 始终假设 AI 对你的项目一无所知,除了你提供的信息。包括相关细节,如编程语言、框架和库,以及具体的函数或代码片段。如果出现错误,请提供确切的错误信息,并描述代码应该做什么。明确性和上下文是区分模糊建议和精确、可操作解决方案的关键。在实践中,这意味着你的提示可能包含一个简短的背景介绍,例如:“我有一个使用 Express 和 Mongoose 的 Node.js 函数,它应该通过 ID 获取用户,但抛出了一个 TypeError。这是代码和错误信息……”。你提供的背景越多,AI 需要猜测的就越少。
- 明确你的目标或问题。 模糊的查询导致模糊的答案。与其问“我的代码为什么不工作?”,不如精确指出你需要什么洞见。例如:“这个 JavaScript 函数返回了 undefined,而不是预期的结果。根据下面的代码,你能帮忙找出原因并修复吗?”这样的提问更有可能得到有用的回答。一个调试的提示公式是:“它预期会[预期行为],但在给定[示例输入]时,它却[当前行为]。错误在哪里?”。同样,如果你想要优化,请要求特定类型的优化(例如,“如何提高这个排序函数处理一万个项目时的运行时性能?”)。明确性引导 AI 的注意力。
- 分解复杂任务。 在实现新功能或处理多步骤问题时,不要把整个问题塞进一个巨大的提示里。将工作分成更小的部分并进行迭代通常更有效。例如,“首先,为产品列表页面生成一个 React 组件的骨架。接下来,我们添加状态管理。然后,我们再集成 API 调用。” 每个提示都建立在前一个的基础上。通常不建议一次性要求完成一个大型功能;相反,从一个高层目标开始,然后迭代地要求每个部分。这种方法不仅使 AI 的回应保持专注和可管理,也模仿了人类逐步构建解决方案的方式。
- 包含输入/输出示例或预期行为。 如果你能用一个例子来说明你想要什么,就这么做。例如,“给定数组 [3,1,4],这个函数应该返回 [1,3,4]。” 在提示中提供具体例子有助于 AI 理解你的意图并减少歧义。这类似于给一个初级开发者一个快速的测试用例——它澄清了需求。在提示工程术语中,这有时被称为“少样本提示(few-shot prompting)”,即你向 AI 展示一个模式让它遵循。即使只有一个正确行为的例子,也能显著地引导模型的响应。
- 利用角色或身份。 在许多病毒式传播的提示示例中,一个强大的技巧是要求 AI“扮演”某个特定角色或身份。这可以影响答案的风格和深度。例如,“扮演一位资深的 React 开发者,审查我的代码中可能存在的错误”或“你是一位 JavaScript 性能专家。优化以下函数。” 通过设定一个角色,你让助手预先进入了相关的状态——无论是成为一个严格的代码审查员,一个帮助初级开发者的老师,还是一个寻找漏洞的安全分析师。社区分享的提示已经证明了这种方法的成功,例如*“扮演一个 JavaScript 错误处理器,帮我调试这个函数。API 调用的数据没有正确渲染。”*。在我们自己的使用中, 我们仍然需要提供代码和问题细节,但角色扮演提示可以产生更结构化和专家级的指导。
- 迭代并优化对话。 提示工程是一个互动过程,而非一蹴而就。开发者通常需要审查 AI 的第一个回答,然后提出后续问题或进行修正。如果解决方案不完全正确,你可能会说,“那个解决方案使用了递归,但我更喜欢迭代的方法——你能不用递归再试一次吗?” 或者,“很好,现在你能改进一下变量名并添加注释吗?” AI 会记住聊天会话的上下文,所以你可以逐步引导它达到期望的结果。关键在于将 AI 视为一个你可以指导的伙伴——追求进步而非第一次就完美。
- 保持代码的清晰和一致性。 这最后一个原则有点间接,但对于处理你代码上下文的工具来说非常重要。即使在 AI 介入之前,也要编写清晰、结构良好的代码和注释。有意义的函数和变量名、一致的格式以及文档字符串不仅使你的代码对人类来说更容易理解,也为 AI 提供了更强的线索来了解你在做什么。如果你展示了一种一致的模式或风格,AI 将会延续它。把这些工具当作极其专注的初级开发者——它们会从你的代码和注释中获取每一个线索。
实现新功能的提示模式
AI 代码助手最令人兴奋的用途之一是帮助你从头开始编写新代码或将新功能集成到现有代码库中。这可以是从为 React 组件生成样板代码到在 Express 应用中编写新的 API 端点。这里的挑战通常是这些任务是开放式的——实现一个功能有很多种方法。用于代码生成的提示工程旨在引导 AI 生成符合你需求和风格的代码。以下是实现这一目标的一些策略:
1. 从高层指令开始,然后逐步深入。 首先用通俗的语言概述你想要构建什么,可能将其分解成更小的任务(类似于我们之前关于分解复杂任务的建议)。例如,假设你想为一个现有的 Web 应用添加一个搜索栏功能。你可能首先提示:“概述一个计划,在我的 React 应用中添加一个搜索功能,该功能可以按名称过滤产品列表。产品是从一个 API 获取的。”
AI 可能会给你一个分步计划:“1. 添加一个用于搜索查询的输入字段。2. 添加状态来保存查询。3. 根据查询过滤产品列表。4. 确保搜索不区分大小写,等等。” 一旦你有了这个计划(你可以在 AI 的帮助下对其进行完善),你就可以用集中的提示来处理每个要点。
例如:“好的,实现第 1 步:创建一个 SearchBar 组件,带有一个可以更新 searchQuery 状态的输入框。” 之后,“实现第 3 步:给定 searchQuery 和一个产品数组,过滤产品(对名称进行不区分大小写的匹配)。” 通过分解功能,你确保了每个提示都是具体的,并且响应是可管理的。这也模仿了迭代开发——你可以在构建每个部分时对其进行测试。
2. 提供相关上下文或参考代码。 如果你要向现有项目添加功能,向 AI 展示该项目中类似功能是如何完成的会非常有帮助。例如,如果你已经有一个与你想要的组件相似的组件,你可以说:“这是一个现有的 UserList 组件(代码…)。现在创建一个类似的 ProductList 组件,但要包括一个搜索栏。”
AI 将会看到这些模式(也许你使用了某些库或风格约定)并应用它们。在你的提示中打开相关文件或引用它们,可以提供上下文,从而产生更具项目针对性和一致性的代码建议。另一个技巧是:如果你的项目使用特定的编码风格或架构(比如使用 Redux 进行状态管理或某个 CSS 框架),请提及它。“我们使用 Redux 进行状态管理——将搜索状态集成到 Redux store 中。”
一个训练有素的模型随后会生成与 Redux 模式等一致的代码。从本质上讲,你是在向 AI 传授你项目的环境,以便它能量身定制输出。一些助手甚至可以利用你的整个仓库作为上下文来借鉴;如果使用这些工具,请确保将其指向你仓库中类似的模块或文档。
- 如果开始一个新项目但你有偏好的方法,你也可以提及:“我想用函数式编程风格来实现这个(没有外部状态,使用数组方法)。” 或者,“确保遵循 MVC 模式,将逻辑放在控制器中,而不是视图中。” 这些都是高级工程师可能会提醒初级工程师的细节,而在这里你就是告诉 AI 的高级工程师。
3. 使用注释和 TODO 作为内联提示。 当直接在带有 Copilot 的 IDE 中工作时,一个有效的工作流程是编写一条描述你需要的下一块代码的注释,然后让 AI 自动补全它。例如,在 Node.js 后端,你可能会写:// TODO: 验证请求负载(确保提供了 name 和 email),然后开始下一行。Copilot 通常会捕捉到意图并生成执行该验证的代码块。这是因为你的注释实际上是一个自然语言提示。然而,如果 AI 误解了,要准备好编辑生成的代码——一如既往,验证其正确性。
4. 提供预期输入/输出或用法的示例。 与我们之前讨论的类似,如果你要求 AI 实现一个新函数,请包含一个关于它将如何被使用的快速示例或一个简单的测试用 例。例如:“用 JavaScript 实现一个函数 formatPrice(amount),它接收一个数字(如 2.5)并返回一个格式化为美元的字符串(如 2.50)。例如,formatPrice(2.5) 应该返回 '2.50'。”
通过给出那个例子,你限制了 AI 生成与之一致的函数。没有这个例子,AI 可能会假设其他一些格式或货币。差异可能很微妙但很重要。另一个在 Web 上下文中的例子:“实现一个记录请求的 Express 中间件。例如,对 /users 的 GET 请求应该在控制台记录 ‘GET /users’。” 这清楚地表明了输出应该是什么样子。在提示中包含预期行为就像一个 AI 会尝试满足的测试。
5. 当结果不符合你的期望时,用更多细节或约束重写提示。 生成新功能的第一次尝试没有成功是很常见的。也许代码可以运行但不够地道,或者它遗漏了一个要求。不要感到沮丧,把 AI 当作一个提交了初稿的初级开发者——现在你需要给出反馈。例如,“这个解决方案能用,但我更希望你使用内置的数组 filter 方法而不是 for 循环。” 或者,“你能将生成的组件重构为使用 React Hooks 进行状态管理而不是类组件吗?我们的代码库都是函数式组件。” 你还可以添加新的约束:“另外,确保函数在 O(n) 或更好的时间内运行,因为 n 可能很大。” 这种迭代式提示非常强大。一个真实世界的场景:一个开发者要求一个大语言模型生成代码来用 JS canvas 库画一个冰淇淋甜筒,但它一直给出不相关的输出,直到他们用更多具体细节和上下文来完善提示。教训是,不要一次尝试就放弃。找出提示中缺少或被误解了什么,并加以澄清。这就是提示工程的精髓——每一次微调都能引导模型更接近你的设想。