Get started with Claude Managed Agents by following our docs . A running topic on the Engineering Blog is how to build effective agents and design harnesses for long-running work . A common thread across this work is that harnesses encode assumptions about what Claude can’t do on its own. However, those assumptions need to be frequently questioned because they can go stale as models improve. As just one example, in prior work we found that Claude Sonnet 4.5 would wrap up tasks prematurely as it sensed its context limit approaching—a behavior sometimes called “context anxiety.” We addressed this by adding context resets to the harness. But when we used the same harness on Claude Opus 4.5, we found that the behavior was gone. The resets had become dead weight. We expect harnesses to continue evolving. So we built Managed Agents: a hosted service in the Claude Platform that runs long-horizon agents on your behalf through a small set of interfaces meant to outlast any particular implementation—including the ones we run today. Building Managed Agents meant solving an old problem in computing: how to design a system for “ programs as yet unthought of .” Decades ago, operating systems solved this problem by virtualizing hardware into abstractions— process, file —general enough for programs that didn't exist yet. The abstractions outlasted the hardware. The read() command is agnostic as to whether it’s accessing a disk pack from the 1970s or a modern SSD. The abstractions on top stayed stable while the implementations underneath changed freely. Managed Agents follow the same pattern. We virtualized the components of an agent: a session (the append-only log of everything that happened), a harness (the loop that calls Claude and routes Claude’s tool calls to the relevant infrastructure), and a sandbox (an execution environment where Claude can run code and edit files). This allows the implementation of each to be swapped without disturbing the others. We're opinionated about the shape of these interfaces, not about what runs behind them. Don’t adopt a pet We started by placing all agent components into a single container, which meant the session, agent harness, and sandbox all shared an environment. There were benefits to this approach, including that file edits are direct syscalls, and there were no service boundaries to design. But by coupling everything into one container, we ran into an old infrastructure problem: we’d adopted a pet . In the pets-vs-cattle analogy, a pet is a named, hand-tended individual you can’t afford to lose, while cattle are interchangeable. In our case, the server became that pet; if a container failed, the session was lost. If a container was unresponsive, we had to nurse it back to health. Nursing containers meant debugging unresponsive stuck sessions. Our only window in was the WebSocket event stream, but that couldn’t tell us where failures arose, which meant that a bug in the harness, a packet drop in the event stream, or a container going offline all presented the same. To figure out what went wrong, an engineer had to open a shell inside the container, but because that container often also held user data, that approach essentially meant we lacked the ability to debug. A second issue was that the harness assumed that whatever Claude worked on lived in the container with it. When customers asked us to connect Claude to their virtual private cloud, they had to either peer their network with ours, or run our harness in their own environment. An assumption baked into the harness became a problem when we wanted to connect it to different infrastructure. Decouple the brain from the hands The solution we arrived at was to decouple what we thought of as the “brain” (Claude and its harness) from both the “hands” (sandboxes and tools that perform actions) and the “session” (the log of session events). Each became an interface that made few assumptions about the others, and each could fail or be replaced independently. The harness leaves the container. Decoupling the brain from the hands meant the harness no longer lived inside the container. It called the container the way it called any other tool: execute(name, input) → string . The container became cattle. If the container died, the harness caught the failure as a tool-call error and passed it back to Claude. If Claude decided to retry, a new container could be reinitialized with a standard recipe: provision({resources}) . We no longer had to nurse failed containers back to health. Recovering from harness failure. The harness also became cattle. Because the session log sits outside the harness, nothing in the harness needs to survive a crash. When one fails, a new one can be rebooted with wake(sessionId) , use getSession(id) to get back the event log, and resume from the last event. During the agent loop, the harness writes to the session with emitEvent(id, event) in order to keep a durable record of events. The security boundary. In the coupled design, any untrusted code that Claude generated was run in the same container as credentials—so a prompt injection only had to convince Claude to read its own environment. Once an attacker has those tokens, they can spawn fresh, unrestricted sessions and delegate work to them. Narrow scoping is an obvious mitigation, but this encodes an assumption about what Claude can't do with a limited token—and Claude is getting increasingly smart. The structural fix was to make sure the tokens are never reachable from the sandbox where Claude’s generated code runs. We used two patterns to ensure this. Auth can be bundled with a resource or held in a vault outside the sandbox. For Git, we use each repository’s access token to clone the repo during sandbox initialization and wire it into the local git remote. Git push and pull work from inside the sandbox without the agent ever handling the token itself. For custom tools, we support MCP and store OAuth tokens in a secure vault. Claude calls MCP tools via a dedicated proxy; this proxy takes in a token associated with the session. The proxy can then fetch the corresponding credentials from the vault and make the call to the external service. The harness is never made aware of any credentials. The session is not Claude’s context window Long-horizon tasks often exceed the length of Claude’s context window, and the standard ways to address this all involve irreversible decisions about what to keep. We’ve explored these techniques in prior work on context engineering. For example, compaction lets Claude save a summary of its context window and the memory tool lets Claude write context to files, enabling learning across sessions. This can be paired with context trimming, which selectively removes tokens such as old tool results or thinking blocks. But irreversible decisions to selectively retain or discard context can lead to failures. It is difficult to know which tokens the future turns will need. If messages are transformed by a compaction step, the harness removes compacted messages from Claude’s context window, and these are recoverable only if they are stored. Prior work has explored ways to address this by storing context as an object that lives outside the context window. For example, context can be an object in a REPL that the LLM programmatically accesses by writing code to filter or slice it. In Managed Agents, the session provides this same benefit, serving as a context object that lives outside Claude’s context window. But rather than be stored within the sandbox or REPL, context is durably stored in the session log. The interface, getEvents(), allows the brain to interrogate context by selecting positional slices of the event stream. The interface can be used flexibly, allowing the brain to pick up from wherever it last stopped reading, rewinding a few events before a specific moment to see the lead up, or rereading context before a specific action. Any fetched events can also be transformed in the harness before being passed to Claude’s context window. These transformations can be whatever the harness encodes, including context organization to achieve a high prompt cache hit rate and context engineering. We separated the concerns of recoverable context storage in the session and arbitrary context management in the harness because we can’t predict what specific context engineering will be required in future models. The interfaces push that context management into the harness, and only guarantee that the session is durable and available for interrogation. Many brains, many hands Many brains. Decoupling the brain from the hands solved one of our earliest customer complaints. When teams wanted Claude to work against resources in their own VPC, the only path was to peer their network with ours, because the container holding the harness assumed every resource sat next to it. Once the harness was no longer in the container, that assumption went away. The same change had a performance payoff. When we initially put the brain in a container, it meant that many brains required as many containers. For each brain, no inference could happen until that container was provisioned; every session paid the full container setup cost up front. Every session, even ones that would never touch the sandbox, had to clone the repo, boot the process, fetch pending events from our servers. That dead time is expressed in time-to-first-token (TTFT), which measures how long a session waits between accepting work and producing its first response token. TTFT is the latency the user most acutely feels . Decoupling the brain from the hands means that containers are provisioned by the brain via a tool call (execute(name, input) → string) only if they are needed. So a session that didn't need a container right away didn't wait for one. Inference could start as soon as the orchestration layer pulled pending events from the session log. Using this architecture, our p50 TTFT dropped roughly 60% and p95 dropped over 90%. Scaling to many brains just meant starting many stateless harnesses, and connecting them to hands only if needed. Many hands. We also wanted the ability to connect each brain to many hands. In practice, this means Claude must reason about many execution environments and decide where to send work—a harder cognitive task than operating in a single shell. We started with the brain in a single container because earlier models weren't capable of this. As intelligence scaled, the single container became the limitation instead: when that container failed, we lost state for every hand that the brain was reaching into. Decoupling the brain from the hands makes each hand a tool, execute(name, input) → string : a name and input go in, and a string is returned. That interface supports any custom tool, any MCP server, and our own tools. The harness doesn’t know whether the sandbox is a container, a phone, or a Pokémon emulator. And because no hand is coupled to any brain, brains can pass hands to one another. Conclusion The challenge we faced is an old one: how to design a system for “programs as yet unthought of.” Operating systems have lasted decades by virtualizing the hardware into abstractions general enough for programs that didn't exist yet. With Managed Agents, we aimed to design a system that accommodates future harnesses, sandboxes, or other components around Claude. Managed Agents is a meta-harness in the same spirit, unopinionated about the specific harness that Claude will need in the future. Rather, it is a system with general interfaces that allow many different harnesses. For example, Claude Code is an excellent harness that we use widely across tasks. We’ve also shown that task-specific agent harnesses excel in narrow domains. Managed Agents can accommodate any of these, matching Claude’s intelligence over time. Meta-harness design means being opinionated about the interfaces around Claude: we expect that Claude will need the ability to manipulate state (the session) and perform computation (the sandbox). We also expect that Claude will require the ability to scale to many brains and many hands. We designed the interfaces so that these can be run reliably and securely over long time horizons. But we make no assumptions about the number or location of brains or hands that Claude will need. Acknowledgements Written by Lance Martin, Gabe Cemaj, and Michael Cohen. Thanks to Nodir Turakulov and Jeremy Fox for helpful conversations on these topics. Special thanks to the Agents API team and Jake Eaton for their contributions.
按照我们的 docs 开始使用 Claude Managed Agents。Engineering Blog 上一个持续讨论的话题,是如何构建有效的 agent,以及如何为长时运行的工作设计 harness(编排框架)。这类工作的一个共同点是:harness 会编码一些关于 Claude 单靠自己“做不到什么”的假设。不过,这些假设需要被频繁重新审视,因为随着模型提升,它们会迅速过时。举一个例子:在此前的工作中,我们发现 Claude Sonnet 4.5 会在感觉自己的 context limit(上下文上限)临近时,过早结束任务——这种行为有时被称为“context anxiety(上下文焦虑)”。我们通过在 harness 中加入 context reset(上下文重置)来解决这个问题。但当我们把同一个 harness 用在 Claude Opus 4.5 上时,却发现这种行为已经消失了。这些 reset 反而成了累赘。我们预计 harness 还会继续演化。因此我们构建了 Managed Agents:这是 Claude Platform 中的一项托管服务,通过一组小而稳定的接口,代表你运行 long-horizon agents(长时程 agent);这些接口的设计目标,是比任何具体实现活得更久——包括我们今天正在运行的实现。构建 Managed Agents,意味着要解决计算领域的一个老问题:如何为“programs as yet unthought of(尚未被想到的程序)”设计系统。几十年前,操作系统通过把硬件虚拟化为抽象——process、file——解决了这个问题;这些抽象足够通用,能够服务于那些当时尚不存在的程序。这些抽象比硬件本身更长寿。read() 命令并不关心它访问的是 1970 年代的 disk pack,还是现代 SSD。上层抽象保持稳定,而下层实现可以自由变化。Managed Agents 采用了同样的模式。我们把 agent 的组成部分虚拟化了:session(记录一切已发生内容的 append-only log,追加式日志)、harness(调用 Claude 并将 Claude 的 tool call 路由到相关基础设施的循环)、以及 sandbox(Claude 可在其中运行代码和编辑文件的执行环境)。这样一来,每一部分的实现都可以被替换,而不干扰其他部分。我们对这些接口的形状有明确主张,但并不预设其背后必须运行什么。不要养一只 pet。我们一开始把所有 agent 组件都放进单个 container 中,这意味着 session、agent harness 和 sandbox 共享同一个环境。这种做法有其好处,比如文件编辑就是直接的 syscall,也不需要设计 service boundary(服务边界)。但把一切都耦合进一个 container 后,我们遇到了基础设施里的老问题:我们养出了一只 pet。在 pets-vs-cattle 这个类比中,pet 是一个有名字、需要人工照料、你承受不起失去它的个体;而 cattle 则是可互换的。在我们的场景里,server 就成了那只 pet;如果一个 container 故障,session 就丢了。如果一个 container 没有响应,我们就得想办法把它“救活”。而“照料”这些 container,就意味着要调试那些卡住、无响应的 session。我们唯一的观察窗口是 WebSocket event stream,但它无法告诉我们故障究竟发生在哪里,这就导致 harness 的 bug、event stream 里的丢包,或 container 离线,表面上看起来都一样。为了弄清到底出了什么问题,工程师必须进入 container 打开 shell;但由于那个 container 往往还持有用户数据,这种做法基本等于我们实际上没有调试能力。第二个问题是,harness 默认假设 Claude 所处理的任何东西都与它一起驻留在同一个 container 里。当客户要求我们把 Claude 连接到他们的 virtual private cloud 时,他们要么得把自己的网络与我们的网络打通对等连接,要么就得在他们自己的环境里运行我们的 harness。一个被硬编码进 harness 的假设,在我们想把它连接到不同基础设施时,就变成了问题。将大脑与双手解耦。我们最终采用的方案,是把我们视为“大脑”的部分(Claude 及其 harness),与“手”(执行动作的 sandboxes 和 tools),以及“session”(session event 的日志)分离开来。它们各自都变成了一个接口,对彼此的假设尽可能少,并且都可以独立失败、独立替换。harness 离开了 container。把大脑与双手解耦,意味着 harness 不再住在 container 里面。它像调用任何其他工具一样调用 container:execute(name, input) → string。container 于是变成了 cattle。如果 container 挂掉,harness 会把这个故障捕获为 tool-call error,并反馈给 Claude。如果 Claude 决定重试,就可以用一个标准配方重新初始化一个新的 container:provision({resources})。我们不再需要费力去“救活”失效的 container。恢复 harness 故障。harness 本身也变成了 cattle。因为 session log 位于 harness 外部,所以 harness 里没有任何东西必须在 crash 后幸存。某个 harness 失效时,可以启动一个新的实例,用 wake(sessionId) 唤醒,再通过 getSession(id) 取回 event log,并从最后一个 event 继续恢复。在 agent loop 运行期间,harness 通过 emitEvent(id, event) 向 session 写入内容,从而为事件保留一份 durable(持久化)的记录。安全边界。在这种耦合式设计中,Claude 生成的任何不可信代码,都和 credentials 运行在同一个 container 中——因此一次 prompt injection 只需要说服 Claude 去读取它自己的环境即可。一旦攻击者拿到了这些 token,他们就可以启动新的、不受限制的 session,并把工作委托给它们。缩小权限范围当然是一种明显的缓解措施,但这又编码了一个关于 Claude “拿着受限 token 还做不了什么”的假设——而 Claude 正在变得越来越聪明。结构性的修复办法,是确保这些 token 永远不可被 Claude 生成代码所运行的 sandbox 触及。我们用了两种模式来做到这一点。认证信息可以与资源打包在一起,或者保存在 sandbox 外部的 vault 中。对于 Git,我们在 sandbox 初始化时使用每个 repository 自己的 access token 来 clone repo,并把它接到本地 git remote 上。于是 Git push 和 pull 可以在 sandbox 内部工作,而 agent 本身从不需要接触 token。对于自定义工具,我们支持 MCP,并将 OAuth token 存放在安全的 vault 中。Claude 通过一个专用 proxy 调用 MCP tools;这个 proxy 接收与 session 关联的 token。然后 proxy 可以从 vault 中取出相应的 credentials,并向外部服务发起调用。harness 永远不会接触到任何 credentials。session 不是 Claude 的 context window。长时程任务经常会超出 Claude 的 context window 长度,而处理这个问题的标准做法,都涉及对“保留什么”作出不可逆的决定。我们在此前关于 context engineering 的工作中探索过这些技术。例如,compaction 可以让 Claude 保存其 context window 的摘要;memory tool 可以让 Claude 把上下文写入文件,从而支持跨 session 的学习。它还可以与 context trimming 搭配使用,后者会选择性移除 token,比如旧的 tool result 或 thinking block。但这种对上下文进行选择性保留或丢弃的不可逆决策,可能导致失败。因为很难预知未来的步骤会需要哪些 token。如果消息经过了 compaction 处理,那么 harness 会把被压缩过的消息从 Claude 的 context window 中移除,而它们只有在被存储时才可恢复。此前的工作已经探索过一种应对方式:把 context 存成一个位于 context window 外部的对象。例如,context 可以是 REPL 中的一个对象,由 LLM 通过编写代码来过滤或切片访问它。在 Managed Agents 中,session 提供了同样的好处,充当一个位于 Claude context window 之外的 context object。但它不是存放在 sandbox 或 REPL 中,而是持久存储在 session log 里。getEvents() 这个接口允许大脑通过选择 event stream 的位置切片来审视上下文。这个接口用法很灵活:大脑可以从上次停止读取的地方继续,也可以在某个特定时刻前回退几条 event 以查看前因后果,或者在某个特定动作之前重新读取上下文。任何被取回的 event,也都可以在传给 Claude 的 context window 之前,先在 harness 中做转换。这些转换可以是 harness 所编码的任何策略,包括为了获得更高 prompt cache hit rate 的上下文组织方式,以及各种 context engineering。我们把“可恢复的上下文存储”这一职责放在 session 中,把“任意的上下文管理”这一职责放在 harness 中,因为我们无法预测未来模型需要怎样的具体 context engineering。这些接口把上下文管理的责任推给 harness,而只保证 session 是持久的,并且可以被查询。多颗大脑,多双手。Many brains。把大脑与双手解耦,解决了我们最早遇到的一类客户抱怨。当团队希望 Claude 作用于他们自己 VPC 中的资源时,唯一的路径就是把他们的网络与我们的网络做 peering,因为承载 harness 的 container 默认假设所有资源都在它旁边。一旦 harness 不再位于 container 中,这个假设就消失了。同样的变化还带来了性能收益。当我们最初把大脑放在 container 里时,就意味着有多少颗大脑,就需要多少个 container。对每颗大脑来说,在那个 container provision 完成之前,都无法开始 inference;每个 session 都必须预先付出完整的 container setup 成本。每个 session——即便它最终根本不会触碰 sandbox——也都得 clone repo、启动进程、从我们的服务器抓取待处理 events。这段无效等待会体现在 time-to-first-token(TTFT)里,也就是一个 session 从接受工作到产出第一个 response token 所需的时间。TTFT 是用户感受最明显的延迟。把大脑与双手解耦后,container 只有在确实需要时,才由大脑通过 tool call(execute(name, input) → string)来 provision。所以,一个不需要立即使用 container 的 session,就不必等待 container。只要 orchestration layer 从 session log 中取出待处理 event,inference 就可以立刻开始。使用这种架构后,我们的 p50 TTFT 大约下降了 60%,p95 则下降了 90% 以上。扩展到 many brains,只意味着启动很多无状态的 harness,并且只在需要时把它们连接到 hands。Many hands。我们也希望能够把每颗大脑连接到 many hands。在实践中,这意味着 Claude 必须对多个执行环境进行推理,并决定把工作发往哪里——这比只在单个 shell 中操作,是更困难的认知任务。我们起初把大脑放在单个 container 中,是因为更早期的模型做不到这一点。随着智能水平提升,单个 container 反而成了限制:一旦那个 container 失败,我们就会失去大脑正在伸手操作的每一只 hand 的状态。把大脑与双手解耦,使每一只 hand 都变成一个工具,execute(name, input) → string:输入一个 name 和 input,返回一个 string。这个接口支持任何自定义工具、任何 MCP server,以及我们自己的工具。harness 并不知道 sandbox 究竟是一个 container、一部手机,还是一个 Pokémon emulator。而且,由于任何 hand 都不耦合到某个特定 brain,brains 之间还可以互相传递 hands。结论。我们面对的挑战是一个老问题:如何为“programs as yet unthought of”设计系统。操作系统之所以能延续几十年,是因为它们把硬件虚拟化成了足够通用的抽象,能够服务于那些当时尚不存在的程序。通过 Managed Agents,我们希望设计出一个系统,能够容纳未来围绕 Claude 出现的 harness、sandbox 或其他组件。Managed Agents 本质上是一个 meta-harness,遵循同样的精神:它并不预设 Claude 在未来具体需要哪一种 harness。相反,它提供的是一套通用接口,让许多不同的 harness 都能接入。例如,Claude Code 就是一个非常出色的 harness,我们在许多任务中广泛使用它。我们也已经证明,面向特定任务的 agent harness 在狭窄领域中表现尤为优秀。Managed Agents 能够容纳所有这些形态,并随着时间推移匹配 Claude 的智能水平。meta-harness 设计意味着:我们对 Claude 周围的接口形态有明确主张。我们预计 Claude 需要操纵 state(session),也需要执行 computation(sandbox)。我们也预计 Claude 需要扩展到 many brains 和 many hands。我们设计这些接口,就是为了让这些能力能够在很长的时间尺度上可靠且安全地运行。但我们并不对 Claude 将需要多少 brains 或 hands、它们位于何处,作出任何假设。致谢。本文由 Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。感谢 Nodir Turakulov 和 Jeremy Fox 就这些主题提供的有益交流。特别感谢 Agents API 团队和 Jake Eaton 的贡献。