☰
AI全栈开发不跑偏:SDD规范驱动与Harness工程驾驭实战
2026/10/2 10:53:52 网站建设 项目流程

做完这个项目,我最大的感受就是:AI全栈开发真正难的从来不是让AI写出代码,而是让AI在二十个功能模块、三千次会话之后,仍然沿着最初的需求主线往前走,不跑偏、不返工、不失控。这一路踩下来,真正管用的不是某个大模型有多聪明,而是两样东西——SDD规范驱动,以及Harness工程驾驭。这个系列写到这里是最后一篇了,我把从理念到落地的完整链路,连同那些坑和取舍,一次性说清楚。

1. 先看清问题:AI全栈开发真正缺的不是能力,是控制面

先聊一个反直觉的结论:我见过很多团队上AI全栈开发,第一周都兴奋得不行,觉得提效好几倍。但做到第二个月,几乎无一例外开始头疼——代码量越堆越多,系统却越来越难维护,AI改一个bug会带出三个新bug,功能逻辑互相打架,接口签名说变就变,测试报告一片绿但生产环境照样出问题。

问题出在哪里?不是模型不行,而是整套开发过程缺少一个"控制面"。

1.1 失控的三个典型表现

我把这段时间观察到的失控现象归纳成三类,每一类都是真实的项目事故来源。

第一个是需求漂移。对话式AI开发有个天然毛病:上下文会漂移。你和Agent聊了四十轮需求,一开始说"给用户发通知",聊到后面变成了"给用户发带附件的多渠道通知",再聊几轮变成"还要做退订管理"。每一步单独看都没错,但最后代码库里的东西跟最初的设计意图已经完全不是一回事。在没有规范约束的情况下,AI会忠实地跟着你最后一句话走,而不是跟着最初的目标走。

第二个是隐性依赖污染。Agent为了完成当前任务,经常自作主张引入新依赖、新工具、新抽象。比如为了做一个时间格式化,顺手装了一个第三方库;为了省事,把某个公共函数的签名改了。这些动作单看都对,但它们打破了其他模块对原有实现的心智预期。最麻烦的是,这些问题不会在编译期暴露,只会在某个深夜上线的瞬间炸给你看。

第三个是上下文膨胀造成的"视野变窄"。项目代码量过了几万行之后,任何单一会话都不可能装下全部上下文。Agent只能在它看得见的局部做决策,而局部最优拼在一起往往不是全局最优。没有机制告诉它"这个模块的边界在哪里""哪些文件是相关约束、哪些可以不管",它就会像修剪灌木一样每个枝条都剪得很齐整,但整棵树却变形了。

1.2 结论:开发的"方向控制"和"过程控制"必须外置

我后来想明白一件事:人类工程师能在大项目里不翻车,靠的是纪律、规范和设计评审,不是靠记忆力。AI比人类记忆力强得多,但它同样需要纪律、规范和评审机制,甚至比人类更需要——因为人类对自己的无知有感知,AI没有。

所以整个系列的核心主张就是这个:把开发的方向控制交给SDD(规范驱动开发),让所有开发和演进都围绕一份结构化契约展开;把开发的过程控制交给Harness(工程驾驭),让每一次AI的产出都经过校验、检查和回退的闭环。两者加起来,才是完整的AI全栈开发方法论。

一句话概括这套思路:SDD管"做什么、什么叫做好",Harness管"怎么做、做坏了怎么办"。

2. SDD规范驱动不是写需求文档,是把需求编译成AI能执行的契约

很多人一听到"规范驱动"就以为是要写更多文档。恰恰相反。我给团队定的规矩是:宁可删掉需求文档,也要保住规范文件。这两个东西的本质区别非常大。

需求文档是给人读的,里面充满了背景故事、术语解释、业务场景描述,甚至还有情绪铺垫。而规范文件是给AI执行系统读的,它必须结构清晰、颗粒度统一、每条都可验证。

传统需求文档AI读不懂,不是因为它不能理解自然语言,而是因为自然语言里充满"模糊量词"和"隐含条件"。比如"系统要在高并发下保持稳定",这句话人和AI都看懂了,但它没法被验证;而"接口在500TPS压力下P99响应时间低于800毫秒,错误率低于0.1%,持续压测30分钟无内存持续增长",这就是可验证的规范。

