搞懂Harness你的AI代理才算真正开始干活
摘要
作者:@rohit4verse 你并非因为没找到正确的模型而用错了 AI 。 你用错...
作者:@rohit4verse
你并非因为没找到正确的模型而用错了 AI 。
你用错 AI ,是因为你没有构建正确的环境。
有些团队用三名工程师交付了百万行代码,另一些团队却连 agent 流水线里一次稳定的重构都难以实现 —— 原因不在于 GPT-5 还是 Claude Opus 的差别,不在于 temperature 设置或 max tokens ,甚至不在于提示词,尽管大家为提示词争论耗费了无数岁月。
差别在于 harness 。
本文要讲的,就是这个词在技术层面和观念层面究竟意味着什么 —— 因为整个行业已经养成了一个坏习惯:把这个词用得漫不经心。
harness 不是系统提示词,不是对 API 调用的封装,不是评估框架、提示词模板,也不是带记忆功能的聊天机器人。
harness 是语言模型运作其中的完整设计环境 ,包括它可以调用的工具、它所接收信息的格式、它的历史记录如何被压缩和管理、在错误级联之前捕获它的护栏,以及让它能够将工作移交给未来的自己而不失去连贯性的脚手架。
当你审视 Anthropic 为使 Claude Code 真正奏效而构建的东西, OpenAI 为通过 Codex 以零手写代码交付百万行代码而构建的东西,以及普林斯顿 NLP 组在其关于 agent- 计算机接口的里程碑论文 SWE-agent 中所发表的内容,你会开始看到一个相同的模式从每一个认真工作于这一领域的团队中浮现出来。
模型几乎无关紧要。 harness 就是一切。
这是一份关于这一理念如何成为 2025 至 2026 年应用 AI 工程核心洞察的详细技术分析。它涵盖了相关研究、真实的实现案例、驱动设计决策的失败模式,以及无论你在构建编程 agent 、研究 agent 还是长期运行的自主软件工程师时都会反复出现的那些模式。
读完之后,你不仅会理解 harness 是什么,更会明白为何正确地构建一个 harness ,已经成为当今行业中最有价值的工程技能。
第一部分:无人谈论的问题
为什么原始能力还不够
2024 年中, AI 基准测试领域发生了一件奇怪的事。研究人员开始注意到,同一个前沿模型在完全相同的编程任务上,会因任务呈现方式和可用工具的不同而产生截然不同的结果。模型没有变,底层智能没有变,变的只是接口。
这本不应令人惊讶。几十年来我们早已知晓,合适的工具能让工程师的效率大幅提升。一名拥有现代 IDE 、调试器、版本控制和 CI/CD 流水线的软件开发者,其效能数量级地高于同一个人只用文本编辑器在纯终端中工作。 IDE 并未让开发者变得更聪明,它减少了摩擦,在恰当的时机浮现信息,尽早捕获错误,并将工作组织成可导航的单元。
语言模型亦然。它们不是从某个无限内部知识库进行推理的通用推理机,而是在上下文窗口中对 token 进行操作的精密模式匹配引擎。它们在某一时刻所知道的一切,由上下文窗口中的内容决定;它们产出的一切,以该上下文的结构为条件。输入的格式不是装饰,它是 agent 的认知架构。
接口不是便利层。对于语言模型 agent 而言,接口就是心智本身。
这是普林斯顿 NLP 组 2024 年发表的 SWE-agent 论文的核心主张,经得起推敲。该论文引入了 Agent- 计算机接口( ACI )的概念,并证明一个精心设计的 ACI 与同一模型通过标准 Linux shell 交互相比,可在基准测试性能上产生 64% 的相对提升 。
相同的模型,相同的任务,相同的计算预算,唯一的变量是接口。
让这个数字沉淀片刻。 64% 不是边际收益,这是有效工具与无效工具之间的差距。而它完全来自环境设计,而非来自底层模型的任何改进。
上下文窗口不是内存插槽
对 AI agent 的朴素认知模型,将上下文窗口视为内存。你加载数据,模型处理,你得到输出。上下文越多等于性能越好,提示词越长等于理解越深。这个心智模型是错误的,而且错误的方式会毁掉你以此为基础构建的 agent 。
上下文窗口实际上更接近于 agent 在特定会话中的整个工作意识。窗口中的每个 token 都有计算成本。每一条无关信息都在与相关信息争夺注意力。模型没有能够干净地忽略噪声的选择性注意机制 —— 噪声在场,就会影响推理。
这对 agent 设计有具体的、可测量的后果。当你在 agent 循环内部对大型代码库运行 grep 并返回一万行匹配结果时,你并没有给 agent 更多可以利用的信息,你是在用无关数据淹没它的工作记忆,这将降低后续每一个步骤的质量,直到上下文被清空。
当你因为 agent 想看两个函数就用 cat 把整个文件倾倒出来时,你在它需要一杯水的时候递给了它一根消防水管。
SWE-agent 的研究者详细记录了这些失败模式。标准 bash 接口会导致 agent 陷入反复横跳:它们会发出返回数千行结果的 grep 命令,迷失于自己在寻找什么,再发出更多 grep 命令,逐渐用噪声填满上下文,最终要么给出错误答案,要么彻底停止进展。
问题不在于模型智能,而在于接口没有任何机制来保护 agent 免于被自身淹没。
ACI 的解决方案是构建一个返回有上限、经过汇总的结果列表的搜索工具。如果你的搜索返回超过 50 条匹配,工具会抑制输出并告知 agent 缩小查询范围。
这个设计决策回头看来几乎简单得令人发笑,却是整篇论文中杠杆效益最高的改变之一。它将一个上下文淹没的失败模式,转化为一个自然的精炼循环。
第二部分: SWE-Agent 论文与 ACI 的诞生
Agent- 计算机接口究竟是什么
ACI 在 SWE-agent 论文中被定义为位于语言模型 agent 与计算机环境之间的一个抽象层。与人机接口( HCI )的类比是有意为之的。正如 HCI 研究追问如何设计契合人类认知架构的接口, ACI 研究追问如何设计契合语言模型认知架构的接口。
人类认知架构涉及视觉模式识别、空间记忆、跨屏幕的并行注意力,以及略读和选择性聚焦的能力。
语言模型的认知架构根本不同 —— 它涉及顺序 token 处理、对上下文顺序和格式的敏感性、有限的工作记忆,以及倾向于锚定在提示词中最突出信息的特性。设计一个好的 ACI ,意味着理解这些约束并围绕它们构建,而非与之对抗。
SWE-agent 面向编程任务的 ACI 有四个主要组件,每一个都反映了对语言模型在获得原始计算机访问权限时如何失败的特定洞察。
搜索与导航
搜索组件用专门构建的工具取代了标准的 grep 和 find 命令: find_file 、 search_file 和 search_dir 。关键差异不在于语法,而在于输出管理。结果上限为 50 条。
如果查询超出该限制,工具会返回一条说明结果过多的消息,并提示 agent 精炼其搜索。这听起来微不足道,实际上却是论文中最具影响力的决策之一。
它之所以重要,是因为 agent 与认知负荷下的人类一样,在感到不确定时往往会延续既有行为。当一个人在大型代码库中迷失时,会越搜越广,产生越来越多的噪声。带上限的搜索工具通过创造一个强制函数打断了这个模式:你无法靠模糊来推进,你必须精确。这推动 agent 朝着更审慎、更有针对性的行为方向发展。
文件查看器
文件查看器是论文对认知架构洞察最为具体的体现。研究人员测试了多种查看器配置,发现每次显示 100 行是一个恰到好处的数字。行数更少(他们测试了 30 行)会导致 agent 失去对周边代码的上下文,从而出现编辑错误;行数更多(或显示完整文件)会导致 agent 迷失方向,遗漏重要细节。
查看器是有状态的,在多次交互之间保持文件中的位置。更重要的是,它在每一个可见行前面加上了明确的行号。这最后一个细节看似纯属表面,实则不然。
当 agent 需要发出一条针对第 47 至 52 行的编辑命令时,它需要能够直接从视图中读取这些数字,而不是数行数或做算术。将这个认知任务从 agent 的工作记忆中移除,为真正的问题求解释放了认知容量。
带 Linting 的文件编辑器
文件编辑器的核心创新是带护栏的即时反馈。 edit 命令接受起始行、结束行和替换文本作为单次操作。每次编辑后,工具会自动对修改后的文件运行 linter 并报告结果。如果编辑引入了语法错误,该编辑在应用之前就会被拒绝, agent 会收到一条清晰的错误消息,同时显示原始代码和失败的编辑内容。
这封闭了在朴素 agent 实现中导致级联失败的反馈回路。没有 linter 时, agent 可能引入一个语法错误,运行测试套件,看到一个看似不相关的失败(因为真正的错误在别处),花费多个步骤追逐错误的问题,最终耗尽上下文窗口追踪一个幽灵。
将 linter 直接集成到编辑器中,语法错误就能在引入的那一刻被捕获,修复被本地化,问题无法传播。
对比使用原始 bash 工具时的情况:使用 sed 或输出重定向,没有集成反馈,编辑静默执行,多行修改需要极易出错的复杂参数格式化。 Agent 可能成功运行了命令,却引入了 linter 本可捕获的细微格式错误,然后花接下来的十个步骤纳闷测试为何失败。
上下文管理
第四个组件解决了一个在长会话中不断累积的问题:过时上下文的堆积。随着 agent 推进任务,其历史记录充满了旧的观察、中间状态以及不再反映当前环境状态的探索步骤。
所有这些历史记录占用上下文窗口的空间,并可能通过提供过时信息而主动误导 agent 。
ACI 的上下文管理系统将较早的观察记录 —— 最后五轮之前的内容 —— 压缩为单行摘要。这使活跃上下文聚焦于近期的相关信息,同时保留了整体轨迹的压缩记录。
Agent 随时能看到自己最近做了什么、当前状态是什么,而不会被自己曾经运行过的每一条命令的完整未压缩历史所淹没。
基准测试结果及其真正含义
SWE-agent 论文在 SWE-bench 数据集上对其 ACI 进行了基准测试,该数据集收录了来自热门 Python 代码库的真实 GitHub Issue 。任务是接收一份自然语言的 bug 报告,并产出一个解决它的代码补丁。这是一项困难的实际任务,需要在陌生的代码库中导航、理解错误消息、编写正确的代码并验证修复效果。
使用 GPT-4 配合标准 bash shell 接口,系统解决了 3.97% 的问题;使用 GPT-4 配合专门构建的 ACI ,系统解决了 12.47% 的问题。这就是前文提到的 64% 相对提升,完全来自接口设计。
研究人员还进行了消融研究,每次移除一个组件,以隔离每项设计决策的贡献。 Linter 集成始终是杠杆效益最高的组件之一;带上限的搜索对于防止上下文淹没至关重要;带行号的有状态文件查看器显著优于原始 cat 命令和更简单的查看器设计。
性能差异与模型智能无关,而与认知负荷管理有关。 ACI 减少了模型追踪状态所需的工作量,为真正重要的工作腾出了空间。
这些启示远远超出了编程 agent 的范畴。任何长期 agent 任务都涉及相同的根本挑战:在大型信息空间中导航、在多个步骤中保持连贯的状态、捕获错误并从中恢复,以及管理上下文窗口注意力这一有限资源。 ACI 的设计原则是可泛化的,具体工具会变,底层问题的架构不会变。
第三部分: Anthropic 的 Harness 工程(长期运行的 Agent 问题)
为什么上下文窗口边界是核心难题
SWE-agent 论文解决的是如何为单个 agent 会话设计接口的问题。 Anthropic 的工程团队在开发 Claude Agent SDK 和 Claude Code 的过程中,遭遇了一个不同的问题:如果一项任务过于庞大,无法在单个上下文窗口内完成,该怎么办?
这不是小众的边缘情况。大多数真实的软件项目都太大,无法装进任何上下文窗口。一个生产级 Web 应用包含数百个文件、数千个函数、测试套件、配置、文档和依赖项。
即便拥有 20 万 token 的上下文窗口,你也无法同时将完整项目装进脑海。人类工程师通过外部记忆、文档、版本控制,以及数周乃至数月在代码库中积累的理解来解决这个问题。而一个开启全新会话的 agent ,什么都没有。
朴素的解决方案是压缩( compaction ),它在一定程度上有效。 Claude Agent SDK 内置了压缩能力,能够在窗口填满时对旧上下文进行摘要。但仅靠压缩是不够的。
Anthropic 的内部实验表明,即使有压缩机制,像 Opus 4.5 这样的前沿编程模型在跨多个上下文窗口循环运行时,也会持续失败,无法仅凭高层提示词构建出生产级质量的 Web 应用。
失败呈现出两种模式,两者都颇具启发意义。
第一种失败模式是 试图一次性完成太多 。当给定 " 构建一个 claude.ai 克隆 " 这样的提示词时, agent 会尝试一次性完成整个应用。
它会不断实现一个又一个功能,但每一个都未完成或测试,便在实现过程中耗尽上下文窗口,留给下一个会话的是一个半成品应用 —— 没有已完成内容的文档,也没有关于代码当前状态的任何明确说明。
下一个 agent 实例会将其大部分上下文预算消耗在理解这团乱麻上,而不是推进工作。
第二种失败模式出现在项目后期。在某些功能已经构建完毕之后,后续的 agent 实例会环顾四周,看到已经取得了进展,便得出 " 工作已完成 " 的结论。它会在应用只完成了一半的情况下宣告胜利并停止工作。
这不是愚蠢,而是从不完整信息中做出的合理推断。 Agent 没有结构化的方式来了解这个项目的 " 完成 " 究竟意味着什么。
两种失败都有一个共同的根源: agent 没有持久的、结构化的项目状态理解,这种理解无法在上下文窗口边界处存活下来,也无法为后续会话提供方向。
双 Agent 架构:初始化 Agent 与编程 Agent
Anthropic 的解决方案是一套两部分架构,此后已成为认真对待长期 agentic 工作的团队所采用的模板。
第一部分是 初始化 Agent 。 这是一个具有专属系统提示词的特化初始会话,其全部目的是搭建好所有后续编程 agent 将在其中运作的环境。它不编写功能,它创建使得跨多个后续会话的功能开发成为可能的脚手架。
初始化 Agent 产出三个关键输出。
首先 ,它创建一个能够可靠启动开发环境的 init.sh 脚本。这听起来平淡无奇,但具有显著的杠杆效益。
后续每一个编程 agent 会话都可以通过运行 init.sh 来开始,而不必花费 token 去摸索如何启动服务器、配置数据库、将应用调整到可测试状态。在每次会话中节省的这部分开销,积累起来不容忽视。
其次 ,初始化 Agent 创建一个全面的 功能列表文件 。在 Anthropic 内部运行的 claude.ai 克隆实验中,这意味着超过 200 条具体的端到端功能描述,例如 " 用户可以打开一个新对话,输入查询,按下回车,并看到 AI 的回复 " 。每个功能最初都被标记为失败。
这个文件是项目的基准事实。一个开启新会话的编程 agent 读取这个文件,就能立刻确定无疑地知道哪些东西已经构建完成、哪些尚未完成。它无法环顾四周,看到一些代码,就得出工作已完成的结论 —— 功能列表会告诉它真相。
第三 ,初始化 Agent 创建一个 claude-progress.txt 文件并进行初始的 git 提交。进度文件是一个人类可读的日志, agent 在每次会话结束时更新它,记录自己处理了什么、完成了什么、以及将事情留在了什么状态。结合 git 历史记录,每一个后续编程 agent 都能快速定向,而无需将上下文预算耗费在考古工作上。
第二部分是 编程 Agent 。 初始化之后的每次会话使用不同的提示词:一次只处理一个功能,将环境留在干净的状态,并在会话结束前更新进度文件和 git 历史。增量推进,记录状态,干净交接。
功能列表作为认知锚点
功能列表值得特别关注,因为它解决了一个容易被低估的问题。没有它,在复杂代码库中运作的 agent 必须从代码本身推断项目的完成度。这种推断是不可靠的。
代码可能存在但并不可用;功能可能存在但并不完整。一个阅读代码并推断完成情况的 agent ,会频繁得出错误答案,严重到足以构成问题。
功能列表使完成度变得明确无歧义。每个功能都有一个 passes 字段,值为 true 或 false 。 Agent 要么在端到端验证某功能可用后更新这个字段,要么不更新。没有歧义,不需要推断,基准事实就在文件里。
Anthropic 有意将这个列表存储为 JSON 而非 Markdown 。原因是行为层面的。从经验来看,模型对 JSON 文件的不当修改或覆写概率,低于对 Markdown 文件的概率。
JSON 具有抵制随意编辑的刚性结构。这是一个细节,但后果真实存在:你希望功能列表是 agent 谨慎更新的东西,而不是它心血来潮随意改写的东西。
{"category": "functional",
"description": " 新建对话按钮创建一个全新的会话 ",
"steps": [" 导航到主界面 ",
" 点击 " 新建对话 " 按钮 ",
" 验证新会话已创建 ",
" 检查对话区域显示欢迎状态 ",
" 验证对话出现在侧边栏中 "],
"passes": false}
伴随此格式的指令是明确的:删除或编辑测试是不可接受的,因为这可能导致功能缺失或存在缺陷。你提示模型将这个文件视为不可侵犯的。 JSON 结构在架构层面强化了这一指令。
增量推进与干净状态要求
多会话 agentic 工作中最困难的问题之一,是确保每次会话结束时都处于一个下一次会话可以安全地在其上构建的状态。
若无明确的执行机制, agent 倾向于将工作留在上下文窗口填满时碰巧所处的状态:半成品功能、破损的测试、未记录的变更。下一个 agent 继承的是这团乱麻。
Anthropic 的解决方案是将干净状态作为一等要求,而非可有可无的附加项。每次编程 agent 会话以一次 git 提交(附有描述性信息)、对进度文件的更新以及在必要时回退到可工作状态为结尾。
他们所说的 " 干净状态 " ,意味着代码适合合并到主分支:没有重大缺陷、有良好文档、处于一个开发者可以合理地开始新功能而无需先解开他人半成品工作的状态。
git 提交不只是一个检查点,它是一个恢复机制。当 agent 做出破坏某些东西的变更时,它可以用 git 回退到最后已知的良好状态并重试。这正是人类工程师的工作方式,事实证明对 agent 同样是正确的纪律。版本控制是认知脚手架,而不仅仅是源代码管理。
测试:无人愿意谈论的失败模式
Anthropic 记录了一种几乎在每个认真的 agentic 编程项目中都会出现的失败模式: agent 在没有端到端验证的情况下将功能标记为完成 。
Agent 会做出代码变更,对开发服务器运行单元测试或 curl 命令,看到通过结果,然后将功能标记为已完成。但当以用户方式通过浏览器测试时,该功能实际上并不可用。
单元测试成功与端到端功能可用之间的差距,是人类工程师通过切换上下文来跨越的 —— 运行应用并尝试使用它。一个没有明确浏览器测试能力的 agent 无法完成这种切换。
它只能观察其工具所允许观察的内容,如果这些工具不包括浏览器自动化,它就会持续遗漏一类只在真实用户流程中显现的缺陷。
解决方案是为 agent 提供访问 Puppeteer MCP 服务器的能力 —— 一个浏览器自动化工具,允许 Claude 实际导航应用、点击按钮、填写表单,并验证功能端到端可用。
性能提升是显著的。仅从代码中不可见的缺陷,当 agent 能够看到用户所见的内容时,变得一目了然。
这是一个通用原则的具体例证: agent 工作的质量受限于其反馈回路的质量 。如果你的 agent 无法在真正重要的领域中观察到其行为的后果,它就会对代理指标进行优化,而这些指标可能与实际正确性并不相关。
启动流程:快速进入状态
Anthropic harness 中每次编程 agent 会话都以一套标准化的启动流程开始,其目的是在不浪费 token 的前提下尽快为 agent 定向。该流程如下:
运行 pwd 确认工作目录;读取进度文件和 git log 以了解近期工作;读取功能列表并选择优先级最高的未完成功能;运行 init.sh 脚本启动开发环境;运行基本的端到端测试以验证应用处于可工作状态。
只有完成所有这些步骤之后, agent 才会开始处理新功能。如果启动测试显示应用已损坏, agent 会在触碰任何新内容之前先修复已有问题。这防止了一种复合问题: agent 在破损的基础上开始新功能,使底层问题更难被隔离和修复。
启动流程还以一种具体的方式节省了 token :因为 init.sh 脚本精确记录了如何启动开发环境, agent 不需要从头摸索。每次会话在环境配置上节省的 token ,在一个漫长项目中积累起来相当可观。
[ 助手 ] 我将首先确认方向,了解项目的当前状态。
[ 工具调用 ] <bash - pwd>
[ 工具调用 ] <read - claude-progress.txt>
[ 工具调用 ] <read - feature_list.json>
[ 助手 ] 让我检查 git log 以查看近期工作。
[
评论 (0)
暂无评论