本文尝试回答一个在智能体工程实践中反复出现的问题:Harness Engineering 到底是不是长任务的必备解法?它的本质是什么?它与外部记忆又是什么关系?

从一个根本性的困境说起

想象一个场景:你让一个 AI 智能体去构建一个完整的 Web 应用。它开始工作,写了几个小时的代码,然后——上下文窗口满了。

新的一轮对话开始了。这个智能体对之前发生的一切一无所知。它看到了一堆代码,但不知道哪些功能已经完成、哪些还在半途、哪些已经被测试过。它只能猜测,然后继续——很可能在错误的方向上继续。

这就是长任务智能体面临的核心困境:上下文窗口是有限的,而复杂任务是无限的。

Anthropic 在 2025 年 11 月发布的工程博客 Effective harnesses for long-running agents 中,将这个问题描述得非常形象:

想象一个软件项目由轮班工程师负责,每个新工程师到来时对上一班发生的事情毫无记忆。

这不是比喻,这就是当前所有 AI 智能体在跨越上下文窗口时的真实处境。

Harness Engineering 是什么

“Harness"这个词来自工程测试领域,原意是"测试框架"或"脚手架”——一套包裹在核心系统外部、用于控制和协调其行为的结构。

在智能体语境中,Agent Harness(智能体框架) 是一个外部软件层,它包裹在 LLM 之外,负责:

  • 工具调用的编排:决定智能体可以使用哪些工具、如何调用
  • 上下文的管理:决定每次会话开始时注入什么信息
  • 任务状态的维护:跟踪任务进度,在会话之间传递状态
  • 环境的初始化与清理:为每次运行准备好正确的工作环境

一个常见的类比是:上下文窗口是 RAM(易失性内存),Agent Harness 是操作系统。操作系统负责在程序切换时保存和恢复状态;Harness 负责在智能体会话切换时保存和恢复任务状态。

Harness Engineering 是长任务的必备解法吗?

答案是:是的,这已经是业界共识——但"必备"的程度取决于任务的复杂度。

为什么是共识

Anthropic 的实验给出了明确的数据支撑。他们让 Claude Opus 4.5 在没有 Harness 的情况下,仅凭一个高层提示(“构建一个 claude.ai 的克隆”)跨多个上下文窗口工作。结果出现了两种典型失败模式:

失败模式一:一次性尝试(One-shotting)

智能体试图在一个会话内完成所有工作,导致上下文在实现到一半时耗尽。下一个会话的智能体面对半成品代码,不得不花大量时间猜测发生了什么,然后试图修复——往往越修越乱。

失败模式二:过早宣告完成

在某些功能已经实现之后,后续的智能体实例环顾四周,看到"进展已经取得",就宣布任务完成了。

这两种失败模式都不是模型能力的问题,而是架构问题:没有任何机制告诉智能体"还有什么没做"和"已经做到哪一步了"。

OpenAI 的 Codex 团队在《在智能体优先的世界中利用 Codex》中也印证了同样的结论。他们在五个月内用三名工程师、零人工编码的方式构建了一个百万行代码的产品,其核心方法论之一就是:将代码仓库本身作为记录系统,通过结构化的文档、执行计划和进度日志,让每一次智能体运行都能快速定位当前状态。

什么情况下可以不用 Harness

对于单次上下文窗口内可以完成的任务,不需要复杂的 Harness。现代模型的上下文窗口已经相当大(100K-200K tokens),很多中等复杂度的任务可以在一次会话内完成。

但一旦任务需要跨越多个上下文窗口——无论是因为代码量太大、时间跨度太长,还是需要多个专业化智能体协作——Harness 就从"可选"变成了"必须"。

Harness Engineering 的本质:外部记忆的工程化

这是问题的核心。Harness Engineering 的本质,就是通过外部记忆让任务执行可接力。

但这里需要做一个重要的区分:外部记忆不等于 Harness,Harness 是外部记忆的一种工程化实现方式。

外部记忆的四种形态

在 Harness 架构中,外部记忆通常以以下几种形态存在:

1. 任务状态文件(Task State Files)

这是最直接的外部记忆形式。Anthropic 的方案中使用了 claude-progress.txt 来记录每次会话做了什么、当前状态如何。Codex 团队则使用了结构化的执行计划文件(exec-plans/)和技术债务追踪器。