2.1 需求文档和SDD规范的本质区别

我做了个对比,这里直接列出核心差异,看完就明白为什么要重写一遍规范文件而不是直接扔需求文档给AI:

维度传统需求文档SDD规范文件
读者人人和AI执行系统
结构叙述性、按业务背景组织条目化、按可验证单元组织
验收标准"尽可能""保证""合理"明确指标、明确断言、明确边界
接口定义散落在各处集中定义,带类型和取值范围
变更管理靠口头沟通走版本号、走变更记录

2.2 好的SDD规范长什么样

我需要讲清楚一个可复用的规范文件模板,这是这一篇里最值得抄作业的部分。一个完整的SDD规范,我习惯分成六个区块:

区块一:模块身份卡。功能编号、模块名称、版本号、依赖的上下游模块。比如NOT-001 用户消息通知中心 v3.2,依赖:用户服务(USER-SVC)、模板服务(TPL-SVC)。别小看这一栏,它是后续Harness做影响范围分析的基础——Agent改代码之前先看依赖,知道哪些模块会被波及。

区块二:接口契约。所有对外暴露的函数、API、事件、数据结构,必须写明输入参数类型、输出类型、取值范围、异常类型。格式直接用Interface Definition风格,就算不用IDL工具,也要用表格列清楚。原则是:接口定义只描述"是什么",不描述"怎么实现"。

区块三:业务规则。每一条规则编号化、可单测化。比如:规则R-101:同一接收人在5分钟内最多收到3条同类型通知,超过则合并为一条摘要通知。规则不允许写"如果用户没有设置偏好,给一个合理的默认值"这种话,必须写明默认值是什么。

区块四:验收标准。这是SDD里最重要的区块。我要求每一条验收标准必须能映射到一个自动化测试断言。比如:A-301:单元测试覆盖发送接口的分支,新建用例时Mock HTTP下游,断言超时分支返回RETRYABLE_ERROR。宁可多写十条测试断言,也不要写一段"系统应正确处理异常情况"。

区块五:边界条件和异常处理。把你能想到的所有异常输入、异常状态、极端情况列出来,要求Agent在实现时必须显式处理。比如数据库连接失败、下游超时、消息体超长、渠道黑名单等。这一块是防止AI代码"表面正确"的关键。

区块六:非功能要求。性能、可观测性、安全约束。不需要贪多,抓到当前最重要的两三条就行。比如"发送接口必须在日志中记录request_id""所有第三方调用必须带超时和熔断配置"。

2.3 从规范到任务分解:拆解规则是SDD落地的关键动作

规范文件本身不直接生代码,它要先被拆成Task序列。拆分的过程我有几条硬性经验:

第一,每个Task必须对应规范里的一个可验证单元,一个Task做完了,对应的验收断言必须能独立跑起来。第二,接口定义先行、实现后置。也就是说Agent必须先输出接口契约,审过接口再写内部实现,这样多模块并行时不打架。第三,每个Task的产物边界必须明确:任务结束后可以新增哪些文件、可以修改哪些文件、绝不能碰哪些文件。

我一开始犯过的错误是拆任务拆得太粗,一个Task让AI"实现消息通知模块",结果它一口气写了两千行代码,检查的时候根本没法定位问题。后来改了规则:单个Task的产出代码量控制在两三百行以内,超过就得继续拆。这不是死规矩,是因为AI在长上下文里保持一致性会衰退,短Task的成功率高很多。

2.4 实操模板:我的SDD最小可用模板

这里直接给一个我目前项目里在用的精简模板,你们可以拿去改。用中英夹杂没关系,关键是结构别丢:

module: id: NOT-001 name: user_notification_center version: 3.2.0 depends_on: [USER-SVC, TPL-SVC, CHANNEL-SMTP, CHANNEL-NOTIFY] interfaces: - name: send_notification request: channel: enum[email, inapp, webhook] receiver: string(<=128) template_id: string(<=64) params: map<string, any> request_id: string(uuid) response: message_id: string status: enum[queued, sent, failed] errors: [INVALID_PARAM, TEMPLATE_NOT_FOUND, CHANNEL_UNAVAILABLE, RATE_LIMITED] business_rules: - id: R-101 desc: same receiver + same template within 5min => merge into digest assert: "count(notification_event) <= 3 per receiver per template per 5min" - id: R-102 desc: webhook channel must retry up to 3 times with backoff 1s/4s/16s assert: "retry_count <= 3 and retry_interval matches spec" acceptance: - id: A-301 desc: unit test covers timeout branch and returns RETRYABLE_ERROR - id: A-302 desc: integration test with mock SMTP asserts email is sent with correct payload - id: A-303 desc: 500TPS load test P99 < 800ms, error rate < 0.1%, 30min no leak

