< BACK 生产代码提示词工程:沉痛教训 -- 线条艺术插图

生产代码的提示工程:惨痛的教训

星期四晚上11点43分,我盯着GPT-4生成的340行React代码。代码很干净。注释完善。但在生产环境中完全崩溃。这个自定义hook以一种导致静默重渲染循环的方式管理状态,这种循环不会抛出错误,只会悄悄地破坏性能,直到客户在周五早上打电话问你为什么他们的结账页面需要九秒钟才能加载。

那晚教我的关于提示工程的东西,比任何 YouTube 教程或 Twitter 帖子都多。从那以后我经历过很多这样的夜晚。

我在web领域已经开发了九年。在Seahawk Media,我们已经交付了超过12000个站点,WordPress、无头架构、定制React应用、处理大交易量的WooCommerce商店。AI编码助手在2023年初才真正进入我的工作流,我在认为它们是奇迹和想砸掉笔记本电脑之间摇摆。

这是我实际学到的。付出了惨痛代价。

---

模型不了解你的代码库。你必须告诉它。

这听起来很明显。但在实际操作中并非如此。

我看到开发者犯的最大错误,包括我最初六个月犯的错误,就是把LLM当成已经读过你所有代码的资深工程师。你问"写一个处理用户认证的函数",它会在真空环境中写出技术上正确的代码。但你的项目用的是Supabase,不是Firebase。你的令牌存在httpOnly cookies中,不是localStorage。你的错误格式是{ status, message, data },而不是模型默认的任何格式。

模型没有错。它只是不了解你。

每一次都要给它一个项目前言

现在我每次进行有意义的编码会话时,都会从我称之为"上下文块"的东西开始。写起来大约需要 90 秒。看起来大概是这样:

  • 技术栈:Next.js 14(App Router)、TypeScript、Supabase、Tailwind CSS 3.4
  • 状态管理:Zustand,完全不用 Redux
  • 身份验证:Supabase Auth,通过中间件的 httpOnly cookies
  • 错误格式:{ success: boolean, error?: string, data?: unknown }
  • 样式约定:优先级优先,除非绝对必要,否则不使用自定义 CSS 文件

在任何非平凡的请求之前粘贴这个。我在Cursor中通过在项目根目录保留_context.md文件来做这件事。两个快捷键就能粘贴。输出质量会明显提升,假设更少,需要我撤销的东西也更少。

---

具体性就是一切

回到 2022 年,在我还没有大量使用 AI 的时候,一个客户给我一份简报,内容就是两句话:"为我们构建一个预订系统。要做好。"我们花了三周时间来回讨论范围。那次经历给了我深刻印象,直接影响了我现在如何编写提示词。

模糊的提示 → 模糊的代码。每次都是这样。

"写一个获取订单的函数"能给你一些东西。"写一个名为 fetchOrdersByUser 的 TypeScript 异步函数,接受 userId: string 参数,查询 Supabase 中的 orders 表,其中 user_id 匹配且 status 不是 cancelled,按 created_at 降序排列结果,返回 Order[] 或抛出类型化错误"会给你一些你真正能发布的东西。

区别不在于模型的能力。在于提示词的具体性。

代码提示词中应该包含什么

  1. 函数名称和签名,不要让模型凭空编造命名约定
  2. 输入类型和输出类型,如果相关的话包括TypeScript泛型
  3. 数据源,哪个表、哪个API端点、哪个缓存层
  4. 你已经知道的边界情况,比如"处理数组为空的情况"
  5. 不要这样做:"不要在这里使用 useEffect,改用服务器操作"

最后这一点比人们意识到的更重要。告诉模型要避免什么能节省大量时间。我已经开始为每个项目记录一个小的"反模式"清单,比如"没有用户交互就不要用客户端组件",然后在该项目的提示词中包含相关的行。

---

链接你的提示。不要一次性要求所有内容。

Seahawk 在 2023 年末有一个金融科技客户,我不能说是谁,我们当时在构建一个多步骤 KYC 流程。相当复杂的东西。文档上传、活体检测集成、状态轮询。我早期犯了个错误,让 GPT-4 "构建完整的 KYC 流程组件"。它生成了 600 行看起来很厉害但实际是垃圾的代码。逻辑混乱、职责混杂、UI 状态和业务逻辑之间没有真正的分离。

所以我放弃了,重新开始用链的方式。

第一个提示:"为 4 步 KYC 流程设计状态机。步骤:身份、文档上传、活体检测、审查。只给我状态类型和转换,不需要 UI。"

第二个提示:"给定这个状态机[粘贴],编写Zustand store。"

第三个提示:"给定这个store[粘贴],编写StepIdentity组件。就这一步。"

链式方法的输出是可用的。不完美,我还是重写了大约 30%,但可用。单体方法什么都没给我。

Anthropic自己关于提示的指导讲述了将复杂任务分解为子任务,老实说,这正好与我通过反复试验发现的相吻合。在你破坏代码库之前,先把问题分解。

---

让它自我争论

这是我完全意外发现的。我在审查一个生成的实用函数时,没有直接运行它,而是添加了一个后续提示:"你刚才写的代码有哪些潜在的bug或边界情况?"

模型找到了三个它没有考虑到的问题。其中一个是真正的问题,异步循环中的竞态条件,在生产环境中调试会是噩梦。

