你可能已经听过无数次这个等式:Agent = Model + Harness

但你有没有想过,这个"Harness"到底该怎么设计、怎么落地、怎么迭代?

Martin Fowler 作为软件工程领域最具影响力的思想家之一,最近在 Harness Engineering 一文中给出了一个非常系统的框架。他把 Agent 的"外围系统"拆解成了前馈(Feedforward)和反馈(Feedback)两大控制机制,并指出了三类 Harness 的设计方向。

这篇文章,就是对 Fowler 核心思想的一次深度解读。老哥我读完之后感觉每一块都是干货,所以决定把它系统地整理成一篇中文技术博客,分享给各位。


目录


一、什么是 Harness?

「Harness」这个词,英文原意是"马具"——你可以理解为套在马身上的那套装备,用来控制、驾驭马的行为。

在 AI Agent 语境下,Fowler 给出了一个简洁的定义:

Agent = Model + Harness

也就是:Model 之外的一切,都是 Harness。 包括代码、配置、执行逻辑、系统提示词、工具调用策略……全打包在一起,构成了 Agent 的外围控制系统。

但这个定义太宽泛了,所以 Fowler 在文中做了一个关键限定——他讨论的是编码 Agent(Coding Agent) 场景下的 Harness 工程。

在这个语境下,Harness 分为两层:

  1. 内置 Harness(Built-in Harness):由 Agent 框架本身提供,比如系统提示词、代码检索机制、编排系统等
  2. 外置 Harness(Outer Harness):我们作为使用者,主动为特定业务场景构建的那套控制机制

Fowler 指出,一个设计良好的外置 Harness,服务于两个核心目标:

提高 Agent 第一次就把事情做对的概率建立反馈循环,让 Agent 在人类审查之前自我纠错

最终效果:减少人工 review 的负担,提升系统质量,同时节省 token 消耗。


二、Feedforward 与 Feedback:缺一不可的双螺旋

这是 Fowler 文章中最核心的概念框架,也是我认为最有启发性的部分。

他把 Harness 的控制机制分成两类:

2.1 Feedforward(前馈):预防优于治理

核心思路:在 Agent 执行之前,用规则、指令、模板来引导它的行为,把"不该做的事"提前封堵。

典型例子

  • AGENTS.md 描述代码规范和项目约定
  • 用 Skill 注入"如何启动一个新项目"的操作步骤
  • 通过代码风格检查规则约束输出格式
  • 用 OpenRewrite recipe 做代码 mods(批量代码迁移)

本质:是一种预防性控制,类似于交通规则——告诉你"这里不能左转",而不是等你撞车了再处理。

2.2 Feedback(反馈):发现问题及时纠正

核心思路:Agent 产出结果后,通过**传感器(Sensors)**检测输出质量,让 Agent 有机会在提交给人类之前自我修正。

典型例子

  • 用 ArchUnit 测试检查模块边界是否被破坏(结构测试)
  • 运行单元测试、集成测试验证功能正确性
  • 静态代码分析工具检测重复代码、圈复杂度
  • 用 LLM 做语义审查(推理型传感器)

本质:是一种检测性控制,类似于刹车——你已经开出去了,但发现前面有障碍,及时停下来。

2.3 为什么两者缺一不可?

Fowler 给出了一个非常精炼的论断:

只有 Feedforward → Agent 遵守规则,但从不验证效果
只有 Feedback → Agent 不断重复同样的错误

这就像一个学生的学习方法:

  • 只有前馈 = 背了规则但从不做题验证
  • 只有反馈 = 做题但不看教科书,永远在犯同样的错

真正的 Harness,需要两条回路同时运作,形成自我迭代的正向循环。


三、两种控制类型:计算型 vs 推理型

在具体实现层面,Fowler 又把控制机制分成了两种执行类型:

3.1 计算型控制(Computational Control)

特点

  • 确定性工具,结果可复现
  • 便宜、快速,可以每次提交都运行
  • 适合结构化、规则明确的场景

