Coding Agent 的 Harness Engineering:如何把 AI 代码的质量关
译者注:
这是一篇迟来的翻译。原文作者 Birgitta Böckeler 是 Thoughtworks 的 Distinguished Engineer,2026 年 4 月发表在 Martin Fowler 的博客上。之所以现在才动手,是因为我在字节做了半年 Coding Agent 后回头看,文章里说的每一个问题都亲身踩过——它不是在写理论,是在总结战场上活下来的人的经验。
翻译保持了原文的框架和论点,加入了一些我们用下来的感受。斜体部分是我们的注解。
介绍
要让 Coding Agent 在更少的监督下工作,我们需要一些方法,对它们产出的结果有更高的信心。作为软件工程师,我们对 AI 生成的代码有一种天然的信任屏障——LLM 是非确定性的,它们不了解我们的上下文,它们不真正理解代码,它们在 token 空间中思考。本文探讨了一个心智模型,将来自上下文工程(context engineering)和 Harness 工程的新兴概念整合在一起,以建立这种信任。
2026 年 4 月 2 日 Birgitta Böckeler
关于作者: Birgitta 是 Thoughtworks 的 Distinguished Engineer 和 AI 辅助交付专家,拥有超过 20 年作为软件开发人员、架构师和技术负责人的经验。
什么是 Harness?
Harness 这个词已经演变成:Agent 中除了模型本身之外的所有东西。
Agent = Model + Harness
这个定义太宽了,值得在 Coding Agent 这个上下文中精确定义。在 Coding Agent 中,部分 Harness 已经内置了(比如:系统提示词、代码检索机制、甚至复杂的编排系统)。但 Coding Agent 也给我们——它们的用户——提供了许多功能,可以针对我们的特定用例和系统构建外部 Harness。
“Harness” 原本的意思是”缰绳”或”马具”。Birgitta 自己也调侃说这个比喻有点勉强——“你试过在狗里面套一副缰绳吗?“——但作为术语它够用。Agent 圈里已经接受了这个词,所以翻译保留了原文。
两个核心机制:前馈与反馈
要驾驭一个 Coding Agent,我们既需要预测不想要的输出并预防它们,也需要设置传感器让 Agent 自我纠正:
引导(前馈控制): 预测 Agent 的行为,在它行动之前进行引导。引导可以增加 Agent 在第一次尝试时就产生好结果的概率。
传感器(反馈控制): 在 Agent 行动之后观察,帮助它自我纠正。当传感器产生的信号是为 LLM 消费优化过的时,它们尤其强大——比如自定义 linter 消息中包含自我纠正的指令——这是一种积极的 prompt 注入。
我们的经验:刚做 Agent 时,很容易两头走极端。要么只加引导(拼命写 AGENTS.md、写 skill 文档),结果 Agent 该犯错还是犯错,因为文档写完了没有验证机制;要么只加传感器(跑测试、跑 linter),结果 Agent 一直在犯同样的错误,因为没有人告诉它应该怎么做。Birgitta 说得对:只有前馈或只有反馈,都不够。
两种执行方式:计算型 vs 推理型
引导和传感器有两种执行方式:
计算型(Computational): 确定性的、快速,由 CPU 运行。测试、linter、类型检查、结构性分析。运行时间毫秒到秒级别,结果可靠。
推理型(Inferential): 语义分析、AI 代码审查、“LLM 当裁判”。通常由 GPU 或 NPU 运行。更慢、更贵、结果更随机。
关键直觉: 计算型引导用确定性工具增加好结果的概率。计算型传感器足够便宜和快速,可以在每次代码变更时运行。推理型控制当然更贵且非确定,但允许我们提供丰富的指导,并添加额外的语义判断。尽管有非确定性,推理型传感器在使用合适的大模型时,可以显著增加我们的信任感。
例子:
| 方向 | 类型 | 实现示例 |
|---|---|---|
| 编码规范(前馈) | 推理型 | AGENTS.md, Skills |
| 新项目引导(前馈) | 两者 | Skill + 引导脚本 |
| 代码重构(前馈) | 计算型 | OpenRewrite recipes 工具 |
| 结构测试(反馈) | 计算型 | 运行 ArchUnit 测试检查模块边界违规的 pre-commit hook |
| 审查指引(反馈) | 推理型 | Skills |
这一段特别有共鸣。我们现在的做法是把计算型反馈作为默认防线——项目里先配好 type check + linter + 关键模块的结构约束。这些便宜、可靠、确定性,每次 commit 之前跑一次不亏。推理型反馈只在关键路径上使用——比如代码审查只在 PR 阶段跑一次。两个的速度差决定了它们在开发周期中的位置。
调控循环(The Steering Loop)
在这个过程中,人的工作是通过迭代 Harness 来指引 Agent。每当一个问题反复出现时,前馈和反馈控制就应该被改进,使问题在未来出现的概率降低,甚至完全预防。
在调控循环中,我们当然也可以用 AI 来改进 Harness。Coding Agent 现在已经使得构建更自定义的控制和更自定义的静态分析变得便宜得多。Agent 可以帮忙编写结构测试、从观察到的模式生成规则草稿、搭建自定义 linter、或从代码库考古中创建操作指南。
时机:让质量左移
持续集成的团队一直面临着将测试、检查、人工审查按成本、速度和关键性分布在开发时间线上的挑战。当你想做持续交付时,你甚至希望每一个 commit 状态都是可部署的。你希望检查尽可能左移到通往生产的路径上——因为越早发现问题,修复成本越低。反馈传感器(包括新的推理型)需要相应地分布在整个生命周期中。
前馈和反馈在变更生命周期中的位置:
- 什么足够快,甚至在集成之前、甚至在 commit 创建之前就应该运行?(比如 linter、快速测试套件、基础代码审查 Agent)
- 什么更贵,因此应该只在集成后在流水线中运行?(比如变异测试、更广泛的代码审查)
持续漂移与健康传感器:
- 什么样的漂移是逐渐累积的,应该由在变更生命周期之外持续运行的传感器监控?(比如死代码检测、测试覆盖质量分析、依赖扫描)
- 什么样的运行时反馈可以由 Agent 监控?(比如让它们寻找降级的 SLO 并提出改进建议,或 AI 裁判持续采样响应质量并标记日志异常)
这个”左移”的概念太核心了。我们在字节踩的坑是:初期把所有检查都放在 PR 阶段——结果 Agent 产生的每个 PR 都要经过 3-4 轮 review,效率极低。后来把 type check、linter、基础测试都塞到 pre-commit 里,发现至少省掉了 60% 的反馈循环。不是传感器本身变了,是把它放到了更早的位置。
Harness 的分类
Agent Harness 像是一个控制论的调节器,结合前馈和反馈,将代码库调节到期望的状态。区分这个期望状态的多个维度是有用的,根据 Harness 要调节什么来分类。这些分类之所以有用,是因为这三个维度的可约束性(harnessability) 和复杂度各有不同。
1. 可维护性 Harness
基本上,我在本文中给出的所有例子都是关于调节内部代码质量和可维护性的。这是目前最简单的一类 Harness,因为我们有很多现成的工具可以用。
反思这些可维护性 Harness 在多大程度上增加了我对 Agent 的信任,我把自己之前整理的常见 Coding Agent 失败模式对照了一下:
- 计算型传感器可以可靠地捕获结构性问题:重复代码、圈复杂度、缺失测试覆盖、架构漂移、风格违规。这些便宜、成熟、确定。
- LLM 可以部分解决需要语义判断的问题——语义重复的代码、冗余的测试、暴力修复、过度设计的方案——但是昂贵且概率性的。不能在每个 commit 上跑。
- 两者都不能可靠地捕获一些影响更大的问题: 对问题的误诊、过度工程和不必要的功能、误解的指令。有时它们能捕获到,但不足以减少监督。如果人一开始就没说清楚想要什么,正确性就超出了任何传感器的职责范围。
我们的最大感受:可维护性是 Harness 的”低垂果实”,因为它有大量现成的工具。但真正难的永远是”语义鸿沟”——Agent 理解了字面问题但没有理解意图。没有测试能解决”你做的功能是对的但不是我们想要的”。
2. 架构适应性 Harness
这组引导和传感器定义并检查应用的架构特性。本质上就是:适应性函数(Fitness Functions)。
例子:
- 告诉 Agent 我们的性能要求的 Skill,以及性能测试——向 Agent 反馈它是否改进或恶化了性能
- 描述更好可观测性编码规范(如日志标准)的 Skill,以及要求 Agent 反思它所能获得的日志质量的调试指令
3. 行为 Harness
这是房间里的大象——我们如何引导和感知应用是否按照我们需要的功能行为来工作?目前,我看到大多数给 Coding Agent 高度自主权的人是这么做的:
- 前馈: 功能规格说明书(详细程度各不相同,从简短提示到多文件描述)
- 反馈: 检查 AI 生成的测试套件是否通过、覆盖率是否足够高、有些人甚至用变异测试来监控测试质量。然后结合人工测试。
这种做法对 AI 生成的测试寄予了很大的信任,这还不够好。我的一些同事在**已核准的夹具模式(approved fixtures pattern)**上看到了不错的结果,但这在某些领域比另一些领域更容易应用。他们只在适合的地方选择性使用,这不是测试质量问题的通用答案。
整体来说,我们在如何为功能行为设计好的 Harness 方面还有很多工作要做,以增加足够的信心来减少监督和人工测试。
可约束性(Harnessability)
并不是每个代码库都能同样容易被 Harness 约束。一个用强类型语言编写的代码库天然就有类型检查作为传感器;清晰可定义的模块边界允许架构约束规则;像 Spring 这样的框架屏蔽了 Agent 甚至不需要关心的细节,从而隐式地增加了 Agent 的成功几率。没有这些特性,相应的控制手段就无法构建。
这一条影响了技术选型。不是哪个语言/框架更好,而是”哪个生态更容易被 Agent 约束”。如果你预见到自己的团队会大量使用 Coding Agent,选技术栈时多考虑一个维度:它的可约束性如何。
Ned Letcher(我的同事)用的术语是 “环境性助益(ambient affordances)“:指 Agent 环境中使其更可约束的结构性质——“环境本身的结构性质,使其对在其中操作的 Agent 更具可读性、可导航性和可处理性。”
这对新建项目和遗留项目的影响不同。新建团队可以从第一天起就把可约束性嵌入进去——技术决策和架构选择决定了代码库的可治理程度。遗留团队,特别是那些有大量技术债务的应用,面临更困难的问题:最需要 Harness 的地方,往往最难构建。
Harness 模板
大多数企业有几种覆盖 80% 需求的常见服务拓扑——通过 API 暴露数据的业务服务、事件处理服务、数据看板。在许多成熟的工程组织中,这些拓扑已经编码在服务模板中。未来这些可能演变成 Harness 模板:一套引导和传感器的组合,将 Agent 约束在特定拓扑的结构、规范和技术栈上。团队未来选择技术栈和结构时,可能会部分基于已经有哪些现成的 Harness 可用。
Ashby 定律(必要多样性定律): 调节器必须至少拥有与被调节系统一样多的多样性,它只能调节它有模型的东西。
一个基于 LLM 的 Coding Agent 几乎可以产生任何东西,而约束在一种拓扑上就缩小了这个空间,使得全面的 Harness 更加可行。定义拓扑是一种减少多样性的动作。
Ashby 定律在这里的应用很妙。Agent 的能力太强了,它会写出你想要的任何东西——包括你不想要的。限制 Agent 的输出空间,本质上就是让这个”调节器”和被调节的系统的复杂度匹配。
当然,我们会面临与服务模板类似的问题。一旦团队实例化了它们,它们就会开始与上游改进不同步。Harness 模板将面临同样的版本控制和贡献问题,对于更难测试的非确定性引导和传感器来说,甚至可能更糟。
人的角色
作为人类开发人员,我们的技能和经验给每个代码库带来了一种隐式的 Harness。我们吸收了规范和实践经验,我们感受到了复杂性的认知痛苦,我们知道自己的名字签在 commit 上。我们还承载了组织的对齐——知道团队试图实现什么、哪些技术债务出于业务原因被容忍、在这个具体上下文中”好”是什么样的。我们小步走,以人类的节奏前进,这为经验被触发和应用的思考空间创造了条件。
一个 Coding Agent 完全没有这些:没有社会责任感,不会对一个 300 行的函数感到审美厌恶,没有”我们这里不这样做”的直觉,没有组织记忆。它不知道哪个规范是承重的、哪个只是习惯,不知道技术上正确的方案是否适合团队正在做的事情。
Harness 是人类开发者经验的外显化尝试——但它只能走那么远。建立一个连贯的引导、传感器和自我纠正循环系统是很昂贵的,所以我们必须有一个清晰的目标来排列优先级:好的 Harness 不一定要完全消除人的输入,而是将人的输入引导到最重要的地方。
这段写在最后,也是最清醒的一段。没有人应该追求 100% 的自动化。Harness 的价值不是替代人,是把人的精力从”纠正颜色不对”解放到”判断方向对不对”。
起点与开放问题
我在这里阐述的心智模型描述了已经在实践中发生的一些技术,并帮助我们构建关于还需要解决什么问题的框架。它的目标是把对话从功能层面提升上来——从 Skills 和 MCP 服务器,到我们如何战略性地设计一套控制系统,让我们对 Agent 产生的东西有真正的信心。
以下是当前讨论中一些与 Harness 相关的例子:
- OpenAI 的一个团队记录了他们的 Harness:由自定义 linter 和结构测试强制的分层架构,以及定期运行的”垃圾回收”——扫描漂移并让 Agent 提出修复。他们的结论:“我们现在最难的问题集中在如何设计环境、反馈循环和控制系统上。”
- Stripe 关于 minions 的文章描述了基于启发式算法在 pre-push hook 中运行相关 linter,他们强调了”将反馈左移”的重要性,他们的 “blueprints” 展示了如何将反馈传感器集成到 Agent 工作流中。
- 变异测试和结构测试是过去被低估的计算型反馈传感器的例子,现在正在复兴。
- 开发者中关于将 LSP 和代码智能集成到 Coding Agent 中的讨论越来越多——这是计算型前馈引导的例子。
- 我听到来自 Thoughtworks 团队关于用计算型和推理型传感器解决架构漂移问题的故事。
还有很多需要解决的问题,不仅仅是已经提到的行为 Harness:如何保持 Harness 在增长过程中的连贯性,确保引导和传感器同步、不自相矛盾?当指令和反馈信号指向不同方向时,我们能在多大程度上信任 Agent 做出明智的权衡?如果传感器从不触发,这是高质量的标志还是检测机制不足?
我们需要一种方法来评估 Harness 的覆盖范围和质量,类似于代码覆盖率和变异测试对测试的作用。前馈和反馈控制目前散布在交付步骤之间,为配置、同步和将它们作为系统推理的工具,这里有真正的潜力。
构建这个外部 Harness 正在成为一种持续的工程实践,而不是一次性的配置。
致谢
感谢 Doppler 团队在上次技术雷达会议上的精彩讨论,特别感谢 Kief Morris 提起了控制论。感谢 Ned Letcher、Chris Ford 和 Ben O’Mahoney 关于”Harness 到底是什么”的讨论,以及 Matteo Vaccari 关于行为 Harness 的见解。感谢所有花时间阅读草稿并提供宝贵反馈的人。
GenAI(Claude 和 Claude Code)被用于研究、从现有笔记中提取相关思想,以及语言润色。
译者后记
读完这篇文章后我最大的感受是:Harness 工程不是”给 Agent 加一堆配置项”,而是一套成体系的控制论实践。
在我们做 Agent 的这半年里,大部分时间花在”加约束”上——但直到看到这篇文章,我才意识到我们缺少一个框架来回答:应该加什么约束?加在哪里?什么时机加?效果怎么衡量?
这篇文章的三个分类(可维护性、架构适应性、行为)提供了一个实用的划分。我们的实践下来:
- 计算型传感器是最划算的投资——type check + linter + 关键模块的结构测试。便宜、可靠,每次 commit 前跑。
- 推理型传感器只在关键路径上用——代码审查只在 PR 阶段跑一次。太贵,不能高频。
- 真正的瓶颈是行为 Harness——如何验证 Agent 生成的功能代码真的符合业务意图?这是目前没有好答案的问题。
如果你也在做 Agent,这篇文章值得好好读。它不是给你答案,是给你一个更有效的问问题的方式。