你可能已经听过无数次这个等式:Agent = Model + Harness。
但你有没有想过,这个"Harness"到底该怎么设计、怎么落地、怎么迭代?
Martin Fowler 作为软件工程领域最具影响力的思想家之一,最近在 Harness Engineering 一文中给出了一个非常系统的框架。他把 Agent 的"外围系统"拆解成了前馈(Feedforward)和反馈(Feedback)两大控制机制,并指出了三类 Harness 的设计方向。
这篇文章,就是对 Fowler 核心思想的一次深度解读。老哥我读完之后感觉每一块都是干货,所以决定把它系统地整理成一篇中文技术博客,分享给各位。
目录
- 一、什么是 Harness?
- 二、Feedforward 与 Feedback:缺一不可的双螺旋
- 三、两种控制类型:计算型 vs 推理型
- 四、三大类 Harness 设计
- 五、Timing = 质量左移
- 六、Harnessability:你的代码库是否容易被"驾驭"
- 七、Harness Templates:从经验到可复用资产
- 八、Human 的角色:不可替代的经验与上下文
- 九、开放问题与未来方向
一、什么是 Harness?
「Harness」这个词,英文原意是"马具"——你可以理解为套在马身上的那套装备,用来控制、驾驭马的行为。
在 AI Agent 语境下,Fowler 给出了一个简洁的定义:
Agent = Model + Harness
也就是:Model 之外的一切,都是 Harness。 包括代码、配置、执行逻辑、系统提示词、工具调用策略……全打包在一起,构成了 Agent 的外围控制系统。
但这个定义太宽泛了,所以 Fowler 在文中做了一个关键限定——他讨论的是编码 Agent(Coding Agent) 场景下的 Harness 工程。
在这个语境下,Harness 分为两层:
- 内置 Harness(Built-in Harness):由 Agent 框架本身提供,比如系统提示词、代码检索机制、编排系统等
- 外置 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 脚本) |
| 代码mods | Feedforward | 计算型(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 高自主权后,主要靠以下方式验证行为:
- 人类 Review(目前最可靠)
- 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:
- 经验积累:我们吸收了代码规范和最佳实践,知道什么是"好代码"
- 认知痛苦:我们能感受到复杂代码带来的认知负担,这种"美学厌恶"会阻止我们写出烂代码
- 社交责任:我们对自己的提交负责,有 Accountability
- 组织对齐:我们知道团队在追求什么,哪些技术债务是业务允许的,“好"在这个具体上下文里意味着什么
- 人类节奏:我们慢速前进、小步走,留出了让经验被触发的思考空间
Coding Agent 缺乏什么?
Fowler 指出,Coding Agent 天然不具备这些:
- 没有社交责任(social accountability)
- 没有美学厌恶(对 300 行函数毫无感觉)
- 没有"这里我们不这么干"的直觉
- 没有组织记忆(不知道哪个约定是"关键约束"还是"习惯做法”)
Harness 的定位
Harness 是人类经验的外部化、显式化,但它只能走这么远:
一个好的 Harness,不应该追求完全消灭人类输入,而应该把人类输入引导到最有价值的地方。
这句话我非常认同。Harness 不是为了替代人类,而是放大人类判断的杠杆效率——让 Agent 处理规则明确的重复性工作,把真正需要经验、上下文感知、价值判断的部分留给人类。
九、开放问题与未来方向
Fowler 在文章末尾列出了一系列尚待解决的问题,我觉得非常诚实,也值得每个正在建设 Harness 的团队思考:
- 如何保持 Harness 的内聚性(随着 Guide 和 Sensor 增多,如何避免相互冲突?)
- 当指令和反馈信号指向不同方向时,Agent 如何权衡(信任规则还是信任检测结果?)
- 传感器从不触发 = 高质量,还是检测机制失效?(需要类似代码覆盖率那样的量化指标)
- 如何评估 Harness 的覆盖率和质量(当前没有类似 mutation testing 这样的工具)
- Feedforward 和 Feedback 控制目前散落在交付流程各处,有没有工具能帮助统一配置、同步和推理?
最后一点尤其值得关注——Harness 工程正在从"一次性配置"演变为"持续性工程实践",这是一个需要专门投入的领域,不是设好就不用管了。
总结
Fowler 的这篇文章给我们的核心启示,可以用三句话概括:
- Agent = Model + Harness,Model 的能力固然重要,但 Harness 的质量才是决定 Agent 能否真正落地的关键
- Feedforward 和 Feedback 是一对双螺旋,缺一不可——前者预防,后者检测,两者共同构成自我迭代的正向循环
- Harness 工程不是一次性配置,而是持续演进的工程实践,需要根据团队上下文化和代码库特点不断迭代
对于正在构建或引入 Coding Agent 的团队,我的建议是:
- 起步阶段:优先构建计算型传感器(代码风格、架构边界、测试覆盖率),ROI 最高
- 进阶阶段:引入推理型控制处理需要语义判断的场景,但控制使用频率
- 长期目标:逐步沉淀领域特定的 Harness Templates,把团队经验固化成可复用的控制资产
- 始终牢记:Human 不是被替代的对象,而是 Harness 体系的最终Owner和价值锚点
欢迎关注收藏我,获取更多硬核技术干货 📌