Harness原意是马具,从马具控制马匹,引申为一个控制和管理框架,用于封装、引导和监督AI代理的行为。后文为了保持原意,作为专有名词不翻译了。
随着 AI agents 变得更加强大,开发者越来越多地要求它们承担需要跨越数小时甚至数天工作的复杂任务。然而,让 agents 在多个上下文窗口中保持一致的进展仍然是一个悬而未决的问题。
长时间运行 agents 的核心挑战在于,它们必须在离散的会话中工作,而每个新会话开始时对之前发生的事情没有任何记忆。想象一个由轮班工程师组成的软件项目,每个新来的工程师对上一个班次发生的事情毫无记忆。由于上下文窗口有限,且大多数复杂项目无法在单个窗口内完成,agents 需要一种方法来弥合编码会话之间的差距。
我们开发了一个双重解决方案,使 Claude Agent SDK 能够在许多上下文窗口中有效工作:一个initializer agent (初始化 agent)在第一次运行时设置环境,以及一个coding agent(编码 agent)在每个会话中取得增量进展,同时为下一个会话留下清晰的工件。您可以在附带的快速入门指南中找到代码示例。
长时间运行 agent 的问题
Claude Agent SDK 是一个强大、通用的 agent harness,不仅擅长编码,还擅长其他需要模型使用工具来收集上下文、规划和执行的任务。它具备上下文管理功能,如压缩(compaction),这使得 agent 能够在不耗尽上下文窗口的情况下继续工作。理论上,在这种设置下,agent 应该能够持续进行有用的工作,时间可长达任意程度。
然而,仅靠压缩是不够的。开箱即用的情况下,即使是像 Opus 4.5 这样的前沿编码模型,在 Claude Agent SDK 上跨多个上下文窗口循环运行时,如果只给它一个高级提示,如"构建一个 claude.ai 的克隆版",也无法构建出生产质量的网络应用程序。
Claude 的失败表现为两种模式。首先,agent 倾向于一次性做太多事情------本质上是试图一举完成整个应用程序。这往往导致模型在实现过程中耗尽上下文,留给下一个会话一个功能半实现且未记录的混乱状态。然后,agent 必须猜测发生了什么,并花费大量时间试图让基本应用程序重新运行。即使有压缩功能,这种情况依然会发生,因为压缩并不总是能向下一个 agent 传递完全清晰的指令。
第二种失败模式通常在项目后期出现。在已经构建了一些功能后,后续的 agent 实例会环顾四周,看到已经取得了进展,就草率地宣布任务完成。
这把问题分解为两个部分。首先,我们需要设置一个初始环境,为给定提示所需的所有功能奠定基础,这为 agent 逐步、逐个功能地工作做好了准备。其次,我们应该提示每个 agent 朝着其目标取得增量进展,同时在会话结束时将环境保持在干净状态。所谓"干净状态",我们指的是适合合并到主分支的代码:没有重大错误,代码有序且文档完善,总体上,开发者可以轻松开始新功能的工作,而无需先清理无关的遗留问题。
在内部实验时,我们使用两部分解决方案解决了这些问题:
- Initializer agent :第一个 agent 会话使用专门的提示,要求模型设置初始环境:一个
init.sh脚本、一个记录 agents 所做工作的 claude-progress.txt 文件,以及一个显示添加了什么文件的初始 git 提交。 - Coding agent:每个后续会话要求模型取得增量进展,然后留下结构化更新。
这里的关键洞察在于找到一种方法,让 agents 在从头开始的上下文窗口中能够快速理解工作状态,这通过 claude-progress.txt 文件与 git 历史记录配合实现。这些实践的灵感来源于了解优秀软件工程师的日常工作习惯。
环境管理
在更新的 Claude 4 提示指南中,我们分享了一些多上下文窗口工作流程的最佳实践,包括一个使用"对第一个上下文窗口使用不同提示"的 harness 结构。这个"不同提示"要求 initializer agent 设置环境,包含未来 coding agents 有效工作所需的所有必要上下文。在这里,我们深入探讨这种环境的一些关键组件。
功能列表
为了解决 agent 试图一次性完成应用程序或过早认为项目完成的问题,我们提示 initializer agent 编写一个全面的功能需求文件,扩展用户的初始提示。在 claude.ai 克隆示例中,这意味着超过 200 个功能,如"用户可以打开新聊天、输入查询、按回车键并看到 AI 响应"。这些功能最初都标记为"失败",以便后续的 coding agents 能够清楚地了解完整功能应该是什么样子。
json
{
"category": "functional",
"description": "New chat button creates a fresh conversation",
"steps": [
"Navigate to main interface",
"Click the 'New Chat' button",
"Verify a new conversation is created",
"Check that chat area shows welcome state",
"Verify conversation appears in sidebar"
],
"passes": false
}
我们提示 coding agents 仅通过更改 passes 字段的状态来编辑此文件,并使用措辞强烈的指令,如"删除或编辑测试是不可接受的,因为这可能导致功能缺失或错误"。经过一些实验,我们选择使用 JSON 格式,因为与 Markdown 文件相比,模型不太可能不当更改或覆盖 JSON 文件。
增量进展
基于这种初始环境脚手架,coding agent 的后续迭代被要求一次只处理一个功能。这种增量方法被证明是解决 agent 倾向于一次性做太多问题的关键。
一旦开始增量工作,模型在进行代码更改后仍需要将环境保持在干净状态,这一点仍然至关重要。在我们的实验中,我们发现引发这种行为的最有效方式是要求模型将其进展以描述性的提交消息提交到 git,并在进度文件中写下工作摘要。这使得模型能够使用 git 来恢复错误的代码更改并恢复代码库的工作状态。
这些方法还提高了效率,因为它们消除了 agent 猜测发生了什么并花费时间试图让基本应用程序重新运行的需要。
测试
我们观察到的最后一个主要失败模式是 Claude 在没有适当测试的情况下就将功能标记为完成的倾向。在没有明确提示的情况下,Claude 倾向于进行代码更改,甚至通过单元测试或针对开发服务器的 curl 命令进行测试,但未能识别功能端到端不工作的问题。
在构建网络应用程序的情况下,一旦明确提示使用浏览器自动化工具并像人类用户一样进行所有测试,Claude 在端到端验证功能方面表现良好。