写完这份规范,AI要做的不是"猜"你的意图,而是严格按照每一条去实现、去测试。规范的每一条规则都是可执行的断言,"什么叫完成"就变成了一个布尔值——测试全过的布尔值。这是SDD核心价值。

3. Harness工程驾驭:不是提示词工程,是给Agent装方向盘、刹车和仪表盘

SDD解决了方向问题,但光有方向还不够。就像一辆车有导航但没方向盘和刹车,你只能看着它冲向路边。落地过程中,真正把SDD的威力释放出来的是外面这层Harness——工程驾驭框架。

3.1 Harness到底是个什么东西

先破除一个误解:Harness不是一段更聪明的系统提示词,也不是某个IDE插件。它是一整套围绕Agent工作流的可执行控制层,包括任务调度、上下文裁剪、工具调用约束、检查点、回退机制、状态记录和人工介入接口。

打个比方:大模型是发动机,SDD是导航路线,Harness是整辆车的控制系统——它决定什么时候给油、什么时候刹车、什么时候转弯、仪表盘报警了怎么处理。没有这层控制,再强的发动机也只是怠速轰鸣的野兽。

在具体落地的时候,Harness可以是独立脚本,可以是基于Claude Code或CodeBuddy这类工具的配置层,也可以是自己写的Agent调度器。社区里常说的deepseek harness这类工作流插件,本质就是这个思路——给模型配一个工程框架,让它在边界内干活。具体用哪个壳不重要,控制层的机制才重要。

3.2 三层驾驭结构:执行层、检查层、回退层

我实践下来,一个可靠的Harness至少要有三个层次,缺一层都会出问题。

第一层是执行层,负责把SDD任务拆成Agent可执行的子任务序列,调度模型的调用,管理上下文窗口。这里有个容易被忽视的点:上下文边界管理。每个Task执行时,执行层只把与该Task相关的接口定义、涉及的文件列表、约束规则喂给模型,其余一律不给。模型每轮能看到的内容是"最小作用域"的,就像函数式编程里的局部变量,它改不到全局的东西。很多Agent越改越乱,就是因为上下文给了全局代码库,它觉得自己什么都该碰。

第二层是检查层,在Agent每完成一个子任务产物后立即运行。检查内容至少包括:静态编译、单元测试、规范符合度扫描、变更影响范围核对。规范符合度扫描是SDD和Harness咬合的关键——用脚本把SDD里的验收断言映射成具体的检查命令,跑不过就返回失败,不会因为Agent声称"完成"就放行。我见过很多团队在这里偷懒,只看编译通过就收工,结果业务规则全错。

第三层是回退层,检查不通过时的处理策略。不是简单地让模型重试一次,而是分级处理:小问题(格式错误、命名不规范)直接进自动修复循环;中等问题(某个测试跑不过)把失败日志、相关代码段、测试输出重新塞回给Agent,让它针对失败点修复;大问题(跨模块接口不一致、验收断言大面积失败)要停下来,回到SDD层面确认是规范写错了还是实现写错了,修正之后再重跑任务。

3.3 工具调用约束与环境隔离

这一块是Harness控制面里最容易被新手跳过、又最容易出事的部分。AI Agent在开发过程中会调用各种工具:执行命令、读写文件、调数据库、发网络请求。如果不加约束,它一个顺手就能把生产环境配置改了,或者把测试库数据洗了。

我的原则是白名单机制加沙箱运行。白名单列出Agent允许触达的工具和命令:文件读写限定在工作区内、命令限定在构建和测试相关指令、网络访问默认阻断。沙箱是另一个关键:所有Agent生成的代码在独立的Docker容器里跑测试,容器里打快照,跑坏了整个环境直接回滚重来。这套机制保证了"AI做错了没事,最坏情况回到上一个检查点",而不是"AI做错了,带着生产环境一起遭殃"。

