扩展 Managed Agents:解耦大脑与双手
原文: English original · Anthropic/OpenAI 官方
扩展 Managed Agents:解耦大脑与双手
发布于 2026 年 4 月 8 日
harness 会编码一些假设,而这些假设会随着模型进步而过时。Managed Agents 是我们面向长时间跨度 agent 工作的托管服务,它建立在一组即使 harness 改变也能保持稳定的接口之上。
要开始使用 Claude Managed Agents,请参阅我们的 文档。
Anthropic Engineering Blog 上持续讨论的一个主题,是如何为 long-running work 构建高效 agents,以及如何设计 harnesses。这些工作的共同线索是:harness 会编码一些关于 Claude 不能独立完成什么的假设。不过,这些假设需要经常被重新审视,因为它们会随着模型进步而过时。
举个例子,在早先的工作里,我们发现,Claude Sonnet 4.5 会在察觉 context limit 逼近时过早收尾,这种行为有时被称为“context anxiety”。我们通过在 harness 中加入 context resets 解决了这个问题。但当我们把同一个 harness 用到 Claude Opus 4.5 上时,发现这种行为已经消失了。那些 resets 反而成了负担。
我们预计 harnesses 还会继续演进。因此,我们构建了 Managed Agents:Claude Platform 中的一项托管服务,它代表你运行长时间跨度的 agents,并通过一小组接口与外界交互。这些接口的设计目标,是比任何特定实现都更持久,包括我们今天正在运行的实现。
构建 Managed Agents 意味着要解决计算领域里的一个老问题:如何为“programs as yet unthought of”设计系统。几十年前,操作系统通过把硬件虚拟化为抽象来解决这个问题,比如 process 和 file;这些抽象足够通用,可以适配当时还不存在的程序。抽象比硬件更长寿。read() 命令并不关心它访问的是 1970 年代的磁盘包,还是现代 SSD。上层抽象保持稳定,而底层实现可以自由变化。
Managed Agents 遵循同样的模式。我们把 agent 的组件虚拟化了:session(发生过的一切所形成的只追加日志)、harness(调用 Claude,并把 Claude 的 tool calls 路由到相关基础设施的循环),以及 sandbox(Claude 可以在其中运行代码和编辑文件的执行环境)。这样一来,每个组件的实现都可以替换,而不会扰动其他组件。我们对这些接口的形状有明确主张,但不预设它们背后运行什么。

不要养宠物
一开始,我们把所有 agent 组件都放进一个容器里,这意味着 session、agent harness 和 sandbox 共享同一个环境。这种做法有一些好处,包括文件编辑就是直接的系统调用,而且也不需要设计服务边界。
但把所有东西耦合到一个容器里之后,我们遇到了一个老的基础设施问题:我们养了一只 pet。在 pets-vs-cattle 的比喻里,pet 是一个有名字、需要人工照料、不能轻易失去的个体;cattle 则是可互换的。在我们的场景里,服务器成了那个 pet;如果容器失败,session 就会丢失。如果容器无响应,我们就必须把它慢慢救回来。
照料容器意味着要调试那些无响应、卡住的 sessions。我们唯一能看到的是 WebSocket event stream,但它无法告诉我们故障究竟出在哪里,这意味着 harness 中的 bug、event stream 丢包,或者容器离线,在表面上看起来都一样。为了弄清楚出了什么问题,工程师必须在容器里打开 shell;但因为那个容器里往往也保存着用户数据,这种做法本质上意味着我们缺乏调试能力。
第二个问题是,harness 假设 Claude 正在处理的任何东西都和它一起放在容器里。当客户要求我们把 Claude 连接到他们自己的 virtual private cloud 时,他们要么必须把自己的网络和我们的网络做 peering,要么在他们自己的环境里运行我们的 harness。harness 中内置的假设,在我们想把它连接到不同基础设施时变成了问题。
解耦大脑与双手
我们最终采用的方案,是把我们所说的“大脑”(Claude 及其 harness)从“双手”(执行动作的 sandboxes 和 tools)以及“session”(session events 的日志)中解耦出来。每一部分都变成一个接口,对其他部分只做很少的假设,而且每一部分都可以独立失败或被替换。
harness 离开容器。解耦大脑与双手意味着 harness 不再住在容器里。它调用容器的方式,就像调用任何其他 tool 一样:execute(name, input) → string。容器于是变成了 cattle。如果容器死掉,harness 会把故障作为 tool-call error 捕获,并传回给 Claude。如果 Claude 决定重试,一个新容器可以用标准 recipe 重新初始化:provision({resources})。我们不再需要把失败的容器慢慢救回来。
从 harness 故障中恢复。harness 也变成了 cattle。因为 session log 位于 harness 之外,所以 harness 中没有任何东西需要在崩溃后存活下来。一个 harness 失败后,新的 harness 可以用 wake(sessionId) 重启,用 getSession(id) 取回 event log,并从最后一个 event 继续。在 agent loop 运行期间,harness 会用 emitEvent(id, event) 写入 session,以保留一份持久的事件记录。