现在我定期这样做。写代码,然后让它批评代码。然后让它修复批评。让模型审查自己的工作感觉有点荒谬,但它持续不断地表面化那些我只会在痛苦的调试会话后才能发现的问题。

你可以更进一步。得到一个有效的函数后,尝试:"重写这个代码以关注性能"或"这在高并发下会如何表现?"答案并不总是适用的,但大约40%的时间它们会浮出值得采取行动的东西。

---

"角色 + 约束"框架

有一个提示词模式我现在经常使用,我真希望在第一年就想到了。它是这样的:"你是一个[具体类型的工程师]。你的约束是[硬性规则]。现在[任务]。"

例子:"你是一名非常关心数据库查询效率的后端工程师。你的约束是,对于这次渲染,你不能获取超过所需的内容,不能过度获取。为管理员仪表板编写一个 Supabase 查询,返回订单计数、总收入和五个最近的订单。"

这个框架做了两件事。它将模型的"人设"与我实际需要的东西对齐。约束充当护栏,模型在生成时明确地根据它进行自我检查。

OpenAI 的提示词最佳实践描述了类似的想法,即给模型一个具有明确指令的人设。如果你还没读过值得一看,不过我得说约束部分在他们的文档中被强调不足。

比较那个框架化提示词的输出和"为管理员仪表板写一个 Supabase 查询"的输出。天差地别。真的。

---

何时停止提示词编写,直接写代码

这是没人想公开说的部分。

AI编码工具在以下方面表现出色:样板代码、CRUD操作、工具函数、为已有代码编写测试、格式转换(JSON schema转TypeScript类型、SQL转Supabase查询等),以及需要大幅修改的初稿。

它们的真正弱点是:理解应用的真实架构、判断哪种权衡对你的具体规模最重要、编写涉及复杂有状态交互的代码(需要大量指导)、以及规范本身就模糊不清的场景。

我现在有个个人规则:如果我为了让一段代码正常运行而发送超过四个后续提示,我就关闭对话,自己写。提示词调试的时间成本可能超过直接编写的时间成本,尤其是对于大约50行以下的代码。

Stack Overflow 开发者调查 2024 发现 76% 的开发者正在使用或计划使用 AI 工具,但同样的数据显示对准确性的信任相对较低。使用和信任之间的这种差距正是好的提示词工程存在的地方。

---

像版本化代码一样版本化你的提示词

去年我开始在需要重要AI辅助的项目中创建prompts/文件夹。Markdown文件。每个主要功能区域一个。当一个提示词产生特别好的输出时,我会保存它。当我找到更好的版本时,我会更新这个文件。

听起来很强迫。仅在上一个大项目就为我节省了大约六个小时,一个为从 Shopify 迁移的零售商构建的无头 WooCommerce。我在四个不同的组件中重用了一个产品查询提示词(有小的编辑),而不是从零开始重新工程化上下文。

使用 Git 追踪它。说真的。如果你把 prompt 当作制品而不是一次性输入来对待,prompt 的质量是可复现的。LangChain 的 prompt 模板在框架层面形式化了这个想法,但你不需要任何框架,对大多数代理工作流来说,一个 Markdown 文件夹就足够了。

---

常见问题

提示工程到底是真正的可转移技能,还是只适用于特定模型?

大多数可转移。核心原则——特异性、上下文设置、链接、批评循环——适用于 GPT-4、Claude 3.5 Sonnet、Gemini 或任何未来的模型。语法略有不同,某些模型对特定框架的响应更好,但底层逻辑成立。我发现 Claude 对明确的约束反应良好;GPT-4 对示例反应良好。细微差异,相同的基础。

你在代码审查中如何处理 AI 生成的代码?

跟任何其他代码一样。如果它要进入生产环境,它就要被审查。句号。我已经停止在 Seahawk 的 PR 中标记"这是 AI 生成的",因为它成了一个误导,审查者会以不同的方式审查它,有时不公平,有时不够严格。代码本身要么成立,要么不成立。我会标记的是:任何逻辑不明显且我没有添加内联注释解释推理的部分。

你用系统提示还是只用聊天提示?

两者都用。在 Cursor 中,我依靠 .cursorrules 文件来存放持久的项目级指令,那些我否则每次都要粘贴的东西。对于 ChatGPT 或 Claude 网页 UI 中的一次性任务,全部在聊天中。.cursorrules 方式有意义地减少了重复,模型在长会话中保持更一致。

你对 AI 替代开发者的真实看法是什么?

它在替代某些任务,不是开发者。判断呼吁、构建什么、如何架构、哪个权衡适合这个客户的实际情况,这些离自动化还远得很。如果有的话,被挤压的开发者是那些纯粹执行导向的,不是设计或架构导向的。磨练判断层。那是防守得住的部分。

---

在网络上建造东西九年了,一个根本的教训不断重复:你的输出质量取决于你的输入质量。当时是客户简报,现在是提示词。

这个模型是一个快速的、偶尔聪慧、频繁过度自信的初级开发者。相应地管理它。

相关阅读:使用 Next.js 与 Supabase 构建实时拍卖网站、2026年自定义软件成本预估:实用计算器,以及定制网络开发。

< BACK