ARTICLE DETAIL

资讯详情

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

AI基于Spec开发是巨坑?从踩坑到实战的正确姿势

AI基于Spec开发是巨坑?从踩坑到实战的正确姿势 你有没有过这种经历拿到一份写得很详细的接口文档打开AI编程工具把Spec一贴让它“照这个实现”结果生成出来的代码跑起来全是问题不是字段对不上就是业务逻辑张冠李戴。改来改去比你自己手写还慢。于是你得出一个结论——AI基于Spec开发是巨坑。这个结论我太熟悉了因为我自己就踩过不少。但作为一个在开发一线折腾多年的老油条我想说这个“坑”很大程度不是AI不行而是我们用错了姿势。Spec开发这活儿本身对“人”的要求就很高指望AI像老员工一样看懂一份残缺的PRD就自动写出完美业务代码本身就是个不切实际的预期。这篇文章我想把AI基于Spec开发的真实情况掰开揉碎讲一遍包括我踩过的坑、背后到底卡在哪里、以及现在我认为比较好用的几个实操套路。如果你正准备在团队里推行“AI照着规格说明书写代码”这篇文章应该能帮你少走很多弯路。1. 先搞清楚AI基于Spec开发到底是个什么活儿1.1 Spec不是“需求文档”那么简单很多人听到Spec第一反应是接口文档。实际上在工程语境里Spec可以指代好几类东西最基础的是API Spec比如OpenAPISwagger那种JSON/YAML定义描述路径、参数、响应结构其次是数据模型Spec比如数据库表结构、JSON Schema、Protobuf定义再往上还包括行为规格比如用Gherkin写的Given-When-Then场景或者一套完整的业务规则描述。“基于Spec开发”这句话真正落地的时候其实有完全不同的做法。第一种是让AI直接根据Spec生成完整功能代码这是最容易翻车的第二种是让AI生成Spec对应的类型定义、Mock数据、单元测试用例这个相对安全第三种是让AI Agent智能体拿到一份Spec之后自主地去建项目、写代码、跑测试、迭代修复这就是所谓的“AI程序员”模式听起来很酷但坑也是最深的。我见过不少团队把AI当成一个“超级外包员工”觉得只要Spec写得够详细AI就能输出完美代码。但真实情况是Spec是一种高度压缩的信息载体它默认读者已经掌握了大量背景知识——比如业务领域的概念、团队内部的命名习惯、哪些字段是兼容历史数据必须保留的。这些背景知识几乎不会写进Spec里但人看的时候能脑补AI却只能靠猜。这就是问题的起点。1.2 为什么大家明知道是坑还是要往里跳谁不想把烦人的CRUD接口开发交给AI对于大部分业务系统真正花时间的不是写代码本身而是对齐字段含义、理清状态流转、处理各种边界条件。Spec恰好是这些信息的载体所以很多人第一反应是如果我把Spec喂给AI它不就能帮我把这些重复劳动全干了这个逻辑本身没错错在低估了两个问题。一是Spec的“表达精度”远没有想象中高打开任意一份OpenAPI文件你会发现很多响应字段的描述是“status 状态码”“data 数据”连枚举值都没列全。你指望AI从这种描述里推导出完整业务逻辑它只能发挥“想象力”。二是AI的“验证能力”基本为零写代码的人写完可以立刻跑测试但AI生成完代码之后它自己并不知道这段代码对不对尤其在没有完整测试环境的前提下它只能靠代码编辑器和静态分析工具做浅层检查。还有一个很现实的因素是团队协作。程序员之间可以通过即时沟通确认需求但AI没有这个机会。当你说“这段逻辑根据Spec第3.2条实现”的时候AI不会反问“第3.2条里的A字段和B字段是什么关系如果A为空怎么处理”它只会安静地给你一个看似合理的实现。这种单向的信息传递决定了基于Spec开发的失败率天然就高。2. 踩坑实录AI基于Spec开发翻车现场2.1 最常见的六种翻车姿势我把过去一年在各个项目里实际遇到的问题整理成了六类每一类都对应具体的场景方便你对号入座。第一类是字段名张冠李戴。Spec里定义的响应字段是user_idAI生成代码时却自作主张变成了userId或者数据库里存的是uid它没有做映射直接把Spec字段当数据库字段用了。这类问题在前后端联调时才会暴露往往是运行时才发现。第二类是边界条件完全缺失。Spec写了“创建订单接口参数:商品ID,数量”AI生成的代码只会校验参数不为空然后直接计算总价。但真实业务里必须考虑库存不足怎么办、商品下架怎么办、用户积分优惠怎么叠加、重复提交怎么防——这些都没写在Spec里AI自然也不会处理。最要命的是它生成的代码看起来逻辑完整你很难一眼看出缺了哪些校验。第三类是状态机被简化。Spec里定义了订单状态枚举CREATED、PAID、SHIPPED、COMPLETED、CANCELLEDAI可能只实现了从CREATED到PAID到COMPLETED的直线路径忽略了PAID之后还能发起退款进入REFUNDING状态更不关心状态流转的合法性校验。第四类是外部依赖的接口暗坑。如果你的代码要调用支付服务、库存服务、优惠券服务Spec里通常只有一句话描述“调用支付接口完成支付”。但真实接入时会涉及超时重试策略、幂等键、回调验签、分布式事务边界AI很可能只给你一个最简单的HTTP调用然后就没有然后了。第五类是Spec变更后AI不感知。你今天上午生成代码用的Spec还是v1.0下午产品经理把字段amount改成了total_amount并且新增了一个discount字段。AI根本不知道你已经改了Spec你再拿同一份上下文去提问它可能会继续用旧字段生成新代码导致新旧代码混在一起灾难级隐患。第六类是AI的“自信幻觉”。这是最隐蔽的坑。你问AI“这个接口的响应结构是否和Spec一致”它可能会斩钉截铁地回答“完全一致”但实际代码里多了一个它自己臆想的字段remark少了一个Spec里明确要求的trace_id。为什么会这样因为AI是根据概率生成文本的它并不像人类一样去逐一对照Spec条款。写代码的人起码会用眼睛检查一遍但AI不会做这种“无聊”的差事。2.2 这些坑背后的深层原因把上面这些现象归纳起来其实就是四个字语义鸿沟。Spec是人类用自然语言和结构化格式表达的意图中间有大量“省略号”。比如Spec里写“用户登录后返回用户信息”人知道“用户信息”包含哪些东西但AI需要去猜。猜对了是运气猜错了是常态。另一个深层原因是上下文窗口有限。虽然现在的大模型能处理几十万字但真实项目代码量远超上下文窗口。你把一份几百行的OpenAPI Spec贴进去再让它生成整个服务端的Controller、Service、Mapper、数据库脚本它不可能把所有文件都“记住”。实际上它每生成一个文件都可能忘了Spec里另一个文件的约束。多文件之间的关联约束是AI编程天然的软肋。还有一个原因是缺少反馈回路。人类开发者写完代码就跑测试测试失败就改代码这是一个“执行—验证—修正”的闭环。但AI生成代码的时候大多数工具链里并没有自动执行测试并回传失败信息给AI的机制。尤其是当你用AI Agent自主开发时它写了一堆代码运行报错它确实会尝试修复但修复的依据往往还是同一份信息不足的Spec而不是真实的运行时堆栈。这就是为什么AI Agent在一些演示视频里看起来很强到了真实业务里就翻车的重要原因。3. 不是不能用而是要用对AI基于Spec开发的正确姿势3.1 把Spec当“契约测试”而不是“生成蓝图”既然让AI直接照着Spec写完整业务代码这么容易翻车正确的思路就应该反过来让Spec成为约束和校验的基准而不是生成逻辑的全部依据。具体来说AI最适合干的活是拿Spec生成“可以验证的东西”比如接口的类型定义、Request/Response的DTO、Mock数据、单元测试用例甚至是契约测试。契约测试这个概念值得展开说。它的核心思想是在微服务架构中服务之间的依赖不是靠联调环境验证的而是通过消费方驱动的契约测试来保证。这样你手里有一份OpenAPI Spec就可以让AI生成Provider端和Consumer端的契约测试代码每个测试都断言“接口响应必须符合Spec定义的字段结构和类型”。这时候AI如果生成了错误的字段名测试会直接失败问题在提交代码前就能暴露。举个实际例子。我之前做一个订单服务拿到一份OpenAPI Spec之后不是让AI去实现Controller而是让它先根据Spec生成契约测试集和数据类型定义。AI生成的DTO长这样# ai_generated_dto.py from pydantic import BaseModel from typing import Optional class OrderCreateRequest(BaseModel): product_id: int quantity: int 1 class OrderCreateResponse(BaseModel): order_id: int total_amount: float status: str这个生成物是完全可以直接用的。我只需要检查字段类型是否匹配然后跑一遍契约测试就能快速确认Spec和实现之间有没有偏差。如果连契约都没对齐后面的业务逻辑实现再快也没有意义。3.2 让AI先写Spec再写代码另一个我常用的反向操作是先让AI基于混乱的需求描述帮你整理出一份初步的Spec由人工确认后再让AI写代码。这么做看起来多了一步实则能省掉大量返工。大多数需求刚提出来时其实是一团浆糊。产品经理可能会说“我们要做一个优惠券领取功能用户每天只能领一次领取后7天有效”。这句话里藏着好几个关键决策点同一天是指自然日还是24小时滚动过期时间是领取时间加7天还是到第7天的23:59:59优惠券有没有总量限制这些不写清楚你直接让AI写代码它必然会选一个“看起来最合理”的方案而这个方案十有八九和产品真实想法不一致。正确的操作是先让AI针对这段需求提出澄清问题或者生成一份草案Spec包含字段定义、状态枚举、业务规则。然后由人逐条审阅、补充、修改直到Spec没有歧义再进入编码阶段。这一步的本质是把AI当成“文档初稿生成器”而不是“代码生成器”。用提示词可以这样写我是订单服务开发者。请根据下面的业务需求生成一份OpenAPI 3.0规格草案要求 1. 列出所有接口路径、请求参数、响应结构 2. 定义订单状态枚举及合法流转 3. 指出需求中存在的模糊点至少3个 4. 不要写实现代码。 需求描述用户可以通过积分兑换商品兑换后生成兑换订单订单24小时内未支付自动取消……AI生成的草案往往能覆盖80%的字段定义剩下20%的模糊点是我们人工补齐的关键。这比直接让它写代码要可控得多。3.3 将Spec拆碎配合TDD小步走就算Spec已经很完善了我也不建议“一次把整个Spec喂给AI然后让它生成所有代码”。这是因为大任务会放大AI的幻觉概率。更稳的做法是把Spec拆成一个个极小的功能点每实现一个功能点就用测试去锁住它。我在实际项目中习惯把开发步骤切成这样从Spec中挑一个最小的接口或一个行为规则。让AI先写一个会失败的测试TDD的Red阶段。让AI实现刚好能让测试通过的代码Green阶段。跑测试失败就把错误信息回传给AI让它修。一个功能点稳定后再进行下一个。这种模式的核心价值在于AI每走一步都有验证反馈。测试失败的信息是具体、明确的AI修复起来远比从Spec里猜逻辑容易。比如订单接口有一个规则“创建订单后如果用户积分不足500抛出一个业务异常”那么测试代码就长这样def test_create_order_with_insufficient_points(): user create_user(points100) with pytest.raises(InsufficientPointsError): order_service.create_order(user.id, product_id1)把这段测试丢给AI它的修复目标非常清晰让这个测试通过。整体下来虽然交互次数变多了但每次都是一个小闭环错误能被快速发现和修正最后代码质量比一次性生成高出一个量级。4. 实操案例一个迷你订单接口的AI开发全记录4.1 项目背景与Spec原文为了让你更直观地理解前面说的那些方法我用一个实际跑过的迷你案例完整走一遍。假设我们要实现一个“创建订单”接口背景是电商系统需要从库存服务扣减库存然后生成订单记录。我们的Spec简化为OpenAPI片段openapi: 3.0.0 info: title: Order Service version: 1.0.0 paths: /orders: post: summary: 创建订单 requestBody: required: true content: application/json: schema: type: object required: [product_id, quantity, user_id] properties: product_id: type: integer quantity: type: integer minimum: 1 user_id: type: integer responses: 200: description: 创建成功 content: application/json: schema: type: object properties: order_id: type: integer status: type: string enum: [CREATED, PAID, CANCELLED]看起来很简单对吧字段都列全了必填也标了。但如果真让AI直接按这个Spec写业务代码几乎必然翻车因为里面没写库存是同步扣减还是异步扣减、扣减失败怎么处理、订单号怎么生成、重复请求怎么防。4.2 AI生成的代码与问题实录我先用最原始的方式把上面的OpenAPI YAML原封不动发给一个知名的AI编程助手要求它“用Python FastAPI实现这个接口”。它生成的代码简化后如下# ai_generated_first_try.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel app FastAPI() class OrderCreateRequest(BaseModel): product_id: int quantity: int user_id: int class OrderCreateResponse(BaseModel): order_id: int status: str app.post(/orders, response_modelOrderCreateResponse) def create_order(req: OrderCreateRequest): # 这里AI自行脑补了库存扣减逻辑 inventory_result deduct_inventory(product_idreq.product_id, quantityreq.quantity) if not inventory_result.success: raise HTTPException(status_code400, detail库存不足) order_id generate_order_id() return {order_id: order_id, status: CREATED}一眼看上去确实像模像样。但实际跑起来问题不少。第一点deduct_inventory这个函数AI没有实现它只给了一句注释代码根本不能运行。第二点generate_order_id也是凭空造出来的。第三点接口没有做任何幂等处理如果用户快速点击两次提交就会生成两笔订单。第四点user_id虽然接收了但完全没有被用到AI根本没有意识到需要去校验用户是否存在。第五点它把库存不足直接映射成HTTP 400这在业务上可能合理但缺少对“库存扣减成功但订单创建失败”这种分布式事务问题的处理。这些问题的本质是什么就是AI在没有上下文的情况下对Spec中未定义的部分进行了“看似合理的补全”。Spec里没有“幂等”这个词它就默认不需要Spec里没有用户状态校验它就默认不需要。这不是AI笨而是它根本没有办法知道你心里的“潜规则”。4.3 复盘我最终是怎么让AI跑通的发现问题之后我没有去责怪AI而是调整了策略。我做了三件事。第一把“未定义行为”显式地列出来写进给AI的提示里。第二用测试闭环驱动AI修复。第三把库存服务、用户服务的接口契约也一并提供让AI不再猜。我给AI的新指令是这样的项目背景订单服务依赖库存服务POST /inventory/deduct参数product_id, quantity返回{success, message}和用户服务GET /users/{user_id}返回{exists, points}。 业务规则 1. 创建订单前需检查用户存在且积分不少于500 2. 先扣减库存扣减失败则返回业务错误码 INSUFFICIENT_STOCK 3. 订单号用雪花ID生成保证唯一 4. 同一用户同一商品在1秒内的重复请求返回已创建的订单不重复扣除库存幂等。 请基于以上规则和OpenAPI Spec实现POST /orders接口并补齐对应的单元测试。这次AI生成的代码明显靠谱了很多关键体现在几个地方它真的去调用了/users/{user_id}接口并且把积分不足作为独立的业务异常处理它引入了幂等表以(user_id, product_id, 时间窗口)作为唯一键它补充了库存扣减失败后的回滚逻辑虽然回滚不完美但至少有了异常处理。当然代码不是一次就通过的我在跑测试时发现它把deduct_inventory的调用方式写错了把响应对象当成了布尔值这时我没有让它重写全部代码而是把测试失败信息原样贴回去让它修。反复三轮之后这个接口终于稳定下来。这个案例给我的最大启发是AI基于Spec开发能否成功取决于你喂给它的“隐含规则”有多少。Spec只负责“是什么”真正决定代码质量的是“要满足什么条件”“失败时怎么办”“如何保证一致性”。这些信息你不给AI就只能瞎猜猜错就是坑。5. 给团队的实用建议Spec AI 开发避坑清单5.1 流程层面AI当不了甩手掌柜如果你们团队决定推广“Spec驱动AI开发”的模式流程上必须增加两个角色Spec审校人和测试兜底人。Spec审校人的职责不是写代码而是确保Spec里没有歧义所有边界条件和业务规则都显式化。测试兜底人的职责是编写关键路径的集成测试并把测试结果作为AI迭代的输入。我见过一个比较成功的团队做法他们建立了“Spec评审会议”但不是人去一篇篇读而是让AI Agent先对Spec提问生成“模糊点清单”然后由产品、开发、测试共同逐条确认。这个流程把原本靠口口相传的隐性需求变成了Spec的显式注释。比如“用户每天只能领一次优惠券”这句话最终被细化成“用户维度去重以自然日为准使用Redis SETNX实现”。这样AI拿到Spec时所有信息都是完整的生成的代码自然不容易跑偏。另外Spec变更管理必须和代码变更绑定。不要只在文档里改Spec要确保AI的上下文也同步更新。我常用的一种做法是把Spec文件纳入Git仓库用版本号管理AI的提示词里引用具体的Spec commit号。这样AI不会拿着旧版本信息去生成新代码出了偏差也容易溯源。5.2 工具链MCP、契约测试和AI Agent的配合最近大家在讨论MCPModel Context Protocol这个东西其实非常适合Spec开发场景。简单说MCP就是给AI提供一个标准化的工具调用通道让AI可以直接获取远程的Spec文件、运行测试、查看接口返回结果而不用把信息都塞在提示词里。举个例子把订单服务的OpenAPI Spec通过MCP工具暴露给AIAI在生成代码前会自动读取最新的Spec内容。测试命令也通过MCP暴露AI每写完一段代码就触发一次pytest拿到失败信息后继续修复。这等于给AI装上了“眼睛”和“手”而不是光靠脑子猜。我自己现在常用的组合是代码编辑器里挂一个AI编程助手负责生成代码另外挂一个基于MCP的Agent负责执行测试和静态检查。AI生成完代码Agent就去跑测试然后自动把失败信息回传给AI。整个过程虽然需要搭一下环境但一旦跑通效率提升非常明显。工具链上还有一类重要的组件是“契约测试框架”比如Java生态的Spring Cloud ContractPython生态的pact-python。让AI基于Spec生成契约测试再基于契约测试去实现服务端这样即使AI对业务逻辑的理解有偏差也能保证服务之间接口的基本一致性。换句话说Spec 契约测试 AI Agent是当前我见过最稳的三角组合。5.3 什么时候我劝你别用AI基于Spec开发不是所有场景都适合这条路。如果你遇到下面几种情况我强烈建议先别上AI老老实实自己写第一种是需求本身极其混乱连一份像样的Spec都没有只有一个“大概想法”。这时候让AI去写代码等于让它在沙地上建高楼结果必然是塌。正确做法是先让AI帮你梳理需求生成初版Spec等Spec稳定了再说。第二种是涉及强业务一致性的系统比如资金结算、库存对账、医疗数据。这些系统的代码容不得半点AI幻觉哪怕一个字段的默认值错了都可能导致资损。不是说完全不能用AI而是AI只能作为辅助生成单元测试或类型定义核心逻辑必须人工一行一行review。第三种是团队里缺少能看懂AI代码的人。这听起来像玩笑但AI生成的代码风格往往和团队差异很大如果没人能看懂、能接手、能维护那AI开发留下的技术债会比收益大得多。记住代码不是生出来就完事后续三个月的维护才是最烧钱的。6. 写在最后我现在的取舍原则折腾了这么多我现在的原则其实很简单把AI当成一个“极其聪明但缺乏常识的实习生”它最擅长的是在边界清晰、规则明确的小任务上快速产出高质量代码。基于Spec开发不是不行但前提是你要像带实习生一样把隐含规则一条条讲清楚并且设置自动测试来兜底。回到标题那句“AI基于Spec开发是巨坑”我的回答是坑是真的但大多数时候坑不是AI挖的是我们自己拿着半张图纸就让它开工了。最后分享一个我一直在用的小技巧每当你发现AI在某个Spec任务上频繁翻车先不要急着骂它停下来看看你给它的信息是不是存在“你以为它知道但它不知道”的地方。把这些缺失信息补进Spec然后继续。这一招帮我省下的时间远比任何所谓“更聪明的AI提示词”要多得多。你如果也在尝试这条路建议先从我文章里的“契约测试优先”这个做法开始应该能感受到明显的不同。
返回列表