安全边界。在耦合式设计里,Claude 生成的任何不受信任代码,都会和凭据运行在同一个容器里,所以 prompt injection 只需要说服 Claude 读取自己的 environment。一旦攻击者拿到这些 tokens,就可以启动新的、没有限制的 sessions,并把工作委派给它们。缩小权限范围显然是一种缓解手段,但这会编码一个关于 Claude 在受限 token 下不能做什么的假设,而 Claude 正在变得越来越聪明。结构性修复方式,是确保这些 tokens 永远无法从运行 Claude 生成代码的 sandbox 中触达。
我们用了两种模式来保证这一点。Auth 可以和资源绑定,也可以保存在 sandbox 外部的 vault 中。对于 Git,我们使用每个 repository 的 access token 在 sandbox 初始化期间 clone repo,并把它接入本地 git remote。Git push 和 pull 可以在 sandbox 内工作,而 agent 从不需要亲自处理 token。对于自定义 tools,我们支持 MCP,并把 OAuth tokens 存储在安全 vault 中。Claude 通过专用 proxy 调用 MCP tools;这个 proxy 接收一个与 session 关联的 token,然后从 vault 中取出对应凭据,并调用外部服务。harness 永远不会知道任何凭据。
session 不是 Claude 的 context window
长时间跨度任务经常超过 Claude context window 的长度,而解决这个问题的标准方法,都涉及对保留什么做出不可逆决定。我们在先前关于 context engineering 的工作中探索过这些技术。例如,compaction 让 Claude 可以保存 context window 的摘要,而 memory tool 让 Claude 可以把 context 写入文件,从而跨 sessions 学习。这可以和 context trimming 配合使用,后者会有选择地移除旧 tool results 或 thinking blocks 等 tokens。
但对 context 进行选择性保留或丢弃的不可逆决定,可能导致失败。我们很难知道未来的 turns 会需要哪些 tokens。如果消息经过 compaction step 转换,harness 会把 compacted messages 从 Claude 的 context window 中移除;只有当它们被存储下来时,才可以恢复。先前的工作也探索过通过把 context 存为一个位于 context window 外部的 object 来解决这个问题。比如,context 可以是 REPL 中的一个 object,由 LLM 通过写代码过滤或切片来程序化访问。

