Anthropic · 工程博客

长运行agent的有效约束

Effective harnesses for long-running agents

二〇二六年七月三十日 · 英文原文

Anthropic 为 Claude Agent SDK 开发了双 agent 方案(初始化 agent 与编码 agent),以解决 LLM agent 在多上下文窗口(context window)中长时间运行时的两大失败模式:一次性完成整个应用导致上下文耗尽,以及过早宣布任务完成。方案通过生成包含 200+ 功能的 JSON 需求文件、要求 agent 一次只处理一个功能、使用 git 提交与 `claude-progress.txt` 文件保持环境干净状态,并提示 agent 通过浏览器自动化工具进行端到端测试,使 agent 能在多个会话中取得增量进展。实验表明,该方法显著提升了 agent 在跨上下文窗口构建生产质量 Web 应用时的表现,但视觉能力限制与浏览器自动化工具局限性等问题仍有待解决。作者 Justin Young 及 Anthropic 代码 RL 与 Claude Code 团队参与了此项工作。

随着AI agent的能力不断增强,开发者越来越多地要求它们承担需要数小时甚至数天才能完成的复杂任务。然而,如何让agent在多个上下文窗口(context window)中保持一致的进展,仍然是一个未解决的问题。

长时间运行agent的核心挑战在于,它们必须在离散的会话中工作,而每个新会话开始时都没有之前会话的记忆。想象一个由轮班工程师组成的软件项目,每位新来的工程师对上一班发生的事情毫无记忆。由于上下文窗口有限,且大多数复杂项目无法在单个窗口内完成,agent需要一种方法来弥合编码会话之间的鸿沟。

我们开发了一个双重解决方案,使Claude Agent SDK能够在多个上下文窗口中有效工作:一个初始化agent(initializer agent),负责在首次运行时设置环境;以及一个编码agent(coding agent),负责在每个会话中取得增量进展,同时为下一个会话留下清晰的工件(artifact)。你可以在随附的快速入门指南中找到代码示例。

Claude Agent SDK是一个强大、通用的agent框架,擅长编码以及其他需要模型使用工具来收集上下文、规划和执行的任务。它具有上下文管理功能,例如压缩(compaction),使agent能够在不会耗尽上下文窗口的情况下处理任务。理论上,有了这样的设置,agent应该能够无限期地持续进行有用的工作。

然而,仅靠压缩是不够的。开箱即用,即使是像Opus 4.5这样的前沿编码模型,在Claude Agent SDK上跨多个上下文窗口循环运行,如果只给一个高级提示(prompt),例如“构建一个claude.ai的克隆”,也无法构建出一个生产质量的Web应用。

Claude的失败表现为两种模式。首先,agent倾向于一次尝试做太多事情——本质上就是试图一次性完成整个应用。这常常导致模型在实现过程中耗尽上下文,使得下一个会话从一个功能半成品且没有文档的状态开始。然后agent不得不猜测发生了什么,并花费大量时间试图让基本应用重新工作。即使有压缩功能,这种情况也会发生,因为压缩并不总能将完全清晰的指令传递给下一个agent。

第二种失败模式通常出现在项目的后期。在构建了一些功能之后,后续的agent实例会环顾四周,看到已经取得了一些进展,然后就宣布任务完成。

这便将问题分解为两个部分。首先,我们需要设置一个初始环境,为给定提示所需的所有功能奠定基础,这能让agent逐步、逐个功能地工作。其次,我们应该提示每个agent朝着目标取得增量进展,同时在会话结束时将环境保持在一个干净的状态。所谓“干净状态”,指的是那种适合合并到主分支(main branch)的代码:没有重大bug,代码有序且文档齐全,总的来说,开发者可以轻松开始一个新功能的工作,而无需先清理无关的混乱。

在内部实验时,我们使用了一个由两部分组成的解决方案来解决这些问题:

这里的关键洞察是找到一种方法,让agent在启动一个新的上下文窗口时能够快速理解工作状态,这是通过claude-progress.txt文件以及git历史记录来实现的。这些实践的灵感来自于了解高效软件工程师每天所做的工作。

