1. 为什么“能立刻复用”比“功能强大”更重要
做AI编程工具链的人都有一个共同的体会:演示视频里行云流水的效果,落到自己项目上往往要折腾半天。问题不在于模型不够强,而在于工作流没有固化下来。我见过太多团队花两周搭了一套看起来很唬人的Agent流水线,结果日常开发里没人用,因为每次调用都要重新想提示词、重新拼上下文、重新检查输出格式。
“能立刻复用”这四个字,本质上说的是确定性。一个工作流能不能复用,取决于三件事:输入是否稳定、中间步骤是否可观测、输出是否可校验。缺了任何一环,它就只能算一次性的实验,而不是工程资产。
我目前手头长期维护着三套工作流,分别覆盖代码生成与重构、测试用例批量生产、遗留代码理解与文档补全。它们不是最花哨的,但胜在每天都能用、换个人也能跑起来。下面把这三套东西完整拆开讲,包括我踩过的坑和后来怎么改的。
提示:本文所有工作流都基于通用的对话式大模型接口设计,不依赖特定厂商的专有功能。你换成任何一家主流模型服务,逻辑都成立,只需要调整提示词里的措辞习惯。
2. 工作流一:带约束的代码生成与重构流水线
2.1 核心思路:把“写代码”拆成四段可控的对话
直接让模型“帮我写一个XXX功能”,得到的结果质量波动极大。我的做法是把一次代码生成拆成四个阶段,每个阶段单独一轮对话,上一轮的输出作为下一轮的输入。这四段是:
- 意图澄清:让模型先复述需求,列出它认为的输入、输出、边界条件。
- 接口设计:只产出函数签名、类型定义、错误码,不写实现。
- 实现填充:基于确定的接口写函数体。
- 自检与重构:让模型以审查者身份挑自己的毛病,给出修改版。
为什么这么拆?因为大模型在“同时做多件事”时最容易出错。让它一边想接口一边写实现,它经常在实现里偷偷改接口。分开之后,每一轮的输出空间被压缩,可控性大幅提升。
2.2 提示词模板与参数选择
第一轮意图澄清的提示词我固定成这样:
你是一名资深后端工程师。我将描述一个功能需求,你不需要写任何代码。 请输出以下四项: 1. 用一句话复述需求 2. 列出该功能的输入参数(名称、类型、是否必填、取值范围) 3. 列出输出结果与可能的错误情况 4. 列出三个你认为需要我确认的边界问题 需求描述:{requirement}这里有个细节:我要求它“列出三个需要确认的边界问题”,而不是“列出所有边界问题”。实测下来,要求“所有”会让模型凑数,列一堆无关紧要的;限定三个,它反而会挑真正关键的。
第二轮接口设计,我会把第一轮确认后的结果贴回去,然后要求:
基于以上确认的需求,只输出接口定义,使用 {language} 语言。 要求: - 包含完整的类型标注 - 每个参数和返回值上方写一行注释说明用途 - 不要写任何函数体实现,函数体用 pass 或 TODO 占位 - 如果涉及外部依赖,在注释中标注依赖名称第三轮实现填充时,我会明确告诉模型“只允许使用标准库和以下已声明的依赖”,把依赖列表写死。这一步能挡掉大量“幻觉依赖”——模型很喜欢引入一些根本不存在的包。
第四轮自检,提示词是:
请以代码审查者的身份检查上面的实现,重点检查: 1. 空值和边界输入的处理 2. 异常是否被正确抛出或捕获 3. 是否存在资源未释放的情况 4. 命名是否清晰 对每个问题给出具体行号和修改建议,然后输出修改后的完整代码。2.3 实操中的关键控制点
这套流程跑顺之后,我把每一轮的输出都存成文件,放在项目里的.ai-workflow/目录下,按01-intent.md、02-interface.md这样编号。这么做有两个好处:一是出问题时能快速定位是哪一轮跑偏了;二是新人接手时能看懂这个功能是怎么一步步定下来的。
参数方面,意图澄清和接口设计这两轮我用较低的温度值(0.2 到 0.3),保证输出稳定;实现填充用 0.4 左右,留一点发挥空间;自检那轮回到 0.2,让它严格挑刺。
注意:不要在同一轮对话里既让它写代码又让它解释代码。解释性文字会挤占输出空间,导致代码被截断。我一般要求“只输出代码,不要解释”,需要解释时单独开一轮。
2.4 一个真实的重构案例
上个月我要把一个六百多行的旧工具函数拆成几个小函数。直接丢给模型说“重构这个文件”,它给出的版本把好几个不相关的逻辑揉在了一起。后来我改用这套四段流程:
第一轮我只描述“这个文件做了哪几件事”,让模型帮我列出职责清单。它列出了七项,我核对后合并成四项。第二轮针对每一项设计独立的函数签名。第三轮分别实现。第四轮统一审查。
最终产出四个函数,每个都在八十行以内,职责清晰。整个过程花了大概四十分钟,比我自己从头拆快了不少,而且因为中间有确认环节,没有出现“重构完发现漏了某个分支”的情况。
3. 工作流二:测试用例的批量生成与去重
3.1 为什么测试生成最容易“看起来很美”
让模型生成测试用例,第一眼效果通常很惊艳——它能一口气写出几十个用例。但真正跑起来你会发现:大量用例在测同一件事,边界条件反而漏了,断言写得含糊(比如只断言“不抛异常”)。
我的解法是先分类、再生成、后去重。分类指的是把测试目标拆成等价类:正常输入、边界输入、非法输入、并发场景、资源耗尽场景。每一类单独生成,最后统一去重和补充断言。
3.2 分类生成的提示词设计
正常输入的提示词:
为以下函数生成正常路径的测试用例,使用 {test_framework}。 要求: - 每个用例只验证一个行为 - 断言必须具体到返回值或状态变化,禁止只断言“不报错” - 用例名称格式:test_{函数名}_{场景描述} - 至少覆盖三种不同的典型输入 函数代码:{code}边界输入的提示词则要求模型先列出边界点,再生成用例:
先列出该函数所有可能的边界输入(如空值、零、最大值、最小值、超长字符串等), 然后针对每个边界点生成一个测试用例。 每个用例上方用注释标明它覆盖的是哪个边界点。非法输入那轮,我会额外要求“每个用例必须断言抛出了特定类型的异常”,避免模型写出“随便抛个异常就算过”的测试。
3.3 去重与覆盖率补全
生成完之后,我会把所有用例喂给模型做一轮去重:
以下是针对同一函数生成的所有测试用例。请找出功能重复的用例, 合并它们,并指出哪些边界点还没有被覆盖。 输出合并后的用例列表和一份“未覆盖清单”。这一步非常关键。实测下来,分类生成加去重,能把冗余用例砍掉三成左右,同时补上两到三个原本遗漏的边界。
3.4 常见问题速查
| 问题现象 | 可能原因 | 处理方式 |
|---|---|---|
| 用例全部通过但线上仍出bug | 断言太弱,只验证不报错 | 强制要求断言具体返回值 |
| 生成的用例引用了不存在的工具函数 | 模型幻觉 | 在提示词中提供可用的工具函数清单 |
| 用例之间互相依赖,单独跑失败 | 共享了可变状态 | 要求每个用例独立初始化数据 |
| 边界用例重复 | 分类不清晰 | 先列边界点再生成,生成后去重 |
实操心得:我习惯在生成测试之后,手动挑一个用例故意改错断言,跑一遍确认它真的会失败。这一步能验证测试本身是有效的,而不是永远为真的空壳。
4. 工作流三:遗留代码理解与文档补全
4.1 面对没有注释的老代码,怎么让模型帮上忙
接手遗留项目最头疼的不是改代码,而是不知道这段代码为什么这么写。我的做法是自底向上:先让模型逐段解释,再让它汇总成模块级文档,最后人工校对关键假设。
具体分三步:
- 函数级解释:把每个函数单独喂给模型,要求它输出“这个函数做什么、依赖什么、被谁调用、有什么副作用”。
- 调用关系梳理:把所有函数级解释汇总,让模型画出调用链(用文字描述,不用图)。
- 模块文档生成:基于前两步,生成模块级 README,包含职责、关键流程、已知陷阱。
4.2 函数级解释的提示词要点
请解释以下函数,输出格式固定为: - 功能:一句话说明 - 输入:参数含义与取值范围 - 输出:返回值含义 - 副作用:是否修改全局状态、是否读写文件或网络 - 可疑点:你认为可能存在问题的地方(如未处理的异常、魔法数字) 函数代码:{code}“可疑点”这一项是我后来加的。一开始模型只做中性描述,加了这一项之后,它会主动指出一些我都没注意到的隐患,比如某个循环里对列表做了删除操作。
4.3 调用关系梳理的实操
把所有函数级解释拼成一个长文本,然后要求:
以下是某模块所有函数的解释。请梳理出函数之间的调用关系, 用缩进列表表示调用层级。对于存在循环调用或跨模块调用的地方, 单独标注出来。这一步的输出我会人工核对一遍,因为模型有时会把同名函数搞混。核对完之后,这份调用关系就成了后续改代码的地图。
4.4 文档补全的注意事项
生成的模块文档不能直接当正式文档用,必须人工过一遍。我一般重点检查三处:一是关键业务规则的描述是否准确,二是“已知陷阱”部分是否遗漏,三是文档里提到的配置项是否和实际代码一致。
注意:涉及业务逻辑的文档,一定要找熟悉该模块的同事确认。模型能读懂代码结构,但读不懂代码背后的业务约定。我踩过一次坑,模型把一段“临时兼容逻辑”当成了正式规则写进文档,差点误导后续开发。
5. 三套工作流共用的基础设施
5.1 统一的目录结构与命名规范
三套工作流我都放在项目根目录的.ai-workflow/下,子目录按工作流名称划分:
.ai-workflow/ codegen/ 01-intent.md 02-interface.md 03-implementation.md 04-review.md testing/ 01-normal.md 02-boundary.md 03-invalid.md 04-dedup.md legacy-doc/ 01-functions/ 02-callgraph.md 03-module-readme.md每个文件头部记录生成时间、使用的模型、温度值。这样半年后回头看,能知道当时是在什么条件下产出的。
5.2 提示词版本管理
提示词我会单独放在prompts/目录下,用 Git 管理。每次调整提示词都提交一次,写清楚改了什么、为什么改。这个习惯是从一次事故之后养成的:有段时间生成质量突然下降,查了半天才发现是有人改了提示词但没记录。
5.3 人工介入的检查点
三套工作流都有强制的人工检查点:
- 代码生成:接口设计确认后才进入实现
- 测试生成:去重后的用例列表需要人工过一遍
- 遗留文档:模块 README 生成后需要业务方确认
这些检查点不能省。模型再强,也不该在没有人确认的情况下直接改生产代码或生成正式文档。
6. 我踩过的坑和后来怎么改的
6.1 上下文塞太多反而变差
一开始我图省事,把整个文件甚至整个模块都塞进提示词,指望模型“通盘考虑”。结果发现输出质量反而下降——模型会抓不住重点,还容易在无关的地方乱改。后来改成“只给当前任务需要的上下文”,比如重构一个函数就只给这个函数和它的直接调用方,效果明显好转。
6.2 输出格式不稳定
早期我没强制输出格式,模型有时用列表、有时用表格、有时大段文字。后来所有提示词都加上“输出格式固定为……”并给出模板,稳定性大幅提升。格式固定之后,我甚至能写脚本自动解析输出,把结果直接写进文件。
6.3 模型会“讨好”你
如果你在提示词里说“请检查这段代码有没有bug”,模型倾向于找出几个问题来迎合你,哪怕代码其实没问题。后来我改成“请检查这段代码,如果没有问题就明确说没有”,它才敢说“未发现问题”。
6.4 长对话的遗忘问题
多轮对话超过一定长度后,模型会忘记前面的约定。我的对策是每一轮都把关键约定重新贴一遍,比如“再次强调:只使用标准库,不要引入新依赖”。虽然啰嗦,但有效。
7. 怎么判断一套工作流值不值得固化
不是所有AI辅助操作都值得做成工作流。我的判断标准有三条:
第一,重复频率。一周用不到一次的操作,没必要固化,每次现想提示词就行。
第二,输出可校验。如果输出结果没法快速判断对错,固化下来也是负担。代码能跑测试、文档能人工核对,这些才适合固化。
第三,步骤可拆分。能拆成独立阶段、每阶段有明确输入输出的,才适合做成流水线。那种“一句话进去、一坨东西出来”的任务,拆不开就别硬拆。
按这三条筛下来,我最终留下的就是上面这三套。它们覆盖了我日常工作中最高频的三类场景,每套都能在十分钟内跑完一轮,产出可以直接进入下一步流程。
最后分享一个小技巧:每套工作流跑完一轮后,我会花两分钟记录“这一轮哪里卡住了”。攒够五次卡点,就回头改提示词或调整步骤。工作流不是搭好就不动的,它需要跟着你的使用习惯一起迭代。我最早那版代码生成流程有六个步骤,用了一个月砍到四个,就是因为发现其中两步纯属多余。