在 Managed Agents 中,session 提供了同样的好处:它充当一个位于 Claude context window 外部的 context object。但 context 不是存储在 sandbox 或 REPL 里,而是持久化存储在 session log 中。getEvents() 这个接口让大脑可以通过选择 event stream 的位置切片来查询 context。这个接口可以灵活使用:大脑可以从上次停止读取的位置继续,也可以在某个特定时刻前回退几个 events 查看前因后果,或者在某个具体动作之前重新读取 context。
任何取回的 events,也可以先在 harness 中转换,再传入 Claude 的 context window。这些转换可以是 harness 编码的任何逻辑,包括为了获得更高 prompt cache 命中率而组织 context,以及 context engineering。我们把 session 中可恢复的 context 存储,与 harness 中任意的 context 管理分离开来,因为我们无法预测未来模型会需要哪一种具体的 context engineering。接口把 context 管理推入 harness,只保证 session 是持久的,并且可供查询。
多个大脑,多个双手
多个大脑。解耦大脑与双手,解决了我们最早收到的客户抱怨之一。当团队希望 Claude 在他们自己 VPC 内的资源上工作时,唯一的路径是把他们的网络和我们的网络做 peering,因为装着 harness 的容器假设每个资源都在它旁边。harness 不再位于容器中之后,这个假设就消失了。同样的改变也带来了性能收益。我们最初把大脑放在容器里时,多个大脑就意味着同样数量的容器。对每个大脑来说,只有等容器 provision 完成之后,推理才能开始;每个 session 都要预先支付完整的容器设置成本。每个 session,即使是永远不会碰 sandbox 的 session,也必须 clone repo、启动进程、从我们的服务器获取待处理 events。
这段空耗时间体现在 time-to-first-token(TTFT)上,它衡量一个 session 从接受工作到产出第一个 response token 之间等待了多久。TTFT 是用户感受最直接的延迟。
解耦大脑与双手意味着,只有当确实需要容器时,大脑才会通过 tool call (execute(name, input) → string) provision 容器。因此,一个一开始不需要容器的 session,就不必等待容器。只要 orchestration 层从 session log 拉取到待处理 events,推理就可以开始。使用这种架构后,我们的 p50 TTFT 下降了约 60%,p95 下降了 90% 以上。扩展到多个大脑,只意味着启动多个无状态 harnesses,并且只在需要时才把它们连接到双手。
多个双手。我们也希望每个大脑都能连接到多个双手。实践中,这意味着 Claude 必须推理多个执行环境,并决定把工作发送到哪里,这比在单个 shell 中操作要困难得多。我们最初把大脑放在单个容器里,是因为更早的模型还不具备这种能力。随着智能扩展,单个容器反而成了限制:当那个容器失败时,我们会丢失大脑触及的每一只 hand 的状态。
解耦大脑与双手后,每一只 hand 都成了一个 tool:execute(name, input) → string。输入 name 和 input,返回一个 string。这个接口支持任何自定义 tool、任何 MCP server,以及我们自己的 tools。harness 不知道 sandbox 是容器、手机,还是 Pokémon 模拟器。而且因为没有任何 hand 与任何大脑耦合,大脑之间可以相互传递 hands。

结论
我们面对的是一个古老挑战:如何为“尚未被想到的程序”设计系统。操作系统之所以能持续几十年,是因为它们把硬件虚拟化成了足够通用的抽象,可以适配当时还不存在的程序。借助 Managed Agents,我们希望设计一个能够容纳 Claude 周围未来 harnesses、sandboxes 或其他组件的系统。
Managed Agents 是一种同样精神下的 meta-harness,它不对 Claude 未来会需要哪一种具体 harness 预设立场。相反,它是一个具备通用接口的系统,可以容纳许多不同的 harnesses。例如,Claude Code 是一个优秀的 harness,我们在各种任务中广泛使用它。我们也已经展示过,面向特定任务的 agent harnesses 在狭窄领域里表现出色。Managed Agents 可以容纳这些形态,并随着 Claude 的智能提升而匹配它。
meta-harness 设计意味着,要对 Claude 周围的接口有明确主张:我们预期 Claude 将需要操作状态(session)和执行计算(sandbox)的能力。我们也预期 Claude 将需要扩展到多个大脑和多个双手。我们设计这些接口,是为了让它们能在长时间跨度内可靠、安全地运行。但对于 Claude 将需要多少个大脑或双手,以及它们位于哪里,我们不做任何假设。
致谢
本文由 Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。感谢 Nodir Turakulov 和 Jeremy Fox 围绕这些话题提供的有益讨论。特别感谢 Agents API team 和 Jake Eaton 的贡献。