
我第一次尝试把完整需求交给 Agent 时,实际并没有省下多少时间。
它写代码很快,我却一直守在聊天窗口里:补一段业务背景,纠正一个状态含义,再提醒它另一个仓库还有入口。遇到外部接口,我还得告诉它哪些结果能信,哪些只是 Mock 出来的。
表面上是 Agent 在开发,实际还是我在带路。它只负责把我刚刚说过的话迅速变成代码。
这种方式做小改动没什么问题。需求一旦跨仓、跨天,事情就开始失控。前面的对话被压缩,口头确认过的边界找不到出处;另一个需求里讨论过的方案,也可能因为关键词相似,被带进当前任务。
我做的是一个多仓 AI 客服项目。产品文档、源码、日志和聊天记录都包含项目知识,但它们表达的东西并不一样。
产品文档写业务想要什么,源码反映系统现在怎么做,日志只证明某个环境在某个时间发生过什么。代码里有消息消费者,不代表生产环境已经订阅;数据库字段是 0,可能来自上游,也可能只是服务端默认值;Mock 接口成功,和外部平台接受真实状态没有直接关系。
我后来把问题归到一个地方:项目里缺少一份 Agent 能长期依赖的业务事实源。
这才是我开始做 SDD 的原因。

我把 Spec 当成业务事实源
SDD 是 Spec-Driven Development,规格驱动开发。
我没有照搬一套固定流程。在这个项目里,我先给 Spec 定了一个很实际的标准:换一个会话,甚至换一个 Agent,它仍然能从 Spec 中知道系统应该怎样表现、这个结论依据什么、当前允许执行到哪一步,还有哪些问题必须等人决定。
如果只能回答需求是什么,这份 Spec 对 Agent 来说还不够。它还得记录批准版本、未知项、验证结果,以及真实环境推翻方案时发生了什么。
所以 Spec 会跟着开发过程变化。需求调查会写进去,方案批准会写进去,任务和测试能追溯回来,沙箱失败和生产观察也要留下。聊天仍然用于讨论,但影响代码行为的决定最后都要回到 Git。
我给 Agent 的边界只有三条:
已批准的行为,可以独立完成。
新增的业务判断,必须交还给人。
未经授权的外部副作用,不能执行。
这三条决定了我和 Agent 的分工。查文档、读代码、定位调用链、做设计、拆任务、写测试、实现和整理证据,Agent 都可以自己完成。我主要参与业务语义、方案取舍、风险授权和最终验收。
这套结构是怎么长出来的
现在的 .specs 看起来比较完整,但并不是一开始就设计成这样。很多结构都是在真实需求里吃过亏以后补上的。
最先确定的是独立规格仓库。业务需求经常横跨几个代码仓,把 Spec 放进任何一个仓库都会缺一部分。于是我把它放在工作区根目录,业务代码仍回到各自仓库提交,跨仓意图统一用 CHANGE-ID 关联。
.specs/
├── project/ # 长期边界、路线图和当前状态
├── codebase/ # 架构、仓库责任、集成、测试基线和风险
├── features/ # 一个需求一个 CHANGE-ID
├── incidents/ # 一个线上事件一个 INC-ID
├── quick/ # 低风险快速变更
├── templates/ # Spec、Design、Tasks、Evidence 模板
└── scripts/ # 结构和一致性检查

project/ 和 codebase/ 只放长期可复用的信息。某个需求的局部方案留在自己的 Feature 中,某次事故的临时现象也不会写成全局规则。
Agent 开始工作时不需要把整个仓库读一遍。它先看项目状态和相关代码地图,再进入当前 Feature。只有执行需要时,才继续加载 Design、Tasks 和 Evidence。这个顺序能减少串台。读得太多并不一定更懂项目,无关的旧方案也会进入上下文。
一项完整 Feature 逐渐拆成四份文件:
| 文件 | 主要内容 |
|---|---|
spec.md | 已批准的业务行为、边界和未决问题 |
design.md | 代码入口、状态流、外部契约、幂等和回滚 |
tasks.md | 能独立完成和验证的实施任务 |
evidence.md | 代码、测试、沙箱、验收和生产观察证据 |
刚开始把这些内容都塞进一份文档也能用,需求变复杂以后就很难维护。更麻烦的是,设计上的发现容易悄悄改掉业务规则。拆开以后,Design 可以调整实现方式,却不能自行改变 Spec;Evidence 可以证明方案失败,却不能替业务选择下一条生产写路径。
后来又加了状态机:
INTAKE → CLARIFIED → SPEC_REVIEW → APPROVED
→ DESIGNED → IMPLEMENTING → VERIFIED → ACCEPTED