还有个看起来琐碎但非常重要的事情:依赖锁定。AI为了省事容易引入新库,白名单机制里要加一道依赖审查——新增依赖必须走人工审批。我经历过一次事故:Agent引入了一个字符串处理库,结果这个库的版本跟核心框架冲突,上线当天疯狂抛异常。从那以后,新增依赖默认禁止,只有人工显式放行才允许。

3.4 与主流AI编程工具的配合方式

我实际工作中用过几套组合,这里把配合逻辑讲清楚。

基于Claude Code这类交互式工具的Harness,主要表现形式是hook脚本和自定义规则文件。工具负责Agent和代码库的交互,Harness在关键节点拦截产物、跑检查。比如在每次Agent修改完成后触发一个钩子,自动跑规范扫描器,不过就让Agent继续改。这套方案优点是上手快,缺点是检查逻辑全散落在各种脚本里,维护成本会起来。

做完整的项目,我建议自己写一个调度器,把模型调用、工具链、检查脚本全部收进来。CodeBuddy这类IDE里也可以嵌入脚本,但更常见的是独立进程。我自己的落地路径,是先以工具插件方式跑通局部,验证了SDD规范的质量,再逐步把检查逻辑收拢成独立Harness核心。社区里deepseek harness这类自定义工作流方案,走的就是这条路径——在模型之上叠规则、叠检查、叠回退。

3.5 本地配置Harness的常见报错与处理

配置Harness时我踩过一个很典型的坑,就是插件清单和运行时版本不匹配导致的加载失败。表现形式往往是控制台报类似failed to load plugins、web boot: entries did not activate这样的错误。排查路径很有代表性:

第一步先看插件清单文件的格式和路径,很多加载失败都是路径写错或清单格式不符合要求。第二步看运行时日志里具体哪个entry没激活,把"不激活"的三个常见原因过一遍:依赖的服务没起来、版本号与当前运行环境不兼容、清单里声明的钩子函数在代码里找不到。第三步,如果是版本不匹配,优先锁定运行环境版本,再调插件声明,不要反过来先升级环境。

这种"加载失败先查清单再查依赖最后查版本"的思路,实际上就是最简单的Harness排查方法论。把这套逻辑内化成肌肉记忆之后,后续遇到更复杂的Agent工作流异常,都能快速定位。

4. 实战拆解:一个消息通知模块从SDD规范到全栈交付的完整链路

方法论说再多,不如一个完整案例来得实在。这里我用一个简化过但保留了关键细节的真实模块做样例,带大家走一遍整个流程:从写规范、拆任务、跑Harness,到一次真实的检查层拦截和回退。

4.1 需求场景和SDD规范编写

假设要做一个用户消息通知中心,支持邮件、站内信、Webhook三种渠道。需求方给的原始描述就一句话:"用户触发某个操作时,系统要给相关人发通知,注意别发太多,别把用户烦死了。"

这句话当然不能直接扔给AI。我把它改写成SDD规范,核心内容就是前面章节里的那个模板。业务规则上,"别发太多"转成了一条可测试的限频规则:同一接收人在5分钟内最多收到3条同类型通知,超出则合并成摘要。这是从模糊需求到可验证断言的关键一步。

接口契约部分,我把send_notification接口的入参、出参、错误类型全部定义清楚。注意这里每个错误类型都有明确触发条件,Agent编码时不用猜。验收标准部分,我写了三类:单测覆盖关键分支、集成测试用Mock Mail Server验证邮件投递、压测验证性能指标。

4.2 Harness执行路径:任务序列与检查点

规范写好后,Harness开始工作。我设计的Task序列如下:

Task1负责初始化项目骨架、建数据库迁移脚本。检查点是迁移脚本dry-run通过、权限校验通过。Task2负责实现send_notification核心接口。检查点是编译通过、针对R-101限频规则的单元测试通过。Task3负责Webhook发送回调与重试逻辑。检查点是重试时序测试通过。Task4负责接入三种渠道的适配器。检查点是集成测试用例全部跑过。

每个Task执行时,Harness只把这个Task对应的接口定义、涉及的文件列表和约束喂给模型。模型每完成一个Task,检查层自动跑一遍对应的测试和控制脚本,不通过就进回退层。

