当前位置: 首页 > news >正文

Harness Engineering:麻绳还是马绳

最近,Birgitta Böckeler 做客了 Thoughtworks Podcast[1],聊了她之前在 Martin Fowler 网站上发表的关于 Harness Engineering 的文章[2](以下称播客)。后来她又叫上了同事 Chris Ford,录了一个 YouTube 视频[3],专门深入聊了传感器的部分(以下称视频)。两个内容看完,我对这个概念的理解又深了一层。

说做客很奇怪,她本身就是TW播客的主播。但因为这次的主角是她,所以就以嘉宾的身份出现了。

我上个月已经读过文章了,还写了一篇《马与骑马人》。但那篇文章更像是一个系统性的概念梳理,播客里的很多内容是文章里装不下的——那些迟疑、那些"我们也不知道答案"的瞬间、那些嘉宾之间互相碰撞出来的类比,反而让这个概念变得具体了。

Harness 到底是什么?

Birgitta 在播客开头就说,写那篇文章最大的挑战是"怎么定义"。AI 时代的术语扩散太快了,今天刚造一个词,下周就有十种不同的理解。所以她用了洋葱模型——最里面是大语言模型,外面包一层是 Coding Agent(也就是Claude Code、Cursor 这些),这是内层的 harness。然后作为用户,我们还可以再包一层,把项目特定的规则、工具、反馈回路都装进去。

这个比喻我在《马与骑马人》里已经写过,但 Birgitta 加了一句提醒,让我愣了一下。她说,她"几乎希望这是两个不同的词",因为 Agent Harness 和 User Harness 是完全不同的两个限界上下文。Chris Ford 说得更好:你不能把马具套在狗的里面。问题是你到底在控制什么?紫色的内层 harness(Claude Code、Cursor)控制的是模型本身;蓝色的外层 harness 控制的其实是 Agent,也就是编码代理的行为。市面上那些讨论,有些讲的是怎么设计 Agent 的系统提示和工具调用,那是给工具厂商看的;有些讲的是怎么在自己的项目里写 AGENTS.md 和 linter 规则,那是给终端用户看的。他们都说自己是 Harness Engineering,但说的其实是两件事。

引导器和传感器:预判与反馈

播客真正让我"啊"了一声的,是 Birgitta 对引导器(Guides)传感器(Sensors)的展开。

她把这个框架分成了两条线。一条是前馈(feedforward):你在事情发生之前给 Agent 的指引,预判它可能会做错什么,提前把规则塞进去。今天最常见的引导器就是各种 markdown 文件——编码规范、架构文档、AGENTS.md、Skills。另一条是反馈(feedback):等 Agent 写完代码之后,你给它一个信号,告诉它"这里不对"。

Nate 突然扔进来一个类比,精准得让人意外。他说,这就像是带实习生。

你先跟他聊编码规范、项目结构,这是引导器。然后你 review 他的代码,同样的问题反复出现,你就再补充一条规则。这个过程——预判错误、设定规则、观察结果、修正规则——就是 Harness Engineering 的日常。

Birgitta 立刻接住了。她说她曾经带过一个刚毕业的学生,不 pair 的时候,静态代码分析工具帮了大忙——那些"函数太长"、"圈复杂度太高"的基础问题,工具可以直接告诉新人,不用等她来 review。但关键是后面这句:有些东西实习生永远不会自动做对。所以确定性的反馈特别重要。

这里有个特别重要的细节,Birgitta 在视频里反复提到:人类其实一直坐在这个反馈循环里。她管这叫驾驶循环(steering loop)。每次你跟 Agent 开完一个会话,你都要想:这次它又犯了什么常见的错?我能不能加一个传感器来捕捉?我能不能改一下引导器来预防?OpenAI 团队那个 Ryan 写过一篇文章,说他们团队尽量不直接改代码,而是把精力花在这个循环上——不是人在修代码,是人在修规则,然后让规则去修代码。

Chris 补了一句:这可能就是"reckless vibe coding"(鲁莽的氛围编程)和可持续开发之间的区别。前者代码熵随时间递增,后者有人持续投资在 harness 上。

计算型工具的价值被低估了

这让我重新理解了计算型(Computational)工具的价值。

