适用于长期运行智能体的有效控制装置

分类技术博客
作者Anthropic
来源跳转
发表时间

内容

随着人工智能(AI)代理变得更加强大,开发人员越来越多地要求它们执行需要跨小时甚至数天工作的复杂任务。然而,如何让代理在多个上下文窗口中保持一致的进展仍然是一个未解决的问题。

长时间运行的代理的核心挑战在于,它们必须在离散的会话中工作,每个新会话开始时都没有之前发生过什么的记忆。想象一个软件项目,由工程师轮流工作,每个新工程师到来时都没有之前轮班发生过什么的记忆。由于上下文窗口是有限的,大多数复杂项目无法在单个窗口中完成,代理需要一种方法来弥合会话之间的差距。

我们开发了一种两部分的解决方案,以使 Claude Agent SDK 能够在多个上下文窗口中有效地工作:一个 初始化代理 ,它在第一次运行时设置环境,和一个 编码代理 ,它的任务是每个会话中都取得增量进展,同时为下一个会话留下清晰的文物。你可以在附件 快速入门 中找到代码示例。

长时间运行的代理问题

Claude Agent SDK 是一个强大、通用的代理框架,擅长编码以及其他需要模型使用工具来收集上下文、规划和执行的任务。它具有上下文管理能力,例如压缩,这使得代理可以在不耗尽上下文窗口的情况下处理任务。理论上,假设这种设置,代理应该能够在任意长的时间内继续做有用的事情。

然而,压缩是不够的。即使是像 Opus 4.5 这样的最先进的编码模型在 Claude Agent SDK 上运行多个上下文窗口中,也会在仅给出高级提示(如“构建 claude.ai 的克隆版”)的情况下,无法构建出生产质量的 Web 应用程序。

Claude 的失败表现出两种模式。首先,代理倾向于尝试一次性完成应用程序——基本上是尝试一次性完成应用程序。通常,这会导致模型在实现过程中耗尽上下文,导致下一个会话从半实现且未记录的功能开始。代理将不得不猜测发生了什么,并花费大量时间尝试使基本应用程序再次工作。这即使使用压缩也会发生,因为压缩并不总是传递完美清晰的指令给下一个代理。

第二种失败模式通常会在项目后期发生。在一些功能已经构建之后,后续的代理实例会查看进度,认为工作已经完成。

这将问题分解为两个部分。首先,我们需要设置一个初始环境,该环境为给定提示所需的所有功能奠定基础,这样代理就可以一步一步地、功能功能地工作。其次,我们应该提示每个代理在实现目标的同时取得增量进展,并在会话结束时保持环境的干净状态。所谓“干净状态”,我们指的是适合合并到主分支的代码:没有主要的错误,代码有序且文档齐全,开发人员可以轻松地开始新功能的工作,而无需首先清理无关的混乱。

在内部实验中,我们使用两部分解决方案解决了这些问题:

  • 初始化代理:第一个代理会话使用一个专门的提示,要求模型设置初始环境:一个 init.sh 脚本,一个 claude-progress.txt 文件记录代理所做的事情,以及一个初始 git 提交,显示添加了哪些文件。
  • 编码代理:每个后续会话都会要求模型取得增量进展,然后留下结构化的更新。

这里的关键见解是找到一种方法,使代理能够快速理解工作状态,当开始新鲜的上下文窗口时,这是通过 claude-progress.txt 文件和 git 历史记录来实现的。这些实践的灵感来自于了解有效的软件工程师每天做的事情。

环境管理

在更新的 Claude 4 提示指南 中,我们分享了一些多上下文窗口工作流的最佳实践,包括使用“第一个上下文窗口的不同提示”的框架结构。这种“不同提示”要求初始化代理设置环境,包括所有必要的上下文,以便未来编码代理能够有效地工作。这里,我们对此类环境的关键组件进行了更深入的探讨。

功能列表

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

{
  "category": "functional",
  "description": "新聊天按钮创建一个新对话",
  "steps": [
    "导航到主界面",
    "点击“新聊天”按钮",
    "验证新对话已创建",
    "检查聊天区域显示欢迎状态",
    "验证对话出现在侧边栏中"
  ],
  "passes": false
}

我们提示编码代理仅通过更改“passes”字段的状态来编辑此文件,并使用强烈的指示,如“删除或编辑测试是不允许的,因为这可能会导致功能缺失或有错误。”经过一些实验,我们决定使用 JSON,因为模型不太可能不恰当地更改或覆盖 JSON 文件,相比之下,Markdown 文件则更容易被更改或覆盖。

增量进展

给定这种初始环境的框架,编码代理的下一个迭代被要求一次只处理一个功能。这种增量方法被证明是解决代理一次性完成应用程序的倾向的关键。