4.3 真实事故复盘:连接池泄漏是怎么在检查层被拦下来的

这里讲一个让我印象极深的真实案例。Task2完成时编译和单测全绿,看起来一切正常。但检查层里有一道"连接泄漏检测":跑集成测试的时候,监控进程的数据库连接数。第一次跑完,连接池里的连接数比预期多了十几个。直觉告诉我这是泄漏——Agent在某个异常分支里获取了连接,但没有归还。

但更值得记录的是回退层的第一轮处理。Harness把连接数异常的报告、相关代码段一起塞回给Agent,让它修复。Agent确实改了,但改完又引出一个新问题:它在某处提前关闭了连接,导致后面一个事务报错。检查层的测试立刻把它拦住了。这就是回退层的好处——Agent可以错一次、错两次,但错误的代价被限制在一个Task范围内,不会扩散到整个项目。

第二轮我再仔细看了代码,发现问题根源不只是"忘了归还连接",还有一处异常处理路径里把defer写在了错误位置,导致异常时资源释放代码根本没执行。我意识到这类资源生命周期问题靠Agent自我修正效率不高,于是在SDD规范里加了一条代码约束:所有数据库连接获取必须使用统一的连接管理器,禁止裸调连接。规则更新后重新跑Task2,一次通过。

这个案例给我的启示是:回退层不是无限重试,当同一个Task连续回退两次,就应该怀疑SDD规范本身有盲区,补规则比继续让Agent试错更有效。

4.4 交付结果与验收复盘

整个模块做完用了四个Task、三次回退、一次规范补全,耗时一个下午。如果纯粹让AI放开了写,可能两个小时就写完了,但后续排查问题可能要花两天。有了SDD和Harness,大部分问题在Task边界内就被拦截了,交付后我只需要做最终的代码走读和性能验收。

最终验收时,500TPS压测过了,P99约700毫秒,连接数平稳。限频规则用脚本灌了十万条事件验证,合并逻辑正确。这个模块上线后跑了一个月,没有因为这个模块本身的逻辑出过一次事故。可以说这是一个"稳稳当当"的AI全栈交付样本。

5. 多AI协作时代的Harness:多Agent并行、争议裁决与RPA落地

单Agent跑通一件事不难,真正考验Harness能力的是多Agent协同。我现在做项目不是让一个AI干所有活,而是让好几个Agent并行,各管一摊。刚开始这么干的时候很混乱,后来总结出一套可行的方法。

5.1 多Agent各说各话的问题与解法

三个Agent并行的时候,如果互相之间没有契约,就会出现典型的"各说各话":A模块的Agent定义了一个UserId字段类型,B模块的Agent用了UserID字符串,C模块的Agent直接拍脑袋用了userId: Long。每个模块单测都过,联调时全崩。

解法其实SDD里已经定了:接口契约阶段的输出就是"协议",所有Agent都必须遵守。Harness在任务调度时强制每个Agent先读到统一接口契约,不允许任何人私自改。如果必须变更,走版本升级流程,变更之后所有依赖模块的测试都要重跑。

5.2 主从式Agent架构和职责边界

我的多Agent配置通常是这样的架构:

一个主控Agent(Orchestrator)负责任务分解、进度跟踪、最终验收,它不直接写业务代码。多个开发Agent(Worker)分别负责不同模块,只接收主控下发的Task和对应的规范片段。一个代码评审Agent负责"挑刺",专职看代码质量、潜在bug、规范偏离。一个测试Agent负责根据规范生成测试用例,补充Harness检查层覆盖不到的场景。一个文档Agent负责维护SDD规范的状态和变更记录。

这个架构最大的好处是职责单一。评审Agent不用写代码,它不会维护自己写的代码而心软;测试Agent不用实现功能,它专门想办法把实现搞坏——这两个角度分开之后,整体质量比单Agent"代码+自测"高出很多。

5.3 状态标记与仲裁机制

多Agent协作必须有个统一的状态视图。我要求所有Task在Harness里显式标记状态:pending/in_progress/done/failed/needs_review。状态变化由Harness统一管理,Agent不能自己把自己标记为完成,必须通过检查层验证后才转done。