现在市面上大家都在写 markdown 文件——编码规范、架构文档、Skills,这些都是推理型(Inferential)的,由 LLM 去"理解"和"解释"。Birgitta 很直接地说,团队目前严重低估了计算型工具。静态代码分析、类型检查、linter、结构测试,这些老工具在 AI 时代反而有了新的生命力。

为什么?因为 AI 会犯那些"人类老手不会犯的低级错误"。超长函数、十个参数、圈复杂度爆表。这些不需要 LLM 去"判断",linter 可以直接报出来,AI 收到反馈自己就能改。Birgitta 说她最近在一个项目里用了大量的静态代码分析,结果"意外地惊喜"。以前她觉得这东西有点鸡肋——毕竟它不能保证质量。但现在有了 Coding Agent,linter 集成进去之后,AI 会持续收到"这里太复杂了"的信号,然后自动拆解、重构,人类甚至不用看。

更妙的是信噪比的问题。以前一堆 warning,没人有耐心逐个 suppress,最后就放弃了。现在你可以把 lint 规则的错误信息设计成给 LLM 看的修复指引——"这个函数太长了,考虑拆成三个 smaller functions,每个只做一件事"。AI 读到之后,可以自己判断要不要修、怎么修、要不要 suppress。甚至你可以再写一个脚本,列出所有 AI suppress 掉的例外,让人 review 的时候先看这些地方。Birgitta 还玩了一个更狠的:她给某些规则设置了"软阈值",允许 AI 在给出理由的情况下微调阈值——比如某个 mock 文件长度超过了 500 行限制,AI 判断后把它改成了 550 行。不是 blanket suppress,而是有边界的弹性。

但计算型工具不只是反馈侧的。Birgitta 提到,前馈侧也有计算型工具——比如 language server(让 Agent 用确定性方式分析调用链而不是让 LLM 猜)、code mods(OpenRewrite 这类有确定性 recipe 的代码迁移工具)、JetBrains 的 MCP rename 服务器。这些东西比让 LLM 自己改代码更可靠,也更省 token。

测试不是全绿就完了

播客还花了不少时间聊测试。这是 Birgitta 文章里着墨不多但播客里深入展开的话题。

Nate 讲了一个真实案例:一个团队的 AI agent 在 dev 环境里因为没有权限执行某个函数,直接把 authorization 给注释掉了。他说,实习生可能会干这种事,但 senior engineer 会把他拉到一边,告诉他永远别这么干。问题是,AI 不会"学到"。你告诉它一次,下次换了个上下文,它可能还会犯。

Nate 的做法是学 Kent Beck:测试的定义和编写是人的责任,AI 只负责写生产代码。你告诉 AI 你要什么,让它写几个测试,你 review 完测试没问题了,再让它去写实现。Birgitta 说她没这么严格,她的 workflow 是让 AI 写测试、她 review、进入修订循环,双方都满意了再写实现。但她也坦言,怎么让 AI 在测试和生产代码之间守住边界,目前还没有好的模式。

她提到了 Thoughtworks 同事 Matteo Vaccari 的"Approved Scenarios"的做法——在 HTTP API 层面写 high level 的输入输出测试,人 review 这些场景,然后让 AI 去填充实现。

但最让我印象深刻的,是 Birgitta 在视频里展示的一个发现。她在一个文件上跑出了 100% 语句覆盖率和 75% 分支覆盖率,然后发现这个文件其实没有任何单元测试。只有一个大型验收测试间接调用了它。更狠的是,她在 AI 生成的测试套件上跑变异测试,发现了大量"无断言测试"——测试看起来跑过了,但其实什么都没验证。覆盖率很高,但有效性很低。

她还观察到 AI 的一种她称为"TDD 表演"的行为:Agent 会说"我先写测试",然后立刻就去写实现,中间根本不运行测试,过两分钟才回来跑。形式上是 TDD,实际上完全没有"红-绿-重构"的循环。

这个细节让我印象深刻。因为我们太容易相信"测试全绿"了。全绿不等于有效,尤其是在 AI 生成测试的情况下。

结构比想象的重要

Birgitta 在视频里还聊了一个我之前没太注意的点:模块化。

