2026/9/10 8:27:35

SDD规范:AI时代可执行规格说明书实战指南

SDD规范:AI时代可执行规格说明书实战指南 1. 为什么“文档先行”在AI时代突然成了硬需求而不是一句空话“SDDAI时代文档先行的开发方法论”——这个标题刚出现时我第一反应是皱眉。不是质疑概念本身而是太熟悉那种“文档写得比代码还勤上线前却没人看”的尴尬现场。过去十年里我带过二十多个中大型项目从金融核心系统到IoT边缘网关几乎每支团队都经历过“先写PRD、再画流程图、最后贴几页接口文档”结果呢需求评审会上产品经理念文档开发盯着手机回消息接口联调时后端说“文档里写了默认值是null”前端翻了三遍没找到那行字上线前测试发现字段长度限制和文档描述差20个字符全组加班改SQL和校验逻辑。这种“文档存在但失效”的状态不是文档没写而是文档没活起来。直到2023年中我们接手一个AI驱动的客服知识库重构项目才真正被逼出SDDSpec-Driven Development的实操路径。当时团队有7人2个算法工程师、3个后端、1个前端、1个测试。客户要求两周内交付可演示的RAG原型但连“用户问题如何分段”“召回结果怎么打分”“答案生成是否允许引用外部链接”这些基础规则都没共识。按老办法开三次会、写五版文档、建十个飞书文档链接来不及。我们试了一种反直觉的做法第一天不写自然语言文档而是用YAML定义一份最小可执行规格说明书spec.yaml包含输入schema、处理链路节点、输出约束、示例数据集并用Python脚本自动校验该spec能否被当前代码框架加载运行。第二天算法同学基于spec写embedding模块的单元测试桩后端同学用spec生成FastAPI路由模板测试同学直接把spec里的example字段转成Postman集合。第三天我们跑通了端到端数据流——不是靠人对齐而是靠机器验证spec的一致性。这才明白“AI时代文档先行”根本不是让程序员多写几页Word而是把文档从“静态说明”升级为“可执行契约”。当大模型能自动生成代码、自动补全接口、自动编写测试用例时人类最不可替代的价值恰恰是定义清楚“系统应该做什么”而不是“怎么去做”。SDD的核心是让这份“应该做什么”的声明具备机器可读、可验证、可衍生的能力。它解决的不是文档要不要写的问题而是文档如何成为整个研发流水线的“事实源头”source of truth。你不需要说服团队重视文档只需要让CI流水线在每次提交时自动拒绝任何与spec冲突的代码变更——这时候文档就不再是负担而是准入门槛。提示SDD不是取代敏捷或取消沟通而是把模糊的口头共识、零散的聊天记录、过期的Confluence页面强制收敛到一份结构化、版本化、可自动化消费的规格文件中。它的价值不在文档本身而在文档与代码、测试、部署之间的强绑定关系。2. SDD不是新瓶装旧酒它和传统文档驱动开发的本质区别在哪很多人看到“文档先行”四个字立刻联想到十年前的“重量级瀑布模型”——需求冻结、文档签字、开发启动、测试验收。这种联想非常危险因为它完全误解了SDD的技术前提和运作机制。我把SDD和传统文档驱动开发DDDDocument-Driven Development做了张对比表列在下面。这不是理论推演而是我们团队在三个项目中踩坑后总结的真实差异维度传统DDD已淘汰SDD当前实践文档形态Word/PDF/Confluence富文本含大量描述性文字、截图、流程图YAML/JSON Schema/TOML结构化文件仅包含机器可解析的字段定义、约束条件、示例数据更新频率需求变更后人工修改文档常滞后于代码版本混乱每次需求讨论后直接编辑spec文件Git提交即版本化与代码同分支管理验证方式人工交叉检查“你写的和我理解的一样吗”无自动化手段CI流水线自动运行spec-validator校验schema合法性、字段必填性、枚举值一致性、示例数据合规性下游消费开发凭记忆/截图实现测试手动造数据文档与实现长期脱节代码生成器如OpenAPI Generator从spec生成DTO类、API路由、Mock服务测试框架如Pytest自动加载spec中的example生成测试用例变更成本修改文档需重新走审批流开发需手动同步代码平均延迟2.3天git commit spec.yaml→ CI触发代码生成测试运行 → 失败则阻断合并全程5分钟关键区别在于传统DDD的文档是“终点”SDD的spec是“起点”和“枢纽”。在我们做的智能工单分类项目中业务方提出“新增‘设备型号模糊匹配’能力”传统做法是PM写一页需求说明开发评估排期两周后上线。而SDD流程是PM和算法同学在1小时内共同编辑spec.yaml新增一个fuzzy_match_config对象定义threshold: float ∈ [0.0, 1.0]、max_candidates: int ≥ 1等字段并给出3组测试示例。提交后CI自动① 生成Python Pydantic模型类② 更新FastAPI/classify接口文档③ 运行pytest用spec里的example触发新逻辑测试④ 若测试失败比如算法返回candidate数超限合并请求被拒绝。整个过程没有会议、没有邮件确认、没有文档交接只有spec文件的版本演进和机器验证。更本质的区别在于责任边界。传统DDD中文档质量由PM或BA负责开发只管实现SDD中spec文件是跨职能协作产物算法工程师必须定义threshold的取值范围否则模型无法部署后端必须确认max_candidates是否影响内存占用否则API会OOM测试必须提供边界值示例否则无法覆盖异常流。我们甚至规定任何spec字段若未被至少两个角色共同编辑过该字段在CI中视为“未激活”生成的代码会抛出NotImplementedError。这倒逼所有人从第一行就深度参与而不是等“文档定稿”后再介入。注意SDD不排斥自然语言。我们在spec.yaml同目录下保留README.md但内容严格限定为三类① spec各字段的业务含义解释非技术实现② 当前版本与上一版本的关键变更日志③ 已知限制如“当前不支持嵌套数组的模糊匹配”。所有技术细节、约束、示例只存在于spec文件中——这是保证机器可消费的前提。3. Spec文件怎么写才真正“可执行”从字段定义到链路编排的实战规范Spec文件不是随便写几个JSON字段就能叫SDD。我们团队踩过太多坑有人把spec写成API文档快照结果字段增删后生成的代码报错有人用注释写业务逻辑但CI校验器根本读不懂还有人把算法参数和UI字段混在一个文件里导致前端生成器和模型训练脚本互相污染。真正的可执行spec必须遵循一套严格的分层规范。我们目前采用三级结构每层解决不同问题且全部通过Git Hooks和CI Pipeline强制校验。3.1 第一层Schema层——定义数据契约杜绝“字段幻觉”这是spec的基石。我们不用OpenAPI 3.0的完整语法太重而是基于JSON Schema Draft 2020-12精简出7个核心关键字type、required、enum、minimum/maximum、minLength/maxLength、pattern、examples。所有字段必须满足① 类型明确string/number/boolean/object/array禁用anyOf② 必填性显式声明required: [user_id, query]③ 枚举值穷尽status: [pending, processing, done, failed]禁用“其他”类兜底项④ 数值范围闭合confidence: { minimum: 0.0, maximum: 1.0 }而非0 and 1。最关键的实战技巧是每个字段的examples必须包含至少3个真实业务场景数据且覆盖边界值。比如timeout_ms字段我们要求示例必须是[100, 5000, 30000]分别代表“极短响应”“常规超时”“长任务容忍”。这样生成的测试用例才能暴露if timeout 1000这类硬编码bug。曾经有个项目因examples只写了[5000]导致上线后遇到100ms的瞬时抖动就触发降级而测试环境从未复现——因为测试数据没覆盖这个量级。3.2 第二层Flow层——编排处理链路让AI能力可组合AI项目最怕“黑盒串联”。传统做法是算法写完模型后端封装成API前端调用——中间任何环节出错排查成本极高。SDD的Flow层用YAML定义清晰的处理节点Node和数据流向Edge。每个Node必须声明①typellm_call/vector_search/rule_filter/fallback_handler②input_schema引用Schema层定义的对象③output_schema同理④config该节点特有参数如llm_call的model_name、temperature。例如智能摘要服务的flow.yaml片段nodes: - id: extract_entities type: rule_filter input_schema: #/components/schemas/RawText output_schema: #/components/schemas/Entities config: patterns: [[A-Z][a-z] [A-Z][a-z]] # 简单人名正则 - id: generate_summary type: llm_call input_schema: #/components/schemas/Entities output_schema: #/components/schemas/Summary config: model_name: qwen2-7b temperature: 0.3 edges: - from: extract_entities to: generate_summary condition: len(entities) 0 # 只有抽到实体才进LLM这个设计带来两个硬收益①可模拟flow-runner工具能加载此文件在无真实LLM情况下用mock数据跑通整条链路验证逻辑是否闭环②可替换当需要把qwen2-7b换成glm4时只需改config.model_name其余节点和连接不变避免牵一发而动全身。我们曾用此机制在2小时内完成某金融客户从开源模型切换到私有化部署模型的迁移零代码修改。3.3 第三层Contract层——声明跨系统契约终结“对接扯皮”AI项目常涉及与第三方系统集成如CRM、ERP、IoT平台。传统对接靠邮件约定字段结果CRM传来的customer_id是字符串ERP要的是整数IoT平台又要求加前缀。SDD的Contract层用独立的contract.yaml文件明确定义① 对方系统名称② 数据方向inbound/outbound③ 字段映射表crm_customer_id → internal_user_id④ 转换规则CUST_ str(crm_id)⑤ 错误码映射CRM_404 → USER_NOT_FOUND。最实用的经验是Contract文件必须由双方技术负责人联合签署Git签名且任何变更需触发通知给对方系统Owner。我们用Git Hook实现当contract.yaml被修改自动向企业微信机器人发送消息“【SDD Contract变更】CRM系统字段映射更新请确认是否影响贵方数据推送逻辑”并附上diff链接。这招让过去平均耗时3.7天的跨系统对接压缩到4小时内完成确认。提示Spec文件严禁出现任何业务规则描述如“VIP用户优先处理”所有规则必须转化为可计算的字段或条件表达式。我们曾因在spec中写“高优先级任务应尽快响应”导致算法同学用time.time()做实时调度而运维同学按CPU负载做资源分配——两者对“尽快”的理解完全不同。改成priority_score: { minimum: 0, maximum: 100 }后问题迎刃而解。4. 从spec到落地我们自研的SDD工具链如何让方法论真正跑起来再好的方法论没有趁手的工具链就是纸上谈兵。我们花了六个月打磨SDD工具链核心原则就一条所有工具必须能在开发者本地终端30秒内安装运行且不依赖任何中心化服务。这意味着放弃SaaS化方案全部基于开源组件二次开发。目前工具链包含四个核心命令全部封装为sddevCLI工具pip install sddev即可。4.1sddev validatespec的“体检仪”让错误在提交前暴露这是SDD流水线的第一道闸门。它不只是校验YAML语法而是执行四层深度检查Schema层用jsonschema库验证字段类型、约束、示例合规性Flow层检查所有input_schema/output_schema是否在Schema层定义验证condition表达式语法用ast.parse安全解析禁用evalContract层比对contract.yaml中字段映射是否与Schema层字段名一致防止拼写错误跨层一致性确保Flow中每个Node的config参数在Schema层有对应定义如llm_call.temperature必须在Schema中声明为number类型。最实用的功能是--fix参数。当检测到examples数量不足时它不会报错退出而是自动生成符合约束的补充示例。比如检测到timeout_ms缺少小于100的示例它会插入50检测到status枚举缺canceled它会提示“是否添加[y/N]”。这个设计源于我们发现开发者最反感“只报错不给解法”的工具。现在sddev validate --fix已成为每日开发的固定动作就像git add前的prettier一样自然。4.2sddev generate从spec到代码的“复印机”消灭重复劳动这是提升效率最直接的环节。我们支持三类生成目标Backend根据Schema生成Pydantic V2模型类、FastAPI路由装饰器、SQLAlchemy ORM映射自动推导VARCHAR(255)等长度Frontend生成TypeScript接口定义.d.ts、React Hook模板useApiQuery、表单校验规则Zod schemaTest生成Pytest测试用例覆盖所有examples、Postman Collection含环境变量、Mock服务基于responses库。关键创新点在于上下文感知生成。比如生成FastAPI路由时sddev会扫描Flow层若发现node.type llm_call则自动在路由中注入limiter.limit(100/minute)限流装饰器若node.config.model_name包含qwen则自动添加X-Model-Vendor: qwen响应头。这些不是硬编码而是通过generator-hooks插件机制实现——团队可自行编写Python函数在生成任意代码前/后注入逻辑。4.3sddev run本地沙箱环境让spec“活”起来这是SDD最具颠覆性的工具。执行sddev run后它会启动一个轻量级HTTP服务器基于uvicorn加载spec中的Flow定义构建内存中处理图为每个Node启动对应Mock服务llm_call返回预设JSONvector_search返回examples中相似度最高的结果提供Web UI界面可拖拽调试数据流实时查看每个Node的输入/输出。我们曾用它在客户现场快速验证需求业务方说“希望摘要里高亮实体”我们当场修改spec中generate_summary节点的config.highlight_entities: truesddev run重启后UI界面立即显示带mark标签的摘要。整个过程不到2分钟比打开Figma改原型快得多。更重要的是这个UI生成的调试数据可一键导出为新的examples反哺spec完善。4.4sddev diff版本演进的“翻译官”让变更可追溯git diff spec.yaml对人类不友好。sddev diff v1.2 v1.3则会生成结构化报告Schema变更列出新增/删除字段、类型变更string → number、约束收紧maxLength: 100 → 50Flow变更显示节点增删、连接变化、条件表达式更新Contract变更标出映射字段增减、转换规则修改影响分析自动标注“此变更将导致Backend生成代码中移除get_user_profile()方法”、“Frontend需更新UserProfileForm组件”。这个功能直接终结了“为什么我的接口突然400了”的排查噩梦。当CI检测到spec变更影响已有API会自动在PR评论中相关开发者并附上sddev diff报告链接。我们统计过API兼容性问题的平均修复时间从17小时降至2.1小时。注意所有工具均设计为“离线可用”。sddev validate的schema校验规则打包在wheel包内sddev generate的模板使用Jinja2不联网下载sddev run的Mock数据完全来自spec文件。这是保障SDD在客户内网、金融隔离环境落地的关键。5. SDD落地的真实挑战我们如何让算法、后端、前端三拨人坐到一张桌子前方法论和工具再好如果团队不愿用就是废纸。SDD推广初期我们遭遇了典型的“三座大山”算法工程师觉得“写spec是浪费时间我直接调API不香吗”后端同学抱怨“生成的代码要手动改还不如自己写”前端同事困惑“为什么我要关心LLM的temperature参数”。破局的关键不是开动员会而是设计一套让每个人都能立刻获得“即时正反馈”的机制。5.1 给算法工程师的“甜头”用spec驱动模型实验告别重复造轮子我们和算法团队达成的第一个协议所有模型实验必须基于spec定义的输入/输出schema进行。具体操作是sddev generate --target experiment会生成一个Jupyter Notebook模板其中自动加载spec中的examples作为测试数据集预置evaluate_model(model, examples)函数自动计算准确率、响应时长、token消耗内置对比表格可一键对比qwen2-7b和glm4在同一组examples上的表现。算法同学第一次用时惊讶地发现以前要花半天准备测试数据、写评估脚本现在sddev generate后shiftenter运行三格代码就得到完整的量化报告。更让他们兴奋的是当spec中新增highlight_entities: boolean字段sddev generate会自动在Notebook中添加对应的评估指标如“高亮准确率”无需手动改代码。现在他们的日常是先和产品讨论清楚spec字段再跑实验——因为spec变了评估基准就变结果才有可比性。算法同学私下说“以前是模型决定能做什么现在是spec决定该做什么我们终于不用替业务做决策了。”5.2 给后端工程师的“减负”让spec接管80%的胶水代码后端最大的痛点不是写业务逻辑而是写那些“连接器”参数校验、DTO转换、错误码映射、日志埋点。SDD通过sddev generate把这些工作自动化。但真正让他们接受的是sddev run带来的调试革命。传统调试API要启服务、写curl、看日志、查数据库。SDD模式下sddev run启动的沙箱环境自带可视化调试面板。点击任意Node能看到实际接收到的原始请求含headers经过参数校验后的干净数据该Node处理后的输出含耗时、内存占用下游Node接收到的数据验证序列化是否丢失精度。我们有个真实案例某次上线后发现/search接口偶发500日志只显示pydantic.ValidationError。用sddev run加载线上spec复现请求面板直接定位到vector_search节点返回的score字段是numpy.float32而Pydantic模型期望floatJSON序列化时报错。问题根源是算法SDK的类型不一致但传统方式要抓包、断点、查源码耗时半天SDD模式下3分钟定位1行代码修复scorefloat(score)。后端同学反馈“现在debug不是猜是看——spec让整个调用链透明了。”5.3 给前端工程师的“安全感”用spec生成的TypeScript比手写更可靠前端最怕后端字段“悄悄变”。SDD的sddev generate --target frontend生成的TypeScript不仅包含接口定义还内置运行时校验。比如生成的SearchResult类型会自动包含export const SearchResult z.object({ id: z.string().uuid(), title: z.string().min(1).max(200), summary: z.string().optional(), confidence: z.number().min(0).max(1) }); // 并导出校验函数 export const validateSearchResult (data: any) SearchResult.safeParse(data);这意味着前端拿到API响应后第一件事不是data.title而是validateSearchResult(data)。如果后端擅自返回confidence: 0.95字符串校验失败前端可立即降级显示“数据异常”而不是渲染空白或报JS错误。我们上线后前端因字段类型不符导致的白屏率从3.2%降至0.1%。更妙的是当spec新增highlighted_entities: string[]字段sddev generate会自动更新TypeScript定义并在组件中提示“highlighted_entitiesis not defined in current props”强迫开发者处理新字段——这比Code Review有效十倍。5.4 让所有人坐到一起的终极机制SDD周会只讨论spec变更我们取消了所有需求评审会代之以每周30分钟的“SDD Spec Sync”。规则极其简单只允许讨论spec.yaml、flow.yaml、contract.yaml的变更每次会议最多讨论3个变更git diff HEAD~1每个变更必须由提出者用sddev run现场演示效果任何未在spec中定义的“口头需求”会议不予讨论。第一次会议产品提了一个“希望搜索结果按热度排序”的需求。算法说“需要加一个popularity_score字段”后端问“这个分数怎么算缓存多久”前端说“UI上怎么展示热度标识”。10分钟后他们共同编辑出popularity_score: { type: number, minimum: 0, maximum: 1000, description: 基于30天点击量和分享量加权计算 }并写入examples。会议结束sddev generate已产出所有代码当天下午就可测试。没有PPT没有争论只有spec文件的演进。这才是AI时代应有的协作节奏——用机器可执行的契约代替人类模糊的语言。提示SDD不是消除沟通而是把沟通焦点从“你理解对了吗”转向“这个spec能跑通吗”。当spec成为唯一真相源角色间的壁垒自然消融。我们团队现在有个潜规则谁在PR中绕过spec直接改代码会被自动回复“请先更新spec并运行sddev validate”。