跳到正文
FunCoding
返回博客
Harness

Harness 工程,模型外面的那套系统

同一个模型放进不同的智能体里,表现可以差很多,差别在模型外面那套系统。本文从 Mitchell Hashimoto 的「每个错误只犯一次」和 OpenAI、Anthropic 公开的经验讲起,介绍 harness 由哪些部分组成,以及用现成的编程智能体时能在哪里下功夫。

阅读约需 7 分钟

#Harness#Agent 工程

本页目录

2026 年 2 月 5 日,Mitchell Hashimoto 写了一篇长文,回顾自己从怀疑 AI 到天天用它写代码的过程。他是 Terraform 和 Ghostty 的作者,这篇文章按阶段写,第五个阶段叫 Engineer the Harness。

那一节的意思很简单。智能体犯了错,别只在对话里纠正,花点时间做个东西,让它以后再也不犯这个错。他提到两种做法。简单的问题,比如智能体总跑错命令、总找错 API,就去改 AGENTS.md,补一句说明。复杂一些的,就写成真正的工具,比如截图脚本、只跑相关测试的脚本,再在 AGENTS.md 里告诉智能体这些工具存在。

六天后,OpenAI 发了一篇《Harness engineering》,讲一个内部实验。按 InfoQ 的报道,一个小团队花五个月做出一个内部测试版产品,大约一百万行代码,没有一行是人手写的。人负责设计环境、说明意图、搭反馈回路,代码全部由 Codex 生成。这两篇文章前后脚出来,harness 这个词就流行开了。

harness 原意是马具,套在马身上,让马的力气变成拉车的力。用在这里,指套在模型外面、让它能干活的那套系统。Claude Code 的官方文档就这么称呼自己,模型负责推理,工具负责行动,Claude Code 是包在模型外面、提供工具并管理上下文的那一层。Codex、Cursor、Gemini CLI 也都是 harness。

为什么等不来更好的模型

智能体犯错,很多人的第一反应是等下一代模型。可很多错误跟能力关系不大,是信息不够。它不知道团队约定字段用下划线命名,不知道某个目录是生成的、不能手改,不知道跑测试前要先起 Docker。模型再聪明也猜不到这些。

项目自己也一直在变,约定会改,依赖会升级,每周都可能冒出新坑。与其等模型适应,不如让环境主动告诉它,或者干脆让它做错时立刻被拦下来。

harness 由哪几部分组成

拆开看,一个编程智能体的 harness 大致有六块。用现成产品时,前几块厂商已经做好,你主要是配置和扩展。

最核心的是执行循环。接到任务后调用模型,模型决定调哪个工具,执行工具,把结果交回模型,再调用模型,直到它认为做完了。循环本身不复杂,难在细节,工具失败怎么重试,上下文快满了怎么压缩,用户中途打断怎么办。这部分一般不用自己写,但后面所有扩展都是插在这个循环的某个环节上的。

然后是工具,决定智能体能做什么。基础的有读写文件、搜索代码、执行命令、访问网页,再往上可以通过 MCP 接数据库、工单、监控和设计稿。工具少一点、精一点更好用,每个工具的定义都占上下文,用途重叠的工具会让模型犹豫。工具的输出也要为模型设计,出错时返回能指导下一步的信息,别只给一个错误码。很多时候不用新写工具,模型对 git、gh、npm、curl 这些命令行工具很熟,告诉它可以用就行。

权限和沙箱决定它被允许做什么,这块最容易被忽略,出了事后果也最重。

层次作用举例
确认危险操作前问人执行不在白名单里的命令前弹窗
规则按模式允许或禁止允许 pnpm test,禁止 git push --force
沙箱从操作系统层面限制只能写项目目录,网络只能访问指定域名
隔离环境整个放到别处运行容器、云端虚拟机、独立的 worktree

提示词里写的不要做某事算不上安全措施。模型可能误解,可能被上下文里的恶意内容带偏,也可能在某个边界情况下觉得做一下没问题。要让它做不了,只能靠系统层面的限制。权限也不是越严越好,确认弹得太多,人会开始不看就点允许,比不弹还危险。常见的安全操作直接放行,危险操作直接禁止,只把真正需要判断的留给人。

第四块是项目知识,让智能体知道那些没写下来就没人知道的事。项目说明文件是最基础的形式。OpenAI 那个团队的做法是把仓库里一个结构化的 docs 目录当作智能体唯一的信息来源,里面放架构说明、执行计划和设计规格。讨论记录在聊天软件里、设计决策只在某人脑子里,对智能体来说就等于不存在。流程性的知识,比如发版步骤、评审清单,可以做成 Skill,平时只占一行描述,用到时才加载全文。