她在自己的实验项目里用 Dependency Cruiser 设置了架构规则,比如 domain 层不允许直接引入外部 SDK,API 客户端必须放在特定文件夹。听起来很老派,但她指出这不仅仅是"人类觉得好看"——对 AI 来说,好的模块边界意味着它每次需要读的文件更少,推理范围更小,出错的概率也就更低。而且不同模块有不同的风险画像:如果 AI 改了 outbound 模块(调用外部 API),你可能需要担心依赖升级;如果改了 inbound 模块(对外暴露的接口),你可能需要担心向后兼容。模块清晰了,你甚至可以做一张"变更热力图",一眼看出这次改动最危险的地方在哪里。

Chris 说了一句很在点的话:到目前为止,对人类好的模块化和对 Agent 好的模块化,看起来是一回事。

传感器 Sidecar 和那场实验

视频里最硬核的部分,是 Birgitta 展示的一个实验。她写了一个传感器 sidecar——一个跟 Agent 并行运行的小工具,实时监控各种传感器的状态。人类看到的是一张红绿灯面板;Agent 看到的是另一种视图:只返回失败项,而且每个失败都附带一条"积极提示注入"——不是冷冰冰的报错,而是告诉它"我希望你怎么修"。

然后她做了一个对照实验:同一个功能,一次开着传感器让 Agent 做,一次关掉传感器。结果不出所料但也有趣:

  • 有传感器:lint 全绿,测试覆盖率不降,模块边界没被突破,安全扫描通过

  • 无传感器:测试覆盖率掉了 6 个百分点,出现了no-explicit-any和未使用变量,模块结构被 Agent 自己重新组织了,函数参数最多达到了 8 个

Birgitta 坦言这个实验样本量很小,不足以得出统计结论,但它揭示了一个重要的 failure mode:没有反馈的时候,Agent 倾向于走捷径。不是因为它笨,而是因为它不知道你的标准是什么。

她还特别提到一点:人在循环中的可视化体验仍然很重要。现在社区里很多能量都花在"怎么让 Agent 完全自主运行"上,但她觉得监督式会话中的可视化同样关键——有很多场景你根本不想让 Agent 无人看管。

什么时候跑、跑多贵

播客后半段聊到了成本和时间线分布。Birgitta 说传感器不是越多越好,你要考虑跑在哪里、多久跑一次。便宜快速的计算型传感器可以持续跑,像 linter、测试套件。贵的推理型传感器可以放在 PR 之后,甚至定期跑——OpenAI 团队有一个叫做"garbage collection"的定期推理型传感器,可以定期扫描技术债。

她还提出了一个我很有共鸣的区分:信息型传感器vs规范型传感器。前者告诉你"你的依赖有 3 个已经两年没更新了",这是信息,需要人或 AI 来判断要不要处理;后者直接亮红灯:"这个函数超过了 20 个参数,build 失败"。信息型适合探索,规范型适合防守。太多团队把本该是信息型的问题硬塞进 build pipeline,结果大家学会了一路 suppress,传感器最后变成了摆设。

她还提到了 token 经济学。她说当补贴期结束、按量付费真正到来时,这些分层策略会变得非常重要。Nate 接了一句:constraints set us free——本意是自律才能自由,放在这里可以理解为"约束才能让我们 token 自由"。

能砍掉一半的 Markdown 吗?

最后 Prem 问了一个好问题:如果听众下周回到公司只想做一件事,应该做什么?

Birgitta 的回答很短,也很有力:想想怎么把 markdown 文件砍掉一半。那些你写在 AGENTS.md 里的规则,有多少可以用计算型工具替代?用 linter 替代文字描述,用类型检查替代"记得检查参数",用架构测试替代"不要跨模块调用"。

但这个问题的另一面,是 Birgitta 在视频结尾提出的。她说每次她激活新规则,都会冒出新的问题。有些是噪声,有些是金子。她甚至问了一个更激进的问题:如果模型 60% 的情况下本来就能做对,剩下 40% 有传感器兜底,那这条引导器是不是可以干脆删掉?

反过来也一样:如果传感器能让弱模型也产出合格代码,那是不是 weaker model + strong sensors 的组合比强模型 + 弱传感器更经济?

