Harness Engineering:让AI编程助手从漂移到可控的工程化实践
深入解析Harness Engineering的起源、三大组成、自改进维度与渐进式硬化原则,帮助团队构建可持续的AI辅助开发护栏,让AI生成的代码保持正确性与一致性。
在过去的两年里,AI辅助编程从一种新鲜事物迅速演变为软件团队日常工作的组成部分。工程师们开始习惯在集成开发环境中让模型补全函数、在终端里让智能体生成测试、甚至在拉取请求审查阶段引入AI同事。然而,随着使用频率的提升,一个并不令人意外的问题开始浮现:AI生成的代码看起来合理,却会在持续集成中悄然偏离项目的命名约定、架构决策乃至安全规范。代码依然可以编译、测试依然可以通过,退化却在静默发生。 Harness Engineering(护栏工程)正是为了回应这一挑战而诞生的工程实践。它源自Birgitta Boeckeler在martinfowler.com上发表的文章,并在ThoughtWorks的真实团队中得到验证。
它的核心洞见借用了一个软件工程中早已存在的概念:测试harness。测试本身并不让代码变得正确,它只是在代码不再正确的时候发出警报。Harness Engineering把这个思路扩展到了更广的范畴:不仅要检查功能正确性,还要检查代码库是否依然体现团队约定俗成的架构决策、命名规范、安全约束与结构规则。功能测试是必要条件,却远非充分条件。 Harness Engineering的真正价值,在于它把AI从"想写什么就写什么"的概率性系统,转变为受到显式约束和持续验证的工作伙伴。它不是限制创造力,而是为创造力铺设护栏,让团队在享受AI效率的同时,避免被缓慢积累的熵增所吞噬。
起源与思想基础 Boeckeler在文章中观察到的现象,几乎所有深度使用AI编程助手的团队都曾独立经历:模型会忘记自己十分钟前的约定,会重复犯同样的错误,会在无人注视时悄悄破坏代码库的内部一致性。这种退化不是由于模型"变笨"了,而是因为模型在每一次生成时都在做局部最优的选择,对全局一致性缺乏内在的把握。 测试harness的类比之所以贴切,是因为它揭示了一个重要的工程原则:自动化验证并不直接产生正确性,但它能以可重复、低成本的方式持续监测正确性的存在与否。同样的逻辑可以平移到AI辅助开发,只不过验证的对象从"函数返回值是否符合预期"扩展到了"代码是否符合团队的隐性契约"。这些契约通常存在于资深工程师的头脑里、散落在代码评审的评论中、隐藏在历史提交的commit message里。它们很少被显式记录,更少被持续验证。
Harness Engineering要求把这些契约显性化、文档化、并通过自动化机制加以守护。 三大组成:上下文工程、架构约束、垃圾回收 Harness Engineering的框架由三个相互支撑的组件构成,每一个都对应着AI辅助开发中某一类典型的失效模式。 上下文工程关心的是AI知道什么。一个AI编程助手只能在它已知的信息范围内工作。如果它不知道项目使用某个特定的日志库,它会自行发明一种方案;如果它不知道团队禁止可变全局状态,它会在方便的时候使用它;如果它不知道所有数据库写入必须经过某个抽象层,它会绕过那层直接写SQL。上下文工程的职责,就是确保AI在每一次生成之前,都已经获得了它所需要的全部知识。
在实践中,这通常体现为一份精心维护的文档 - - 在Harness Engineering的惯例中,这份文档被命名为HARNESS.md。它记录了技术栈选型、架构决策、命名约定、各种约束及其背后的理由。这份文档不是写给人类新成员阅读的README,它是写给AI智能体使用的知识库。两者服务于不同的目的,有着不同的更新节奏:README解释项目做什么,HARNESS.md告诉AI必须做什么、不能做什么、为什么。 架构约束关注的是规则如何被执行。仅仅在文档里写下约束是不够的,因为AI是优化"看起来合理"的概率系统,而不是严格遵守规则的确定性系统。
上下文工程能减少违规,但不能消除违规。架构约束就是捕捉违规的机制。Boeckeler把执行点称为"verification slot"(验证槽位),即开发工作流中那些被明确定义、运行检查、并以通过或阻止来影响进度的时刻。每一个验证槽位都面临一个关键的设计决策:使用确定性工具还是基于智能体的审查。 确定性工具包括代码检查器、脚本、正则表达式检查、文件结构断言等,它们产生无歧义的通过或失败结果。当约束可以被精确表达时,这类工具是首选:速度快、成本低、在其规范内完全可靠。
基于智能体的审查则使用语言模型对照约束描述对代码做出判断,这种方式在约束涉及意图、语义或难以机械表达的复杂模式时不可或缺。智能体审查成本更高、确定性更低,但能捕捉到任何脚本都无法捕捉的东西。一个成熟的Harness Engineering体系会同时容纳两类验证槽位,并随着团队对约束理解的深入,逐步把约束从智能体审查迁移到确定性工具。这个过程被称作渐进式硬化,是Harness Engineering中一条贯穿始终的演进路线。 垃圾回收处理的是时间维度上的熵增。即使拥有出色的上下文工程和严格的架构约束,代码库作为活的系统依然会积累无序:死代码生长、待办注释悬挂数月、依赖过期、曾经合理的抽象在项目演进中变成障碍、早期建立的约定在不方便时被悄悄放弃。
垃圾回收就是周期性地对抗这种熵增的过程。它不像前两个组件那样在代码生成或审查的瞬间运行,而是按照预定的时间表触发 - - 不是因为某个具体编码事件,而是因为时间已经过去。在Harness Engineering框架中,垃圾回收规则是对"整洁"标准的显式声明,配以定期运行的智能体或脚本检查代码库是否依然符合这些标准。其输出不是阻止拉取请求的错误列表,而是一份报告,在问题变得严重之前就引起注意。 活文档:让护栏自我维护 一个维护良好的Harness Engineering体系最重要的特征,是它不是静态的。一份写完就再也不过问的文档,只能反映团队在某个时间点的理解。
代码库会持续演化,新模式会涌现,旧约束会过时,团队最初没有预料到的AI失误类别会出现又消失。 HARNESS.md被设计为自指文档。它不仅描述哪些约束正在生效,还跟踪每个约束的当前状态:未经验证、处于智能体审查、还是已被确定性执行。文档声明什么应该为真,智能体、钩子和持续集成检查验证它是否为真,护栏审计智能体 - - 在该插件中是一个定时任务 - - 读取这些检查的结果,并更新HARNESS.md中的状态条目以反映现实。 这种设计形成了一个反馈循环。文档同时是规范和健康记录。
在任何时间点阅读HARNESS.md,不仅能了解团队一致认为代码库应该是什么样,还能了解这些约定在多大程度上得到了真正的维护。自指属性让活文档区别于那些逐渐过时、最终被忽略的普通文档。因为护栏本身也是被强制执行的对象 - - 护栏审计智能体会检查HARNESS.md是否准确反映了当前的验证状态 - - 忽视护栏会变得可见,而不是悄无声息。这种日常自检的入口是/harness-sync命令,它运行审计的检测逻辑并呈现统一的漂移表,使用户看到声明的护栏与现实之间的不一致,而无需记住单独调用某个诊断工具。 渐进式硬化:约束成熟的阶梯 并非所有约束都同等重要,也并非所有约束都从一开始就准备好被确定性执行。渐进式硬化描述的是约束从粗放到精密的演进阶梯。
未经验证是起始状态。团队在HARNESS.md中声明了一项约束,相信它很重要,但还没有机制来检查它。这种状态不是失败,而是诚实的记账。未经验证的约束是对建立验证机制的承诺,而不是声称验证已经存在。 智能体审查是第二阶段。团队编写了智能体提示词,在拉取请求审查或定期检查中评估该约束。
约束正在被执行,但执行者是做出判断的语言模型,而不是确定性规则。智能体审查能在大多数时候捕捉到大多数违规。它并非完全可靠,需要人类审查其输出。 确定性执行是最终阶段。团队已经将约束表达得足够精确,可以编码为脚本、代码检查规则或结构化检查。它在持续集成中运行,要么通过,要么阻止合并。
没有判断的余地,检查也不会被混淆或误导。 演进的方向总是朝着确定性。当智能体反复捕捉到同一类违规时,这种重复就是一个信号:模式已经被充分理解,可以自动化了。编写脚本,退役该特定约束的智能体检查,把HARNESS.md中的条目移动到确定性状态。渐进式硬化之所以重要,是因为它防止了两种失败模式:其一是试图从一开始就对所有约束进行确定性执行,这对于新颖或语义复杂的约束是不可能的;其二是把智能体审查当作永久状态接受下来,这既昂贵又不可靠。阶梯为两者之间提供了一条路径。
三种执法循环:内、中、外的节奏 Harness Engineering把验证槽位组织成三种执法循环,在不同的时间尺度和不同的误报容忍度下运行。 内循环是建议性的,在编辑时运行。当你保存一个文件或完成一次编码会话,轻量级检查会运行,把潜在问题以建议而非阻塞的形式呈现出来。内循环优化低摩擦,不应打断心流,它的工作是让问题尽早可见,而不是阻止工作。 中循环是严格的,在拉取请求时运行。当你开启一个拉取请求,完整的智能体和确定性检查套件会运行。
这个循环有权阻止合并,它是架构约束的主要执行点。这里的失败必须在代码落地之前得到解决。 外循环是调查性的,按计划运行。垃圾回收规则、健康度函数和护栏审计定期执行 - - 每天、每周或按适合该规则的任何节奏。外循环产生报告而非阻塞,其发现反馈回护栏,作为潜在的新约束或现有约束的更新。 三个循环大致对应三个组件:内循环服务于上下文工程(在当下保持AI信息充足),中循环服务于架构约束(在集成时执行约定标准),外循环服务于垃圾回收(在集成事件之间检测缓慢的熵增)。
Harness Engineering中的智能体以有界信任运作。任何智能体都没有单方面修改生产代码或合并变更的权力。智能体审查、建议、报告、标记,人类做决定。这是有意的设计选择:护栏放大人类判断,而非取代它。 自改进维度:从静态护栏到学习型护栏 原始的Boeckeler框架把护栏描述为团队构建和维护的东西。而Harness Engineering在此之上增加了一个新维度:护栏可以从自身的运行中学习。
每次编码会话之后,/reflect命令捕捉哪些做得好、哪些失败了、哪些约定被违反了、哪些新模式出现了。这些反思累积在学习日志中。护栏智能体在做出决策时读取这份日志,过去错误的模式得以影响当前的审查。回归检测沿同样的方向工作。当护栏审计智能体运行时,它不仅检查当前约束是否得到满足,还查看约束违规的历史以识别模式:同样的约束是否在反复被违反?如果是,这就是一个信号:要么约束需要更强的执行机制,要么上下文文档没有把理由解释得足够清楚,要么约束本身是错误的,需要重新考虑。 这关闭了原始框架留开的循环。
静态护栏只有在人类注意到失败并手动更新时才会变好。自改进护栏把自身的运行历史当作输入数据,并为自己改进提出建议。人类仍然决定接受哪些建议,但模式识别的工作 - - 阅读违规日志、注意到同样的错误反复出现 - - 被委托给了智能体。 这一思路在自动护栏的扩展中走得更远:harness-init过程本身读取现有代码,以推断代码库中已经存在但尚未声明的约束。团队不需要从头指定一切,智能体从观察到的模式引导出候选的HARNESS.md,并请开发者确认、拒绝或细化每个条目。人类仍然是权威者,但构建护栏的初始成本被大幅降低。
harness-init也支持渐进式采用。团队选择要配置的功能 - - 上下文工程、约束、垃圾回收、持续集成、可观测性 - - 之后可以重新运行命令添加更多。现有配置在多次运行之间被保留下来。这意味着团队可以从仅配置上下文和约束开始,证明价值,然后在准备好时添加垃圾回收和持续集成执行。护栏随着团队的成熟而成长,而不是一开始就要求全面承诺。 落地实践:从评估到仪表盘 Harness Engineering并非抽象理论,它有具体的落地路径。
从最初评估代码库的当前状态开始,团队可以识别哪些约束已经在隐性层面被执行、哪些被AI助手反复违反、哪些垃圾回收规则值得自动化。评估的输出是一份护栏健康度报告,它既是改进的起点,也是后续追踪进展的基线。 接下来是搭建HARNESS.md骨架,把评估中识别出的约束显性化。每个条目都应包含约束描述、执行机制、当前状态和理由。文档不是一次性写完的,而是随着理解的深入持续迭代。 然后是配置三种执法循环。
内循环通过编辑器钩子或保存时触发的轻量级检查实现,中循环通过拉取请求工作流中的持续集成检查和智能体审查实现,外循环通过定时任务实现。每种循环的误报容忍度和响应速度都应与其时间尺度匹配。 最后是建立观测性和仪表盘。护栏本身需要被观测:哪些约束被触发的频率最高、哪些智能体审查产生了最多的误报、垃圾回收报告中的反复问题是什么。这些数据不仅是改进的输入,也是向团队展示Harness Engineering价值的窗口。 护栏工程的真正意义 Harness Engineering的真正意义,不在于它把AI编程助手变成了什么都不会犯错的系统 - - 那是不存在的 - - 而在于它把AI的失败从静默退化变成了可见信号。
在缺乏护栏的情况下,AI的失误会缓慢侵蚀代码库,直到某次重大重构或生产事故才被察觉。在护栏的守护下,同样的失误会在拉取请求阶段被捕捉、在编辑时被建议、在定期审计中被报告。 这是一种从被动应对到主动管理的转变。它要求团队投入时间在前期构建护栏,但回报是后续每一次AI辅助开发都建立在更稳固的基础上。护栏不是束缚,它是让AI编程助手能够长期可靠地融入团队工程实践的基座。当代码库持续演化、当团队成员持续更替、当AI模型持续更新,那份被精心维护的HARNESS.md和围绕它建立的三种执法循环,会成为整个工程体系中最稳定的常量之一。
软件工程的本质,是在约束中创造。Harness Engineering所做的,是把这条原则延伸到AI辅助开发的每一个瞬间。它让约束可见、让验证自动化、让改进持续。在这个意义上,它不是AI编程的附属品,而是AI时代软件工程的基础设施。 。