Caret Up

什么是 Harness Engineering

Harness,直译过来叫「马具」,或者更形象一点 —— 缰绳。

想象一下骑马的场景:马本身有强大的力量,能跑能跳能驮东西,但如果没有缰绳和马具,这股力量就是失控的。马可能往悬崖上跑,可能把你甩下来,可能跑去吃草不回来了。马具的作用,就是让这股力量为你所用。

把这个比喻放到 AI 系统里,就特别贴切了。大模型就像那匹马,能力很强,但你得有一套东西来驾驭它。

Agent = Model + Harness

这个等式在圈子里流传很广。翻译成人话就是:在一个 AI Agent 系统里,除了模型本身之外,几乎所有决定它能不能稳定交付的东西,都属于 Harness。

反过来也一样:Harness = Agent − Model。把模型拿走,剩下的就是 Harness。

这个词最早被正式叫响,是在 2026 年 2 月 Mitchell Hashimoto(HashiCorp 创始人)的一篇博客 《My AI Adoption Journey》 里。他把自己从「不信 AI」到「每天让 Agent 替我干活」的心路历程拆成了 6 个 Step,其中第五步他起了个名字叫 Engineer the Harness —— 打磨这套马具。

他给的定义特别简洁:

每次当你发现 Agent 犯了一个错误,就花点时间去工程化一个解决方案,让它永远不会再犯同样的错误。

不是骂两句然后手动改掉,而是把修复沉到环境里,让 Agent 在结构上就不可能再犯。

一周后,OpenAI 也发了一篇官方博客 《Harness engineering: leveraging Codex in an agent-first world》,讲他们一个 3 人小团队用 5 个月、靠 Agent 写出了 100 万行代码、合并了 1500 个 PR,全程没人手动写过一行代码。这篇博客直接把 Harness Engineering 推上了风口浪尖。

好,交代完背景,接下来我会沿着「从提示词到 Harness」这条路径,一步步讲清楚这三个阶段分别解决了什么问题、又撞上了什么天花板。

Harness 的演进过程

AI 工程的重心,过去两年悄悄换了三次。

一开始大家在聊 Prompt Engineering,后来发现光把话讲明白不够,模型还得「知道」该用什么信息,于是 Context Engineering 接棒。再后来,信息都给对了,模型在长链路任务里还是会跑偏,Harness Engineering 就这么被推到了台前。

这三个阶段不是谁取代谁,而是每一步都在上一步撞到天花板之后,继续往前啃出来的。

Prompt Engineering

要讲清楚 Prompt Engineering,得先搞明白大模型到底在干啥。

扒开 ChatGPT 那层对话框的壳,里面其实就一个东西:一个特别大的参数文件,存在硬盘上,加载到显卡内存里。给它配一个 HTTP 接口,就成了 API 服务;套一个聊天界面,就成了 ChatGPT;嵌一个代码编辑器,就成了 Cursor。

那这个参数文件怎么工作的?说白了就一件事:根据你输入的内容,预测下一个字最有可能是啥。

注意,是「预测」,不是「思考」。你给它的输入越模糊,它猜的范围就越大;输入越具体,猜的范围就越收敛。

知道了这个原理,你就能理解为啥同一个问题,换个说法效果能差十倍。比如你丢一段代码给模型说「加个排序」,它大概率给你回一段排序代码片段,但不告诉你放哪、要不要改其他函数。但如果你说「这是我的完整代码,请加上对 users 列表按年龄从大到小的排序,保留原有逻辑,输出完整代码」,结果就靠谱多了。

大模型本质上是一个对上下文极度敏感的概率生成器。你给它一个角色身份,它就往那个方向靠;你给它几个示例,它就沿着那个范式补全;你强调某个约束,它就把那个约束当成重点。

写提示词的本质,不是在「命令」模型,而是在塑造它的概率空间。你给它的每一句话,都在悄悄把输出往某个方向推。

正因为这种敏感性,大家慢慢摸索出了一套「提示词配方」:角色设定、背景信息、参考资料、明确任务、约束条件、输出格式。把这些东西按一定结构组装起来,有意识地去设计和调整,让模型稳定输出你想要的内容 —— 这就是 Prompt Engineering。