Claude 通过 Puppeteer MCP 服务器测试 claude.ai 克隆时拍摄的截图。
为 Claude 提供这些类型的测试工具显著提高了性能,因为 agent 能够识别并修复从代码本身看不出的错误。
仍然存在一些问题,比如 Claude 视觉能力和浏览器自动化工具的局限性,使其难以识别每种类型的错误。例如,Claude 无法通过 Puppeteer MCP 看到浏览器原生的警告模态框,依赖这些模态框的功能因此往往存在更多错误。
快速了解情况
有了以上所有设置,每个 coding agent 都被提示运行一系列步骤来了解情况,有些步骤相当基本但仍然有帮助:
- 运行
pwd查看您正在工作的目录。您只能编辑此目录中的文件。 - 阅读 git 日志和进度文件,以了解最近的工作进展。
- 阅读功能列表文件,选择尚未完成的最高优先级功能来处理。
这种方法为 Claude 在每个会话中节省了一些令牌,因为它不必重新摸索如何测试代码。还有助于要求 initializer agent 编写一个可以运行开发服务器的 init.sh 脚本,然后在实现新功能之前运行基本的端到端测试。
在 claude.ai 克隆的情况下,这意味着 agent 总是启动本地开发服务器并使用 Puppeteer MCP 开始新聊天、发送消息并接收响应。这确保了 Claude 能够快速识别应用程序是否处于损坏状态,并立即修复任何现有错误。如果 agent 反而开始实现新功能,很可能会使问题恶化。
考虑到所有这些,典型的会话以以下助手消息开始:
csharp
[Assistant] 我将首先了解情况并理解项目的当前状态。
[Tool Use] <bash - pwd>
[Tool Use] <read - claude-progress.txt>
[Tool Use] <read - feature_list.json>
[Assistant] 让我检查 git 日志以查看最近的工作。
[Tool Use] <bash - git log --oneline -20>
[Assistant] 现在让我检查是否有 init.sh 脚本来重启服务器。
<Starts the development server>
[Assistant] 太好了!现在让我导航到应用程序并验证一些基本功能是否仍在工作。
<Tests basic functionality>
[Assistant] 基于我的验证测试,我可以看到基本功能运行良好。核心聊天功能、主题切换、会话加载和错误处理都正常工作。现在让我更全面地查看 tests.json 文件,以了解接下来需要实现什么。
<Starts work on a new feature>
Agent 失败模式和解决方案
| 问题 | Initializer Agent 行为 | Coding Agent 行为 |
|---|---|---|
| Claude 过早宣布整个项目胜利。 | 设置功能列表文件:基于输入规范,建立一个包含端到端功能描述列表的结构化 JSON 文件。 | 在会话开始时阅读功能列表文件。选择单个功能开始处理。 |
| Claude 将环境留在有错误或未记录进展的状态。 | 编写一个初始 git 仓库和进度笔记文件。 | 在会话开始时阅读进度笔记文件和 git 提交日志,并在开发服务器上运行基本测试以捕获任何未记录的错误。在会话结束时编写 git 提交和进度更新。 |
| Claude 过早将功能标记为完成。 | 设置功能列表文件。 | 自我验证所有功能。只有在仔细测试后才将功能标记为"通过"。 |
| Claude 必须花时间弄清楚如何运行应用程序。 | 编写一个可以运行开发服务器的 init.sh 脚本。 |
在会话开始时阅读 init.sh。 |
总结了长时间运行 AI agents 中四种常见失败模式和解决方案。
未来工作
这项研究展示了在长时间运行 agent harness 中一套可能的解决方案,使模型能够在许多上下文窗口中取得增量进展。然而,仍然存在未解决的问题。
最值得注意的是,目前仍不清楚单一的通用 coding agent 是否在所有上下文中表现最佳,或者是否可以通过多 agent 架构获得更好的性能。看起来合理的想法是,专门的 agents,如测试 agent、质量保证 agent 或代码清理 agent,可以在软件开发生命周期的子任务上做得更好。
此外,这个演示针对全栈网络应用程序开发进行了优化。未来的一个方向是将这些发现推广到其他领域。这些教训中的一些或全部很可能可以应用于,例如,科学研究或金融建模中需要的长时间运行 agent 任务类型。
致谢
由 Justin Young 撰写。特别感谢 David Hershey、Prithvi Rajasakeran、Jeremy Hadfield、Naia Bouscal、Michael Tingley、Jesse Mu、Jake Eaton、Marius Buleandara、Maggie Vo、Pedram Navid、Nadine Yasser 和 Alex Notov 的贡献。
这项工作反映了 Anthropic 多个团队的集体努力,这些团队使 Claude 能够安全地进行长时程自主软件工程,特别是代码 RL 和 Claude Code 团队。欢迎有兴趣做出贡献的候选人申请 anthropic.com/careers。
脚注
- 我们在此上下文中称它们为独立的 agents,仅仅是因为它们有不同的初始用户提示。系统提示、工具集和整体 agent harness 在其他方面是相同的。