ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

如何写好AI编程的spec?让AI生成更准确的代码

如何写好AI编程的spec?让AI生成更准确的代码 刚接触 AI 编程或者用过一段时间 AI 编程工具的人大概率都经历过这个场景你花了大半天把需求文档写得密密麻麻背景、目标、接口、边界条件恨不得全塞进去结果 AI 生成的代码一跑核心逻辑全是错的甚至方向都跟你想的完全不一样。那一刻真的会很怀疑人生到底是 spec 写得不够好还是 AI 太笨其实都不是。问题出在大多数人对“spec 写得完整”这件事的理解和 AI 实际需要的东西之间存在一条巨大的认知鸿沟。spec 这个词在 AI 编程、spec coding、AI agent 工作流里几乎每天都要打交道它确实是指导 AI 的第一资料但它的定位不是“产品需求文档”而更像是“给一个聪明但失忆的程序员的操作手册”。这篇内容我把自己的踩坑经历和反复验证过的经验整理出来掰开揉碎讲给你听希望能帮你少走点弯路。内容适合三类人看正在用 Cursor、Copilot、通义灵码这类工具的开发者做 AI Agent / AI 工程实践相关工作的工程师以及产品、测试、研发任何需要跟 AI 协作的人。下面直接进入正题。1. 先搞清楚你以为的“完整” vs AI 眼里的“完整”1.1 需求完整不等于指令可执行很多人的 spec 确实很“完整”功能、流程、界面元素、接口参数全都有但 AI 还是做错。这里的第一层原因往往出在“完整的方向错位”上。你觉得完整是把业务讲清楚AI 需要的完整是把每一步该干什么、在什么条件下干什么、输出长什么样用可执行的语言确定下来。打个比方你请了一位家教帮孩子辅导作文你把背景资料、主题、字数要求都给得很足但你没有告诉他“每段第一句必须是论点句”、“不能用感叹号”、“动词一律用主动语态”。家教最终可能写出了一篇好文章但它不符合你的评分标准。AI 写代码也是这个道理。业务描述再丰富如果编码层面的可执行约束缺失它就只会生成“看起来差不多”的代码。我自己实测过一组对比给 AI 一份写满业务细节、但缺少明确验收条件的 spec和一份只写了核心功能、但明确给出“输入是什么、输出是什么、异常时返回什么、错误码是什么”的 spec后者的完成精准度反而高很多。原因后面会展开说。简单讲模型是根据 token 概率生成内容的它更容易抓住有“可检验性”的句子而不是描述性的背景信息。1.2 spec 完整的三个维度如果要把“完整”给一个可操作的定义我建议拆成三个维度缺任何一个都会有“AI 做不对”的现象出现。需求完整性功能目标是全的角色和权限是清楚的数据来源是明确的。指令可执行性每一步的输入、处理、输出、异常路径都有足够具体、无歧义的描述。验收可检验性你给出了判断“做对”的客观标准通常体现为验收条件、测试用例、边界值、性能阈值。这三个维度不是并列关系而是从“想清楚”到“写得清”再到“可验证”的递进关系。很多 spec 其实输在第二层和第三层而需求层的“完整”反而经常是信息过量把模型的注意力带偏了。2. 为什么 AI 会把“明确的 spec”做歪根因拆解2.1 上下文窗口不是无限内存AI 的上下文窗口虽然越来越大但“能放进窗口”不等于“能被有效利用”。模型对长文本的处理有一个注意力分配的问题它会在每个 token 上计算权重长文本很容易出现“中部信息被稀释”、“开头和结尾的记忆更强”的现象业内管这个叫 “lost in the middle”。你的 spec 如果长达一两千行真正影响核心逻辑的约束可能藏在中间等模型读到后面重心已经偏移了。实操中我习惯把核心约束分成两类。一类是“最高优先级指令”必须放在 spec 开头 10% 的位置甚至直接在系统提示词或第一条 user 消息里重复一遍另一类是“背景资料”放到最后让模型在需要时查阅。把背景资料和硬性约束混在一起写是 AI 做错的最常见原因之一没有例外。2.2 模型的“理解”是概率性解释不是逻辑推演无论模型多强它的底层机制还是“根据前文预测下一个词”。它不是像编译器那样按语义严格执行而是在生成时寻找“最像正确答案”的路径。所以 spec 里如果存在两个互不冲突、但也没有强制关联的信息模型可能只取其中一个如果存在一个可做可不做的描述模型倾向于选择比较常见的做法而不是你的做法。这就是为什么 spec 里写着“校验失败时返回错误提示”AI 可能返回 HTTP 400也可能返回 200 业务错误码甚至可能只打日志不返回。人类看到“返回错误提示”能理解需要看后面上下文确定具体格式但模型只能在概率分布里做选择它觉得哪种写法“更像正确答案”就写哪种。2.3 隐式假设你没写AI 也不会替你补每个人心里都有很多“不用写也知道的东西”。你写“用户上传头像限制大小”你觉得“大小”是指文件体积AI 可能理解成“尺寸”结果生成了 2MB 的限制而不是 512×512 像素。你写“列表按时间倒序”你默认是新创建的排在前面AI 可能按更新时间排序因为“时间”这个字段本身就有歧义。隐式假设是 spec 的隐形杀手。越是熟悉业务的人越容易漏因为有些东西对你来说太显然了而 AI 唯一的输入就是文字。你没写它就只能猜猜完可能还猜得很自信。处理隐式假设的诀窍是把自己当业务小白把 spec 当交接文档把所有用到的名词做一次“名词-定义表”。“大小”是什么大小是文件字节数、像素尺寸、还是列表长度“时间”是什么时间是创建时间、更新时间还是业务发生时间“已完成”是什么状态是用户操作完成还是后台任务执行完成写进 spec 里哪怕只有一句话效果也立竿见影。2.4 输出格式你觉得没问题但解析就是失败还有一类“AI 做不对”不是逻辑问题而是输出格式问题。你需要 AI 输出 JSON它偶尔会在 JSON 前后加解释文字你需要它输出数组它给你套了个对象。这在 spec coding 里是高频问题尤其当 spec 只描述业务、不描述输出结构时。解决方式很简单也很土在 spec 里直接用代码块给出“输出模板”告诉模型必须严格按照该结构输出并加上“除该结构外不要输出任何其他内容”这种强约束。实测下来这种显式模板比任何自然语言的描述都管用几乎能把这个错误率降到零。3. 从 spec 到代码AI 真正“读懂”你需要的几个条件3.1 把“目标”翻译成“指令”我经常跟团队做 AI 工程实践培训里面最核心的一句话是不要把 spec 写成产品需求文档要写成给一个聪明但失忆的程序员的操作手册。产品需求文档描述的是“系统应该是什么样”操作手册描述的是“你现在按顺序做什么”。AI 每一次对话都是重新阅读整个 spec它没有长期记忆也没有“我上次读过了”的缓存所以你要假设它什么都不知道。举例说明需求写法“系统需要对上传文件进行病毒扫描。”指令写法“文件上传完成后调用扫描服务若扫描状态为 fail 则删除文件并返回错误码 A102若超时 3 秒返回 A103其他情况返回 A100。”后者不仅告诉它做什么还告诉了决策树和每种结果对应的可执行动作。AI 做对的比例会显著上升因为不存在概率性的“该返回什么”的选项了判断标准和动作都是确定的。3.2 顺序就是优先级Spec 的排列顺序会被模型当作隐式优先级。越靠前的内容在生成时权重越高越靠后的内容越容易被忽略。很多人的 spec 开头写“背景介绍”、“产品目标”把真正的技术约束放在中后段结果 AI 在生成代码时优先满足它理解的“产品目标”技术约束就被忽略了。我的经验是spec 的结构严格按下面的顺序组织硬性约束永远在最前硬性约束禁止事项、强制技术栈、必须使用的函数或模式、必须返回的格式。功能入口与触发条件。完成标准与验收用例至少 3 个包含边界值和异常场景。正常流程描述。异常流程与兜底逻辑。背景资料、业务知识、参考代码可选放最末。这样调整之后模型的输出会明显更稳。我对同一个任务做过两次对比一次按产品需求风格写一次按“指令优先”的风格写同样是做一个批量文件重命名的小工具后者第一次跑通的概率高很多而且重新生成时的一致性也更好。3.3 伪代码也算代码在 spec 里预置关键逻辑片段很多人舍不得在 spec 里写伪代码觉得“我把伪代码写完了还要 AI 干什么”我的观点是AI 的价值不是替你思考算法而是替你实现工程化代码。在 spec 里插入关键逻辑的伪代码能让 AI 百分之百对齐你的思路尤其是排序规则、状态机、边界判断这类逻辑。比如你要实现一个“任务重试”与其写“重试失败的请求”不如写对于每个任务最多重试3次 每次重试间隔为 [1,2,4] 秒指数退避 重试前必须更新任务状态为 retrying并写入日志 若第三次仍失败标记任务为 failed同时发送告警通知。AI 拿到这样的伪代码后生成的重试逻辑基本不需要改因为它不再需要“猜测”你的重试策略你给出的规则就是它唯一的生成依据。这个方法有个前提你自己得对关键逻辑有清晰的预期。如果你自己都是模糊的那确实应该先让 AI 帮你列出方案而不是直接写代码。把“方案设计”和“编码”分两步走很多问题能在编码前先暴露出来。4. 实测案例一份 spec 的三次整改记录4.1 场景背景拿我以前做过的一个小项目当案例用 AI 生成一个通用的“Excel 导入”功能。需求包括文件上传、格式校验、按批次写入数据库前端异步显示导入进度。这个项目规模不大但非常适合展示 spec 质量对 AI 输出的影响因为里面有大量边界条件。第一版 spec 我写成了用户可以通过页面上传 Excel 文件系统需要校验文件格式将数据导入数据库中导入过程中显示进度导入完成后通知用户。4.2 第一版 AI 输出业务大体对细节全是雷AI 第一版输出看起来挺完整有上传接口、有读取 Excel 的方法、有批量插入、有进度字段。但几个关键细节全军覆没校验只做了扩展名判断没读文件内容导致伪装的 .xlsx实际是文本文件直接报错。日期格式检测写死了 yyyy-MM-dd但用户大量使用 yyyy/M/d导入结果全是空字符串。批次写入没有做事务5000 条数据中间有一条错了前面 4999 条全部入库而且进度卡在 97% 不更新。失败记录没有返回给用户只打了日志用户完全不知道哪几条失败。站在 AI 的角度分析它有没有读懂 spec读懂了大部分。它没有自动补齐这些边界条件原因是 spec 里根本没提“伪装的扩展名怎么办”、“日期格式有哪些”、“单条失败要不要影响整批”、“失败明细要不要给用户看”。这些对我是常识对它是盲区。4.3 第二版 spec补了边界但补法不对第二版我补充了很多细节写法是“注意文件格式要校验日期格式要兼容导入失败要提示用户”。结果好了一些但依然不理想。原因是这些描述仍然是“目标性”的没有“可执行性”。比如“校验文件格式”AI 可能只判断扩展名“兼容日期格式”AI 可能只加一个 yyyy/MM/dd少了 yyyy-M-d 和 yyyyMMdd“提示用户”AI 可能写个 alert 就完了。这个阶段我意识到AI 需要的不是我提示它注意什么而是我告诉它判断标准和动作。后来我把这三条改成了文件格式校验解析文件头判断是否为合法 Excel 格式若文件头不属于指定魔数段开头返回错误码 E001不得入库。日期解析入参支持 yyyy-MM-dd、yyyy/M/d、yyyyMMdd 三种格式解析失败返回错误码 E002记录到失败明细。失败明细导入完成后返回属性包含 successCount、failCount、failDetails数组每条含行号和失败原因。这一版 AI 的输出质量提升非常明显因为每条都有“判断标准 动作 输出形式”。我拿这个经验回头检查自己写的所有 spec补了一遍之后才真正稳定下来。4.4 第三版把验收用例直接写进 spec第三版更进一步我在 spec 末尾加了一段“验收用例”编号输入预期结果TC01上传非 Excel 扩展名的文件返回 E001数据库无新增TC02上传内容为文本但扩展名为 .xlsx 的文件返回 E001数据库无新增TC03上传含 5000 行有效数据 5 行非法日期的 Excel返回 successCount5000failCount5failDetails 含对应行号TC04上传 100 万行数据分批入库每批 1000进度值单调递增最后返回 100%TC05导入过程中数据库断开显示失败状态进度停留在当前值已提交批次不重复入库加验收用例的效果比我预想中还好。AI 会以这些用例为目标反向修正自己的实现。比如它为了满足 TC03会主动把失败明细的数据结构设计充分为了满足 TC04会实现分批和进度更新而不是一次性插入。这里多说一句验收用例放在 spec 里不是“仅供参考”而是“强制条件”。最好在用例后面加一句“实现必须通过这些验收用例若有场景无法满足需要先说明原因再编码”。这样 AI 就不会在部分实现无法覆盖时不说明原因地跳过。5. 常见问题与排查技巧实录5.1 典型问题速查表这几类问题是我实际使用中反复遇到的整理成一张速查表遇到“AI 做不对”可以先对号入座。现象常见原因排查方向功能有但边界处理粗放spec 缺少边界值和异常场景描述补充“如果…怎么办”的规则和验收用例代码风格不像项目既有代码spec 没给定风格约束和参考代码在 spec 开头附一段“本项目代码风格示例”或指定遵循的框架约定返回格式不符合接口契约只有自然语言描述没有输出模板在 spec 中直接给 JSON / TS 输出模板标明字段类型和必填项逻辑实现与产品预期不同需求描述有歧义或排序/判断逻辑缺明确规则用伪代码定义判断流程必要时给出状态机上下文很长时后半部分做错硬性约束被“深埋”在长文的中间将硬性约束前置重复或拆分为多个子任务分步执行一次改完仍然多个错误且“很自信”模型幻觉或对 spec 的局部理解偏差让 AI 先用自然语言复述需求再进入编码编码后用验收用例自测并解释每步给的数据样例和 spec 不一致时表现混乱spec 自身存在冲突自查 spec 中的数据定义、字段、名词是否全文统一这张表对我的排查流程帮助很大。遇到不对的代码我第一反应不是怪 AI而是先回看 spec是不是漏了规则是不是有歧义是不是约束放得太靠后5.2 两个高性价比的“防错动作”动作一先让 AI 复述需求再编码。这是我在 spec coding 中收益最大的一个小技巧。在正式让 AI 写代码前先让它“用你的理解复述一遍需求并列出你将要使用的技术方案、接口设计和完成标准”。这一步能暴露绝大部分理解偏差。AI 复述得有多准后面做错的概率就有多低。而且它的复述文本会成为后续对话的锚点等于把 spec 里最重要的信息又强化了一遍。动作二要求 AI 做“自测并解释”。让 AI 写完代码后要求它“按 spec 里的验收用例逐条自测并输出测试过程与结果”。如果它自己都没信心说“这条用例覆盖不了”那大概率是 spec 有盲区或实现有缺陷。这个过程类似让 AI 做 code review虽然它不能真正执行代码但能通过静态推演发现很多不一致的地方。5.3 长文本项目的拆分策略如果你的项目很大一定要避免把全部需求放在一个超长 spec 里。我推荐“总 spec 子任务”的方式总 spec 只写全局架构、目录结构、模块划分、约束规范每个子任务单独写一个局部 spec里面包含该模块的详细要求和验收用例。这样做有两个好处。第一模型每次看到的信息量小了注意力更集中第二你可以在子任务的 spec 里引用总 spec 的架构约束让模型“带着全局观写局部代码”。很多 AI agent 工具本身就有任务拆解能力但你自己拆分后注入可控性要高得多。6. 把 spec 和测试绑定让 AI 自己给自己验收6.1 在 spec 里内置“测试计划”刚刚说验收用例要写进 spec这里还有一个更进阶的玩法把测试计划也做成可执行的、能让 AI 生成测试代码的东西。操作上每个验收用例不仅要写输入和预期还要附上“如果实现语言是 Python用 pytest 写如果是 TypeScript用 vitest 写”。这样 AI 在生成业务代码时会顺手生成对应的测试代码甚至在写实现时就已经在想怎么满足测试。我实测过一个账号系统的接口改造任务。spec 里的验收用例比较多我加了一句话“请为以下每个验收用例生成 pytest 测试测试文件命名 test_*.py全部放在 tests 目录”。结果 AI 生成的测试和实现代码几乎是配套完整的。虽然部分测试因为环境原因需要微调但整体覆盖度和逻辑一致性比我人工补测试快了很多。6.2 测试先行 vs 后置说明传统的 TDD 是“先写测试再写实现”。对 AI 编程来说因为需要一次性生成很多东西比较现实的做法是“同一轮对话里测试和实现一起生成但测试代码在逻辑顺序上要先于实现代码被思考”。这样说有些抽象换个说法在 prompt 中明确要求“先定义接口契约和测试用例再写实现代码”。AI 依然在同一轮中完成但输出顺序会影响它自己的逻辑一致性因为先想清楚验收再写实现隐含的推理链是顺的。如果工具支持多轮执行更推荐的做法是第一轮让 AI 根据 spec 输出测试用例清单。第二轮确认或修正测试用例。第三轮让 AI 根据“spec 已确认测试用例”生成实现。第四轮让 AI 根据实现跑一遍“自测步骤”无法真实运行的话至少做静态走查并输出结论。这套流程在 AI agent 工具里基本都能实现手动在对话里分轮问也行就是辛苦一点。但你可以明显看到经过“先用例后实现”流程生成的代码比一刀切直接生成的要稳很多。6.3 找不到问题的最后手段可复现的最小样例如果 spec 已经很完整、验收用例也写了AI 还是做错而且错得很莫名其妙大概率需要给 AI 一个“最小化样例”来对齐。常见做法是在 spec 里给出一份小的输入文件比如只有 3 行数据的 CSV并写出期望输出同时要求 AI 用这份样例做数据流走查输出每一步中间变量的值。这个过程很费 token但定位问题的效率极高。它能把“AI 对 spec 的理解在哪里发生了偏移”非常显式地暴露出来。尤其是在处理复杂状态机、嵌套数据结构这类问题时最小样例简直是指路明灯。我之前处理过一个订单状态流转的错误整整查了两轮对话都没定位到问题后来给了一个只有 3 条状态迁移的最小样例AI 一下就意识到自己把“已取消”状态从状态机里漏掉了。7. 一点更深的思考spec 与工具链的协同7.1 spec 不只是给模型看也是给 AI Agent 的“任务书”现在很多人的工作流已经从“单次对话生成代码”升级到了“AI Agent 自主完成一个模块”这就对 spec 提出了更高的要求。在 Agent 场景下spec 不仅仅是“给模型看的描述”还是 Agent 规划任务、拆分步骤、调用工具、自我检查时的依据。如果你的 spec 只写了“做什么”没写“怎么拆”Agent 可能自己拆出一个你完全没预料到的执行路径。我见过一个典型翻车场景用户给了 Agent 一个任务“把图片上传接口加上压缩功能”spec 里写了压缩质量 85%、格式转 WebP但没有写“压缩后要删除原图”。Agent 执行完以后原图和新图都留存了而且上传逻辑变成了“先传原图再传压缩图”完全偏离了“客户端压缩后再上传”的初衷。这个问题的根源不是 Agent 笨而是 spec 没有表达“在哪个环节做压缩”。把这个写清楚之后Agent 的执行路径就正常了。所以如果你在使用 AI Agent建议在 spec 里增加一小节“执行顺序与依赖关系”明确列出任务的先后步骤、步骤之间的输入输出、哪些环节可以并行、哪些环节必须串行。这对 Agent 的规划能力是一个很好的补强。7.2 spec 的版本管理把 spec 当作代码一样维护还有一点很多人忽略spec 是有生命周期的它不是写一遍就完事了。AI 生成的代码改了spec 如果不跟着更新下一次让 AI 修改功能时它会把旧 spec 和新需求混在一起生成的结果大概率是矛盾的。我的经验是把 spec 放进仓库里和代码一起做版本管理。每次需求变更先改 spec再让 AI 改代码。这样模型以及未来的你始终能看到一份“和代码同步演进”的需求文档。如果不这样做你会陷入一个死循环代码改了三轮spec 还是最早那一版AI 每次修改都在拿旧地图找新路错误率当然降不下来。7.3 警惕 spec 与数据样例的不一致最后提醒一个很容易被忽略的细节如果你的 spec 里引用了字段名、枚举值、示例数据一定要保证和代码里实际的数据结构一致。比如 spec 里写“status 字段可选值为 pending、processing、done”结果示例 JSON 里出现了一个 “completed”AI 就会非常困惑它可能会把 “completed” 当作合法值也可能直接报错。这种问题在长 spec 里特别常见因为手写的时候很难做到全文一致。建议在写完 spec 后自己先做一次“字段命名一致性扫描”或者直接把关键字段列表放在一个显眼的地方然后用一句话约束 AI“以上字段定义为本需求的唯一可信来源若在需求描述中发现不一致以该字段定义为准。”结尾我自己用了很长一段时间的 AI 编程工具最大的一个感受是AI 不是不聪明而是它“不知道自己不知道什么”。它不会像同事一样反问“你这里的校验是指文件头还是扩展名”、“时间倒序是按哪个字段”它只会按自己的理解一路走下去。所以 spec 写作的核心不是把内容写得更长而是把所有可能产生歧义的边界用文字钉死。我的建议是每次 AI 做错先别急着怪它把错误整理成一个清单然后逐条反向补充到 spec 里。补了两三次之后你会发现自己的 spec 越来越薄而不是越来越厚因为你已经知道哪些话 AI 需要听、哪些话它对理解帮助不大。这是一种双向磨合磨合好了后面新项目的 AI 产出质量会直接上一个台阶。最后分享一个小技巧写完 spec 后先让 AI 用 100 字以内概括它理解到的需求。如果这个概括和你心里的预期一致性超过八成再让它开工否则先把 spec 说清楚。这一步真的能省下后面大量的返工时间。
返回列表