它解决的核心问题就一个:模型不是不会,而是你没把问题说明白。

但 Prompt Engineering 很快撞上了天花板。你让模型分析公司财报,它再聪明,没看过你的财报能分析啥?让它按公司内部规范写代码,它没看过规范怎么知道怎么写?

提示词写得再漂亮,也替代不了「事实」本身。

Context Engineering

Prompt Engineering 解决的是「表达」的问题,不是「信息」的问题。当 Agent 火了之后,模型不再只是回答问题,而是要进到真实环境里去干活 —— 多轮对话、调用工具、查数据库、在多个步骤之间传递中间结果。

这时候,系统面对的不再是「这次回答对不对」,而是「整条链路能不能跑通」。

每一次发给大模型的所有信息加在一起,就叫上下文。提示词只是其中很小一部分,还包括用户输入、历史对话、检索到的资料、工具返回的结果、当前任务状态、中间产物、系统规则等等。

Context Engineering 的核心思想就一句话:模型未必知道,所以系统必须在合适的时机,把正确的信息送进去。

你可能会想,那把相关资料全塞进去不就完了?

问题是,大模型一次能处理的上下文是有上限的,这个上限叫上下文窗口(Context Window)。而且就算窗口够大,塞太满之后模型会出现「上下文腐化」(Context Rot)—— 开始记不住前面的内容,前后矛盾,忽略最初定下的规则。Anthropic 在 《Effective context engineering for AI agents》 中对此做了详细分析。

所以 Context Engineering 要解决的问题是:在上下文窗口有限的前提下,把最相关的信息,以最合理的方式,送给模型。

实践下来,基本拆成三步:

  1. 召回:从一堆资料里找出跟当前任务最相关的那部分。这里涉及 RAG 技术 —— 把文档切块、向量化、建索引,每次问题进来去向量数据库搜最相关的几块
  2. 压缩:找到的信息可能还是太多,那就压缩。比如对历史对话,把太老的部分压成一句话总结,只保留最近几轮原文
  3. 组装:按一定顺序和格式组装。因为模型对靠后的信息更敏感,所以重要指令、当前任务要放在靠后的位置

这里有一个很重要的理念叫「渐进式披露」(Progressive Disclosure):一开始只给模型看每个能力的目录,等它判断需要用某个工具了,再把详细说明动态加载进来。上下文优化的本质不是「给得更多」,而是按需给、分层给、在正确的时机给

Context Engineering 在 Prompt Engineering 的基础上往前迈了一大步:从「把任务讲清楚」升级到了「把信息送对」。

但它也有天花板 —— 你把提示词写得再漂亮,上下文管得再完美,模型在单步上的表现确实好了,可只要任务链路一长,还是会出问题。

Harness Engineering

当模型真正开始「连续行动」的时候,出现了一个全新的问题:谁来监督它?谁来约束它?谁在它跑偏的时候把它拉回来?

提示词工程优化的是「意图的表达」,上下文工程优化的是「信息的供给」,但这两个都还停留在输入侧。当模型开始连续行动,你需要的不只是喂信息,而是驾驭整个执行过程

我用一个例子把三个阶段的区别讲清楚。

假设你是一个销售经理,要派一个刚入职的新人去做重要的客户拜访:

  • Prompt Engineering:把任务讲清楚。「先寒暄,再介绍方案,然后问需求,最后确认下一步」
  • Context Engineering:把资料准备齐。客户是谁?之前聊过啥?产品报价多少?这次会议的目标?
  • Harness Engineering:让他带 checklist 去每个节点打勾;关键节点实时汇报;录音回来复盘;跑偏了马上电话纠正;按明确标准验收成果

Harness 的重点已经不是「把话讲清楚」「把资料给齐」了,而是有没有一整套机制能持续观测、持续纠正、最终验收

三者不是替代关系,而是包含关系:

  • Prompt 是对「指令」的工程化
  • Context 是对「输入环境」的工程化
  • Harness 是对「整个运行系统」的工程化

Prompt 是 Context 的一部分,Context 是 Harness 的一部分。当你做 Harness 的时候,里面一定包含 Context 工程,Context 工程里又一定包含 Prompt 工程。

Harness 的分层