一旦开始增量工作,仍然很重要的是模型在代码更改后保持环境的干净状态。在我们的实验中,我们发现引出这种行为的最佳方法是要求模型将其进展提交到 git 中,并使用描述性的提交消息,并在进展文件中写入其进展的摘要。这使得模型能够使用 git 来恢复工作代码库的状态并恢复工作状态。

这些方法还提高了效率,因为它们消除了代理需要猜测发生了什么并花费时间使基本应用程序再次工作的需要。

测试

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

在构建 Web 应用程序的情况下,Claude 在被明确提示使用浏览器自动化工具并像人类用户一样进行所有测试后,大多数情况下都能很好地验证功能是否从头到尾工作。

Claude 通过 Puppeteer MCP 服务器测试 claude.ai 克隆版时拍摄的截图。

为 Claude 提供这些测试工具大大提高了性能,因为代理能够识别和修复仅凭代码无法明显发现的错误。

仍然存在一些问题,例如 Claude 的视觉和浏览器自动化工具的局限性,使得代理难以识别每种类型的错误。例如,Claude 不能通过 Puppeteer MCP 查看浏览器原生的警报模态框,因此依赖这些模态框的功能往往更容易出错。

加速

有了上述所有内容,每个编码代理都会被提示运行一系列步骤来获得方向,其中一些步骤非常基本但仍然有帮助:

  • 运行 pwd 以查看您正在工作的目录。您只能编辑此目录中的文件。
  • 读取 git 日志和进展文件以了解最近的工作内容。
  • 读取功能列表文件并选择最高优先级且尚未完成的功能来处理。

这种方法可以在每个会话中为 Claude 节省一些令牌,因为它不需要猜测如何测试代码。它还可以帮助初始化代理编写一个 init.sh 脚本来运行开发服务器,然后在实现新功能之前运行基本的端到端测试。

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

在 claude.ai 克隆版的例子中,这意味着代理始终启动本地开发服务器,并使用 Puppeteer MCP 启动新聊天、发送消息和接收响应。这确保 Claude 可以快速确定应用程序是否处于破坏状态,并立即修复任何现有的 bug。如果代理相反地开始实现新功能,它可能会使问题变得更糟。

鉴于所有这些,典型的会话从以下助手消息开始:

[助手] 我将首先了解项目的当前状态。 [工具使用] <bash - pwd>[工具使用] <read - claude-progress.txt>[工具使用] <read - feature_list.json>[助手]让我检查 git 日志以查看最近的工作。 [工具使用] <bash - git log --oneline -20>[助手] 现在让我检查是否有 init.sh 脚本来重启服务器。 <启动开发服务器>[助手] 太好了!现在让我导航到应用程序并验证一些基本功能是否仍然有效。 <测试基本功能>[助手] 根据我的验证测试,我可以看到基本功能工作得很好。核心聊天功能、主题切换、对话加载和错误处理都正常工作。现在让我更全面地审查 tests.json 文件,以了解下一步需要实现什么。 <开始工作于新功能>

代理故障模式和解决方案

问题初始化代理行为编码代理行为
Claude 过早宣布整个项目的胜利。根据输入规范,设置一个包含端到端功能描述列表的结构化 JSON 文件。在会话开始时读取功能列表文件。选择一个功能开始工作。
Claude 将环境留在带有 bug 或未记录进度的状态中。编写一个初始 git 存储库和进度笔记文件。在会话开始时读取进度笔记文件和 git 提交日志,并在开发服务器上运行基本测试以捕获任何未记录的 bug。通过编写 git 提交和进度更新来结束会话。
Claude 过早地将功能标记为已完成。设置功能列表文件。自我验证所有功能。只有在仔细测试后才将功能标记为“通过”。
Claude 必须花时间弄清楚如何运行应用程序。编写一个可以运行开发服务器的 init.sh 脚本。在会话开始时读取 init.sh。

总结了四种常见的故障模式和解决方案,用于长期运行的 AI 代理。

未来工作

这项研究展示了一套可能的解决方案,用于长期代理的马甲中,以使模型在许多上下文窗口中实现增量进展。然而,仍然存在一些开放性问题。

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

此外,这个演示是针对全栈 Web 应用程序开发进行优化的。未来的方向是将这些发现推广到其他领域。可能其中一些或所有这些经验可以应用于科学研究或金融建模等领域所需的长期代理任务。

致谢

由 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 代码团队。有兴趣的候选人可以在 anthropic.com/careers申请。

脚注

  1. 我们在此上下文中将这些称为单独的代理,只是因为它们具有不同的初始用户提示。系统提示、工具集和整体代理马甲在其他方面是相同的。

评论

(0)
未配置登录方式
暂无评论