ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SDD与Harness:AI辅助开发的可控化工程实践

SDD与Harness:AI辅助开发的可控化工程实践 1. 从能跑就行到可控可审AI辅助开发正在经历的范式切换过去一年里我参与过三个不同规模的研发团队做AI辅助编码落地最大的感受是工具本身早就够用了真正卡住团队的是不可控。模型能一口气生成两百行代码但没人说得清它为什么这么写、边界条件覆盖了没有、下次换个需求还能不能复现同样的质量。这种状态下AI更像一个灵感喷射器而不是工程资产。SDDSpec-Driven Development规范驱动开发和Harness驾驭工程这两个词最近在圈子里被反复提起本质上就是在回应同一个问题怎么把AI从随机发挥的助手变成受约束的工程组件。SDD解决的是输入端的确定性——用结构化规范替代模糊的自然语言描述Harness解决的是执行端的可控性——给AI套上一圈可观测、可干预、可回滚的工程外壳。这套组合拳适合谁我的判断是三类人一是已经在用Codex、Claude Code这类工具但被结果不稳定折磨的开发者二是想把AI编码引入团队流程、但卡在评审和合规环节的技术负责人三是做AI测试开发、需要把模型行为纳入自动化验证链路的工程师。如果你只是偶尔让AI写个正则表达式那这套东西对你来说属于杀鸡用牛刀但只要你开始把AI产出往生产分支上合SDDHarness就值得认真研究。需要先说明一点本文不会涉及任何具体网络接入方案或工具获取渠道只讨论工程方法论和落地结构。所有代码示例都是本地可验证的伪代码或配置骨架你可以根据自己的技术栈替换实现。2. SDD到底规范了什么把提示词升级成可执行契约2.1 为什么自然语言提示词天生不适合工程协作大多数人用AI写代码的方式是打开对话框敲一段描述看结果不满意就改描述再试。这个循环在个人场景下没问题但一旦进入团队协作就暴露三个硬伤。第一是不可版本化。你昨天写的那段提示词今天可能因为模型版本更新、上下文长度变化、甚至温度参数微调而产出完全不同的结果。提示词躺在聊天记录里没法像代码一样做diff、做review、做回滚。第二是隐含假设太多。你说写一个用户登录接口模型默认用了JWT但你的系统用的是Session模型默认返回200但你的规范要求业务错误也走200带错误码。这些假设在生成前不可见生成后才暴露返工成本极高。第三是验收标准模糊。写得对是一个主观判断没有可执行的检查项。这就导致AI产出无法进入自动化流水线只能靠人工肉眼看。SDD的核心思路就是在让AI动手之前先把要做什么、做到什么程度、怎么算做完写成机器可读的规范。这份规范不是给人看的文档而是给AI和验证工具共同消费的契约。2.2 一份最小可用的SDD规范长什么样我不建议一上来就搞大而全的规范体系先从单个功能点开始。下面是我在实际项目中用过的一个最小结构用YAML描述你也可以用JSON或TOMLspec_id: user-login-v1 intent: 用户通过邮箱和密码完成登录返回会话令牌 inputs: - name: email type: string constraints: 符合RFC5322邮箱格式长度不超过254 - name: password type: string constraints: 长度8-64至少包含一个数字和一个字母 outputs: success: session_token: string expires_in: integer failure: error_code: enum[INVALID_CREDENTIAL, ACCOUNT_LOCKED, RATE_LIMITED] message: string invariants: - 密码在任何日志中不得明文出现 - 连续5次失败后锁定账户15分钟 - 响应时间P99不超过300ms acceptance: - 给定合法凭证返回200且session_token非空 - 给定错误密码返回error_codeINVALID_CREDENTIAL - 第6次失败请求返回ACCOUNT_LOCKED这份规范的价值在于每一条都可以被翻译成一个测试用例。acceptance字段直接就是验收清单invariants字段就是不可违反的约束。AI生成代码时这份规范作为系统提示的一部分注入代码生成后验收清单自动转成测试脚本跑一遍。整个过程不需要人去读代码判断对不对。2.3 规范粒度太粗等于没写太细等于自己写这是我在实践中踩得最深的坑。一开始我把规范写得非常细细到第3行应该声明一个局部变量叫temp结果发现两个问题一是写规范的时间比直接写代码还长二是把AI的发挥空间压没了生成的代码死板且不优雅。后来我总结出一个粒度原则规范描述契约和约束不描述实现。具体来说要写输入输出的类型和边界、错误码枚举、性能指标、安全不变量、验收条件不要写用什么数据结构、循环怎么写、变量叫什么名字、函数拆几个判断标准很简单如果这条规范换一种实现方式仍然成立那它就该写进SDD如果它绑定了特定实现那它属于代码本身不属于规范。举个例子响应时间P99不超过300ms是契约该写使用Redis缓存会话是实现不该写。后者可以放在一个单独的技术选型说明里供AI参考但不作为验收条件。2.4 规范与代码的双向追溯SDD真正发挥威力是在你建立起规范-代码-测试三者之间的追溯关系之后。我的做法是给每个规范一个spec_id然后在生成的代码文件头部、对应的测试文件头部都标注这个ID。这样带来两个好处一是变更影响分析。当规范从v1升到v2我能立刻定位到哪些代码文件和测试用例需要同步更新。二是覆盖率度量。我可以统计有多少条验收条件被测试覆盖这个数字比代码行覆盖率更能反映AI产出的可信度。在实际操作中我用一个简单的脚本扫描代码库中的spec_id标注生成一张追溯矩阵表。这张表在代码评审时特别有用——评审者不需要逐行读AI生成的代码只需要确认每条验收条件都有对应测试且通过即可。3. Harness工程层给AI套上可观测、可干预的外壳3.1 Harness和Agent的本质区别很多人把Harness和Agent混为一谈其实两者关注点完全不同。Agent关注做什么——任务规划、工具调用、多步推理Harness关注怎么管——执行边界、状态观测、失败干预、结果验证。打个比方Agent是一个能自己找路走的司机Harness是车上的行车记录仪、限速器、紧急刹车和安全气囊。没有Harness司机可能开得很快很聪明但一旦出事你既不知道发生了什么也没法及时止损。在AI辅助开发场景里Harness要解决的具体问题是AI调用了哪些工具、读了哪些文件、改了哪些代码全程可追溯AI在执行过程中如果偏离规范能实时拦截而不是事后发现AI的每一步产出都有中间快照可以回滚到任意节点整个执行过程有统一的日志和指标能接入现有监控体系3.2 一个Harness的最小骨架下面是我在一个内部项目里搭的Harness骨架用Python伪代码表示核心是拦截-记录-校验三段式class DevHarness: def __init__(self, spec, sandbox, validator): self.spec spec # SDD规范对象 self.sandbox sandbox # 隔离执行环境 self.validator validator # 规范校验器 self.trace [] # 执行轨迹 def execute(self, task): self.trace.append({event: start, task: task}) # 1. 前置校验任务是否符合规范范围 if not self.validator.in_scope(task, self.spec): return self._reject(task out of spec scope) # 2. 沙箱内执行所有文件操作被重定向 with self.sandbox.isolate() as env: result self._run_agent(task, env) self.trace.append({event: agent_done, result: result}) # 3. 后置校验产出是否满足验收条件 verdict self.validator.check(result, self.spec) self.trace.append({event: validated, verdict: verdict}) if not verdict.passed: return self._rollback(verdict) return result def _rollback(self, verdict): # 回滚沙箱内所有变更保留trace供分析 self.sandbox.revert() return {status: rejected, reasons: verdict.failures}这个骨架的关键设计点有三个。第一沙箱隔离——AI的所有文件写入都发生在临时副本上只有通过校验才合并回主工作区。第二前置范围校验——防止AI顺手改了规范之外的文件这在多人协作时特别重要。第三后置验收校验——把SDD里的acceptance字段自动转成检查逻辑不通过就整体回滚。3.3 执行轨迹Harness最有价值的副产品self.trace这个列表看起来不起眼但它是我用下来觉得Harness最值钱的部分。每次AI执行任务trace里记录了任务描述、调用的工具序列、读写的文件列表、每步的耗时、校验结果。这些数据积累起来能做很多事定位高频失败模式如果某个类型的任务反复在校验环节失败说明规范写得有问题或者模型在这个领域能力不足需要调整策略优化提示词对比成功和失败的trace能看出哪些上下文信息是关键的成本核算统计每个任务的token消耗和工具调用次数为资源分配提供依据审计合规当需要回答这段代码是谁在什么时候基于什么规范生成的trace就是答案我建议trace用结构化格式JSON Lines就够落盘不要只放在内存里。落盘之后可以接入现有的日志分析工具不需要另起炉灶。3.4 干预机制什么时候该让AI停下来Harness的驾驭二字核心体现在干预能力上。我设置了四类触发条件任何一类命中就暂停执行并通知人工介入触发类型具体条件处理动作范围越界修改了规范未授权的文件立即回滚记录告警资源超限单任务token消耗超过阈值暂停等待人工确认是否继续校验失败验收条件连续2次不通过回滚转人工分析规范敏感操作涉及删除、权限变更等操作强制人工审批后放行这四类条件不是拍脑袋定的是从实际事故中总结出来的。比如范围越界这一条就是因为有一次AI在实现登录功能时顺手重构了一个不相关的工具类导致另一个模块的测试挂了。加了范围校验之后这类问题再没出现过。4. 把SDD和Harness接起来一条完整的可控化流水线4.1 从规范到代码的完整链路单有SDD或单有Harness都不够两者必须串成一条链路才能发挥价值。我实际跑的链路是这样的规范编写开发者用YAML写SDD规范提交到独立的specs/目录走正常的代码评审流程规范解析CI流水线解析规范自动生成验收测试骨架和提示词模板Harness执行AI在Harness沙箱内基于提示词生成代码全程trace落盘自动校验验收测试在沙箱内跑一遍不通过则回滚并输出失败原因人工评审通过校验的代码进入正常PR流程评审者对照规范检查合并归档合并后规范、代码、测试、trace四者通过spec_id关联归档这条链路里第2步和第4步是自动化的关键。规范解析器把acceptance字段转成pytest用例把inputs和outputs转成类型定义和mock数据。这部分工作一次投入后续每个规范都受益。4.2 提示词模板的自动生成很多人忽略的一点是SDD规范本身就是最好的提示词。与其手写你是一个资深工程师请帮我写一个登录接口……不如把规范结构化地注入你正在实现规范 {spec_id}。 意图{intent} 输入约束{inputs} 输出契约{outputs} 不可违反的不变量{invariants} 验收条件{acceptance} 要求 - 只修改 {allowed_files} 中列出的文件 - 不要引入规范未提及的外部依赖 - 每个验收条件对应至少一个测试用例这个模板由解析器自动填充开发者不需要手写提示词。好处是提示词的质量不再依赖个人经验而是由规范的质量决定——而规范是可以评审、可以迭代的。4.3 失败回滚的粒度控制回滚不是简单地全部撤销。我在实践中把回滚分成三个粒度文件级回滚只撤销某个文件的变更适用于多文件任务中单个文件校验失败的情况任务级回滚撤销整个任务的所有变更适用于核心校验失败会话级回滚撤销一个会话内所有任务的变更适用于发现规范本身有问题的场景粒度选择由失败类型决定。验收条件失败通常是文件级或任务级规范范围越界直接任务级连续多次失败则升级到会话级并冻结该规范直到人工修复。这里有个经验回滚一定要保留trace和中间产物。我见过有团队回滚时把临时文件也删了结果事后想分析失败原因却无从下手。正确做法是把失败现场完整保留在一个failed/目录下供后续复盘。4.4 与现有CI/CD的集成点Harness不需要替代现有CI/CD而是嵌入其中。我的集成方式是在CI里加一个独立的ai-harness阶段位置在代码提交之后、单元测试之前。这个阶段做三件事跑Harness执行、跑验收校验、输出trace报告。只有这个阶段通过才进入后续的常规测试和构建。这样做的好处是AI产出和人工产出走同一套质量门禁不会因为这是AI写的就降低标准。同时Harness阶段的trace报告作为构建产物归档出问题时可以追溯到具体的AI执行会话。5. 实战中踩过的坑与应对策略5.1 规范膨胀从够用到失控只有一步之遥项目初期我犯的最大错误是规范写得越来越细最后一份登录功能的规范写了三百多行涵盖了从数据库索引到日志格式的所有细节。结果是写规范花了两天AI生成代码花了十分钟然后发现规范里有两处自相矛盾又花了一天修规范。后来我定了一条硬规矩单份规范的验收条件不超过15条不变量不超过5条。超过这个数量就说明这个功能该拆了。拆分之后每份规范聚焦一个明确的契约写起来快校验起来也快。5.2 模型对规范的选择性遵守即使规范写得很清楚模型有时也会选择性遵守——比如忽略了某个不变量或者验收条件只满足了一部分。这不是模型故意对抗而是长上下文中的信息衰减。我的应对策略是把关键约束前置并重复。具体做法是在提示词的开头和结尾各放一次invariants中间放具体任务描述。实测下来关键约束的遵守率从七成左右提升到九成以上。另外把不变量转成运行时的断言assert嵌入生成的代码里即使模型忘了运行时也会暴露。5.3 沙箱环境的性能开销沙箱隔离听起来很美但每次任务都复制一份完整工作区在大型仓库上开销很大。我试过全量复制一个任务光准备环境就要几十秒。优化方案是增量快照只对任务可能涉及的文件做写时复制copy-on-write其余文件通过只读挂载共享。这样环境准备时间降到秒级。具体实现依赖操作系统能力Linux上可以用overlayfs其他平台可以用文件系统层的快照工具。核心思路是只隔离会被修改的部分。5.4 校验器的误报与漏报自动校验器不可能百分之百准确。我遇到过两类问题一是误报把合法的实现判为失败二是漏报明显违反规范的代码却通过了。误报主要来自验收条件写得过于死板。比如返回200这条如果实现返回了201校验器就判失败但实际上201也是合理的。解决办法是把验收条件写成范围或集合而非单值比如返回2xx。漏报主要来自不变量无法自动检查。比如密码不得明文出现在日志中这个需要扫描日志输出才能验证。我的做法是给这类不变量单独写检查器作为校验器的一个插件。写检查器有成本所以只对真正重要的不变量做不是每条都做。5.5 团队协作中的规范所有权问题SDD规范由谁维护这个问题在团队里争论了很久。一开始让每个开发者自己写自己的规范结果风格五花八门校验器没法统一处理。后来改成规范由功能负责人写由架构组评审质量稳定了但流程变重了。最终的折中方案是提供规范模板和校验器插件库开发者基于模板写架构组只评审不变量和验收条件这两部分。模板保证了结构统一插件库让常见校验逻辑可以复用架构组聚焦在真正需要把关的地方。这样既保证了质量又没有把流程压得太死。6. 从单点工具到工程体系我的落地节奏建议如果你打算在团队里推这套东西我的建议是分三步走不要一上来就搞全套。第一步先在一个小功能上跑通SDD。选一个边界清晰、验收条件明确的功能比如一个数据校验工具或者一个API端点。只做规范编写和人工对照检查先不引入Harness。这一步的目标是让团队习惯先写规范再写代码的节奏大概需要一到两周。第二步引入Harness的沙箱和校验能力。在第一步的规范基础上搭建最小Harness实现沙箱隔离和验收校验。这一步的技术投入比较大需要有人熟悉容器或文件系统隔离技术。跑通之后你会发现AI产出的返工率明显下降。第三步接入CI并积累trace数据。把Harness阶段嵌入CI开始积累执行轨迹。有了数据之后你才能做优化——哪些规范写得好、哪些提示词有效、哪些任务类型适合AI、哪些不适合。这一步是长期投入但回报也最大。需要提醒的是这套体系的收益不是线性的。前期投入大、收益不明显容易让人放弃。但只要跨过某个临界点——通常是积累了二三十份规范、trace数据足够分析之后——收益会突然显现AI产出的可信度上来了评审成本下去了团队对AI的信任度也上来了。最后分享一个我自己的体会SDD和Harness的价值不在于让AI写得更快而在于让AI写得更可预期。速度提升是副产品可预期性才是工程化的前提。一个产出速度一般但结果稳定的AI比一个偶尔惊艳但经常翻车的AI对团队的价值大得多。这套体系就是奔着稳定可预期去的如果你也认同这个方向那它值得你投入时间。
返回列表