一个成熟的 Harness 大致可以拆成六层。这六层不是一个「必须一次搭完」的任务清单,而是一张路标 —— 告诉你下一次 Agent 犯错时,修复应该落到哪里。

按功能可以分成三组:

  • 输入侧(让模型看到正确的东西):上下文管理 + 记忆与状态
  • 动作侧(让模型做出正确的事):工具系统 + 执行编排
  • 校验侧(让模型知道做没做对 + 出错能爬起来):评估观测 + 约束恢复

为了方便理解,我假设一个场景贯穿整章:做一个 Agent,每天定时扫一遍 GitHub 上关注的仓库的新 PR,挑出值得关注的,生成摘要和点评,最后发到 Slack。

第一层,上下文管理

这一层管的是「空间」:这一轮发给模型的上下文,长啥样、装了些啥、怎么排布。

回到 PR Review Agent。它每处理一个 PR 的时候,到底该看到什么?最粗暴的做法是把整个 PR 的 diff 全扔给它。但它真正需要的是精挑细选的信息:

  • PR 的标题和描述
  • diff 里真正被改动的那几个文件
  • 这些文件在仓库里对应的模块说明
  • 仓库的 code review 惯例

你塞给它越多无关信息,注意力就越散。

这一层的核心工作三件事:

  1. 把角色和目标钉死:模型得知道自己是「PR 审查助手」,当前任务是「挑出值得关注的 PR 并生成摘要」。大部分 Agent 跑偏,根源就是这一步没说清楚
  2. 动态筛选而不是一次塞满:只把当前这个 PR 相关的信息拉进来,其余等需要了再取。这就是前面说的「渐进式披露」
  3. 结构化组织:固定规则放一处,动态证据放一处,中间结论放一处,三者分开。否则模型会「自我污染」,用前面错的中间结论影响后面的判断

第二层,工具系统

聊完「看到什么」,再聊「能做什么」。

没有工具的大模型本质上还是一个文本预测器。它能告诉你 PR 应该怎么审,但它没法真的去 GitHub 上读那个 PR。

但工具不是接得越多越好。OpenAI 做 Codex 早期踩过这个坑:给 Agent 接了一堆工具,想着选择多总是好的,结果 Agent 频繁用错工具。后来砍掉一大半,效果反而上去了。

这一层要回答三个问题:

  1. 给它哪些工具? PR Review Agent 至少需要:gh 命令行工具(拉 PR 列表、看 diff)、读文件工具、代码搜索工具、Slack 发送工具。别的工具除非真的必要,先别加
  2. 什么时候用哪个工具? 该查的时候查,不该查的时候别瞎查。Agent 判断「这个 PR 改的函数是不是核心逻辑」时应该去代码搜索,而不是凭感觉猜
  3. 工具结果怎么喂回模型? 这条最容易被忽略。调用代码搜索拿到 30 条匹配,不能原样塞回去,要先做提炼,只留核心的几条

你最近听很多的 MCP(Model Context Protocol),本质上就是在做工具层的标准化,让任何工具都能用同一种方式接到任何 Agent 上。

第三层,执行编排

工具接上了,模型就能动手了。但能动手不等于能做成事。

Agent 的本质,说白了就是一个 for 循环:思考一步 → 行动一步 → 观察结果 → 再思考下一步。这个循环结构有个经典名字叫 ReAct(Reasoning + Acting)。

Agent 经常翻车的场景是:每一步它都会做,但把所有步骤串起来之后就不会了。它会拉 PR 列表,会读 diff,会写摘要,但它不知道应该先拉全列表再逐个分析,还是边拉边评。

这就是第三层的职责:给模型一条明确的工作轨道

PR Review Agent 的轨道应该是:

  1. 拉取仓库当前所有开放的 PR 列表
  2. 对每一个 PR:读 diff 和描述 → 判断核心还是边缘模块 → 核心模块做深度分析 → 打分
  3. 按重要性排序,选前 3 个
  4. 为每一个生成摘要和点评
  5. 汇总发到 Slack
  6. 检查发送是否成功,失败则重试

有了这条轨道,Agent 就知道「现在在哪一步,下一步该干啥」。