典型工具

场景工具/方法
代码风格检查ESLint、Prettier
架构边界检查ArchUnit、Structure101
重复代码检测SonarQube、PMD
圈复杂度检查各类静态分析工具
测试覆盖率JaCoCo、Coverage.py

一句话总结:计算型控制是我们的"基础设施级传感器",便宜且可靠,是 Harness 的基石。

3.2 推理型控制(Inferential Control)

特点

  • 由 LLM 做语义判断,能处理模糊、开放的场景
  • 昂贵、非确定性,结果有概率波动
  • 适合需要"语义理解"的任务

典型工具

  • 用 LLM 审查代码是否符合业务意图
  • 用 Skill 注入包含业务上下文的复杂指令
  • 用 AI 做代码语义重复检测(不是字面重复,而是逻辑重复)

一句话总结:推理型控制更"聪明",但成本高,适合在关键节点部署,不适合高频触发。

3.3 两者的实用主义选择

Fowler 在文章中给出了一个实用的对照表:

控制方向类型示例实现
编码规范引导Feedforward推理型(AGENTS.md、Skills)
新项目启动指引Feedforward两者混合(Skill + bootstrap 脚本)
代码modsFeedforward计算型(OpenRewrite recipes)
架构边界检查Feedback计算型(ArchUnit tests)
代码审查指引Feedback推理型(Skills)

关键洞察:能用计算型传感器解决的问题,优先用计算型。推理型传感器留给真正需要语义理解的场景。


四、三大类 Harness 设计

Fowler 将 Harness 按其调节维度(Regulation Categories)分成三大类,这个分类对于我们设计自己的 Harness 系统有很强的指导意义。

4.1 Maintainability Harness(可维护性 Harness)

调节目标:代码内部质量和可维护性——重复度、圈复杂度、测试覆盖率、架构漂移等。

这是目前最容易搭建的一类 Harness,因为相关工具链已经非常成熟:

  • 计算型传感器效果最好:代码重复度检测、圈复杂度检查、测试覆盖率、架构漂移检测(ArchUnit)
  • 推理型传感器能做部分语义判断:语义重复代码检测、冗余测试识别、过度设计的方案审查——但成本高,不适合每次提交都跑

局限性(重要!):无论是计算型还是推理型传感器,以下高影响力问题的检出率都不够可靠:

  • 对问题的误判(Misdiagnosis of issues)
  • 过度工程(Overengineering)
  • 需求理解偏差(Misunderstood instructions)

原因:这些问题的根源在于人类没有在需求中明确说明,而传感器只能检测已定义的标准,超出定义范围的问题天然是盲区。

4.2 Architecture Fitness Harness(架构适配性 Harness)

调节目标:应用架构特征是否符合预期,即 Fitness Functions(适配度函数)

Fowler 在这里借用了 James Cove-Nelson 提出的概念,本质上是检查架构的"健康指标":

  • 模块间依赖是否符合分层架构?
  • 核心域(Core Domain)是否被外围模块直接依赖?
  • 关键业务逻辑的调用路径是否清晰?

这类 Harness 的价值:在系统演进过程中,持续检测架构漂移(Architecture Drift)。很多团队在快速迭代中会不自觉地破坏架构原则,Fitness Functions 就像架构层面的"持续集成测试",让腐化在第一时间暴露。

典型实现:除了 ArchUnit 这类结构测试,还可以结合架构决策记录(ADR)、依赖规则(如 Java 项目中的 archunit-maven-plugin)等。

4.3 Behaviour Harness(行为正确性 Harness)

调节目标:应用功能是否按预期工作,即业务行为正确性。

这是 Fowler 认为最难的一类 Harness,也是目前社区讨论最热烈、但方案最不成熟的领域。

现状:大多数团队在给予编码 Agent 高自主权后,主要靠以下方式验证行为:

  1. 人类 Review(目前最可靠)
  2. AI 生成测试(但质量还不够稳定,不足以完全依赖)

