三大护法:SDD、TDD、MDD#
Harness Engineering 并没有发明一门新的工程学科。它从三门既有学科里招兵买马,给它们重新排了顺序,再把它们前置到智能体运转的那片空间里。
第 03 章把 Harness Engineering 定义为:塑造 AI 编码智能体 周围 的那些结构,以使它所产出的软件具备 可验证、可观测、可理解 这三项属性。这三个形容词不是修辞上的点缀。它们每一个都指向一门成熟的软件工程学科,背后都有四十年左右的谱系和一份经典文献。本章把这三门学科一一引出来,并阐明本书做出的最关键的一个选择——为什么一旦一个 AI 智能体进入回路之中,这三门学科就必须按 SDD → TDD → MDD 这个顺序施行,而不是传统上的 TDD → MDD → SDD。
为什么一旦智能体进入回路,因果顺序就翻了#
在传统软件工程里,可写的单元是源代码,人是作者。测试摆在最前(TDD),因为测试是第一份可执行的规约,人会花上几个小时把代码往它身上推,直到两边达成一致。文档(也就是经典的、以 文档 为中心的那种 SDD)常常是最后才写的——如果写了的话——而度量(MDD)则作为一张安全网,在生产环境里被监视着。这条因果链看起来是 测试 → 实现 → 观察,中心是由人亲手写下的代码。
在 AI 辅助的工程实践里,可写的单元整体上移。人所写下的是 提示词、技能文件、AGENTS.md、CLAUDE.md 兼容层——代码是由智能体写的。而一旦它读到的规约含糊,智能体会 极其自信地 把行为幻觉出来;于是任何一份在规约钉稳之前就被写出来的测试,都会把幻觉编码进去,而不是把幻觉捕住 [Peng et al., 2023, Ziegler et al., 2022]。因果链于是翻转成 规约 → 测试 → 观察:
SDD 优先——因为规约现在是由人亲手写下的主要制品;它塑造的是 智能体试图去建什么。
TDD 其次——因为测试现在要 核验智能体对规约的诠释,而不再是它自己充当第一份规约。
MDD 压阵——因为度量闭合反馈回路的方式,是 持续确认规约 + 测试仍与生产现实相符,而不是在末端当作一张安全网。
本章接下来会按这一因果顺序逐一走过三大护法,为每一位给出一份经典定义、一份 AI 时代的诠释、以及一个具体的马具示例。章末会指向第 08 章,那里三大护法会化作本书 3 × 4 矩阵 的三行,与"四区域"(缰绳/护栏/牧场/梳理)相乘。
护法一 —— SDD(Specification-Driven Development,规约驱动开发)#
经典定义#
Specification-Driven Development 的意思是:在系统被造出来 之前,先写下一份关于系统行为的、机器可核验的描述,并把这份描述当作一件和代码一起演进的主要制品。它的经典谱系从 Meyer 的 Design by Contract [Meyer, 1992](把前置条件、后置条件、不变式当作一等公民)出发,途经 Adzic 的 Specification by Example [Adzic, 2011](把可执行的例子当作产品与工程之间的活合同),一直走到 Martraire 的 Living Documentation [Martraire, 2019](文档由代码生成,又对着代码做核验)。贯穿四十年、始终不变的论点是:含糊的规约产出含糊的软件,而修正这份含糊,最便宜的地方是规约本身。
AI 时代的诠释#
当作者变成智能体时,规约就成了 输入,而不是事后追加的附注。大部分工作由三类制品承担:
AGENTS.md/CLAUDE.md兼容层——家规、文件边界,以及智能体在动手改动之前必须尊重的不变式 [Agentic AI Foundation, 2025, Anthropic, 2024]。SKILL.md文件——针对反复出现的任务、期望智能体一步一步照做的过程 [Vincent, 2025]。MCP 服务器 manifest——机器可读的工具契约,说明智能体 能做什么,以及 每一次调用要花多少 [Anthropic, 2024]。
这些东西不是关于代码的文档。它们是 编码过程本身的输入——智能体每一轮都要读一遍的那种,而不是新员工入职那天看一次就忘的那种。这就是为什么 SDD 被推到了因果链的最前面:一个拿着过时或含糊的 AGENTS.md 的智能体,从第一轮起就会写下过时或含糊的代码;下游再多的测试,也找不回当初规约没钉住的那份意图。
这里值得命名的因果机制叫 含糊放大(ambiguity amplification)。一个人读一份含糊的规约,通常会 察觉 到这份含糊——他会迟疑、重读、找同事问、或保守地猜一版。语言模型默认不做这些事。给它一句"写一个地道的错误处理器",它就会自信满满地吐出 某一个 具体的处理器版本;提示词里的含糊被洗成了代码里的"假精确"。于是 AGENTS.md 里一条含糊的 bullet,在接下来一个季度里会变成一百个写得自信却彼此微妙不同的实现。这份含糊的代价,被智能体的吞吐率乘了出去——这也正是为什么智能体一进回路,收紧规约 就成了杠杆最高的那项投入。
陷阱——规约漂移(SDD 那种无声的失败)
AGENTS.md 上写着 "所有数据库写入都走 repo.Repository"。六个 sprint 之后,有三个服务文件绕过了 repository,直接走 sqlx 写——这是一次事故里引入的模式,后来没被回滚掉。AGENTS.md 上那条 bullet 一字未改。把 AGENTS.md 当作权威来读的智能体,继续产出 与规约相符 的代码,而代码库 越来越不相符。智能体每写一个新文件,这条裂缝就被拉宽一点——因为它是"对规约是对的";人每写一个新文件,这条裂缝也被拉宽一点——因为它是"对当下代码库是对的"。症状:评审者对这条规则"到底"是什么意思意见不一;同一种模式的新代码有时被拒、有时被收;智能体的违规和规约相关,而非和评审人的个人偏好相关。解法:一个定时任务(即一位梳理 Groom),每周把 AGENTS.md 上那些机器可核验的主张拿来和代码库对一次;一旦它们不一致,那 必有一方 是错的,评审必须选出到底哪一方错了。
马具示例#
SDD 这位护法以一份 AGENTS.md 片段的形式交付,其中点名:(a)入口点;(b)文件边界规则;(c)由一条测试签字盖章的存储契约;(d)由一段检查脚本强制执行的不变式。这份片段放在 _handson/07-three-guardians/AGENTS.md.fragment(见 04 的动手环节)。一位开发者在发出第一条提示词之前就把这份文件提交进仓库,他就在自己的仓库里走出了第一步具体的 Harness Engineering。
护法二 —— TDD(Test-Driven Development,测试驱动开发)#
经典定义#
Test-Driven Development 这门学科,正如 Beck 在 TDD by Example [Beck, 2002] 中所表述的,跟随一条 红 → 绿 → 重构 的循环:先写一条失败的测试,再写最小的改动把它变绿,然后在这条已经变绿的测试的安全网下做重构。测试既是规约(可执行,所以不含糊),又是回归网(有持久性,所以能捕捉漂移)。四十年的行业实践告诉我们:能把这条循环握得紧的团队,平均每一次变更的缺陷数更少,也能以更高的速度容忍重构 [Forsgren et al., 2018]。
AI 时代的诠释#
一旦智能体进入回路,这条循环中 红在前 的半段变得更重要,重构 的那半段则变得更可自动化。更重要,是因为由人亲手写下的测试,如今是 唯一一件 能核验智能体是否听懂规约的东西——跳过这一步,智能体自信幻觉出来的代码就会毫无阻碍地溜进仓库 [Peng et al., 2023]。更可自动化,是因为一旦测试是红的,智能体很擅长产出 某一段 让它变绿的代码;人要做的事被压缩成一件:决定这段产出,是否也 配得上通过一条人还没写、但对它更有敌意的测试 [Ziegler et al., 2022]。
落到具体操作上,这重塑了人这边的工作流:
人写下那条失败的测试,以及一段简短的规约 delta。
智能体把测试变绿。
人审阅这份 diff,找出测试没覆盖到的那些 沉默 的不变式,再补上一条失败的测试,然后循环继续。
它仍然是 TDD,只是人现在把 TDD 的大部分时间都花在循环的 测试 这一侧,而几乎不花在 实现 那一侧。马具必须让这种分工变便宜:在测试红时拦住 commit 的钩子 [Humble and Farley, 2010]、给 flaky 测试准备的隔离区、只重跑受本次 diff 影响的那些测试的快通道。
AI 时代特有的失败机制叫 把错误诠释钉进了测试里(test-pinning of the wrong interpretation)。人写下的那条测试,对于他脑子里想的那个行为来说,在 字面意义上 是对的,但留下了一维诠释性的自由度,而智能体顺势把这个自由度按自己的便利解决了。测试变绿。代码里编码下来的是智能体的那个解,而不是人的那份意图。两个月后,一场事故暴露出这个分歧——而那条测试,如今成了承重墙,被引用为 "这个行为是规约过的" 的证据。它并不是;被规约下来的只是与这条测试相容的众多行为中的一种。每一条这样的测试,都把智能体最初的那个误解硬化进了仓库的记忆里。解药不是"加更多测试",而是 敌意测试(adversarial tests):专门写来证伪"从提示词到变绿的最省力路径"的那种测试。
陷阱——"它第一把就把测试过了"
智能体第一轮就把一条失败的测试变绿了。评审者松了口气,把 PR 批了。这是 AI 时代里 TDD 最常栽跟头的地方。为什么:第一把就过,通常意味着测试比规约更好满足——要么测试没定够,要么智能体恰好落到了测试正好覆盖的那一角解空间里。一个被"红—绿—重构"节律推着走的人,会迭代三四轮,留下一些评审者可以从中学到东西的碎片;智能体留下的是一份干净利落、能过的 diff,这 感觉上像 质量。症状:第一把通过率上升;沉默缺陷率也跟着上升;每个特性平均新加的测试数量下滑,因为"第一条测试已经覆盖了"。解法:每一次"第一把就过",评审者都要再写 一条测试——而且专门写那种攻击"智能体本可以走的最省力的那条捷径"的测试。若这条测试也过了,那份信心就算挣到了;若它失败了,你刚好挡下了一个沉默缺陷。
马具示例#
TDD 这位护法以一份 pytest 骨架的形式交付,位置在 _handson/07-three-guardians/test_skeleton.py。它在第一次被提交时 故意是失败的——两条测试分别钉住智能体随后必须实现的两条行为(add 恰好追加一条;add 拒绝空标题)。一条 pre-commit 钩子会跑 pytest -q -m "not slow",从第一条提示词起,红的测试就是一道拦路的闸 [Humble and Farley, 2010],不是那种只在 CI 里碰一次的愿望。来自第 03 章动手环节的 pre-commit-config.fragment.yaml 就是与它配套的那道护栏,正好演示了第 03 章那组最小三件套如何把 04 现在命名的 TDD 护法具体化出来。
护法三 —— MDD(Metric-Driven Development,度量驱动开发)#
经典定义#
Metric-Driven Development 这门学科的核心立场是:把生产环境里的信号——而不仅仅是单元测试的通过率——当作判定"系统是否仍在做它的规约与测试声称它在做的事"的权威反馈回路。它的思想谱系包括:Cunningham 的技术债比喻 [Cunningham, 1992](看不见的成本会不断累积,直到一个度量把它显出来)、Majors/Fong-Jones/Miranda 的 Observability Engineering [Majors et al., 2022](把高基数事件当作原生信号)、以及 Lehman 的软件演化律 [Lehman, 1980](一个有用的系统必须被持续适配,否则它的适应度会下降——而这一论点只有靠度量才量得出来)。MDD 比传统可观测性再往前一步:坚持让度量在 生产事故把你逼出来之前 就被选好,于是运营的第一周是从一份监控契约开始的,而不是从一块空仪表盘开始的。
AI 时代的诠释#
当智能体作者上场后,一具成熟的马具至少要监视三类信号:(a)经典的生产 SLI(错误率、p99 延迟、吞吐量);(b)马具内部信号(智能体 turns-to-green、每轮成本、提示词缓存命中率)[LangChain, 2026];(c)规约遵守信号(文档站点上的坏链数、动手制品里过期的 verified: 日期、AGENTS.md 与 MCP manifest 之间的 schema 漂移)[Martraire, 2019]。第三类信号能捕捉的、智能体特有的失败——AGENTS.md 与代码库之间的 规约漂移、以及智能体轮次间的 成本失控——后面会深入讨论;其中规约漂移这一条,已经在上面 SDD 那一节的陷阱框里专门讲过了。
智能体时代特有的失败机制叫 没有对照物的成本失控(cost runaway with no correlate)。人类工程师若引入一个慢 10 倍的函数,会被发现——因为他自己的下一个任务也跟着慢 10 倍。智能体若引入一个贵 10 倍的提示词,则没人察觉:从团队这一侧看,它 下一个 提示词跑得一样快,而成本就这么无声地记在了账单上。没有一个"每轮成本"的信号被接进某条护栏时,回归的第一份证据要么是限流、要么是账单、要么是季度复盘——每一个都来得太晚,晚到无法和"造成它的那次 commit"对应起来。MDD 的作用,就是让这种对应变便宜:每轮成本按技能、按仓库、按变更集打上标签,于是任何一次马具改动带来的增量成本,在它落地的第二天就可见,而不是下一个季度才可见。
陷阱——仪表盘剧场
一个团队搭了一块十二面板的 Grafana 仪表盘,盯着智能体成本、turns-to-green、缓存命中率,以及其他九路信号。它很漂亮。六周之后,问一句 "这一周哪一路信号动了,我们为此做了什么?",回来的是一片茫然的眼神。为什么:这块仪表盘没有 owner、没有告警阈值、没有固定的复盘节奏;信号在那里,但并 不引导 任何人。一个没有人对它负责的度量,最好的情况下只是诊断性的,最坏的情况下就是装饰。症状:仪表盘在 demo 上被骄傲地展示,却从不在 PR 讨论里被引用;事故复盘还是靠直觉解释,哪怕数据明明摆在那里。解法:把十二面板里的十个降级到第二个标签页,把 一个 提升为北极星指标,给它一位署名 owner、一个会触发告警的阈值,和每周一上午议程里的一格时间。第 08 章的"MDD × 缰绳"一格,会深入讲这种"独一北极星"的纪律。
马具示例#
MDD 这位护法以一份 metrics.yaml 的形式交付,位置在 _handson/07-three-guardians/metrics.yaml。十五行 YAML,点名四路信号(pytest 通过率、坏链数、平均评审时长、智能体 turns-to-green),给每一路设定一个目标,然后——真正把它从"可观测性"变成"MDD"的那一步文化动作——把团队按在一套 每周复盘节奏 上。没有复盘节奏的马具只是收藏仪表盘;有复盘节奏的马具,才真正闭合了前两位护法打开的反馈回路。
为什么每一位护法单独都撑不住#
第 08 章那张矩阵的解读,建立在一个论点之上:没有任何一位护法独自足够。三种失败模式彼此分明,又彼此自我加强:
只有 SDD,你得到的是一份没人核验的精确规约——智能体一路循着一份写得漂亮的
AGENTS.md,走进了一个沉默地坏掉的实现,因为从来没有一条测试试图把它打破。只有 TDD,你得到的是一堆变绿的测试,它们把智能体对规约的 第一份误解 钉了进去——"代码做的是测试所说的事"成了真,而"代码做的是用户所需要的事"却悄悄发散开来 [Ziegler et al., 2022]。
只有 MDD,你得到的是一块漂亮的仪表盘,它能在漂移 发生之后 把它 诊断 出来,却 阻止 不了漂移——因为本该在更早的地方把漂移挡下的那份规约和那些测试,都没有被写出来。
三大护法只有在三位同时在场、并且按那个因果顺序在场时,才真正是承重的。第 08 章把这一论点落成一张 3 × 4 的方法论矩阵:三行护法与四列操作区域(缰绳/护栏/牧场/梳理)相乘,得到十二格工程单元,它们合在一起就是本书的分析骨架。
研究脉络#
之所以选出这三位护法,不是因为它们在美学上凑成一套,而是因为每一位都靠在一条独立、可引用的谱系上,每一位都解决了另外两位解决不了的一种失败模式。
SDD 的谱系。 Meyer 的 Design by Contract [Meyer, 1992] 把机器可核验的前置/后置条件确立为一等工程制品;Adzic 的 Specification by Example [Adzic, 2011] 把这一立场扩展到可执行的业务规则;Martraire 的 Living Documentation [Martraire, 2019] 则是
AGENTS.md作为一份持续被核验的规约面的直系祖先。TDD 的谱系。 Beck 的 TDD by Example [Beck, 2002] 是经典文本;Humble 与 Farley 的 Continuous Delivery [Humble and Farley, 2010] 展示了红—绿—重构这条循环如何放大到组织节奏上;关于 Copilot 生产率的几项研究 [Peng et al., 2023, Ziegler et al., 2022],则提供了经验证据:未经测试 的智能体产出会沉默地拖低代码质量——这就是在 AI 时代仍然坚持"红在前"这条纪律的经验依据。
MDD 的谱系。 Cunningham 的"债"之比喻 [Cunningham, 1992] 激起了"把看不见的成本显出来"这一动机;Majors/Fong-Jones/Miranda 的 Observability Engineering [Majors et al., 2022],把生产信号重新框定为首要的设计关切;Lehman 的演化律 [Lehman, 1980] 则论证了"一个有用的系统必须被持续再适配",而这一主张只有通过度量才能落地为可操作的实践。
马具一侧的综合。 2026-01-30 那篇引入三大护法的博客 [Fan, 2026] 是本书自己的前置综合;想读短版的读者可以把它和 Thoughtworks 技术雷达的条目 [Thoughtworks Technology Radar, 2026] 以及 LangChain 那篇"解剖"式博客 [LangChain, 2026] 放在一起读。
动手环节#
source/_handson/07-three-guardians/ 下交付了三份制品,每位护法一份,顺序与本章所主张的因果顺序一致:
SDD——
AGENTS.md.fragment:一段机器可核验的规约块,点名入口点、文件边界、存储契约,以及智能体的规则。十四行;可以原样拷进任何仓库的AGENTS.md。TDD——
test_skeleton.py:一份故意失败的 pytest 模块,在任何提示词被发出 之前 就把todo add的行为钉住。用pytest -q tests/test_skeleton.py运行;commit 时它应该是红的,在智能体第一轮成功之后变绿。MDD——
metrics.yaml:一份十五行的信号契约,点名四项度量(pytest 通过率、坏链数、平均评审时长、智能体 turns-to-green),以及一套每周复盘节奏。
配套的 README.md 给出阅读顺序,以及每一份文件的意图。把这三份文件提交进任何一个活跃的仓库——和第 03 章的那组三件套一起——就得到了本书要求读者运行的最小完整"三大护法马具"。下一章把这具马具变成一张矩阵。