除了 ReAct 之外,业界还有几个常见编排模式:Plan-and-Execute(先规划完整计划再执行)、Reflexion(每次失败都让 Agent 反思再重试)、Tree of Thoughts(同时探索多条思路再选最好的)。

第四层,记忆与状态

这一层管的是「时间」:上一轮发生过的事情,怎么流动到下一轮。

没有状态管理的 Agent,每一轮调用之间都是失忆的。你的 PR Review Agent 今天跑了一遍,明天再跑时完全不记得「这个 PR 昨天审过了」,于是又审一遍再发一次,Slack 频道变成重复消息的坟场。

一个核心洞察:Agent 的状态不应该放在上下文窗口里,而应该外化到文件系统。

Anthropic 在 《Effective harnesses for long-running agents》 里给出的做法是让 Agent 维护一个 progress.txt 日志、一个 init.sh 启动脚本、加上完整的 git history,作为长期记忆介质。下一轮换一个干净的上下文窗口接手时,从这些文件里一读,立刻知道「现在到哪一步了」。

PR Review Agent 需要管三类状态,必须分层存:

  • 任务状态:今天处理到哪个 PR 了?每个打分多少?写在 today-progress.json 里,任务跑完归档
  • 会话中间结果:当前轮对某个 PR 的初步判断,随会话结束就可以丢
  • 长期记忆和用户偏好:你喜欢关注什么类型的 PR?看重哪些模块?写在 user-preferences.md 里,每次调用都注入

这三类记忆的生命周期完全不同:任务状态活到任务结束,会话结果活到当轮结束,长期记忆跨所有任务。混在一起就乱了,分清楚才能用好。

顺便说一句,Claude Code、Cursor 这些 AI IDE 里的规则文件(CLAUDE.md / .cursorrules),就是「长期记忆」这一类的典型实现。

第五层,评估与观测

这一层是最容易被跳过、但跳过了就进退两难的一层。

我见过太多团队,Agent 做出来高高兴兴上线,跑了两周才发现实际成功率只有 50%。不是它不出结果,而是每次都出结果,但一半是错的 —— 问题是这两周里没人发现,因为根本没有机制能告诉团队「它这次到底做得对不对」。

这层拆成两件事:

尺子:Eval 集

Eval 集(evaluation set)是这一层的灵魂。简单说就是:手写一批典型任务,每个标注好「正确答案长啥样」,每次对 Harness 做任何改动,都让 Agent 把这批任务再跑一遍,对比成功率。

对 PR Review Agent,一个最小可用的 Eval 集:从过去三个月挑 20 个真实 PR,每个标注「是不是重要」「摘要应该怎么写」。每次改完 Agent 就跑一遍这 20 个,看它挑对了几个、写对了几个。

没有 Eval 集,你对 Agent 好不好的判断永远停留在「我感觉这次变好了」的玄学阶段。LangChain 在 《Improving Deep Agents with harness engineering》 中分享过一个实例:他们基于 Eval 集做 trace 回放和迭代,把 Terminal Bench 成绩从 52.8 推到 66.5 分,直接从榜单 30 名开外冲到前 5。

量:Trace + 日志 + 指标

有了尺子还不够,你还得看到 Agent 每一次的真实足迹 —— 每一步做了什么决策、调了哪个工具、拿到什么返回、花了多少 token。这就是 LangSmith、Langfuse 这类 trace 系统存在的意义。能看到 trace,调试才能从「猜」变成「看」。

第六层,约束与恢复

在真实环境里,失败不是例外,是常态。

PR Review Agent 真跑起来之后,一定会遇到:GitHub API 限流、某个 PR 的 diff 太大把上下文冲爆、Slack webhook 过期、Agent 误判一个无关 PR 然后疯狂读代码把 token 耗光……

这一层要做三件事:

  1. 约束:定义「什么事不能做」。比如一次最多分析 20 个 PR、不能对已 closed 的 PR 评论、token 用量超 10 万就停下。这些约束最好硬编码到代码或 linter 规则里,而不是写在提示词里靠 Agent 自己遵守
  2. 校验:每一步输出前后做自动检查。摘要出来后先跑格式校验(是不是 Markdown?几个段落都在?),发 Slack 前检查频道名是不是在白名单里
  3. 恢复:每种典型失败都有明确的恢复路径。GitHub 限流 → 等一会重试;Slack 发送失败 → 落到本地队列下次重试;token 快耗光 → 立即停下保存进度,下一轮继续

