智能体在跨多个上下文窗口工作时仍面临挑战。我们从人类工程师身上汲取灵感,为长时间运行的智能体打造更有效的控制框架。
随着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 项功能,例如“用户可以打开一个新对话,输入查询内容,按下回车键,然后看到 AI 的回复”。这些功能最初都被标记为“未通过”,以便后续的编码智能体能够清晰地了解完整功能的全貌。
{
"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
}
我们提示编码智能体只能通过更改“通过”字段的状态来编辑此文件,并使用了措辞强硬的指令,例如“删除或编辑测试是不可接受的,因为这可能导致功能缺失或出现错误”。经过一些实验后,我们最终决定为此使用 JSON 格式,因为与 Markdown 文件相比,模型不太可能不适当地更改或覆盖 JSON 文件。
渐进式推进
在完成初始环境搭建后,我们要求下一轮迭代的编码智能体每次只处理一项功能。事实证明,这种渐进式的方法对于解决智能体倾向于一次性做太多事情的问题至关重要。
在逐步推进工作的过程中,模型在修改代码后保持环境整洁仍然至关重要。在我们的实验中,我们发现引导模型实现这一行为的最佳方式是要求它使用描述性的提交信息将进度提交到 git,并在进度文件中记录进展摘要。这使得模型能够利用 git 回滚错误的代码修改,并恢复代码库的正常工作状态。
这些方法还提高了效率,因为它们消除了智能体需要猜测之前发生了什么、并花费时间试图让基础应用重新正常工作的需求。
测试
我们观察到的最后一个主要故障模式是,Claude 倾向于在未进行充分测试的情况下就将某个功能标记为已完成。在没有明确提示词的情况下,Claude 倾向于进行代码修改,甚至使用单元测试或针对开发服务器执行 curl 命令来进行测试,但却未能识别出该功能无法端到端正常工作。
在构建 Web 应用的情况下,一旦被明确提示使用浏览器自动化工具并像人类用户一样进行所有测试,Claude 在端到端验证功能方面表现相当不错。

