跳到正文
Anthropic Engineering·· 2025-11-26精选AI 评分67

Anthropic 分享长时运行智能体的有效 harness 设计

Effective harnesses for long-running agents

AI 导读

Anthropic 工程团队介绍让 Claude Agent SDK 跨多个上下文窗口持续工作的两段式方案:首个会话由 initializer agent 搭建环境,包括 init.sh 脚本、claude-progress.txt 进度日志和初始 git commit,后续每个 coding agent 会话只推进单个功能并留下结构化更新。

推荐理由

Anthropic 公开了长时运行智能体的两段式 harness 设计与四类失败模式,可迁移到多上下文窗口的工程实践。

正文 · AI 翻译

随着 AI 智能体能力越来越强,开发者越来越多地要求它们承担需要跨越数小时甚至数天工作的复杂任务。然而,让智能体在多个上下文窗口之间保持一致的进展仍然是一个未解决的问题。

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

我们开发了一个双管齐下的解决方案,使 Claude Agent SDK 能够在多个上下文窗口之间高效工作:一个初始化智能体,在首次运行时搭建环境;以及一个编码智能体,其任务是在每个会话中取得增量进展,同时为下一个会话留下清晰的产物。你可以在随附的快速入门中找到代码示例。

长时间运行的智能体问题

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

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

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

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

这将问题分解为两部分。首先,我们需要搭建一个初始环境,为给定提示所要求的所有功能奠定基础,使智能体能够一步一步、一个功能一个功能地工作。其次,我们应该提示每个智能体朝着目标取得增量进展,同时在会话结束时让环境保持干净状态。所谓“干净状态”,我们指的是适合合并到主分支的那种代码:没有重大 bug,代码有序且文档完善,总体而言,开发者可以轻松开始开发新功能,而无需先清理无关的烂摊子。

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

  1. 初始化 agent:第一个 agent 会话使用一个专门的提示词,要求模型设置初始环境:一个 init.sh 脚本、一个记录 agent 已完成工作的 claude-progress.txt 文件,以及一个显示新增了哪些文件的初始 git 提交。
  2. 编码 agent:之后的每个会话都要求模型进行增量式推进,然后留下结构化的更新。1

这里的关键洞见是找到一种方法,让 agent 在从全新的上下文窗口开始时能快速理解工作状态,这通过 claude-progress.txt 文件配合 git 历史来实现。这些做法的灵感来自了解高效的软件工程师每天在做什么。

环境管理

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

功能列表

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

{
    "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
  }

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

增量式推进

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

在实现增量式推进之后,模型在做出代码更改后仍必须让环境保持干净状态。在我们的实验中,我们发现引发这种行为的最佳方式是要求模型用描述性的提交信息将其进度提交到 git,并在进度文件中写下进度摘要。这使模型能够使用 git 回退错误的代码更改,并恢复代码库的可工作状态。

这些方法还提高了效率,因为它们消除了 agent 不得不猜测发生了什么、并花时间试图让基本应用重新运行的需要。

测试

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

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

 Screenshots taken by Claude through the Puppeteer MCP server as it tested the claude.ai clone.
Claude 在测试 claude.ai 克隆版时通过 Puppeteer MCP 服务器截取的屏幕截图。

为 Claude 提供这类测试工具显著提升了性能,因为该智能体能够识别并修复仅凭代码无法发现的 bug。

仍存在一些问题,例如 Claude 的视觉能力和浏览器自动化工具的限制,使其难以识别所有类型的 bug。例如,Claude 无法通过 Puppeteer MCP 看到浏览器原生的警告模态框,因此依赖这些模态框的功能往往更容易出现 bug。

快速上手

在完成上述所有设置后,每个编码智能体都会被提示执行一系列步骤来熟悉情况,其中一些相当基础但仍然有帮助:

  1. 运行 pwd 查看你正在工作的目录。你只能编辑此目录中的文件。
  2. 阅读 git 日志和进度文件,了解最近的工作内容。
  3. 阅读功能列表文件,选择尚未完成的最高优先级功能来处理。

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

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

鉴于以上所有情况,典型的会话以以下助手消息开始:

[Assistant] I'll start by getting my bearings and understanding the current state of the project.
[Tool Use] <bash - pwd>
[Tool Use] <read - claude-progress.txt>
[Tool Use] <read - feature_list.json>
[Assistant] Let me check the git log to see recent work.
[Tool Use] <bash - git log --oneline -20>
[Assistant] Now let me check if there's an init.sh script to restart the servers.
<Starts the development server>
[Assistant] Excellent! Now let me navigate to the application and verify that some fundamental features are still working.
<Tests basic functionality>
[Assistant] Based on my verification testing, I can see that the fundamental functionality is working well. The core chat features, theme switching, conversation loading, and error handling are all functioning correctly. Now let me review the tests.json file more comprehensively to understand what needs to be implemented next.
<Starts work on a new feature>

智能体失败模式与解决方案

问题初始化智能体行为编码智能体行为
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 能够安全地执行长周期自主软件工程任务,尤其是代码强化学习(code RL)与 Claude Code 团队。有兴趣做出贡献的候选人欢迎在 anthropic.com/careers 申请。

脚注

1. 在此语境下我们之所以称它们为独立的智能体,仅仅是因为它们有不同的初始用户提示。除此之外,系统提示、工具集以及整体智能体框架都是相同的。

来源:Anthropic Engineering · anthropic.com