第五块是验证。智能体最常见的毛病之一是太早说做完了。Anthropic 在 2025 年 11 月的《Effective harnesses for long-running agents》里写过,Claude 经常跑完单元测试,或者用 curl 试一下接口,就把功能标成已完成,没有做端到端验证。后来他们让它用浏览器自动化工具像真实用户一样测试,结果明显变好。

验证手段从快到慢大概是类型检查和 lint,单元测试,构建,端到端测试,最后是让另一个智能体评审。OpenAI 那个团队还用自定义 linter 和结构测试来守架构约束,比如依赖只能按固定的层次方向走,智能体越界,检查就不过。错误信息写得越具体越好,只报违反了某条规则,智能体得猜半天;写清楚这里该改成调用哪个模块,它一次就能改对。

最后一块是钩子,在执行循环的固定时刻插入你自己的逻辑,比如工具调用前后、会话开始、模型准备结束回合的时候。钩子的好处是确定。提示词里写改完代码跑一下格式化,模型大部分时候会照做,偶尔会忘;写成每次编辑文件后自动运行格式化的钩子,就每次都会执行。在结束回合前检查测试有没有通过,没通过就让它继续,这个用法特别实用,相当于把完成的标准变成了一道关卡。

每个错误只犯一次,怎么落地

回到 Hashimoto 那条原则。它的好处是把 harness 工程变成了一个日常小习惯,不用先规划一个大工程。智能体每犯一次错,就问一句怎样让它下次不犯,再按错误的类型挑办法。

错误类型例子对应的改进
不知道某个事实不知道本地测试要先起数据库补进项目说明
不知道某个流程发版时漏了更新 changelog做成 Skill
违反架构约定在组件里直接查数据库写 lint 规则,报错里写明正确做法
引入了 bug改分页时弄坏了第一页补测试
做了危险操作想强制推送加权限规则禁止
没验证就说做完了没跑测试就说改好了加结束前检查的钩子
拿不到需要的信息看不到线上报错的详情接入对应的 MCP 或命令行工具

能用一行说明解决的,就别写工具。越确定的手段越可靠,维护成本也越高,挑够用的最轻的那种。

harness 用久了也要清理。说明会越写越长,规则会过时,测试会变慢。Martin Fowler 网站上有篇评论把 OpenAI 那套做法归成三类,其中一类叫垃圾回收。做法是定期派智能体去查,文档有没有前后不一致,代码有没有违反约束。

这些改进对人一样有用。清楚的文档、明确的约定、带修复说明的 lint、完善的测试,新同事来了照样受益。以前大家总说以后再补,现在补上马上就能看到效果。

在现成的智能体上能做什么

用 Claude Code、Codex、Cursor 这类产品,骨架已经有了,能做的扩展大致是这些。

扩展方式作用文档
项目说明文件放每次都要知道的事实Claude Code 记忆
Skill按需加载的流程和知识Agent Skills 是怎么设计的
MCP Server接入外部系统MCP 是怎么设计的
权限规则和沙箱限制能做什么Claude Code 权限
Hooks在固定时刻执行确定的逻辑Claude Code Hooks
子智能体隔离上下文,分工Claude Code 子智能体
项目自己的测试、lint、脚本让智能体能自己检查你的项目

最后一行最容易被忽略。产品提供的扩展是通用的。放到你的项目里,智能体表现好不好,很大程度上要看项目本身有没有像样的测试、lint 和脚本。

harness 做好以后,智能体在一次会话里就能比较可靠地完成任务。接下来的问题是每个任务还得有人来发起、有人来看结果,这是 Loop 工程要处理的。

参考资料

  • Agent 工程

    从提示词到 Loop,AI 编程的四层工程

    提示词工程、上下文工程、Harness 工程、Loop 工程,三年里先后流行起来。它们管的是同一件事的四层,外层包着内层,新词出现时旧的那层照样要做。本文顺着时间讲每一层为什么出现、各管什么、精力先放在哪。

    阅读约需 7 分钟

  • 上下文

    上下文工程,管好模型每一步看到的东西

    上下文窗口越来越大,塞得越满,模型反而越容易犯糊涂。上下文工程管的是智能体每一步能看到什么。本文结合 Chroma、Anthropic 和 Manus 公开的研究与经验,讲上下文为什么会越用越差,以及压缩、即时检索、外部记忆和子智能体隔离各适合什么情况。

    阅读约需 8 分钟