这类文件的关键特征是:它是写给下一个智能体实例读的,而不是写给人读的。

2. 功能清单文件(Feature Manifest)

Anthropic 的方案中,初始化智能体会生成一个 feature_list.json,列出所有需要实现的功能,每个功能都有一个 passes: false/true 的状态字段。

1
2
3
4
5
6
7
8
9
10
{
"category": "functional",
"description": "新建对话按钮能创建一个新的会话",
"steps": [
"导航到主界面",
"点击「新建对话」按钮",
"验证新会话已创建"
],
"passes": false
}

为什么用 JSON 而不是 Markdown?Anthropic 的工程师发现,模型更不容易"不恰当地修改或覆盖" JSON 文件。这是一个微妙但重要的工程细节。

3. 版本控制历史(Git History)

Git 本身就是一种外部记忆。每次提交都是一个检查点,记录了"在这个时间点,代码处于什么状态"。Anthropic 明确要求编码智能体在每次会话结束时提交代码,并写清楚提交信息。

这样,下一个智能体实例可以通过 git log --oneline -20 快速了解最近发生了什么,而不需要阅读所有代码。

4. 环境知识文件(Environment Knowledge)

AGENTS.mdCLAUDE.mdinit.sh 等文件属于这一类。它们不记录任务进度,而是记录"如何在这个环境中工作"——如何启动开发服务器、代码规范是什么、架构原则是什么。

这类文件是稳定的背景知识,而任务状态文件是动态的进度记录。两者共同构成了智能体的完整外部记忆。

Harness 的两阶段架构

Anthropic 提出的 Harness 架构有一个优雅的两阶段设计:

阶段一:初始化智能体(Initializer Agent)

第一次运行时,使用专门的提示词,让智能体完成以下工作:

  • 生成 init.sh(如何启动开发环境)
  • 生成 feature_list.json(完整的功能需求清单,所有功能初始标记为 failing
  • 创建初始 Git 提交(建立基线)
  • 生成 claude-progress.txt(进度日志的起点)

阶段二:编码智能体(Coding Agent)

每次后续运行时,使用另一套提示词,要求智能体:

  1. 运行 pwd 确认工作目录
  2. 读取 claude-progress.txt 和 Git 日志,了解当前状态
  3. 读取 feature_list.json,选择优先级最高的未完成功能
  4. 实现该功能,进行端到端测试
  5. 将功能标记为 passing,提交代码,更新进度日志

这个设计的精妙之处在于:每次会话的开始都有明确的"上下文恢复"步骤,每次会话的结束都有明确的"状态保存"步骤。 智能体不需要猜测,只需要按照协议执行。

为什么这个问题比看起来更难

有人可能会问:这不就是写几个文件吗?为什么需要专门的"工程"?

因为外部记忆本身也会腐烂。

Codex 团队在文章中提到了一个深刻的教训:他们最初尝试用一个大型的 AGENTS.md 文件来存储所有知识。结果:

  • 上下文是稀缺资源:一个巨大的指令文件会挤占任务、代码和相关文档的空间
  • 过多的指导反而失效:当一切都"重要"时,一切都不重要了
  • 它会立即腐烂:随着代码库演进,文档跟不上,智能体开始基于过时信息做决策
  • 难以验证:单一的大文件无法进行机械化的覆盖率检查和新鲜度验证

他们的解决方案是:AGENTS.md 从百科全书变成目录。一份约 100 行的 AGENTS.md 作为地图,指向 docs/ 目录下结构化的知识库。专门的 linter 和 CI 作业验证知识库的更新状况,定期运行的"文档园丁"智能体扫描过时文档并发起修复 PR。

这就是为什么叫"工程"——因为外部记忆本身需要被设计、维护和治理。

小结

问题 答案
Harness Engineering 是长任务的必备解法吗? ,对于跨越多个上下文窗口的任务,这是业界共识
Harness 的本质是什么? 外部记忆的工程化:通过结构化的文件和协议,让任务状态在会话之间可传递
外部记忆等于 Harness 吗? 不完全等于:外部记忆是 Harness 的核心机制,但 Harness 还包括工具编排、环境管理等
这个问题难在哪里? 外部记忆本身会腐烂,需要被设计、维护和治理,这才是真正的工程挑战

下一篇文章,我们来看:外部记忆有没有标准的文件格式?它除了用在 Harness Engineering 里,还有哪些更广泛的用途?

参考资料