
1. 为什么“工作流”比“提示词”更值得花时间大多数人接触 AI 编程第一步都是去搜“好用的提示词”。收藏夹里存了几百条真到写代码的时候还是一条条复制粘贴改改变量名然后祈祷模型这次能给出能跑的代码。这个做法在 2023 年还行到了现在模型能力早就溢出了瓶颈根本不在提示词写得好不好而在于你有没有把重复动作固化下来。我自己的转折点是有一次重构一个老项目三天里让模型帮我写了四十多个函数。写到第二十个的时候我突然意识到每次我都在做同样的事贴上下文、说清楚输入输出、要求加类型注解、要求补边界处理、要求写单测。这五步我重复了四十遍。如果把这五步做成一个固定流程我只需要换掉“贴上下文”那部分剩下的全是自动的。这就是工作流和提示词的本质区别。提示词是“一次性对话”工作流是“可复用的生产线”。前者靠你临场发挥后者靠你提前设计。三个能立刻复用的 AI 编程工作流说的不是三个提示词模板而是三条从“需求”到“可合并代码”的完整链路。它们分别解决三类最高频的场景新功能从零到一、存量代码的理解与改造、重复性代码的批量生成。适合谁看如果你已经在用 AI 写代码但总觉得“效率提升没有想象中大”那这篇就是写给你的。如果你还没开始用那更好直接按工作流的方式上手少走我踩过的弯路。下面三个工作流每一个我都会拆到“为什么这么设计”“每一步在干什么”“实测中哪里会翻车”你可以直接抄也可以按自己的技术栈改。2. 工作流一从需求到可合并 PR 的“三段式生成”2.1 为什么不能一步到位让 AI 写完整功能新手最容易犯的错是把一整段需求丢给模型说“帮我实现这个功能”。模型确实会给你一大段代码看起来结构完整、注释齐全但你一跑就发现依赖的库版本不对、接口签名和项目里现有的不一致、错误处理全是throw new Error(TODO)。然后你开始一轮轮对话去修修到最后代码面目全非还不如自己写。根本原因在于模型在单次生成里要同时做太多决策。它要猜你的项目结构、猜你的编码规范、猜你的依赖版本、猜你的错误处理策略。每一个猜测都是一次抛硬币猜错一个整段代码就废了。三段式生成的核心思路是把“决策”和“实现”分开。第一段只做决策不写实现第二段只做实现不做决策第三段只做校验不做实现。每一段都有明确的输入和输出模型不需要在多个目标之间权衡。2.2 第一段让 AI 先输出“接口契约”而不是代码第一段的产物是一份接口契约包含函数签名、输入输出类型、依赖项、边界条件列表、以及“不做什么”的明确声明。这一步的关键是强制模型先想清楚再动手。我常用的指令结构是这样的基于以下需求只输出接口契约不要写实现代码。 需求[粘贴需求描述] 项目技术栈[TypeScript Node 18 Prisma] 现有相关模块[粘贴相关类型定义或接口文件] 输出格式 1. 函数签名含完整类型 2. 依赖的现有模块列出 import 路径 3. 边界条件清单至少 5 条 4. 明确不处理的场景为什么这一步能大幅提升后续质量因为当模型被迫先写签名和边界条件时它实际上在做一次“设计评审”。很多需求里的模糊点在这一步就会暴露出来。比如你写“处理用户上传的文件”模型会问你文件大小上限是多少并发上传怎么处理失败重试几次这些问题你在写提示词的时候根本没想过但它们是真实代码里必须回答的。实测下来这一步大概花 2 到 3 分钟但能省掉后面至少 20 分钟的来回修改。而且这份契约本身就是很好的文档review 的时候直接看契约就知道这个函数该干什么。2.3 第二段基于契约生成实现并强制自检第二段把契约和具体上下文一起喂给模型要求它生成实现。但这里有个关键动作要求模型在生成代码后自己逐条对照边界条件清单做自检。指令大概长这样基于以下接口契约生成实现代码。 契约[粘贴第一段的输出] 项目编码规范[粘贴 ESLint 配置或团队规范摘要] 要求 1. 严格按契约中的签名实现不得修改签名 2. 每处理一个边界条件在代码中用注释标注 // boundary: xxx 3. 生成后逐条列出边界条件的处理情况未处理的说明原因这个“自检”动作看起来多余但实测非常有效。模型在生成代码时是“流式”的它写到后面可能忘了前面的约束。强制它在生成后回头对照清单能抓出大概 30% 的遗漏。我印象最深的一次模型在自检时自己发现“并发场景下没有加锁”然后主动补上了。如果我不要求自检这个 bug 就会留到测试阶段。另外一个小技巧要求模型用注释标注边界条件的处理位置。这样你 review 的时候可以直接搜// boundary:快速定位每个边界条件的处理代码不用通读全文。2.4 第三段生成测试用例并跑通而不是“建议你写测试”第三段是最容易被忽略的。很多人让模型“顺便写个测试”模型就给几个expect(true).toBe(true)糊弄过去。正确的做法是把测试当成独立交付物要求模型生成可执行的测试文件并且明确测试框架和运行命令。指令结构基于以下实现代码生成单元测试。 实现[粘贴第二段代码] 测试框架Vitest 要求 1. 每个边界条件至少一个测试用例 2. 包含正常路径和异常路径 3. 测试文件可直接运行不需要额外配置 4. 输出运行命令这里的关键是“可直接运行”。模型经常生成需要额外 mock 配置的测试跑不起来。要求它输出运行命令等于逼它确认测试是自包含的。如果它输出的命令跑不通你就知道测试有问题直接打回去重生成。三段跑完你拿到的是一份接口契约、一份带边界标注的实现、一份可运行的测试。这三样东西合起来就是一个可以直接提 PR 的完整变更。我实测过一个中等复杂度的功能大概 200 行代码三段式跑完加上人工 review总共 25 分钟。如果纯手写保守估计 2 小时。2.5 这个工作流最容易翻车的地方第一个坑是契约阶段模型过度设计。它会给你加一堆你根本不需要的抽象层比如为了一个简单的 CRUD 搞出 repository、service、controller 三层。解决办法是在指令里加一句“优先使用项目现有模式不引入新的抽象层”。第二个坑是第二段模型偷偷改签名。虽然你明确说了“不得修改签名”但模型有时候会“好心”地加个可选参数。所以第二段生成后第一件事是对比签名不一致直接打回。第三个坑是测试用例覆盖了实现而不是覆盖了需求。模型会照着实现代码写测试实现里有的分支它都测实现里漏掉的需求它也不管。解决办法是在第三段指令里附上第一段的边界条件清单要求“测试必须覆盖契约中的所有边界条件而不是实现中的所有分支”。3. 工作流二存量代码的“逆向理解 安全改造”3.1 接手老代码时AI 最该帮你做的不是重写第二个工作流针对的是另一种高频场景你接手了一个没文档、没测试、原作者已经离职的老模块现在要加个功能或者修个 bug。大多数人的第一反应是让 AI “帮我理解这段代码”然后贴一大段进去模型给你一段似是而非的总结你看了等于没看。问题在于理解代码不是目的安全地改代码才是目的。单纯让模型“解释代码”它只会给你逐行翻译告诉你“这行是赋值这行是循环”。这对你判断“能不能改、改了会影响什么”毫无帮助。正确的做法是把“理解”和“改造”绑在一起。让模型在理解的同时输出一份“改造影响面报告”明确告诉你这个函数被谁调用、改了会影响哪些路径、哪些地方有隐式依赖。3.2 第一步生成“调用链路图”而不是“代码解释”我用的指令是这样的分析以下代码模块输出调用链路报告。 代码[粘贴模块代码] 项目结构[粘贴相关目录树] 要求 1. 列出该模块对外暴露的所有函数/类 2. 对每个暴露项列出项目内所有调用点文件 行号 调用方式 3. 标注每个调用点是否依赖返回值结构 4. 标注是否存在通过全局变量/单例的隐式依赖 5. 输出格式用表格这个报告的价值在于它把“改这个函数会不会炸”变成了一个可以查表回答的问题。如果某个调用点依赖返回值结构那你改返回值就要同步改调用点如果有隐式依赖那就要特别小心。实测中模型找调用点的准确率大概在 85% 左右会漏掉一些动态调用比如通过字符串反射调用的。所以这一步的产物需要人工扫一遍但比你自己从头 grep 快得多。我一般会拿模型的报告和 IDE 的 “Find Usages” 结果对一下两边都有的就是确定的只有一边有的就重点看。3.3 第二步先写“特征测试”锁住现有行为在改任何老代码之前必须先有测试锁住现有行为。但老代码往往没有测试这时候让 AI 帮你补“特征测试”是最划算的。特征测试和普通单元测试的区别是它不验证“代码应该做什么”只验证“代码现在做什么”。哪怕现有行为是错的特征测试也照实记录。这样你改造的时候如果测试挂了你就知道行为变了需要判断这个变化是不是预期的。指令基于以下代码生成特征测试。 代码[粘贴待改造函数] 测试框架Jest 要求 1. 测试现有行为不判断对错 2. 覆盖所有分支包括异常分支 3. 对不确定的输入先用 console.log 打印实际输出再据此写断言 4. 输出运行命令这里有个实操技巧让模型先打印再断言。因为模型不知道某些边界输入的实际输出是什么它可能会猜一个错的断言。让它先打印你跑一遍拿到真实输出再让它据此写断言准确率高很多。3.4 第三步小步改造 每步跑特征测试有了特征测试之后改造就可以小步走了。我的习惯是每次只改一个点改完立刻跑特征测试。如果测试挂了要么是改错了要么是行为变化是预期的那就更新测试。改造阶段的指令要强调“最小变更”基于以下代码和改造目标输出最小变更方案。 代码[粘贴函数] 改造目标[描述你要加的功能或修的 bug] 约束 1. 不改变现有函数签名 2. 不引入新的外部依赖 3. 变更行数尽量少 4. 输出 diff 格式不要输出完整文件要求输出 diff 而不是完整文件有两个好处一是变更范围一目了然二是避免模型“顺手”重构无关代码。模型有个坏习惯你让它改 A它会把 B、C、D 一起“优化”了。输出 diff 能有效抑制这个冲动。3.5 存量改造中最隐蔽的坑隐式状态老代码里最危险的不是复杂的逻辑而是隐式状态。比如某个模块级变量在初始化时被赋值后续所有函数都依赖它或者某个单例在第一次调用时懒加载之后一直复用。这些隐式状态在代码里看不出来但改造时一旦破坏就是线上事故。模型对隐式状态的识别能力有限因为它只能看到你贴给它的代码。如果隐式状态定义在另一个文件里模型根本不知道。所以这一步必须人工介入在让模型分析之前先自己 grep 一遍模块级变量和单例把相关代码一起贴给模型。我踩过最惨的一次坑改一个工具函数本地测试全过上线后另一个模块挂了。原因是那个模块在启动时调用了这个工具函数来初始化一个缓存我改了函数的副作用导致缓存没初始化。特征测试没覆盖到这个路径因为它是跨模块的。从那以后我改任何有副作用的函数之前都会先问模型一句“这个函数有没有可能被用于初始化场景”4. 工作流三重复性代码的“模板 参数化批量生成”4.1 什么代码值得批量生成什么不值得第三个工作流解决的是“重复性代码”问题。但这里要先泼一盆冷水不是所有重复代码都值得用 AI 批量生成。如果重复代码只有三五处手写可能比设计模板更快。批量生成的价值在“数量足够多 模式足够稳定”的场景。我判断的标准是如果重复次数超过 10 次且每次的差异可以用不超过 5 个参数描述那就值得做模板。典型的场景包括为一批 API 接口生成请求函数、为一批数据模型生成 CRUD 操作、为一批配置项生成校验逻辑、为一批组件生成 Storybook 故事。反过来如果每次的差异需要写一段逻辑来描述那就不适合模板化。因为描述差异的逻辑本身可能比手写还复杂。4.2 模板的设计把“变化点”抽成参数模板设计的核心是识别“不变的部分”和“变化的部分”。不变的部分写死在模板里变化的部分抽成参数。听起来简单但实操中最大的坑是你以为不变的部分其实会变。我的做法是先手动写三个实例然后对比这三个实例找出真正不变的部分。比如生成 API 请求函数三个实例对比下来不变的是请求方法、URL 拼接方式、错误处理结构、返回类型解包。变化的是URL 路径、请求参数类型、响应类型、是否需要鉴权。模板大概长这样生成一个 API 请求函数遵循以下模板 - 函数名{{name}} - URL 路径{{path}} - 请求参数类型{{requestType}} - 响应类型{{responseType}} - 是否需要鉴权{{auth}} 模板结构 export async function {{name}}(params: {{requestType}}): Promise{{responseType}} { // 鉴权处理根据 auth 参数决定是否包含 // 请求发送 // 错误处理统一结构 // 返回解包 }注意模板里我用的是“结构描述”而不是完整代码。因为完整代码会让模型倾向于照抄而结构描述给了模型一定的发挥空间能适应项目里的具体写法。4.3 批量生成的执行分批 抽样验证一次性让模型生成 50 个函数质量会断崖式下降。模型在长输出里会“偷懒”后面的函数越来越简略甚至直接复制前面的。我的做法是分批每批 5 到 8 个每批生成后立刻抽样验证。抽样验证的方法是从每批里随机挑 2 个实际跑一遍或者至少做类型检查确认能编译、能运行。如果发现某批质量下降就停下来检查是不是参数描述不够清晰调整后再继续。这里有个提效技巧把参数列表做成表格一次性贴给模型让它按表格逐行生成。表格格式比自然语言描述更结构化模型不容易漏项。namepathrequestTyperesponseTypeauthgetUser/user/:idGetUserReqUseryeslistOrders/ordersListOrdersReqOrder[]yesgetConfig/configvoidConfigno4.4 生成后的统一校验类型检查 命名规范批量生成最容易出的问题是命名不一致。模型可能这批用getUser下批用fetchUser。所以生成后必须做一次统一校验。我的做法是写一个简单的脚本扫描生成的文件检查函数名是否符合命名规范、import 是否完整、是否有重复定义。这个脚本不用很复杂正则匹配就够了。发现不一致的地方要么手动改要么把不一致的列表贴回给模型让它统一。类型检查是必须的。批量生成的代码里类型错误率大概在 10% 到 15%主要是 import 路径写错、泛型参数漏了、可选属性没处理。跑一遍tsc --noEmit就能全部抓出来。4.5 批量生成的边界什么时候该停下来手写批量生成不是万能的。遇到以下情况我会停下来手写生成的代码需要复杂的条件分支超过 3 层嵌套、需要调用项目里特有的工具函数模型不知道、涉及副作用比如写文件、发请求且副作用顺序重要。这些情况的共同点是正确性依赖于模型看不到的上下文。模型只能看到你贴给它的东西项目里的隐式约定、运行时行为、外部系统状态它都看不到。强行让它生成只会得到看起来对但跑起来错的代码。我的一般原则是批量生成负责“骨架”人工负责“关节”。骨架是结构化的、模式化的部分关节是需要和项目其他部分咬合的部分。把这两者分开效率和质量都能兼顾。5. 三个工作流共用的“防翻车”检查清单5.1 生成前上下文给够但别给太多上下文给少了模型瞎猜给多了模型抓不住重点。我的经验是给“直接相关”的代码不给“可能相关”的代码。比如你要改一个函数就给它这个函数、它的类型定义、它的直接调用点。不要给它整个文件更不要给它整个项目。如果模型需要更多上下文它会在生成过程中问你。这时候你再补给它比一开始就塞一大堆更有效。因为模型在有了具体问题之后对上下文的理解会更聚焦。5.2 生成中要求“可验证”的输出格式所有指令都要要求模型输出可验证的东西。什么叫可验证就是你能用工具检查对错。类型检查、测试运行、diff 对比都是可验证的。自然语言的解释、建议、总结都是不可验证的。所以我的指令里几乎都会加一句“输出必须能通过tsc检查”或者“输出必须能直接运行”。这句话能过滤掉大量“看起来对但跑不了”的代码。5.3 生成后先跑再读不要先读再跑很多人拿到 AI 生成的代码第一反应是通读一遍。这个习惯要改。正确的顺序是先跑类型检查、先跑测试、先跑 lint让工具告诉你哪里有问题。工具报错的地方重点看工具没报错的地方快速扫。为什么因为人读代码会陷入细节而工具能快速定位结构性问题。先跑工具能把 80% 的低级错误过滤掉你只需要关注剩下的 20% 逻辑问题。我实测过先跑再读比先读再跑review 时间能省一半。5.4 一个通用的“回滚点”习惯不管用哪个工作流改代码之前先 commit 一次。AI 生成的代码有时候会引入莫名其妙的问题有个干净的回滚点你就能大胆试错。我甚至会在跑工作流之前专门建一个分支叫ai-workflow-temp跑完验证通过再合并回主分支。这个习惯看起来简单但能省掉很多“改乱了想重来”的痛苦。尤其是批量生成的时候生成到一半发现模板设计有问题直接回滚重来比一个个改快得多。6. 把工作流变成肌肉记忆之后这三个工作流我用了大概半年最大的感受是AI 编程的效率提升不来自模型变强而来自你变懒。你越懒得重复同样的动作越会去设计流程流程设计得越好你越不需要重复。这是个正循环。现在我看到一个新需求第一反应不是“怎么写”而是“这属于哪类工作流”。新功能走三段式老代码改造走逆向理解重复代码走模板批量。分类之后每一步该干什么都是固定的我只需要填参数。当然工作流不是死的。项目不同、团队规范不同、技术栈不同每个工作流的具体步骤都要调整。但核心思路是一样的把决策和实现分开把生成和验证分开把批量和小步分开。这三条原则比任何具体的提示词都重要。最后分享一个我最近在用的技巧把这三个工作流的指令模板存成代码片段用编辑器的 snippet 功能管理。需要的时候敲几个字符就能展开比存在笔记里复制粘贴快得多。工作流的价值在于“立刻复用”如果每次用还要去找模板那复用就打折扣了。