仲裁机制是另一个容易被忽略的部分。两个开发Agent对同一个公共接口的实现方案有分歧时,我们不能指望AI之间自己谈拢。我的做法是:争议先升级给主控Agent,主控对照SDD规范作出裁决;如果SDD规范里也没写清楚,就开一次"人机评审会",由我把规则补进规范再继续。简单说,AI之间的分歧永远不要在代码层面解决,一定要回到规范层面解决。

5.4 Harness思路在RPA落地中的延伸

多AI协作这套东西,我后来发现直接可以用在RPA(机器人流程自动化)落地项目里。以前做RPA,每个机器人脚本各写各的,流程一多就互相踩脚,数据格式不统一,状态还不可追踪。用了Harness的思路之后,每个RPA机器人被视为一个Agent,每个自动化流程先写一份"流程SDD"——明确输入、输出、异常处理、数据格式、验收断言,然后由一个调度管理器来编排机器人之间的依赖和状态。

实践下来,最直观的变化是两个:第一,机器人之间的数据交接不再靠"约定俗成",而是靠契约文档和运行时校验;第二,某个流程失败后的重试策略和影响范围被清晰管控,一个机器人挂掉不会拖垮整条链路。这套跨领域的方法迁移,是我在这个系列里最意外的收获。

6. 连载至此的边界认知:SDD和Harness到底管什么、不管什么

写了这么多篇,到这篇收尾,我觉得有必要把边界也讲透。任何方法论都有适用边界,SDD和Harness不是万灵药,搞清它们的边界反而能让它们用得更准。

6.1 SDD会失效的场景:探索型需求

当需求本身处于探索阶段,连你自己都不知道"什么叫对"的时候,SDD的强行规范会起反作用。比如做一个新的UI视觉风格、探索一个全新的交互模式,这类任务的核心价值在于试错和发散,你如果先写一堆验收断言,反而把AI的手脚捆死了。

我的建议是分阶段处理:探索阶段用"非正式对话+原型验证"的方式,让AI自由发挥,跑通一个粗糙雏形评估方向;一旦方向确认,立刻把探索结论固化成SDD规范,再进入正式的开发阶段。先发散、后收敛,比全程发散或全程收敛都高效。

6.2 Harness误伤:"管太死"导致Agent能力萎缩

约束太轻会失控,约束太重也会有问题。我有段时间在Harness检查层加了非常多的规则,结果发现Agent变得极度保守,每次改动都只做最小变更,宁可重复代码也不肯抽取公共函数,因为怕"碰了别的模块引发检查失败"。

后来我调整了策略:把检查规则分成"硬性门禁"和"软性建议"两类。硬性门禁必须通过——测试、编译、安全扫描;软性建议供参考——代码风格、命名规范。同时定期做"松绑实验",随机放宽某些约束让Agent发挥创造力,看看有没有带来意外的好方案,有就吸收到规范里,没有就恢复约束。

6.3 对AI测试开发方向的延伸思考

最后聊聊热词里提到的AI测试开发。现在我越来越觉得,SDD规范驱动在测试领域价值更大——因为测试本身就是为规范服务的。AI测试开发的正确路径不是让AI随机生成一堆测试用例,而是让它从一个模块的SDD规范出发,针对每一条业务规则、每一个边界条件、每一个验收标准,去设计能够证明"代码满足规范"的测试策略和测试用例。这套方法论完全可以复刻过来。

6.4 写在最后的一点体会

这个系列到这里收尾了。我记得最开始做AI全栈开发的时候,被那种"AI一口气写好几千行代码"的爽感冲昏过头。后来经历了几次线上事故,才意识到工程化的核心不是让AI多快,而是让它出错之后的代价多小。SDD和Harness给我的最大改变不是"交付更快",而是"睡得安稳"——该有的验收跑过了再上线,该收敛的边界有人盯着,该回退的时候一步退到干净的地方。

如果你正在做AI全栈开发的落地,我真心建议不要急着堆更多更好的模型,先把规范文件的结构定下来,先让每个Agent的产出有明确的验收断言,先让每次失败都有低成本的回退机制。做到这三点,哪怕用最普通的模型,整个项目的可控性也会上一个台阶。反过来,这三样缺一样,换再强的模型也只是把跑偏的速度提得更快而已。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询