这三件事加起来,才能让 Agent 从「能跑」升级到「能在生产环境跑」。


六层讲完,你会发现 Mitchell Hashimoto 说的「每次 Agent 犯错,把修复沉到环境里」—— 那个修复沉到哪?就是这六层的其中某一层:

  • Agent 总是漏掉某个上下文信息 → 改第一层
  • 总是用错工具 → 改第二层
  • 步骤乱 → 改第三层
  • 跨天记不住进度 → 改第四层
  • 没法判断做得好不好 → 搭第五层
  • 一失败就崩溃 → 强化第六层

Harness 的难点

概念清晰是一回事,落地是另一回事。下面是大厂在真实项目里踩过的几个典型坑。

难题一:Agent 跑久了越走越偏

现象:一开始 Agent 表现挺好,目标清晰步骤明确。但跑着跑着开始「忘」—— 忘了最初的目标,忘了之前的决定,开始重复劳动。

更诡异的是,Cognition(做 Devin 那家公司)在 《Rebuilding Devin for Claude Sonnet 4.5: Lessons and Challenges》 中观察到一个现象叫「上下文焦虑」(Context Anxiety):模型自己好像也能感觉到「快撑不住了」。当它觉得上下文窗口快用完时,不仅开始丢细节,还会急匆匆想收尾 —— 突然简化方案、跳过验证、宣布「任务完成」。而且模型对自己还剩多少上下文的估计非常不准,经常以为快没空间了,其实还剩一大半。

很多团队第一反应是「上下文压缩」:把前面的历史压成摘要腾空间。但 Anthropic 发现光压缩不够 —— 那种「已经累了」的负担感模型还是带着。

真正解这个结的做法叫 Context Reset:直接把旧的上下文窗口整个丢掉,换一个干净的接手。状态全部外化到文件系统,每一轮开始时代理面对一个完全干净的上下文窗口,靠读取文件系统里的「交接文档」来恢复进度。

这特别像工程里遇到内存泄漏的时候:你不拼命优化内存,你直接重启进程,把状态从磁盘恢复出来。

原则:重启胜过修补,状态沉到文件里。

难题二:自己给自己打分,总是偏乐观

很多人让 Agent 干完活再让它自己评估「做得怎么样」。结果呢?Agent 永远觉得自己干得不错。特别是在没有标准答案的任务上,比如「设计一个 UI」「写一篇文案」「评估代码可读性」,自评偏差会特别明显。

Anthropic 在 《Harness design for long-running application development》 中搞出了一个三角分工:

  • Planner(规划者):把模糊需求扩展成完整规格说明
  • Generator(生成者):一步一步去实现
  • Evaluator(验收者):像 QA 一样真实地测试

关键是 Evaluator 不能只看代码,必须真的去操作页面、看交互、检查实际运行结果。只要三个角色足够独立,就能形成真正的闭环:规划 → 生成 → 验收 → 修复 → 再验收。

原则:生产和验收必须分离,验收方必须能摸到真实世界。

难题三:Agent 反复失败,工程师该干啥

当 Agent 反复失败时,一般人的本能反应是:要么调提示词,要么换更强的模型。

但 OpenAI 在 Codex 项目里发现,这两招其实都是错的方向。

Codex 的人类工程师几乎不写代码,全部由 Agent 来写。人类工程师真正在干的三件事:

  1. 把产品目标拆解成 Agent 能力边界内的小任务
  2. 当 Agent 反复失败时,不去催它「再努力一点」,而是看它「环境里缺了什么能力」,然后把那个能力补进去
  3. 建立反馈链路,让 Agent 能看到自己工作的结果

以前遇到 Agent 写代码有 bug,传统做法是加一句提示词「请仔细检查代码不要有 bug」,然后祈祷。而 Codex 的做法是:给 Agent 接上 lint、单测、运行环境,让它写完自己跑,看见 bug 自己改。

原则:Agent 反复失败的时候,别问模型能不能更努力,要问环境还缺什么。

