1. 为什么说“一句话需求”是AI编程最大的坑
这两年AI编程工具越来越能打,从自动补全到多文件重构,再到能理解整个仓库的上下文,很多团队已经把AI Agent当作日常开发的标配。但实际用下来你会发现一个特别扎心的现象:同一个AI工具,在不同人手里产出质量天差地别。
有人一句话丢给AI“帮我写个订单管理页面”,得到的代码基本不能用——字段对不上、状态逻辑纠缠不清、接口参数全凭AI脑补;有人只需要把需求文档拆成字段级Spec再逐段喂给AI,生成的代码几乎可以拿来直接用。
差别到底在哪?我的结论是:AI编程的上限不在模型,而在你输入的需求规格。
AI本质上是一个“按规格生产”的引擎。你给它的是模糊意图,它只能回你模糊实现;你给它的是精确到字段、状态、约束的规格,它才能产出精确的结果。很多人在这一步偷懒,把“一句话需求”直接丢给AI,然后抱怨生成质量差,这个锅AI不该背。
我见过不少团队推行AI编程,一开始特别兴奋,觉得“以后提个需求就能出代码了”,结果一跑真实项目立刻碰壁。碰壁的原因几乎都是同一个:产品经理给的一句话需求、前后端接口文档缺失、字段命名不统一、状态流转没人说清楚。AI拿到这种输入,只能靠概率猜。猜对了是运气,猜错了是常态。
所以这篇文章想聊的就是一件事:怎么把模糊的一句话需求,拆成AI能直接消费的字段级Spec,让AI生成代码的准确率从“碰运气”变成“基本可控”。这中间涉及需求拆解的方法、Spec的结构设计、提示词的写法、以及围绕Spec组织AI编程流程的完整套路。我会用一个小项目作为贯穿全文的实战案例,把每一步拆开讲透。
先说我自己的背景:最近半年我一直在用各类AI编程工具做真实项目交付,从内部管理系统到对外的小型API服务都有涉及。中间踩过无数坑,也总结出一套相对稳定的方法论。今天这篇相当于把我自己的实操流程完整复盘一遍,希望能给正在用AI编程但总觉得“不怎么好用”的朋友一些可落地的参考。
2. 字段级Spec到底是什么,拆给谁看
2.1 一句话需求的典型困境
先看一个最常见的例子。假设产品丢过来一句话:“帮我做一个简单的客户管理功能,能增删改查就行。”
这句话里没有任何一个字段、没有任何一条校验规则、没有状态流转、没有权限边界。你让AI去写,它大概率会给你生成一个标准的CRUD——但这里的“标准”是AI自己脑子里的标准,不是你们项目的标准。
具体会出现什么问题?我给你列一下我实际遇到过的情况:
- 客户表的字段命名和你们现有的数据库规范不一致,比如你们项目统一用
customer_name,AI给你写client_name - 手机号校验规则写死了11位,但你们公司有座机、有海外号
- “删除”被实现成了物理删除,而你们项目所有表都要逻辑删除(
deleted_at标记) - 分页参数风格和你们现有接口不统一,别的接口用
page开头,AI写的是offset - 没有考虑唯一约束——客户名称重复怎么办?状态是否需要审批?
这些问题单看都不大,但加起来,AI生成的代码你基本要重写一遍。省下来的时间,全花在跟AI来回拉锯、清理烂摊子上了。
核心问题出在一个地方:你给AI的输入信息量太低了。一句话需求对于人来说足够,因为人有常识、有上下文记忆、知道你们项目的潜规则;但AI没有这些,它的所有行为都只能基于你给的提示词和它训练时见过的模式。
2.2 Spec的两个层级:模块级和字段级
既然问题在于信息量不足,解决办法就是提高输入信息的密度。这个密度分两个层级:
模块级Spec描述的是“这个功能模块整体长什么样”,包括:
- 模块的业务目标是什么
- 包含哪些子功能(页面/接口/操作)
- 和外部系统/模块之间的关联
- 核心业务流程和状态流转
- 非功能性要求(权限、性能、日志等)
字段级Spec描述的是“每一个数据字段的细节”,包括:
- 字段名称(精确到大小写和命名风格)
- 字段类型和长度
- 是否必填、是否唯一、默认值
- 取值范围和枚举值
- 校验规则(格式、正则、边界)
- 字段之间的依赖关系
- 展示规则(列表是否显示、表单如何渲染)
模块级Spec是骨架,字段级Spec是血肉。很多人写需求文档就停在模块级,觉得“功能说清楚了就行”,但如果AI编程要产出高质量代码,你必须把字段级也补齐。这不是写文档给评审看,而是把产品逻辑真正具象化,让你和AI都在同一套精确语义下工作。
2.3 一个小型实战案例:客户管理系统
为了把后面的方法论讲具体,我编一个贯穿全文的小项目:一个客户管理系统的最小闭环。这个项目足够简单——复杂到每个人都能理解业务,又足够覆盖去做一个真实模块所需要的各类细节。
产品的一句话需求就是开头那句:“帮我做一个简单的客户管理功能,能增删改查就行。”
我后面要做的,是把这句话一步步扩展成一份完整的、字段级的Spec,并围绕这份Spec让AI生成可用的代码。这个流程适用于任何规模的项目——你只需要把同样的方法套到更大的模块上,粒度细化即可。
3. 从一句话到字段级Spec的完整拆解流程
3.1 第一步:先和业务方把“功能边界”问透
拿到一句话需求,不要急着打开AI工具。先花半小时和业务方把细节问清楚。这一步做的事情不是写文档,而是搞清楚“到底要做什么”的每一个边界。
我常用的提问清单大概是这样的:
- 这个功能的用户是谁?(管理员、普通员工、外部客户?)
- 最核心的操作是什么?(录入客户、查询客户、还是修改客户信息?)
- 客户信息里必须包含哪些字段?哪些是可选?
- 客户有没有唯一性要求?比如公司名不能重复、手机号不能重复?
- 是否允许删除?删除是软删还是硬删?删除后还能不能找到?
- 同一个客户可能被多个员工跟进吗?需要归属人字段吗?
- 客户有没有状态?比如潜在客户、已签约、流失?
这些问题看起来琐碎,但每一个的答案都会直接影响后面的字段设计和AI生成的代码逻辑。业务方可能有些问题也答不上来,那就需要你基于常识和项目规范给出建议。我的原则是:能问清楚的先问清楚,问不清楚的给默认方案并在Spec里标注“待确认”。
还是拿客户管理系统举例,我假设和业务方聊完之后得到了下面这些关键信息:
- 使用场景是销售团队内部管理客户,不需要面向外部用户
- 核心字段包括:客户名称、联系人、联系电话、所属行业、客户状态、备注
- 客户名称全局唯一,不允许重复
- 联系电话允许座机和手机,格式校验不卡死
- 删除走逻辑删除,列表默认不显示已删除数据
- 客户状态只有三个:潜在客户、跟进中、已成交
- 客户归属人记录创建人,不需要多销售协作
就这7条,已经比“能增删改查就行”的信息量高出一个数量级了。
3.2 第二步:先产出模块级Spec
拿到业务信息之后,我来写模块级Spec。这个文档不需要太长,但要结构清晰、表述无歧义。模块级Spec的重点是把“功能范围和主流程”先定下来,这样后面拆字段才不会跑偏。
我习惯用“功能清单 + 业务规则 + 主流程”三块来组织模块级Spec:
功能清单:
| 编号 | 功能点 | 说明 |
|---|---|---|
| F1 | 客户列表查询 | 分页展示客户列表,支持按状态筛选和关键字搜索 |
| F2 | 新建客户 | 填写客户信息表单,包含唯一性校验 |
| F3 | 编辑客户 | 修改客户基本信息,状态独立变更 |
| F4 | 删除客户 | 逻辑删除,列表不再展示,管理员可在后台恢复 |
| F5 | 查看客户详情 | 展示客户完整信息 |
业务规则:
- 客户名称全局唯一,重复创建时提示错误
- 客户状态只能从“潜在客户”开始,不允许从“已成交”回退到“潜在客户”(除非管理员强制调整,这个先不做)
- 删除操作为逻辑删除,数据保留在数据库中,列表默认隐藏
主流程:
- 销售登录系统 → 进入客户列表 → 点击新建 → 填写表单 → 提交 → 系统校验唯一性 → 创建成功 → 回到列表 → 后续可编辑/删除/状态变更
模块级Spec到这里,已经足够指导下一步的字段级拆解,也足够让团队其他成员对需求有共同理解。但注意,这个粒度还不够让AI直接生成代码——因为你还没告诉他字段叫什么名字、是什么类型、校验规则是什么。
3.3 第三步:核心工作——逐字段拆解
这是整个流程里最重要的一个环节,也是最耗时的环节。我在做实际项目时,这个环节花的时间大概占整个需求阶段的一半以上。
字段级拆解不是把字段名罗列出来就完了,而是要为一个字段做一套完整定义。我提供一个我常用的表格模板,每个字段一行,字段信息写到最细粒度:
| 字段名 | 显示名称 | 类型 | 长度 | 必填 | 唯一 | 默认值 | 校验规则 | 备注 |
|---|---|---|---|---|---|---|---|---|
| id | 客户ID | BIGINT | - | 是 | 是 | 自增 | 系统生成 | 主键 |
| customer_name | 客户名称 | VARCHAR | 200 | 是 | 是 | 无 | 去首尾空格后判重;长度≤200 | 全局唯一 |
| contact_person | 联系人 | VARCHAR | 50 | 是 | 否 | 无 | 长度≤50 | |
| contact_phone | 联系电话 | VARCHAR | 30 | 否 | 否 | 无 | 允许手机/座机;仅校验字符集 | 不强制正则 |
| industry | 所属行业 | VARCHAR | 100 | 否 | 否 | 无 | 长度≤100 | |
| status | 客户状态 | VARCHAR | 20 | 是 | 否 | potential | 枚举: potential/following/deal | 见状态流转规则 |
| remark | 备注 | TEXT | - | 否 | 否 | 无 | 长度≤2000 | |
| created_by | 创建人 | VARCHAR | 50 | 是 | 否 | 当前用户 | 系统自动填充 | 关联用户表 |
| created_at | 创建时间 | DATETIME | - | 是 | 否 | 当前时间 | 系统自动填充 | |
| updated_at | 更新时间 | DATETIME | - | 是 | 否 | 当前时间 | 系统自动填充 | 每次更新刷新 |
| deleted_at | 删除时间 | DATETIME | - | 否 | 否 | NULL | 默认为NULL;删除时填充 | 逻辑删除标记 |
这张表看似简单,但它解决了AI编程里80%的“瞎猜”问题。为什么这么说?我给你拆解几个关键点:
字段命名:customer_name而不是name,contact_person而不是username,这些命名规范直接决定了代码的可维护性。如果你不规定,AI生成clientName、personName之类的字段,后面的重构成本极高。
类型和长度:AI从“一句话需求”推断字段类型,50%的概率会推断错。你说“联系电话”,它可能给你生成int类型,存一个13812345678直接溢出;你说“备注”,它可能生成varchar(255),长一点的备注就写不进去。Spec里写清楚类型和长度,就是从源头上杜绝这类低级错误。
校验规则的边界:“允许手机/座机,仅校验字符集”这个描述,比写死一个手机号正则要精确得多。因为你写的正则一旦卡死,海外号、座机号全部没法录入,业务直接就“断”了。校验规则宁可宽松到业务能接受,也不要严格到业务跑不通。
3.4 第四步:补状态流转和操作约束
字段级表格完成之后,还有两类信息需要单独描述:状态流转和操作级约束。
状态流转如果不写清楚,AI会自己发挥。比如字段定义里给出了三个枚举值,但AI不知道“已成交之后不能直接改回潜在客户”这个规则,它生成的代码可能就是普通的UPDATE,随便改状态。所以我会在Spec里单独加一段状态机描述:
- 初始状态:potential - potential -> following:销售开始跟进后手动变更 - following -> deal:完成签约后手动变更 - deal:终态,不允许变更到其他状态 - 任何状态都允许编辑除status外的其他字段 - 任何状态都允许删除(逻辑删除)操作级约束描述的是每个功能点对应的接口行为。比如对于删除操作,我要写明“删除时先检查客户是否存在,存在则更新deleted_at为当前时间,不物理删除”;对于新建操作,我要写明“先检查customer_name去重,重复则返回错误提示”。
为什么这一点极其重要?因为AI很容易把删除实现成DELETE FROM,把唯一性校验实现成前端简单判断。这些业务逻辑一旦不写明白,AI生成的东西就只是“看起来像CRUD”,实际上到处是坑。
4. 把Spec变成AI提示词:结构化输入的正确姿势
4.1 Spec写完之后,不要整篇甩给AI
很多人以为Spec写完直接丢给AI就行,其实不是。一份完整的Spec信息量很大,AI的上下文窗口虽然越来越大,但一次性塞太多细节反而会降低生成质量,你发现它写着写着就忘了前面的约束。
我的做法是:按照模块拆分、按功能点逐个让AI生成代码。客户管理系统这个例子,我把它拆成了这几个会话:
- 数据库表结构 + 模型层(基于字段级Spec)
- 客户列表查询接口(含筛选、分页、逻辑删除过滤)
- 新建客户接口(含唯一性校验、字段校验)
- 编辑客户接口(含状态流转规则)
- 删除客户接口(逻辑删除)
- 客户详情接口
- 前端列表页
- 前端新建/编辑表单
每开一个会话,我只把相关的Spec片段+当前任务描述给AI。这样做的好处是:AI的注意力能集中在当前这个功能点上,不会因为信息过载而“开小差”。
4.2 提示词模板:项目上下文 + 任务描述 + Spec片段 + 输出要求
我自己反复调整后觉得比较好用的提示词结构是四段式,你可以直接拿去用:
【项目背景】 这是一个客户管理系统,后端技术栈为 Java Spring Boot 3 + MyBatis-Plus,数据库为 MySQL 8。项目已有统一响应体 R(String code, String message, Object data),分页参数统一使用 page (从1开始) 和 pageSize,逻辑删除通过 deleted_at 字段实现,所有实体继承 BaseEntity(包含 id、created_at、updated_at、deleted_at)。 【任务】 生成“新建客户”功能的 Service 层代码,包含唯一性校验和状态初始值。 【Spec片段】 - 表结构见字段定义表:customer_name 必填且全局唯一;contact_phone 可选;status 默认 potential - 校验规则:customer_name 去除首尾空格后不能为空,长度不超过200;contact_phone 长度不超过30 - 业务规则:创建时必须校验 customer_name 是否已存在,存在则返回错误码 CUSTOMER_NAME_DUPLICATED - 所有错误返回均使用统一响应体 R 【输出要求】 1. 只输出代码,不输出解释 2. 接口签名使用 R<Long> createCustomer(CustomerCreateCmd cmd) 3. 使用 MyBatis-Plus 的 LambdaQueryWrapper 做唯一性校验 4. 不要生成 Controller 和 Mapper 层模板里最关键的是**“项目背景”和“输出要求”**这两块。前者给AI提供了足够的上下文(技术栈、已有约定、类结构),后者把输出的边界卡死了(不要给我多生成不需要的东西)。没有这两块的提示词,AI很容易产出一大堆你用不上的代码。
4.3 为什么“先给Spec,再让AI写单点功能”比“直接让AI全栈开发”靠谱
这个策略我用一句话概括:**把大任务切碎,让AI每次只做一件确定性最高的活。**全栈开发的诱惑力很大,你一句“帮我实现整个客户管理系统”,AI能给你生成几十个文件——但每个文件你都得仔细审查,很多文件之间还有隐含的不一致,排查成本高到爆炸。
单点功能生成的优势是:验收标准清晰,一个功能生成完,review完,合入,再开始下一个。这样做有三个好处:
- 出错的影响面小,一个方法写得不好,改一个方法就行
- 每个功能都能独立测试,有问题立刻暴露
- 穿行上下文少,AI不需要在同一份提示词里权衡太多约束,生成质量更稳定
这套流程下来,我实测“AI生成代码可复用率”能从一种很不稳定的状态(大概30%-40%左右)提升到稳定的70%-80%以上——剩下的20%-30%主要集中在一些非常具体的业务分支逻辑上,需要人肉补齐。
5. 给AI喂Spec时的提示词工程进阶技巧
5.1 如何让“核心字段”不丢失
AI生成代码有一个通病:你给了丰富的Spec,但它写着写着就丢字段。比如customer_name做判断的时候写成了contact_person,或者状态流转处理的时候忘了默认值问题。
我摸索出来的对付办法是:在提示词里有意识地使用“结构化引用”。不要只是把整个Spec贴在提示词里,而是用“我在下面定义了字段清单,所有字段名必须严格按此命名”这种强约束句式,并且在Spec片段之前加上“以下内容为不可偏离的字段定义”。
当然提示词只是增强约束,没办法百分之百锁死AI的发挥。真正靠谱的兜底是生成代码后的自动检查。我在实际项目中会用脚本把Spec里的字段清单提取出来,再去生成的代码里做关键字匹配,确认为核心字段都出现了。这一步我会写在后面“效果评估”那一节里,这里先留个悬念。
5.2 “正例 + 反例”比单纯说规则更管用
这一条是我自己的体会。你光说“这个字段要唯一校验”,AI可能理解成“提示用户”“检查非空”——它有很多种理解方式。但如果我在提示词里同时给一个正确实现的反例,效果会好很多。
比如我会在Spec后面补一句:
正确写法示例(参考风格,不要照抄): private void validateCustomerNameUnique(String name) { long count = customerMapper.selectCount( new LambdaQueryWrapper<Customer>() .eq(Customer::getCustomerName, name) .eq(Customer::getDeletedAt, null)); // 逻辑删除过滤 if (count > 0) { throw new BizException("CUSTOMER_NAME_DUPLICATED", "客户名称已存在"); } }注意这里我不仅给了代码风格参考,还顺便把“逻辑删除过滤”这个容易漏掉的点写进去了。AI看到类似的范例代码,比只看文字描述要容易产生正确理解。
5.3 动态变化的需求,怎么维持Spec和代码的同步
现实项目里需求一定会变,Spec也不会是一成不变的。我最怕的情况是:产品改了一个字段,我更新了Spec,但AI已经生成完的代码里还是旧的字段名——两头对不上,最后有一堆不一致的问题。
这块我的经验是:每次修改Spec之后,把变更点单独列出来,用“只改这些”的方式重新生成受影响的部分,而不是重新生成整个模块。
举例,如果客户管理系统里新增了一个字段customer_level(客户等级),我会这样给AI提示:
【变更说明】 在现有 Customer 实体中新增一个字段 customer_level,类型为 VARCHAR(20),默认值为 normal,枚举包括 normal/silver/gold/platinum,其余字段不变。 【任务】 1. 更新 Customer 实体类,添加 customer_level 字段及相关注解 2. 更新新建客户接口的入参对象 CustomerCreateCmd,添加 customer_level 3. 更新数据库建表语句,增加 customer_level 列(如需迁移脚本也一并生成) 4. 前端表单在新建/编辑页增加客户等级下拉框,选项为上述四个枚举值这样的局部变更提示词,比起“再帮我生成一遍客户管理”要可控得多。AI只需要处理增量变化,不需要重新考虑整个模块,出错的概率能压低不少。
6. 围绕Spec的AI开发流程如何落地到真实项目
6.1 一套可复用的开发节奏:Spec评审 → 单点生成 → 代码审查 → 合入
我在团队里推行的节奏是四步循环,每一步都有明确产出物和验收标准:
- Spec评审:产品、研发一起过一遍字段级Spec,确认字段、校验规则、状态流转都符合预期
- 单点生成:按前面说的方式,一次只让AI做一个功能点
- 代码审查:Review生成的代码,重点查字段名、校验规则、异常处理、逻辑删除等Spec里定义过的内容是否被遵守
- 合入:通过人肉审查和自动化检查后合入主干
这个节奏最关键的价值在于:把Spec当作代码审查的checklist。以前审查代码靠经验,靠对照着需求文档“感觉一下”,现在直接对照字段级Spec逐项核对。谁是必填字段,AI有没有处理非空判断?Status的流转规则,AI有没有在状态更新时校验合法性?这些都能精确检查,而不是凭感觉。
6.2 项目里的真实效果:时间账和质量变化
我不吹数据,就说我的真实感受。以前手写一个客户管理模块,从建表到接口到前端页面,大概需要2到3天。用这套Spec驱动AI开发的流程之后,建表、模型层、CRUD代码大约半天能搞定,剩余的时间花在联调和一些特殊业务逻辑的处理上。
更值钱的变化是在代码一致性上。以前不同开发写出来的CRUD风格都不一样——有人分页从0开始,有人从1开始;有人统一返回R,有人直接返回裸对象;有人做了逻辑删除过滤,有人忘了。Spec把这些都锁死了,AI每次生成出来的代码风格高度一致,代码审查的压力小很多。
6.3 自动化检查:让Spec里的约束变成脚本里的断言
这个是我自己额外做的一层保障,觉得挺值得分享的。
因为字段定义是结构化的表格,我会写一个小脚本扫描AI生成的代码,检查三个维度的约束:
- 字段名是否都在代码里出现(防丢失)
- Spec里标记“必填”的字段,在代码里是否有
@NotNull之类的校验 - 状态枚举值是否有遗漏(对照Spec的三个值在代码里做匹配)
这个检查不是用来替代人肉审查的,它是用来快速筛掉“低级遗漏”的。实测下来,用这个小脚本能在合入前拦掉大约15%-20%有明显问题的生成结果。
7. 什么样的需求适合“字段级Spec + AI生成”,什么样的不适合
7.1 适合的场景
基于我这半年来的实践,以下场景用字段级Spec驱动AI开发的效果最明显:
- CRUD类业务模块:管理系统、后台、报表、审批流,这类功能结构化程度高、字段明确、逻辑相对标准,AI发挥空间大且准确率高
- 表单和列表页面:前端表单的字段渲染、校验、提交逻辑,只要Spec里定义了字段名和校验规则,AI基本能一次生成跑通的页面
- 接口和模型层:给定表结构定义,AI生成Mapper、Service、Controller这层的代码准确率非常高
- 批量生成相似模块:一个系统里有10个类似的模块,每个模块的字段和规则都差不多,一旦整理出一个模板Spec,剩下9个就是复制粘贴换字段名
7.2 不适合的场景
反过来,下面这几类情况我要特别提醒,不要硬套这套流程:
- 逻辑高度自定义的业务:比如复杂的价格计算、多维度的优惠叠加规则、供应链里的复杂排程——这类业务逻辑的核心难点根本不在字段定义,而在算法设计。Spec只能解决“字段长什么样”,解决不了“逻辑怎么推演”。
- 强交互的UI界面:拖拽画布、流程图编辑器这种重度交互组件,Spec能定义数据和事件,但生成出来的交互细节往往不符合产品预期,需要大量人工调整。
- 架构层面的决策:要不要拆微服务、用不用消息队列、缓存策略怎么设计,这些是技术架构决策,不是字段级Spec能回答的问题。这种时候你还是得靠自己(或和有经验的同事一起)做判断。
我看到过很多团队拿着AI编程工具,啥需求都往里丢,然后对结果感到失望。其实AI编程跟任何工具一样,它适合的场景和它不适合的场景同样清晰,搞清楚边界再上,体验完全不一样。
7.3 判断标准:能具象到字段的就是好喂给AI的需求
我给自己定了一个很简单的判断标准:**当产品需求能拆解成“字段名+类型+校验规则+流转规则”的时候,就意味着AI已经具备了生成高质量代码的基本输入条件。如果拆了老半天只有一句话“逻辑比较特殊”,那就说明这个需求还没想清楚,或者太依赖人的场景经验,先把它放一边。
8. 一次典型的实操复盘:客户列表查询功能的完整过程
这一节我想完整走一遍,“一个具体的功能是怎么从Spec变成AI代码的”。以客户列表查询接口为例,我带你完整过一遍我的操作过程。
8.1 先定义好接口的输入输出
列表查询这个功能,对应的Spec片段如下:
【功能点】F1 客户列表查询 【接口路径】GET /api/customers 【入参】page(从1开始),pageSize(默认10),status(可选,不传查全部),keyword(可选,模糊匹配客户名称和联系人) 【出参】R<PageResult<CustomerVO>> 【CustomerVO字段】id, customerName, contactPerson, contactPhone, industry, status, createdAt 【业务规则】 1. 默认只查 deleted_at IS NULL 的数据 2. 状态筛选:status 不等于空时才追加条件 3. 关键字搜索:keyword 不等于空时,匹配 customer_name OR contact_person LIKE 4. 排序:按 created_at DESC8.2 给AI的最终提示词
基于上面这段Spec,我会在AI工具里这样提问:
【项目背景】 客户管理后端,Spring Boot 3 + MyBatis-Plus + MySQL 8,统一返回体 R(code,message,data),分页结果统一使用 PageResult<T>(包含 total、records 两个字段)。已有实体类 Customer,包含 id、customerName、contactPerson、contactPhone、industry、status、remark、createdBy、createdAt、updatedAt、deletedAt 字段。 【任务】 实现“客户列表查询”接口的 Service 层方法。 【Spec片段】 - 入参:page、pageSize、status、keyword - 条件:只查 deleted_at IS NULL;status 非空时按 status 精确匹配;keyword 非空时按 customer_name 或 contact_person 模糊匹配 - 排序:created_at DESC - 返回 PageResult<CustomerVO>,CustomerVO 字段包括 id、customerName、contactPerson、contactPhone、industry、status、createdAt 【输出要求】 1. 使用 MyBatis-Plus 的 LambdaQueryWrapper 构建查询条件 2. 输出一个完整方法,不要分步 3. 不需要生成 Controller、Mapper、VO 定义,假设已存在 4. 注意 keyword 的模糊查询要包含 AND deleted_at IS NULL 条件8.3 AI生成结果和我做的微调
这段提示词生成的代码,大概90%我是满意的。分页逻辑OK、逻辑删除过滤OK、状态筛选OK。但有一个细节AI写得不太对:关键词查询的时候,它只匹配了customer_name一个字段,没有匹配contact_person。
这其实不算AI的锅——我的Spec里写了两个字段,但它权重分配时可能觉得“客户名”更重要,就只生成了一个字段的匹配。我直接在回复里追了一句:
keyword 模糊匹配需要同时匹配 customer_name 和 contact_person,用 or 包裹,保持原风格修改。AI立刻改了,第二次就是对的。
这个经历正好说明前面说的问题:Spec驱动不等于AI零失误,但Spec驱动让失误变得可预期、可发现、可低成本修正。因为对照Spec,你能清楚知道它哪里漏了,然后精准指出来,而不是面对一堆代码抓瞎。
9. 字段级Spec的维护:别让文档和代码分家
最后聊一个比较容易被忽略但非常重要的话题:Spec写完之后怎么维护。
谁维护?怎么维护?如果Spec只在最开始写一次,后面再也不更新,那过两周就过期了——代码已经在演进,文档还停在旧版本。
这里我的建议很朴素:**把Spec当作代码仓库的一部分来管理。**具体做法是:
- 把字段级Spec表格放进仓库,比如放在
docs/specs/目录下 - 每次需求变更,先改Spec再让AI改代码
- 让AI改完代码之后,把Spec的相关部分回读一遍,确认它理解的和你要的一致
这套流程走下来,你的Spec就成了“活文档”——它跟代码同生共死,你说的字段、规则、状态流转,在代码里都能找到对应的实现。长远来看,这比起维护一份跟代码脱节的需求文档要省心得多。
另外想提一个细节:**建议用表格或固定格式的Markdown维护Spec。**因为结构化的内容更容易做差异对比,也更方便写脚本做一些自动化检查。如果你用大段自然语言描述字段,人看着确实挺舒服,但脚本没法解析,后续的自动化能力基本就废了。
10. 实操中踩过的坑和最后想说的话
10.1 几个真实踩坑记录
我把自己在“AI代码需求实战”中最常翻车的几个点整理一下,希望你别重蹈覆辙:
**一是Spec写得过于细致导致AI“为了合规而过度实现”。**有一次我在Spec里写了很长的校验规则列表,AI生成的代码里加了20多个判断条件,有些条件业务上根本不需要。后来我调整了写法——在Spec里标注哪些规则是“关键约束”,哪些是“描述性参考”,并且要求AI只对关键约束做硬校验。
**二是直接让AI跑完整流程,跳过了“人工审查”这一环。**有次我觉得Spec已经足够清楚,AI生成的代码也看着不错,直接合入了。结果有个隐藏bug:更新操作没有把updated_at刷成当前时间,测试的时候才发现。从此以后,哪怕AI代码看起来再完美,我也坚持把Spec逐项核对一遍。
**三是没有处理好空值和默认值的语义。**比如“联系电话可选”这一条,在接口里到底它是null、空字符串、还是不传该字段,这三种语义完全不一样。AI最容易做的事就是把空字符串当成“用户填了空值”,如果业务上要求可选字段允许不传,它可能直接报错。这个必须你在需求定义阶段就跟业务对齐清楚。
10.2 最后想分享的个人体会
AI编程这事,说实话最根本的转变不在于你掌握了多少提示词技巧,而在于你愿不愿意把需求描述这件事,从“大概说说就行”变成“精确到字段级的定义”。前者是人的聊天习惯,后者是工程的做事方式。如果你自己都不愿意把字段理清楚、把规则写明白,AI生成出来的代码一定是很粗糙的。
反过来,一旦你养成了“先拆Spec再写代码”的习惯,哪怕不用AI,你纯手写代码的效率也会提升。因为需求清晰,开发就是在翻译确定性极高的规格,跟“边想边写边猜”完全不是一个量级的体验。
这篇文章的方法不复杂,难的是习惯的改变。我建议你下次拿到“一句话需求”的时候,先别急,多花一两个小时把字段级Spec拆出来。试过一次,你就会理解为什么我这么强调Spec——那种“AI生成的代码拿过来基本能合入”的感觉,确实是会上瘾的。