这些状态直接限制 Agent 在当前阶段可以执行的动作。
INTAKE 可以调查,不能改业务代码。SPEC_REVIEW 可以继续澄清,不能自行关闭业务问题。Spec 明确批准后,才能写 Design 和 Tasks。IMPLEMENTING 只执行已经映射的任务。VERIFIED 表示证据已经准备好,仍然要等业务验收和生产观察。
状态也允许后退。真实环境推翻方案前提时,Feature 会回到评审。没有这条退路,Agent 很容易把完成任务理解成持续向前,遇到阻塞就再猜一条路。
证据分级来自另一个实际问题。Agent 很容易把代码里看到的、日志里查到的、业务方说的和自己的推断写成同一种事实。我最后固定了四个标签:
VERIFIED_CODE:已经核对源码、配置或提交;VERIFIED_RUNTIME:已经由日志、查询或真实回放证明;BUSINESS_CONFIRMED:有决定权的人确认了规则;HYPOTHESIS:目前只是调查方向,不能直接实现。
源码只能说明代码怎么写,不能说明生产开关是否开启;运行日志能证明一次行为,不能决定未来应该怎样;业务确认可以定义新规则,也不能反过来证明系统已经做到。
追踪关系也随之固定下来。Spec 中每条行为都有稳定的 Requirement ID,后面沿着同一条链继续:
Requirement → Design → Task → Code/Test → Evidence → Acceptance
一个 Task 找不到对应 Requirement,多半已经超出范围。一条 Requirement 没有测试和证据,也不能因为提交了代码就算完成。分支、提交、CR 和测试记录都引用同一个 CHANGE-ID@version,换 Agent 时不必重新从聊天和 Git 历史里拼需求。
一项需求怎样被 SDD 驱动
前段时间,我让 Agent 处理一项工单自动流转需求。以下细节已经脱敏。
系统原本支持状态 Y 下的自动回流:等待约定时间,重新开启工单,再转回指定处理人。新需求希望状态 X 也遵循同样的规则。
我给 Agent 的第一段指令大致是:
使用 sdd-feature-intake 处理需求 CHANGE-ID。
已确认的验收语义:
WHEN 目标工单被非目标组成员执行状态 X
THEN 系统 SHALL 重新开启自动流转
AND 规则 SHALL 与状态 Y 的现有回流保持一致。
请读取原始需求文档和 .specs 中的历史证据,在本地代码中定位入口、
状态判断、操作人身份判断、消息、定时任务、数据库或 Redis 字段。
先确认应该修改哪个仓库和目标分支,不要根据历史需求猜。
优先复用状态 Y 的现有路径,明确幂等、并发、人工指定和非目标工单边界。
输出 Spec、未知问题、验收规则、测试计划和实际开发时间估算。
我批准 Spec 前不要修改业务代码。
我没有预设仓库。过去相似需求落在某个服务,不代表这次还是同一个入口。我也没有指定函数,只确认业务规则应当复用。入口和复用点由代码证据确定。
Agent 找到了消息入口、延迟任务、状态枚举、Redis 记录和外部工单调用。旧流程很完整:消息登记任务,定时 Job 扫描到期记录,检查工单状态,重新开启,然后转交。
调查也暴露出几处缺口。任务只保存了最初转交双方,没有记录后来执行状态 X 的操作人。外部接口文档明确支持状态 Y 重新开启,却没有承诺状态 X。重复的转交消息还可能重置等待时间。
于是 Spec 多出一组边界:目标处理组成员本人执行状态 X 后是否保持现状;从哪个状态重新开始;是否清理原因和历史信息;重复消息、重复操作和并发转交如何处理;普通工单、已经结束的工单和人工指定工单是否受影响;身份查不到时应该等待还是继续。
能由旧规则和源码证明的部分,Agent 直接给建议。会改变业务结果的内容留在 Q 表中,由我确认。整个 Intake 阶段,我只参与了几次边界选择。
最终批准内容带着版本、方案和失败条件:
批准 CHANGE-ID@v0.3,采用方案 B。
状态 X 先复用既有 reopen 路径,并在沙箱验证外部契约。
如果外部平台拒绝,记录偏差并停止,不尝试替代写操作。
批准之后,Agent 才开始写 Design 和 Tasks。测试也先于实现出现:非目标组成员触发状态 X 应该回流;目标组成员本人操作保持原样;身份查询失败不能猜成非目标组;重复消息不能重置等待时间;状态 Y 的旧规则不能回归;reopen 失败后不能继续 handover;延迟任务不能覆盖同时发生的人工转交。
随后是实现、定向回归、编译、提交和沙箱部署。每完成一个 Task,任务状态和 Evidence 一起更新。
沙箱验证使用了一张明确标记为测试用途的工单。消息识别正确,操作人身份判断正确,延迟任务也进入了新状态的处理路径。
到了真正调用外部工单平台时,对方返回业务拒绝:状态 X 不能执行 reopen。
内部代码和测试都符合已批准方案,真实环境却推翻了方案前提。Agent 记录请求、响应和未执行的后续动作,标记 SPEC_DEVIATION,把 Spec 退回评审,然后停下。