难题四:规范文件越写越长,Agent 反而更糊涂

OpenAI 早期做 Codex 时,搞了一个特别大的 AGENTS.md,把所有规范、约定、最佳实践全塞进去。他们当时的想法是:规则写得越全越好,Agent 就越不会出错。

结果 Agent 更糊涂了。因为上下文窗口是稀缺资源,文件作为系统提示词每次都注入,变得越长,模型注意力越被稀释。

后来他们把 AGENTS.md 从「百科全书」改成了「目录页」:主文件控制在 100 行左右,不告诉 Agent 每条细节,只告诉它「想看什么,去哪儿看」。详细内容拆到子文档里,Agent 平时只看目录,只有真需要时才钻进对应子文档。

这就是渐进式披露在规则文件上的具体应用。

原则:规则文件宁缺毋滥,给模型看的东西少即是多。

难题五:Agent 写的代码越堆越烂

当 Agent 负责写绝大多数代码后,会发生一件很自然但糟糕的事:Agent 会疯狂模仿仓库里已有的代码模式。好的模式被复制,坏的模式也被复制。一旦早期某段代码写歪了,Agent 会把那个歪的写法当成「惯例」,越堆越歪。

OpenAI 团队叫这个现象 AI slop —— AI 代码泔水。

他们一开始用最朴素的办法:每周五让人类工程师手工打扫。结果失败了 —— Agent 产出代码太快,人类清理速度根本跟不上。

后来改成了 Harness 的思路:

  1. 把人类工程师关于「什么是好代码」的经验写成 Golden Principles(黄金原则),沉进仓库。比如「优先用共享工具包而不是手写 helper」「不要瞎猜数据格式,必须校验边界或用带类型的 SDK」
  2. 让一批后台 Agent 按固定节奏自动跑,定期扫描仓库,对比 Golden Principles,找出偏离的地方,自动开修复 PR。大部分修复 PR 可以在 1 分钟内被人类审完,直接 auto merge

原则:技术债不是攒一堆集中还,而是每天让后台 Agent 自动偿还一点。

一个反直觉的发现:Agent 用「老技术」反而更稳

很多人觉得 AI 是最前沿的东西,自然该配上最前沿的技术栈。但 OpenAI 在实践中发现,Agent 对那些被人类称为「boring」的老技术反而掌握得最好。

原因很朴素:老技术组合性好、API 稳定、训练数据里出现得多。一个出现了十几年的老库,相关的代码示例、StackOverflow 问答、博客文章多如牛毛,AI 对它的各种用法了如指掌。而昨天才出的新框架,AI 只看过零星几篇文档,很容易张冠李戴。

如果你要做一个让 Agent 跑得稳的项目,选技术栈的时候别一味追新。越老、文档越齐全、在开源社区沉淀越久的技术,Agent 越容易帮你做对。

总结

AI 工程的重心,过去两年换了三次:

  • Prompt Engineering 解决「怎么把任务讲清楚」—— 塑造模型的概率空间,让它「听懂」你想干啥
  • Context Engineering 解决「怎么把信息送对」—— 动态管理上下文,让它「知道」该用什么信息
  • Harness Engineering 解决「怎么让它持续做对」—— 设计一整套包裹模型的运行环境,让它「做对」一连串的事

三者不是替代关系,是层层包含:Prompt 是 Context 的一部分,Context 是 Harness 的一部分。

到了今天,AI 落地的核心挑战正在从「让模型看起来更聪明」转向「让模型在真实世界里稳定地工作」。模型决定了 Agent 的天花板,但 Harness 决定了它能不能落地、能不能稳定交付。

如果你最近在做 Agent 相关的事情,我特别建议:别再把所有精力花在调模型、调提示词上了。回头看看你的 Harness —— 有没有规则文件、有没有校验闭环、有没有任务编排、有没有评估机制、有没有失败恢复。这些东西,每一项都能让你的 Agent 上一个台阶。

最后用那个等式收尾:Agent = Model + Harness。你的 Agent 想变得更好,要么换更强的模型,要么写更好的 Harness。在模型迭代速度逐渐放缓的今天,Harness 这部分提升空间,可能比你想象的大得多。