当然,她也有担忧。一切全绿的时候,人会不会变得更麻痹?Chris 用安全带反驳:有人说安全带让人开车更鲁莽,但就算是真的,我还是选择系安全带。

Birgitta 还抛了一个很远的想法:未来会不会有 harness templates?就像现在的 service templates 一样,但不是只 scaffold 项目结构,而是一整套预设的引导器和传感器。数据仪表盘有一种配置,事件处理模块有另一种,HTTP API 服务有第三种。你 init 项目的时候选一种,harness 就自动配好了。

缰绳一直都在

这期播客和视频看完,我对 Harness Engineering 的理解变具体了。上个月我写《马与骑马人》的时候,把这玩意讲得有点大,好像要从头搭建一套新体系。其实根本不是。它就是你已有的工程实践(linter、测试、CI)重新组合,服务对象从人换成 AI。

缰绳一直都在你手里。问题是你攥的是一根麻绳,还是一套有韧性有层次的马绳。

引用链接

[1]Thoughtworks Podcast:https://www.thoughtworks.com/en-sg/insights/podcasts/technology-podcasts/what-harness-engineering
[2]Birgitta Böckeler 关于 Harness Engineering 的文章:https://martinfowler.com/articles/harness-engineering.html
[3]Birgitta Böckeler 和 Chris Ford 在 YouTube 上的视频:https://www.youtube.com/watch?v=uLWOLmeHOSE

http://www.zskr.cn/news/1364012.html

相关文章:

  • 高维数据压缩:秩-1格点与双曲交叉方法原理与应用
  • Claude Code-入门篇-Claude-Code基础与环境配置
  • 基于图元随机游走的网络嵌入:提升同质性与下游任务性能
  • 告别Python踩坑:用ioapi的m3mask工具5分钟搞定CMAQ-ISAM区域文件(附int转float关键一步)
  • 量子机器学习数据集构建:从核心要素到工程实践
  • 经典通信赋能分布式量子机器学习:NISQ时代的实用化路径探索
  • LabVIEW 的Actor 框架原理与应用
  • AI Agent安全治理框架缺失导致客户数据泄露?(Gartner 2024新评估模型首次落地解读)
  • AI Agent记忆方案大比拼:RAG、Mem0、Zep、Letta怎么选?告别选型迷茫!
  • 基于共享潜在空间的贝叶斯优化:解决异构算法超参数联合选择难题
  • Leslie矩阵建模:从种群动力学到捕食竞争与机器学习拟合
  • B物理反常的全局拟合:有效场论与机器学习解析新物理信号
  • [智能体-31]:Streamlit:告别命令行,用 Python 手工构建专属 AI/Web UI
  • [智能体-30]:告别命令行,Chatbox 不是 “智能体(Agent)” 本身,而是一个可以承载 / 连接智能体的终端(客户端), 通过前后端技术管理智能体和大模型
  • OSINT+机器学习:构建多语言钓鱼邮件检测系统的实战解析
  • 车企AI Agent团队组建白皮书(附2024头部厂商组织架构图+7个核心岗位能力雷达图)
  • Spark Transformer:稀疏激活优化与计算效率提升
  • 如何用OneMore插件让OneNote成为你的高效笔记神器
  • Godot 4.2回合制RPG生产级框架设计与实践
  • 虚幻引擎程序化体积云渲染:告别天气纹理,实现动态天空
  • KNO标度律与粒子多重数:从QCD喷注结构到夸克-胶子鉴别的理论推导
  • 别急着重启!深入理解Ubuntu 22.04的needrestart:守护进程、库文件与系统更新背后的原理
  • 因果机器学习在精准医疗中的验证挑战:从理论到实践的可靠性检验
  • 蒙特卡洛采样与MCMC:从基础原理到实战调优
  • 量子特征选择与量子核方法融合:破解NISQ时代机器学习维度灾难
  • Decompyle++:Python字节码源码恢复实战指南
  • Unity深度调试框架UniHacker:突破IL2CPP可观测性断层
  • 深度学习框架与编程语言选型指南:从TensorFlow、PyTorch到Java生态的实战解析
  • 3D高斯渲染技术原理与Lumina架构优化实践
  • 大型语言模型推理加速:Lyanna架构与推测解码优化