关于 AI 生成测试质量,Fowler 提到他的同事在部分场景中使用了 Approved Fixtures Pattern(已批准 Fixture 模式)——用可信的测试数据 Fixture + AI 生成的测试逻辑组合,取得了不错效果。但这个方案适用范围有限,不能作为通用答案。

一句话总结:行为正确性 Harness 目前还处于探索阶段,AI 生成测试能起到辅助作用,但还不足以可靠地降低人工监督和测试的必要性。


五、Timing = 质量左移

Fowler 在文章中提到了一个软件工程领域的老生常谈——质量左移(Shift Left),但他用 Harness 的视角给出了一个新角度。

持续集成团队面临的核心挑战是:如何根据检查的成本、速度和关键性,把测试、人工 review 分布到开发时间线的合适位置。

理想状态是:每次提交的状态都应该是可部署的,这意味着我们要把尽可能多的检查左移到开发早期。

为什么 Timing 对 Harness 至关重要?

因为不同类型的传感器,成本和速度差异巨大

传感器类型典型延迟适合部署时机
计算型(代码风格、圈复杂度)毫秒级每次提交(pre-commit)
计算型(测试覆盖率、ArchUnit)秒级pre-commit / PR checks
推理型(LLM 语义审查)秒~分钟级PR checks / 定期
人类 Review分钟~小时级PR review / 代码审查

关键原则:反馈传感器需要按其成本/速度分布在软件生命周期的各处——便宜快速的放最左边(高频),昂贵缓慢的放右边(低频但关键)。

Fowler 的原话非常到位:

越早发现问题,修复成本越低。
Feedback sensors, including the new inferential ones, need to be distributed across the lifecycle accordingly.


六、Harnessability:你的代码库是否容易被"驾驭"

Farnessability(可驾驭性)是 Fowler 提出的一个很有洞见的概念——不是所有代码库都同等地适合被 Harness 控制

影响 Harnessability 的因素

因素说明
强类型语言天然具备类型检查传感器,Java/TypeScript 项目比 JavaScript 项目更容易构建 Harness
清晰的模块边界可定义的模块边界 → 可以写 ArchUnit 类的架构约束规则
框架约束Spring/Rails 等框架隐式抽象了很多细节,Agent 不需要关心这些"坑",成功率自然更高
技术债务技术债务越多,需要的 Harness 越复杂,而越复杂的 Harness 越难构建和维护

Greenfield vs Legacy 的差异

Fowler 特别指出了这一关键差异:

Greenfield(全新项目)

  • 可以从第一天就把 Harnessability 内嵌到技术决策和架构选择中
  • 语言选型、模块划分、框架选择,都会决定未来代码库的"可驾驭程度"
  • 这是理想状态,但大多数工程师面对的是 Legacy 系统

Legacy(遗留系统)

  • Harness 最需要它的地方,往往也是最难构建它的地方法
  • 技术债务累积、模块边界模糊、缺乏测试覆盖……
  • 这里的建议:优先从高价值、低复杂度的局部开始,逐步构建外围 Harness,不要试图一口吃成胖子

七、Harness Templates:从经验到可复用资产

Fowler 在文章末尾提出了一个很有前景的方向——Harness Templates(驾驭模板)

什么是 Harness Templates?

大多数企业都有几类常见的服务拓扑,覆盖了 80% 的业务场景:

  • API 服务:暴露数据接口的业务服务
  • 事件处理服务:监听事件、做业务逻辑处理
  • 数据仪表盘:展示数据的看板服务

在成熟的工程组织中,这些拓扑已经通过 Service Templates(服务模板) 进行了标准化(比如用模板创建新服务,自动带有一致的项目结构、技术栈、日志规范)。

Harness Templates = Service Templates 的进化版:不仅是项目骨架,而是一整套 Guides + Sensors 的 Bundle,把特定拓扑的规范、约束、检查机制都打包进去,让 Coding Agent 被"套"进一个经过验证的结构里。

