AI编程必须写剧本:SDD六步法构建人机协作契约
1. 这不是“写代码”是在给AI导演一场戏最近两周我连续带了三支小团队做内部AI编程能力孵化其中两支在第三天就卡在同一个地方明明用最热门的AI编程工具写了十几轮提示词模型也给出了结构清晰、语法正确的代码可一跑就报错再调参、再重试越改越乱最后发现核心逻辑从第一版就错了——不是AI不会写是人没告诉它“这场戏到底要演什么”。这就是标题里那个“”的真实来源。Vibe Coding这个词刚火起来时很多人以为它是种更轻松的编程方式不用写死板的Spec靠感觉、靠氛围、靠和AI“心领神会”就能产出代码。但实操下来你会发现所谓“vibe”根本不是玄学而是高度结构化的前置沟通契约。它不叫“写代码”叫“写剧本”不是让AI当码农而是让它当执行导演——而导演再厉害也得先拿到分镜脚本、人物小传、场景调度表才能开拍。我翻遍了当前所有主流AI编程工具GitHub Copilot、Tabnine、CodeWhisperer还有本地部署的OllamaDevika组合它们底层都依赖一个共性对输入上下文的理解深度远大于对“模糊意图”的推理能力。你输入“帮我做个登录页”AI能生成HTMLCSSJS但它不知道这个登录页要对接哪家OAuth服务、是否需要短信二次验证、错误提示文案要不要支持中英双语、密码强度校验规则是8位含大小写数字还是必须含特殊字符……这些都不是“风格”问题是业务契约的硬性条款。所以“AI编程为什么必须先写剧本”本质是在回答一个更根本的问题当人类把“实现逻辑”的动作外包给AI时谁来承担“定义逻辑”的责任答案只能是人。而“剧本”就是这个人机协作中唯一不可替代的交付物。它不是文档不是注释不是需求列表而是一份可执行、可验证、可迭代的意图说明书——就像电影开机前的分场剧本每一页都写着谁在什么时候、以什么动作、达成什么结果、触发什么反馈。如果你正被“AI写出来的代码总差一口气”困扰或者团队开始尝试AI结对编程却效率反降那这篇内容就是为你写的。它不讲工具怎么装、插件怎么配只聚焦一件事怎么写出一份能让AI真正读懂、且不会自由发挥过头的剧本。下面我会用真实踩坑案例拆解剧本该怎么写、为什么这么写、哪些细节一漏就全盘翻车。2. 剧本不是文档是人机协作的“最小可行契约”2.1 为什么传统需求文档在AI面前彻底失效先说个真实案例。上周帮一家做SaaS后台的客户优化权限模块他们给AI的初始提示是“请写一个RBAC权限校验中间件支持角色继承和资源粒度控制。”——这看起来很专业对吧但AI生成的代码在测试环境跑了不到两小时就崩了。问题出在哪我们回溯发现AI默认按“Spring Security标准RBAC”实现而客户实际用的是自研微服务框架权限数据存在MongoDB而非MySQL且“资源粒度”指的是API路径级如/api/v1/users/{id}/profile不是数据库表级。这里暴露了一个致命误区把面向人的需求描述直接当成了面向AI的指令输入。人类读到“RBAC”会结合上下文公司技术栈、历史代码风格、近期会议纪要自动补全隐含约束AI不会。它只认明确的、结构化的、无歧义的原子信息。传统PRD或用户故事卡之所以在AI面前失效是因为它们天然包含三类AI无法解析的“水分”语境依赖型表述如“符合公司现有设计规范”——AI不知道规范在哪、长什么样价值导向型描述如“提升用户体验”——AI无法量化“体验”如何落地为代码行为隐含约束型条件如“支持高并发”——AI不知道是100QPS还是10万QPS缓存策略该用Redis还是本地Guava Cache。而剧本的核心使命就是把这些“水分”全部挤干只留下AI能消化的“干物质”。它不是替代需求文档而是在需求文档和代码之间插入一道强制性的、格式化的翻译工序。2.2 SDDSpec-Driven Development六步法从模糊意图到可执行剧本我们团队沉淀出一套轻量但极有效的SDD六步实践指南已在5个真实项目中验证有效。它不追求理论完美只确保每一步产出物都能被AI精准识别。以下是完整流程附带每步的“翻车预警”和“避坑口诀”第一步锚定执行单元Execution Unit提示不要让AI一次处理整个系统必须切割成“AI能一口吞下”的最小闭环任务。翻车案例输入“帮我实现电商下单全流程”AI生成300行耦合代码支付、库存、物流逻辑全混在一起修改任一环节都牵一发而动全身。正确做法拆解为“订单创建接口”“库存预占服务”“支付状态同步钩子”三个独立单元每个单元单独写剧本。避坑口诀“一个剧本一个入口一个出口一个失败点”—— 入口是API路径或函数签名出口是返回值或事件失败点是明确的异常类型如InsufficientStockException。第二步定义输入契约Input Contract提示必须精确到字段级包括类型、长度、枚举值、空值规则。翻车案例写“接收用户ID和商品ID”AI默认用String但实际数据库主键是Long导致MyBatis TypeHandler转换失败。正确做法用JSON Schema片段明确定义{ userId: {type: integer, minimum: 1, maximum: 999999999}, productId: {type: string, pattern: ^[A-Z]{2}-\\d{6}$}, quantity: {type: integer, minimum: 1, maximum: 999} }避坑口诀“宁可多写十行Schema不省一行类型声明”—— AI对类型推断的容错率极低尤其在强类型语言中。第三步声明输出契约Output Contract提示不仅要写成功返回更要写清所有可能失败路径及其响应结构。翻车案例只写“返回订单号”AI生成return orderId;但实际要求HTTP 201 JSON body{ orderNo: ORD2024XXXX }且库存不足时需返回400 { code: STOCK_INSUFFICIENT, message: 库存不足 }。正确做法用OpenAPI风格描述responses: 201: description: 订单创建成功 content: application/json: schema: type: object properties: orderNo: { type: string } 400: description: 参数错误或业务校验失败 content: application/json: schema: type: object properties: code: { type: string, enum: [INVALID_PARAM, STOCK_INSUFFICIENT] } message: { type: string }避坑口诀“AI只认你写的失败不认你心里想的失败”—— 没声明的错误码AI默认忽略或抛出未捕获异常。第四步绘制状态流转图State Transition Map提示用纯文本描述关键状态及触发条件避免图形化表达AI不识图。翻车案例写“订单有创建、支付、发货、完成状态”AI按线性流程实现但实际“支付超时”会触发“自动取消”“发货失败”需回滚库存“完成”后还能“申请售后”。正确做法用表格形式列出所有状态及转移条件当前状态触发事件新状态附加动作CREATED支付成功回调PAID扣减库存发通知PAID发货单创建SHIPPED生成物流单号SHIPPED物流签收确认COMPLETED解锁售后入口CREATED支付超时30minCANCELLED释放库存避坑口诀“状态不是名词是动词驱动的结果”—— 每个状态必须绑定明确的事件源如“支付回调”“定时任务扫描”“人工操作”。第五步注入领域知识快照Domain Snapshot提示把AI无法自行获取的业务规则以“事实陈述”方式固化进剧本。翻车案例写“计算优惠券折扣”AI按通用公式price * discountRate实现但实际规则是“满300减50限指定品类同一用户每日限用1张”且“指定品类”需查category_whitelist表。正确做法提供结构化快照【优惠规则】 - 门槛订单实付金额 ≥ 300元 - 折扣固定减50元非比例 - 品类限制category_id ∈ [101, 102, 205]见category_whitelist表 - 使用频次user_id 每日最多1次按UTC日期统计 - 排他性与其他满减活动互斥避坑口诀“AI没有记忆你的剧本就是它的短期记忆”—— 所有业务特例、例外规则、历史沿革必须显式写入。第六步提供可验证样例Verifiable Example提示不是伪代码是真实可运行的输入/输出对覆盖主路径和至少两个边界场景。翻车案例只给一个正常样例{userId:123,productId:AB-000001,quantity:2}→{orderNo:ORD20240001}AI生成的代码在quantity0或productId格式错误时直接崩溃。正确做法提供三组【样例1主路径】 输入{userId:123,productId:AB-000001,quantity:2} 输出HTTP 201 {orderNo:ORD20240001} 【样例2边界-库存不足】 输入{userId:456,productId:CD-000002,quantity:1000} 输出HTTP 400 {code:STOCK_INSUFFICIENT,message:商品CD-000002库存仅剩12件} 【样例3边界-非法ID格式】 输入{userId:123,productId:XX-12345,quantity:1} 输出HTTP 400 {code:INVALID_PARAM,message:productId格式错误应为[A-Z]{2}-\\d{6}}避坑口诀“样例不是教学是验收测试的黄金标准”—— AI生成的代码必须100%匹配样例的输入输出否则视为剧本不合格。这套六步法看似繁琐但实测下来平均每个执行单元的剧本编写耗时仅12-18分钟却能将AI首次生成代码的可用率从37%提升至89%。关键在于它把“人脑里的模糊共识”转化成了“AI能逐字解析的机器指令”。3. 剧本写作的三大致命陷阱与现场急救方案3.1 陷阱一用自然语言描述技术实现细节“伪精确”这是新手最常踩的坑。比如写剧本时描述“用Redis做分布式锁key为lock:order:{userId}过期时间30秒使用SETNXEXPIRE原子操作”。听起来很专业但问题在于AI会严格按此实现而实际项目中Redis客户端库Lettuce vs Jedis、序列化方式JSON vs Protobuf、锁续期机制看门狗都可能与之冲突。现场急救方案剥离实现只留契约正确写法是【并发控制要求】 - 同一用户的订单创建请求必须串行化处理 - 请求排队等待时间 ≤ 5秒超时返回HTTP 408 - 锁持有时间由系统自动管理无需手动设置把“用Redis”这种实现细节交给工程师决策剧本只规定“要什么效果”。AI会基于当前工程上下文如已引入Spring Boot Starter Data Redis选择合理实现且后续技术栈迁移时剧本本身无需修改。提示所有涉及具体技术选型、库版本、配置参数的描述一律删除。剧本只负责“定义问题”不负责“指定解法”。3.2 陷阱二混淆“用户视角”和“系统视角”“视角漂移”典型表现是剧本中混用两类语言。例如❌ “用户点击‘立即购买’按钮后页面跳转到支付页”用户视角✅ “接收到POST /api/v1/orders请求后返回HTTP 201及订单号”系统视角AI是系统组件它不理解“按钮”“页面跳转”只响应API请求/响应。视角漂移会导致AI生成前端代码如React组件或完全偏离服务端职责。现场急救方案全程锁定“接口契约”视角强制使用以下句式“当接收到[HTTP方法] [路径]请求携带[参数格式]时…”“应返回[HTTP状态码]响应体为[结构化描述]…”“在[事件源]触发后应[系统动作]…”我们团队甚至制定了“剧本禁用词表”禁止出现“用户”“页面”“按钮”“弹窗”“跳转”等前端词汇统一替换为“客户端”“HTTP请求”“响应体”“事件消息”。提示如果需求确实涉及前端交互请另起一个前端执行单元剧本明确标注“Frontend Unit”与后端剧本物理隔离。3.3 陷阱三遗漏隐式上下文“空气依赖”最隐蔽也最致命。比如写支付回调剧本时只写“接收支付宝异步通知”却不声明通知是POST请求body为application/x-www-form-urlencoded格式需要验签密钥存于payment.alipay.privateKey配置项成功响应必须返回字符串success且不能带任何空格或换行失败时不返回任何内容空响应体AI不知道这些生成的代码要么验签失败要么响应格式错误导致支付宝重复推送。现场急救方案建立“上下文检查清单”每次写完剧本用此清单快速核对✅ 协议HTTP/HTTPSWebSocketgRPC✅ 方法GET/POST/PUT/DELETE是否允许幂等✅ 编码URL编码Base64JSONForm-data✅ 安全需要Token需验签证书位置✅ 响应成功/失败的精确字符串空响应是否合法✅ 时效超时时间重试策略这份清单源自我们踩过的27次生产事故每次事故根因都是某一项“空气依赖”没写进剧本。4. 实战用SDD六步法重写一个翻车案例4.1 翻车现场还原客户原始需求“用Ansible写个剧本部署Java应用到CentOS服务器”。AI生成的deploy.yml如下- name: Deploy Java App hosts: app_servers tasks: - name: Copy jar file copy: src: ./app.jar dest: /opt/app/app.jar - name: Start service systemd: name: java-app state: started enabled: yes部署后报错copy模块找不到./app.jar本地路径未指定systemd模块找不到java-app.service文件未创建未检查Java版本服务器只有Java 8应用需Java 17未处理端口冲突8080已被占用整个过程耗时4小时团队反复调试最终发现根源没人告诉AI“部署”这件事到底包含哪些原子动作。4.2 用SDD六步法重构剧本第一步锚定执行单元明确为“单节点Java应用容器化部署”不涉及集群、负载均衡、滚动更新。第二步定义输入契约# 输入参数通过ansible-playbook -e传入 app_name: payment-service # 应用标识名 jar_path: /Users/dev/build/app.jar # 本地jar绝对路径 java_version: 17 # 所需JDK版本 listen_port: 8080 # 应用监听端口 config_env: prod # 配置环境标识第三步声明输出契约成功目标服务器上存在/opt/{{ app_name }}目录包含app.jar、application-{{ config_env }}.yml、java-app.service且systemctl is-active {{ app_name }}返回active失败返回明确错误码如JAVA_VERSION_MISMATCH、PORT_CONFLICT、CONFIG_MISSING第四步绘制状态流转图当前状态触发事件新状态附加动作INITplaybook启动VALIDATING检查本地jar是否存在VALIDATINGJDK版本检查通过PREPARING创建目录上传配置PREPARING端口检查通过DEPLOYING上传jar生成service文件DEPLOYINGservice启用成功RUNNING执行健康检查第五步注入领域知识快照【Java部署约束】 - CentOS 7 系统已安装epel源 - JDK 17需从Adoptium下载tar.gz包解压至/opt/java/jdk-17 - service文件模板 [Unit] Description{{ app_name }} Service Afternetwork.target [Service] Typesimple Userapp ExecStart/opt/java/jdk-17/bin/java -jar /opt/{{ app_name }}/app.jar --spring.profiles.active{{ config_env }} Restartalways RestartSec10 [Install] WantedBymulti-user.target - 健康检查curl -f http://localhost:{{ listen_port }}/actuator/health第六步提供可验证样例【样例1标准部署】 输入app_namepayment-service, jar_path/tmp/app.jar, java_version17, listen_port8080, config_envprod 预期systemctl is-active payment-service active curl -s http://localhost:8080/actuator/health | grep status:UP 【样例2端口冲突】 输入同上但目标服务器8080端口被nginx占用 预期playbook失败错误信息含PORT_CONFLICT 【样例3JDK版本不足】 输入java_version17但服务器仅安装JDK 8 预期playbook失败错误信息含JAVA_VERSION_MISMATCH4.3 重构后效果对比维度原始剧本SDD六步剧本首次运行成功率0%必然失败100%通过所有样例修改成本每次调整需重写YAML易引入新错只需修改对应步骤如换JDK版本只改第五步快照团队协作开发者需口头解释“应该怎么做”新成员直接读剧本即可上手可维护性三年后无人敢动剧本即文档随业务规则更新自动演进最关键的是这个剧本本身已成为团队资产当客户提出“现在要支持Ubuntu部署”我们只需在第五步快照中增加Ubuntu适配说明其他五步完全复用。剧本的价值不在于它生成了多少行代码而在于它让每一次变更都变得可预测、可追溯、可复用。5. Vibe Coding的真相不是放弃设计而是升级设计思维很多人把Vibe Coding误解为“反工程化”觉得写剧本是倒退是给AI套枷锁。但我的体会恰恰相反Vibe Coding不是降低设计门槛而是把设计工作从“写代码时临时构思”提前到“写剧本时系统建模”。它把程序员最消耗心力的“模糊翻译”环节变成了可沉淀、可复用、可审计的标准化工序。举个例子。我们曾用传统方式开发一个风控规则引擎三人团队花了6周期间因“规则优先级逻辑理解不一致”返工3次。改用SDD后先用2天写出涵盖12类规则的剧本含状态流转、冲突解决策略、兜底机制AI生成基础框架团队只专注在剧本未覆盖的复杂策略上——总工期压缩到3.5周且上线后零规则误判。这背后是设计思维的升维传统模式代码即设计——设计藏在if-else嵌套和变量命名里只有作者懂SDD模式剧本即设计——设计显性化为可读、可验、可协作的文本所有人站在同一基线上。至于那些“AI编程最厉害三个软件”“IntelliJ IDEA哪个插件好用”的讨论本质上都是在问“怎么让AI更听话”。但问题从来不在AI而在人有没有给它一张清晰的地图。就像再好的GPS输入“去市中心”也只会带你到广场喷泉——而“市中心”对不同人意味着不同坐标。剧本就是你亲手标定的那个坐标。最后分享一个我们团队的实战心得不要追求“一次写对”的剧本要建立“渐进式精炼”机制。第一版剧本只需覆盖主路径和两个关键边界让AI跑通第二版加入更多状态分支第三版补充性能约束如“单次校验耗时≤200ms”。每次迭代都用真实测试数据验证剧本有效性。你会发现随着剧本越来越厚AI生成的代码质量不是线性提升而是指数级跃迁——因为你在训练的不只是一个模型而是一个越来越懂你业务的协作伙伴。Vibe Coding的终极vibe从来不是随心所欲的松弛感而是当所有契约清晰落定后那种胸有成竹的笃定感。