为 Claude 提供这类测试工具显著提升了性能,因为智能体能够识别并修复那些仅从代码本身不易察觉的错误。
一些问题仍然存在,例如 Claude 视觉能力的局限以及浏览器自动化工具的局限,使得识别所有类型的错误变得困难。例如,Claude 无法通过 Puppeteer MCP 看到浏览器原生的 alert 弹窗,因此依赖这些弹窗的功能往往更容易出错。
快速上手
在完成上述所有设置后,每个编码智能体都会被提示执行一系列步骤来熟悉环境,其中一些步骤虽然基础但仍有帮助:
- 运行 `pwd` 查看你当前的工作目录。你只能编辑此目录下的文件。
- 阅读 git 日志和进度文件,以快速了解最近的工作进展。
- 阅读功能列表文件,选择尚未完成且优先级最高的功能进行开发。
这种方法在每个会话中为 Claude 节省了一些模型 token,因为它无需再自行摸索如何测试代码。同时,它还有助于让初始化智能体编写一个 `init.sh` 脚本,该脚本能够运行开发服务器,并在实现新功能之前先执行一次基本的端到端测试。
以 claude.ai 克隆项目为例,这意味着智能体总是先启动本地开发服务器,然后使用 Puppeteer MCP 开启一个新对话、发送一条消息并接收响应。这确保了 Claude 能够快速判断应用是否处于损坏状态,并立即修复任何现有错误。如果智能体转而直接开始实现新功能,很可能会让问题变得更糟。
综上所述,一个典型的会话通常以以下助手消息开始:
[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 将环境遗留为存在错误或未记录进度的状态。 | 编写初始的 git 仓库和进度记录文件。 | 通过读取进度记录文件和 git 提交日志来启动会话,并对开发服务器运行基本测试以捕获任何未记录的错误。通过编写 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 能够安全地执行长周期自主软件工程任务,尤其是代码强化学习团队和 Claude Code 团队。有兴趣并希望为此做出贡献的候选人,欢迎在 anthropic.com/careers 提交申请。
脚注
1. 在此上下文中,我们之所以称它们为独立的智能体,仅仅是因为它们拥有不同的初始用户提示词。除此之外,系统提示词、工具集和整体智能体框架是完全相同的。
获取开发者通讯
产品更新、操作指南、社区亮点等。每月发送到您的邮箱。
Agents still face challenges working across many context windows. We looked to human engineers for inspiration in creating a more effective harness for long-running agents.
As AI agents become more capable, developers are increasingly asking them to take on complex tasks requiring work that spans hours, or even days. However, getting agents to make consistent progress across multiple context windows remains an open problem.
The core challenge of long-running agents is that they must work in discrete sessions, and each new session begins with no memory of what came before. Imagine a software project staffed by engineers working in shifts, where each new engineer arrives with no memory of what happened on the previous shift. Because context windows are limited, and because most complex projects cannot be completed within a single window, agents need a way to bridge the gap between coding sessions.
We developed a two-fold solution to enable the Claude Agent SDK to work effectively across many context windows: an initializer agent that sets up the environment on the first run, and a coding agent that is tasked with making incremental progress in every session, while leaving clear artifacts for the next session. You can find code examples in the accompanying quickstart.
The long-running agent problem
The Claude Agent SDK is a powerful, general-purpose agent harness adept at coding, as well as other tasks that require the model to use tools to gather context, plan, and execute. It has context management capabilities such as compaction, which enables an agent to work on a task without exhausting the context window. Theoretically, given this setup, it should be possible for an agent to continue to do useful work for an arbitrarily long time.
However, compaction isn’t sufficient. Out of the box, even a frontier coding model like Opus 4.5 running on the Claude Agent SDK in a loop across multiple context windows will fall short of building a production-quality web app if it’s only given a high-level prompt, such as “build a clone of claude.ai.”
Claude’s failures manifested in two patterns. First, the agent tended to try to do too much at once—essentially to attempt to one-shot the app. Often, this led to the model running out of context in the middle of its implementation, leaving the next session to start with a feature half-implemented and undocumented. The agent would then have to guess at what had happened, and spend substantial time trying to get the basic app working again. This happens even with compaction, which doesn’t always pass perfectly clear instructions to the next agent.
A second failure mode would often occur later in a project. After some features had already been built, a later agent instance would look around, see that progress had been made, and declare the job done.
This decomposes the problem into two parts. First, we need to set up an initial environment that lays the foundation for all the features that a given prompt requires, which sets up the agent to work step-by-step and feature-by-feature. Second, we should prompt each agent to make incremental progress towards its goal while also leaving the environment in a clean state at the end of a session. By “clean state” we mean the kind of code that would be appropriate for merging to a main branch: there are no major bugs, the code is orderly and well-documented, and in general, a developer could easily begin work on a new feature without first having to clean up an unrelated mess.
When experimenting internally, we addressed these problems using a two-part solution:
- Initializer agent: The very first agent session uses a specialized prompt that asks the model to set up the initial environment: an
init.shscript, a claude-progress.txt file that keeps a log of what agents have done, and an initial git commit that shows what files were added. - Coding agent: Every subsequent session asks the model to make incremental progress, then leave structured updates.1
The key insight here was finding a way for agents to quickly understand the state of work when starting with a fresh context window, which is accomplished with the claude-progress.txt file alongside the git history. Inspiration for these practices came from knowing what effective software engineers do every day.
Environment management
In the updated Claude 4 prompting guide, we shared some best practices for multi-context window workflows, including a harness structure that uses “a different prompt for the very first context window.” This “different prompt” requests that the initializer agent set up the environment with all the necessary context that future coding agents will need to work effectively. Here, we provide a deeper dive on some of the key components of such an environment.
Feature list
To address the problem of the agent one-shotting an app or prematurely considering the project complete, we prompted the initializer agent to write a comprehensive file of feature requirements expanding on the user’s initial prompt. In the claude.ai clone example, this meant over 200 features, such as “a user can open a new chat, type in a query, press enter, and see an AI response.” These features were all initially marked as “failing” so that later coding agents would have a clear outline of what full functionality looked like.
{
"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
}
We prompt coding agents to edit this file only by changing the status of a passes field, and we use strongly-worded instructions like “It is unacceptable to remove or edit tests because this could lead to missing or buggy functionality.” After some experimentation, we landed on using JSON for this, as the model is less likely to inappropriately change or overwrite JSON files compared to Markdown files.
Incremental progress
Given this initial environment scaffolding, the next iteration of the coding agent was then asked to work on only one feature at a time. This incremental approach turned out to be critical to addressing the agent’s tendency to do too much at once.
Once working incrementally, it’s still essential that the model leaves the environment in a clean state after making a code change. In our experiments, we found that the best way to elicit this behavior was to ask the model to commit its progress to git with descriptive commit messages and to write summaries of its progress in a progress file. This allowed the model to use git to revert bad code changes and recover working states of the code base.
These approaches also increased efficiency, as they eliminated the need for an agent to have to guess at what had happened and spend its time trying to get the basic app working again.
Testing
One final major failure mode that we observed was Claude’s tendency to mark a feature as complete without proper testing. Absent explicit prompting, Claude tended to make code changes, and even do testing with unit tests or curl commands against a development server, but would fail recognize that the feature didn’t work end-to-end.
In the case of building a web app, Claude mostly did well at verifying features end-to-end once explicitly prompted to use browser automation tools and do all testing as a human user would.

Providing Claude with these kinds of testing tools dramatically improved performance, as the agent was able to identify and fix bugs that weren’t obvious from the code alone.
Some issues remain, like limitations to Claude’s vision and to browser automation tools making it difficult to identify every kind of bug. For example, Claude can’t see browser-native alert modals through the Puppeteer MCP, and features relying on these modals tended to be buggier as a result.
Getting up to speed
With all of the above in place, every coding agent is prompted to run through a series of steps to get its bearings, some quite basic but still helpful:
- Run
pwdto see the directory you’re working in. You’ll only be able to edit files in this directory. - Read the git logs and progress files to get up to speed on what was recently worked on.
- Read the features list file and choose the highest-priority feature that’s not yet done to work on.
This approach saves Claude some tokens in every session since it doesn’t have to figure out how to test the code. It also helps to ask the initializer agent to write an init.sh script that can run the development server, and then run through a basic end-to-end test before implementing a new feature.
In the case of the claude.ai clone, this meant that the agent always started the local development server and used the Puppeteer MCP to start a new chat, send a message, and receive a response. This ensured that Claude could quickly identify if the app had been left in a broken state, and immediately fix any existing bugs. If the agent had instead started implementing a new feature, it would likely make the problem worse.
Given all this, a typical session starts off with the following assistant messages:
[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> Agent failure modes and solutions
| Problem | Initializer Agent Behavior | Coding Agent Behavior |
|---|---|---|
| Claude declares victory on the entire project too early. | Set up a feature list file: based on the input spec, set up a structured JSON file with a list of end-to-end feature descriptions. | Read the feature list file at the beginning of a session. Choose a single feature to start working on. |
| Claude leaves the environment in a state with bugs or undocumented progress. | An initial git repo and progress notes file is written. | Start the session by reading the progress notes file and git commit logs, and run a basic test on the development server to catch any undocumented bugs. End the session by writing a git commit and progress update. |
| Claude marks features as done prematurely. | Set up a feature list file. | Self-verify all features. Only mark features as “passing” after careful testing. |
| Claude has to spend time figuring out how to run the app. | Write an init.sh script that can run the development server. | Start the session by reading init.sh. |
Summarizing four common failure modes and solutions in long-running AI agents.
Future work
This research demonstrates one possible set of solutions in a long-running agent harness to enable the model to make incremental progress across many context windows. However, there remain open questions.
Most notably, it’s still unclear whether a single, general-purpose coding agent performs best across contexts, or if better performance can be achieved through a multi-agent architecture. It seems reasonable that specialized agents like a testing agent, a quality assurance agent, or a code cleanup agent, could do an even better job at sub-tasks across the software development lifecycle.
Additionally, this demo is optimized for full-stack web app development. A future direction is to generalize these findings to other fields. It’s likely that some or all of these lessons can be applied to the types of long-running agentic tasks required in, for example, scientific research or financial modeling.
Acknowledgements
Written by Justin Young. Special thanks to David Hershey, Prithvi Rajasakeran, Jeremy Hadfield, Naia Bouscal, Michael Tingley, Jesse Mu, Jake Eaton, Marius Buleandara, Maggie Vo, Pedram Navid, Nadine Yasser, and Alex Notov for their contributions.
This work reflects the collective efforts of several teams across Anthropic who made it possible for Claude to safely do long-horizon autonomous software engineering, especially the code RL & Claude Code teams. Interested candidates who would like to contribute are welcome to apply at anthropic.com/careers.
Footnotes
1. We refer to these as separate agents in this context only because they have different initial user prompts. The system prompt, set of tools, and overall agent harness was otherwise identical.
Get the developer newsletter
Product updates, how-tos, community spotlights, and more. Delivered monthly to your inbox.