挑战

当然这也面临 Service Templates 同样的问题:

  • 版本同步:团队基于模板实例化后,模板的上游更新如何同步?
  • 非确定性控制:推理型 Guides 和 Sensors 本身就难以测试,版本管理更难
  • 贡献者模式:如何让各团队既能享受模板便利,又能为模板贡献改进?

这些是开放问题,但方向是清晰的。


八、Human 的角色:不可替代的经验与上下文

这是我认为 Fowler 文章中最值得品味的部分——Human 在 Harness 体系中的位置

人类开发者带来的隐性 Harness

我们作为人类开发者,其实自带一套隐性的 Harness:

  1. 经验积累:我们吸收了代码规范和最佳实践,知道什么是"好代码"
  2. 认知痛苦:我们能感受到复杂代码带来的认知负担,这种"美学厌恶"会阻止我们写出烂代码
  3. 社交责任:我们对自己的提交负责,有 Accountability
  4. 组织对齐:我们知道团队在追求什么,哪些技术债务是业务允许的,“好"在这个具体上下文里意味着什么
  5. 人类节奏:我们慢速前进、小步走,留出了让经验被触发的思考空间

Coding Agent 缺乏什么?

Fowler 指出,Coding Agent 天然不具备这些:

  • 没有社交责任(social accountability)
  • 没有美学厌恶(对 300 行函数毫无感觉)
  • 没有"这里我们不这么干"的直觉
  • 没有组织记忆(不知道哪个约定是"关键约束"还是"习惯做法”)

Harness 的定位

Harness 是人类经验的外部化、显式化,但它只能走这么远:

一个好的 Harness,不应该追求完全消灭人类输入,而应该把人类输入引导到最有价值的地方。

这句话我非常认同。Harness 不是为了替代人类,而是放大人类判断的杠杆效率——让 Agent 处理规则明确的重复性工作,把真正需要经验、上下文感知、价值判断的部分留给人类。


九、开放问题与未来方向

Fowler 在文章末尾列出了一系列尚待解决的问题,我觉得非常诚实,也值得每个正在建设 Harness 的团队思考:

  1. 如何保持 Harness 的内聚性(随着 Guide 和 Sensor 增多,如何避免相互冲突?)
  2. 当指令和反馈信号指向不同方向时,Agent 如何权衡(信任规则还是信任检测结果?)
  3. 传感器从不触发 = 高质量,还是检测机制失效?(需要类似代码覆盖率那样的量化指标)
  4. 如何评估 Harness 的覆盖率和质量(当前没有类似 mutation testing 这样的工具)
  5. Feedforward 和 Feedback 控制目前散落在交付流程各处,有没有工具能帮助统一配置、同步和推理?

最后一点尤其值得关注——Harness 工程正在从"一次性配置"演变为"持续性工程实践",这是一个需要专门投入的领域,不是设好就不用管了。


总结

Fowler 的这篇文章给我们的核心启示,可以用三句话概括:

  1. Agent = Model + Harness,Model 的能力固然重要,但 Harness 的质量才是决定 Agent 能否真正落地的关键
  2. Feedforward 和 Feedback 是一对双螺旋,缺一不可——前者预防,后者检测,两者共同构成自我迭代的正向循环
  3. Harness 工程不是一次性配置,而是持续演进的工程实践,需要根据团队上下文化和代码库特点不断迭代

对于正在构建或引入 Coding Agent 的团队,我的建议是:

  • 起步阶段:优先构建计算型传感器(代码风格、架构边界、测试覆盖率),ROI 最高
  • 进阶阶段:引入推理型控制处理需要语义判断的场景,但控制使用频率
  • 长期目标:逐步沉淀领域特定的 Harness Templates,把团队经验固化成可复用的控制资产
  • 始终牢记:Human 不是被替代的对象,而是 Harness 体系的最终Owner和价值锚点

欢迎关注收藏我,获取更多硬核技术干货 📌