周四晚上11点43分,我盯着340行由 GPT-4 满怀信心生成的 React 代码。代码干净。注释完善。在生产环境中彻底坏了。这个自定义 hook 以导致无声重新渲染循环的方式管理状态,这种循环不会抛出错误——它们只是悄悄地摧毁你的性能,直到周五早上一个客户打电话给你,问为什么他们的结账页面需要九秒钟才能加载。
那晚教我的关于提示工程的东西,比任何 YouTube 教程或 Twitter 帖子都多。从那以后我经历过很多这样的夜晚。
我在网络上开发了九年。在 Seahawk Media,我们已经发布了超过12000个站点——WordPress、无头构建、定制 React 应用、处理大额交易量的 WooCommerce 商店。AI 编码助手大约在2023年初正式进入我的工作流程,从那时起我一直在觉得它们是神奇的和想把笔记本电脑扔进泰晤士河之间摇摆不定。Seahawk Media we've shipped well over 12,000 sites — WordPress, headless builds, bespoke React apps, WooCommerce stores handling serious transaction volume. AI coding assistants entered my workflow properly around early 2023, and I've swung between thinking they're miraculous and wanting to throw my laptop into the Thames.
这是我实际学到的。付出了惨痛代价。
---
模型不了解你的代码库。你必须告诉它。
这听起来很明显。但在实际操作中并非如此。
我看到开发者犯的最大错误——包括我自己在最初的六个月里——就是把 LLM 当成已经读过你所有代码的资深工程师。你问"写一个处理用户身份验证的函数",它会写出在真空中技术上正确的东西。但你的项目用的是 Supabase,而不是 Firebase。你的令牌存在 httpOnly cookies 里,而不是 localStorage。你的错误格式是 { status, message, data },而不是模型默认的任何格式。{ status, message, data }, not whatever the model defaulted to.
模型没有错。它只是不了解你。
每一次都要给它一个项目前言
现在我每次进行有意义的编码会话时,都会从我称之为"上下文块"的东西开始。写起来大约需要 90 秒。看起来大概是这样:
- 技术栈:Next.js 14(App Router)、TypeScript、Supabase、Tailwind CSS 3.4
- 状态管理:Zustand,完全不用 Redux
- 身份验证:Supabase Auth,通过中间件的 httpOnly cookies
- 错误格式:{ success: boolean, error?: string, data?: unknown }
{ success: boolean, error?: string, data?: unknown } - 样式约定:优先级优先,除非绝对必要,否则不使用自定义 CSS 文件
在任何非平凡的请求之前粘贴它。我在 Cursor 中通过在项目根目录保留一个 _context.md 文件来做到这一点。两次按键就能粘贴。输出质量会明显提升——更少的假设,更少需要删除的东西。Cursor by keeping a _context.md file in the project root. Two keystrokes to paste. The output quality jumps noticeably — fewer assumptions, fewer things I have to rip out.
---
具体性就是一切
回到 2022 年,在我还没有大量使用 AI 的时候,一个客户给我一份简报,内容就是两句话:"为我们构建一个预订系统。要做好。"我们花了三周时间来回讨论范围。那次经历给了我深刻印象,直接影响了我现在如何编写提示词。
模糊的提示 → 模糊的代码。每次都是这样。
"写一个获取订单的函数"能给你一些东西。"写一个名为 fetchOrdersByUser 的 TypeScript 异步函数,接受 userId: string 参数,查询 Supabase 中的 orders 表,其中 user_id 匹配且 status 不是 cancelled,按 created_at 降序排列结果,返回 Order[] 或抛出类型化错误"会给你一些你真正能发布的东西。fetchOrdersByUser that accepts a userId: string, queries the orders table in Supabase where user_id matches and status is not cancelled, orders results by created_at descending, and returns Order[] or throws a typed error" will get you something you can actually ship.
区别不在于模型的能力。在于提示词的具体性。
代码提示词中应该包含什么
- 函数名和签名——不要让模型自己创造命名约定 — don't let the model invent naming conventions
- 输入类型和输出类型 — TypeScript 泛型(如相关) — TypeScript generics if relevant
- 数据源 — 哪个表、哪个 API 端点、哪个缓存层 — which table, which API endpoint, which cache layer
- 你已经知道的边界情况 — "处理数组为空的情况" — "handle the case where the array is empty"
- 不要做什么 — "不要为此使用 useEffect,使用服务器操作" — "don't use useEffect for this, use a server action"
最后这一点比人们意识到的更重要。告诉模型要避免什么可以节省大量时间。我已经开始为每个项目保留一个小的"反模式"笔记 — 比如"除非用户交互需要,否则不使用客户端组件" — 并在该项目的提示中包含相关的几行。
---
链接你的提示。不要一次性要求所有内容。
Seahawk 在 2023 年末有一个金融科技客户 — 我不会说是谁 — 我们当时在构建一个多步骤 KYC 流程。复杂的东西。文档上传、活体检测集成、状态轮询。我早期犯的错误是让 GPT-4 "构建完整的 KYC 流程组件"。它产生了 600 行看起来很漂亮的垃圾代码。逻辑混乱,关注点混杂,UI 状态和业务逻辑之间没有真正的分离。
所以我放弃了,重新开始用链的方式。
第一个提示:"为 4 步 KYC 流程设计状态机。步骤:身份、文档上传、活体检测、审查。只给我状态类型和转换,不需要 UI。""Design the state machine for a 4-step KYC flow. Steps: identity, document upload, liveness, review. Give me the state type and transitions only, no UI."
第二个提示:"给定这个状态机[粘贴],编写Zustand store。""Given this state machine [paste], write the Zustand store."
第三个提示:"给定这个store[粘贴],编写StepIdentity组件。就这一步。""Given this store [paste], write the StepIdentity component. Just this step."
链式方法的输出是可用的。不完美——我仍然重写了大约30%——但可用。单体方法没有给我任何东西。
Anthropic自己关于提示的指导讲述了将复杂任务分解为子任务,老实说,这正好与我通过反复试验发现的相吻合。在你破坏代码库之前,先把问题分解。 talks about breaking complex tasks into subtasks, and honestly, this aligns exactly with what I found through trial and error. Break the problem down before you break your codebase.
---
让它自我争论
这是我完全意外发现的。我在审查一个生成的实用函数时,没有直接运行它,而是添加了一个后续提示:"你刚才写的代码有哪些潜在的bug或边界情况?""What are the potential bugs or edge cases in the code you just wrote?"
模型找到了三个它没有考虑到的问题。其中一个是真正的问题——一个异步循环中的竞态条件,在生产环境中调试会很nightmare。
现在我经常这样做。编写代码,然后要求它批评代码。然后要求它修复批评。感觉有点荒谬——让模型审查自己的工作——但它一直会浮出痛苦的调试会话中才能发现的问题。
你可以更进一步。得到一个有效的函数后,尝试:"重写这个代码以关注性能"或"这在高并发下会如何表现?"答案并不总是适用的,但大约40%的时间它们会浮出值得采取行动的东西。"Rewrite this with a focus on performance" or "How would this behave under high concurrency?" The answers aren't always applicable, but about 40% of the time they surface something worth acting on.
---
"角色 + 约束"框架
有一个提示词模式我现在经常使用,我真希望在第一年就想到了。它是这样的:"你是一个[具体类型的工程师]。你的约束是[硬性规则]。现在[任务]。""You are a [specific type of engineer]. Your constraint is [hard rule]. Now [task]."
例如:"你是一个深度关注数据库查询效率的后端工程师。你的约束是不能获取超过这次渲染所需的数据 — 禁止过度获取。为管理员仪表板写一个 Supabase 查询,返回订单数、总收入和五个最近的订单。""You are a backend engineer who cares deeply about database query efficiency. Your constraint is that you cannot fetch more than what's needed for this render — no over-fetching. Write a Supabase query for the admin dashboard that returns order count, total revenue, and the five most recent orders."
这种框架做两件事。它将模型的"人设"与我实际需要的东西对齐。约束充当护栏 — 模型在生成内容时会明确对自己进行检查。
OpenAI 的提示词最佳实践描述了类似的想法,即给模型一个具有明确指令的人设。如果你还没读过值得一看,不过我得说约束部分在他们的文档中被强调不足。 describe a similar idea around giving the model a persona with explicit instructions. Worth reading if you haven't, though I'd say the constraint piece is underemphasised in their docs.
比较那个框架化提示词的输出和"为管理员仪表板写一个 Supabase 查询"的输出。天差地别。真的。
---
何时停止提示词编写,直接写代码
这是没人想公开说的部分。
AI编码工具在以下方面表现出色:样板代码、CRUD操作、工具函数、为已有代码编写测试、格式转换(JSON schema转TypeScript类型、SQL转Supabase查询等),以及需要大幅修改的初稿。
它们的真正弱点是:理解应用的真实架构、判断哪种权衡对你的具体规模最重要、编写涉及复杂有状态交互的代码(需要大量指导)、以及规范本身就模糊不清的场景。
我现在有个个人规则:如果我为了让一段代码正常运行而发送超过四个后续提示,我就关闭对话,自己写。提示词调试的时间成本可能超过直接编写的时间成本,尤其是对于大约50行以下的代码。
Stack Overflow开发者调查2024发现76%的开发者正在使用或计划使用AI工具——但同一份数据显示准确性信任度相对较低。使用量和信任度之间的这个差距,正是优秀提示词工程发挥作用的地方。Stack Overflow Developer Survey 2024 found that 76% of developers are using or planning to use AI tools — but the same data showed relatively low trust in accuracy. That gap between usage and trust is exactly where good prompt engineering lives.
---
像版本化代码一样版本化你的提示词
去年我开始在需要重要AI辅助的项目中创建prompts/文件夹。Markdown文件。每个主要功能区域一个。当一个提示词产生特别好的输出时,我会保存它。当我找到更好的版本时,我会更新这个文件。prompts/ folder in projects where AI assistance is significant. Markdown files. One per major feature area. When a prompt produces particularly good output, I save it. When I find a better version, I update the file.
听起来有点强迫症。在上一个大项目中,这为我节省了大约六小时——一个为从Shopify迁移过来的零售商构建的headless WooCommerce项目。我在四个不同的组件中重复使用了一个产品查询提示词(做了少量编辑),而不是每次都从零开始重新构建上下文。
用Git跟踪它。认真点。如果你把提示词当作工件而不是一次性输入,提示词质量是可重现的。LangChain的提示词模板在框架层面将这个想法形式化了,但你不需要任何框架——对于大多数代理工作流来说,一个Markdown文件夹就足够了。LangChain's prompt templates formalise this idea in a framework context, but you don't need any framework — a folder of Markdown files is enough for most agency workflows.
---
常见问题
提示工程到底是真正的可转移技能,还是只适用于特定模型?
主要是可转移的。核心原则——具体性、上下文设置、链式提示、批评反馈循环——适用于 GPT-4、Claude 3.5 Sonnet、Gemini,以及接下来的任何模型。语法会有些差异,有些模型对特定的表述方式反应更好,但底层逻辑是一致的。我发现 Claude 对明确的约束条件反应很好;GPT-4 对示例反应很好。细微差别,基础相同。syntax varies a bit and some models respond better to certain framing, but the underlying logic holds. I've found Claude tends to respond well to explicit constraints; GPT-4 responds well to examples. Minor differences, same fundamentals.
你在代码审查中如何处理 AI 生成的代码?
和其他代码一样对待。如果要上线生产,就得审查。没有例外。在 Seahawk 时我已经停止在 PR 中标记"这是 AI 生成的",因为这成了干扰因素——审查者会以不同的眼光审视它,有时过度审查,有时不够。代码本身的优劣才是关键。我标记的是:任何逻辑不明显的部分,我都会加上行内注释解释推理。
你用系统提示还是只用聊天提示?
两者都用。在 Cursor 中,我依靠 .cursorrules 文件来处理持久的项目级指令——那些我本来每次都要粘贴的东西。对于 ChatGPT 或 Claude 网页版的一次性任务,全部在聊天里。.cursorrules 这种方法明显减少了重复,而且模型在长时间会话中能保持更一致的表现。.cursorrules file for persistent project-level instructions — things I'd otherwise paste every time. For one-off tasks in the ChatGPT or Claude web UI, it's all in the chat. The .cursorrules approach has meaningfully reduced repetition and the model stays more consistent across a long session.
你对 AI 替代开发者的真实看法是什么?
它替代的是某些任务,不是开发者。判断性工作——要构建什么、如何架构、哪个权衡适合这个客户的实际情况——这些远未实现自动化。反而说,被挤压的开发者是那些纯粹面向执行的,缺乏设计或架构思维的。强化你的判断层。那才是有防守力的。tasks, not developers. The judgment calls — what to build, how to architect it, which trade-off fits this client's actual situation — those are nowhere near automated. If anything, the developers getting squeezed are the ones who were purely execution-oriented, not design or architecture-oriented. Sharpen the judgment layer. That's the defensible bit.
---
在网络上建造东西九年了,一个根本的教训不断重复:你的输出质量取决于你的输入质量。当时是客户简报,现在是提示词。the quality of your output is determined by the quality of your inputs. That was true when it was client briefs. It's true now with prompts.
这个模型是一个快速的、偶尔聪慧、频繁过度自信的初级开发者。相应地管理它。