这次验证里,静态代码检查通过,单元测试通过,工程可以构建,真实消息可以被沙箱消费者识别,外部契约不支持最终动作。它们并不矛盾,每层证据回答的是不同问题。
需求最后没有上线。代码做到哪里、为什么停、下一轮需要谁决定,都留在同一个 CHANGE-ID 下。换一个会话继续,不必重新翻聊天记录。
这次经历让我确认,SDD 确实能支撑 Agent 独立开发。它没有保证每个方案都成功,但能让失败停在证据允许的位置。
Skill 是后来补上的执行层
知识结构和门禁跑通以后,我才把重复流程做成 Skill。目前一共五个:
| Skill | 在 SDD 中承担的职责 |
|---|---|
sdd-feature-intake | 从原始诉求建立可评审 Spec,批准前禁止改业务代码 |
sdd-feature-delivery | 按批准版本完成 Design、Tasks、实现、测试和 Evidence |
sdd-incident-repair | 把已确认根因转换成独立的永久修复 Feature |
sdd-quick-change | 给单仓、规则明确、风险很低的改动提供轻量路径 |
sdd-continue-work | 从 Git 恢复批准版本、任务状态、阻塞和下一步 |
它们固定了文档读取顺序、状态门禁、停止条件和维护方式。每次接到需求,Agent 不用临时理解一遍我的工作习惯。
sdd-feature-intake 保存原始意图,区分事实和假设,只把会改变验收结果的问题交给人。sdd-feature-delivery 先确认 Spec 已批准,再进入设计和实现,出现偏差就返回评审。sdd-continue-work 从 Git 恢复上下文,不能凭聊天记忆直接续写。
线上问题单独走 sdd-incident-repair。Incident 先保存影响、时间线、事实、假设和止血动作,根因确认后再建立 Feature。这样,一次故障里的临时现象不会被直接写成永久业务规则。
sdd-quick-change 的门槛比较严。消息、RPC、工单状态、Redis、数据库、配置、权限、个人信息和跨仓依赖,只要碰到一项,就升级成完整 Feature。代码只改一行,业务风险也可能很大。
这五个 Skill 来自已经反复出现的问题。没有 Skill 时,团队也可以手动执行同一套 SDD。Skill 只是让 Agent 稳定地照做。
我现在少做了什么
以前我最常做的是给 Agent 补上下文:下一步去哪找,哪个结论不能信,另一个仓库还有什么。现在这些可验证工作基本由 Agent 自己完成。
我仍然会参与需求,但参与点少了很多。主要是确认目标和非目标、选择会改变业务结果的方案、授权环境或生产写操作,以及业务验收。
长任务也更容易继续。给出 CHANGE-ID 后,Agent 可以从 Spec、Design、Tasks、Evidence 和代码仓状态中恢复现场。聊天记录还在,却不再是唯一记忆。
另一个变化发生在测试上。过去一句测试通过很容易掩盖验证范围。现在本地单测、跨仓契约、异步消息、沙箱、业务验收和生产观察分别记录。某一层成功,只关闭这一层的风险。
还不够稳的地方
这套实践已经跑过真实需求,仍有不少手工环节。
STATE 和 ROADMAP 偶尔会落后于具体 Feature。目前只能约定单项工作以 Feature 目录为准,全局文件用于导航。
历史样本也出现过 spec.md、design.md、tasks.md 和 evidence.md 引用版本不一致。熟悉背景的人能判断,换 Agent 就可能读错批准基线。
校验脚本目前只检查基础文件、Status、Version 和 Requirement ID。它还不会验证每条 Requirement 是否有 Task、测试和 Evidence,也不会检查状态转换是否合法。
脱敏也依赖 Skill 约束、Agent 自检和提交前扫描。账号、工单号、IP、内部链接和凭证还需要更严格的自动门禁。
我准备先补跨文件版本一致性、Requirement 覆盖、敏感信息扫描,以及批准 Spec 的内容哈希。如果构建产物和运行日志能带上哈希,线上行为就能反查到真正批准的规格内容。
如果重新开始
我不会先做五个 Skill,也不会先建设平台。
先建一个 .specs 目录,准备 spec.md、design.md、tasks.md、evidence.md 四个模板。团队只约定三件事:Spec 批准前不改业务代码;提交引用 Spec 版本;遇到规格外事实必须停下并回到评审。
第一次选一个中等复杂的真实需求。最好有旧规则可以参考,同时包含一两个需要人确认的边界。太简单看不出差异,第一次就跨很多系统又容易把流程成本放大。
完整跑过一个需求,再补状态校验和中断恢复。遇到线上事件,再拆 Incident 和永久修复;真的出现低风险小改动,再设计 Quick Change。Skill 从反复发生的问题中抽出来就够了。
我的 SDD 现在也还在改。至少有一件事已经确定:Agent 独立开发不能只依赖更长的提示词。
它需要知道当前依据哪个规格版本工作,哪些事实已经证明,哪些决定没有权限作出,出现偏差以后怎样留下现场。
这些信息稳定下来,我才真的敢从聊天窗口前走开。
评论