在更新的Claude 4提示指南中,我们分享了一些关于多上下文窗口工作流的最佳实践,包括一个使用“针对第一个上下文窗口使用不同提示”的框架结构。这个“不同提示”要求初始化agent设置环境,其中包含未来编码agent有效工作所需的所有必要上下文。在这里,我们将深入探讨此类环境的一些关键组成部分。

为了解决agent一次性完成应用或过早认为项目已完成的问题,我们提示初始化agent编写一个全面的功能需求文件,对用户的初始提示进行扩展。在claude.ai克隆的例子中,这意味着超过200个功能,例如“用户可以打开一个新聊天,输入查询,按回车,并看到AI响应”。这些功能最初都被标记为“失败”,以便后续的编码agent能够清楚地了解完整功能的样子。

我们提示编码agent仅通过更改passes字段的状态来编辑此文件,并使用措辞强硬的指令,例如“删除或修改测试是不可接受的,因为这可能导致功能缺失或出现bug”。经过一些实验,我们决定为此使用JSON,因为与Markdown文件相比,模型不太可能不恰当地更改或覆盖JSON文件。

有了这个初始环境框架,编码agent的下一次迭代被要求一次只处理一个功能。事实证明,这种增量方法对于解决agent一次做太多事情的倾向至关重要。

一旦采用增量工作方式,模型在修改代码后保持环境处于干净状态仍然至关重要。在我们的实验中,我们发现引发这种行为的最佳方式是要求模型将其进展提交到git,并附上描述性的提交信息,并在一个进度文件中总结其进展。这允许模型使用git来回滚错误的代码更改并恢复代码库的工作状态。

这些方法也提高了效率,因为它们消除了agent需要猜测发生了什么并花费时间试图让基本应用重新工作的需求。

我们观察到的最后一个主要失败模式是Claude倾向于在没有适当测试的情况下将功能标记为完成。如果没有明确的提示,Claude倾向于进行代码更改,甚至使用单元测试或针对开发服务器的curl命令进行测试,但会未能识别出该功能无法端到端地工作。

在构建Web应用的情况下,一旦被明确提示使用浏览器自动化工具并像人类用户一样进行所有测试,Claude在端到端验证功能方面表现良好。

为Claude提供这类测试工具极大地提升了性能,因为agent能够识别并修复那些仅从代码中看不出来的bug。

一些问题仍然存在,例如Claude视觉能力的限制以及浏览器自动化工具的局限性,使得识别所有类型的bug变得困难。例如,Claude无法通过Puppeteer MCP看到浏览器原生的alert弹窗,因此依赖这些弹窗的功能往往更容易出现bug。

在实施了上述所有措施后,每个编码agent都会被提示执行一系列步骤来了解情况,其中一些步骤非常基础但仍然有帮助:

这种方法在每个会话中为Claude节省了一些token,因为它不必再弄清楚如何测试代码。它还有助于要求初始化agent编写一个init.sh脚本,该脚本可以运行开发服务器,然后在实现新功能之前运行一个基本的端到端测试。

在claude.ai克隆的例子中,这意味着agent总是启动本地开发服务器,并使用Puppeteer MCP来开始一个新聊天、发送消息并接收响应。这确保了Claude能够快速识别应用是否处于损坏状态,并立即修复任何现有bug。如果agent转而开始实现一个新功能,它很可能会使问题变得更糟。

综上所述,一个典型的会话以如下的助手消息开始:

这项研究展示了在长时间运行的agent框架中,一组可能的解决方案,使模型能够在多个上下文窗口中取得增量进展。然而,仍然存在未解决的问题。

最值得注意的是,目前尚不清楚一个单一的通用编码agent在所有上下文中表现最佳,还是通过多agent架构可以获得更好的性能。似乎合理的推测是,专门的agent,例如测试agent、质量保证agent或代码清理agent,可以在软件开发生命周期的子任务中做得更好。

此外,这个演示是针对全栈Web应用开发优化的。未来的方向是将这些发现推广到其他领域。这些经验中的部分或全部很可能适用于科学研究和金融建模等领域所需的长时间运行的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申请。

译自 Anthropic · 工程博客 · 录于 二〇二六年七月三十日