Anthropic · 工程博客

扩展托管Agent:将大脑与双手解耦

Scaling Managed Agents: Decoupling the brain from the hands

二〇二六年七月三十日 · 英文原文

Anthropic 推出Claude Managed Agents,这是一个托管服务,通过将agent组件虚拟化为session(事件日志)、harness(调用循环)和sandbox(执行环境)三个独立接口,解决长周期agent运行中的耦合问题。解耦后,harness不再存在于容器内,容器变为可替换的“牲畜”,p50首令牌时间下降约60%,p95下降超90%。安全方面,通过令牌保险库和MCP代理确保凭证无法从sandbox访问。该设计由Lance Martin、Gabe Cemaj和Michael Cohen撰写。

通过 Claude Managed Agents 入门

请按照我们的文档开始使用 Claude Managed Agents。工程博客上有一个持续讨论的话题:如何构建有效的 agent 以及为长时间运行的工作设计 harness。这些工作的一个共同点是,harness 编码了对 Claude 自身无法完成之事的假设。然而,这些假设需要经常被质疑,因为随着模型能力的提升,它们可能会过时。

仅举一例,在之前的工作中,我们发现 Claude Sonnet 4.5 会在感知到上下文窗口接近上限时提前结束任务——这种行为有时被称为"上下文焦虑"。我们通过在 harness 中添加上下文重置来解决这个问题。但当我们在 Claude Opus 4.5 上使用相同的 harness 时,发现这种行为已经消失了。重置变成了累赘。

我们预计 harness 会继续演进。因此我们构建了 Managed Agents:Claude Platform 中的一个托管服务,通过一组旨在超越任何特定实现(包括我们当前运行的实现)的小型接口,代表您运行长周期 agent。

构建 Managed Agents 意味着解决计算领域的一个老问题:如何为"尚未想到的程序"设计系统。几十年前,操作系统通过将硬件虚拟化为抽象层——进程、文件——来解决这个问题,这些抽象层足够通用,足以支持尚未存在的程序。这些抽象层比硬件本身更持久。read() 命令不关心它访问的是 1970 年代的磁盘组还是现代 SSD。上层的抽象保持稳定,而下层的实现可以自由更换。

Managed Agents 遵循相同的模式。我们将 agent 的组件虚拟化:session(所有已发生事件的仅追加日志)、harness(调用 Claude 并将 Claude 的工具调用路由到相关基础设施的循环)和 sandbox(Claude 可以运行代码和编辑文件的执行环境)。这使得每个组件的实现可以互换而不影响其他组件。我们对这些接口的形状有明确主张,但对背后运行什么没有意见。

我们最初将所有 agent 组件放在一个容器中,这意味着 session、agent harness 和 sandbox 共享同一个环境。这种方法有好处,包括文件编辑是直接的系统调用,并且无需设计服务边界。

但通过将所有内容耦合到一个容器中,我们遇到了一个老问题:我们养了一只"宠物"。在"宠物 vs 牲畜"的类比中,宠物是一个有名字、需要手工照料的个体,你不能失去它;而牲畜是可互换的。在我们的案例中,服务器变成了那只宠物;如果容器失败,session 就丢失了。如果容器无响应,我们必须把它"护理"回健康状态。

"护理"容器意味着调试无响应的卡住 session。我们唯一的窗口是 WebSocket 事件流,但这无法告诉我们故障发生在哪里,这意味着 harness 中的 bug、事件流中的数据包丢失或容器离线都呈现相同的症状。要找出问题所在,工程师必须在容器内打开一个 shell,但由于该容器通常也包含用户数据,这种方法本质上意味着我们缺乏调试能力。

第二个问题是,harness 假设 Claude 处理的任何内容都与它一起存在于容器中。当客户要求我们将 Claude 连接到他们的虚拟私有云时,他们要么必须将他们的网络与我们的网络对等,要么在自己的环境中运行我们的 harness。当我们想要将 harness 连接到不同的基础设施时,这个嵌入在 harness 中的假设就成了问题。

我们最终得到的解决方案是将我们认为是"大脑"(Claude 及其 harness)的部分与"手"(执行操作的 sandbox 和工具)以及"session"(session 事件的日志)解耦。每个部分都成为一个对其他部分做出很少假设的接口,并且每个部分都可以独立失败或被替换。

Harness 离开容器。 将大脑与手解耦意味着 harness 不再存在于容器内部。它像调用任何其他工具一样调用容器:execute(name, input) → string。容器变成了牲畜。如果容器死亡,harness 会将其捕获为工具调用错误,并将其传回给 Claude。如果 Claude 决定重试,可以用标准配方重新初始化一个新容器:provision({resources})。我们不再需要"护理"失败的容器。

从 harness 故障中恢复。 Harness 也变成了牲畜。由于 session 日志位于 harness 之外,harness 中没有任何东西需要在崩溃后存活。当一个 harness 失败时,可以用 wake(sessionId) 重新启动一个新的 harness,使用 getSession(id) 取回事件日志,并从最后一个事件继续。在 agent 循环期间,harness 通过 emitEvent(id, event) 写入 session,以保持事件的持久记录。

安全边界。 在耦合设计中,Claude 生成的任何不可信代码都与凭证在同一个容器中运行——因此一次 prompt 注入只需说服 Claude 读取自己的环境即可。一旦攻击者获得了这些令牌,他们就可以生成新的、不受限制的 session 并将工作委托给它们。窄范围限定是一种明显的缓解措施,但这编码了一个关于 Claude 在有限令牌下不能做什么的假设——而 Claude 正变得越来越聪明。结构性的修复是确保令牌永远无法从 Claude 生成的代码运行的 sandbox 中访问到。

我们使用两种模式来确保这一点。Auth 可以与资源捆绑在一起,或者保存在 sandbox 外部的保险库中。对于 Git,我们在 sandbox 初始化期间使用每个仓库的访问令牌来克隆仓库,并将其连接到本地 git remote。Git push 和 pull 在 sandbox 内部工作,而 agent 本身从不处理令牌。对于自定义工具,我们支持 MCP 并将 OAuth 令牌存储在安全的保险库中。Claude 通过专用代理调用 MCP 工具;该代理接收与 session 关联的令牌。然后代理可以从保险库中获取相应的凭证,并对外部服务进行调用。Harness 永远不会知道任何凭证。

上下文管理。 长周期任务通常超过 Claude 上下文窗口的长度,而解决这个问题的标准方法都涉及关于保留什么内容的不可逆决策。我们在之前关于上下文工程的工作中探索了这些技术。例如,压缩(compaction)允许 Claude 保存其上下文窗口的摘要,而记忆工具(memory tool)允许 Claude 将上下文写入文件,从而实现跨 session 的学习。这可以与上下文修剪(context trimming)结合使用,后者选择性地移除令牌,例如旧的工具结果或思考块。

但对上下文进行选择性保留或丢弃的不可逆决策可能导致失败。很难知道未来的步骤需要哪些令牌。如果消息经过压缩步骤转换,harness 会从 Claude 的上下文窗口中移除压缩后的消息,这些消息只有在被存储时才能恢复。之前的工作探索了通过将上下文存储为存在于上下文窗口之外的对象来解决这个问题的方法。例如,上下文可以是 REPL 中的一个对象,LLM 通过编写代码来过滤或切片它以编程方式访问。

在 Managed Agents 中,session 提供了同样的好处,充当存在于 Claude 上下文窗口之外的上下文对象。但上下文不是存储在 sandbox 或 REPL 中,而是持久存储在 session 日志中。接口 getEvents() 允许大脑通过选择事件流的位置切片来查询上下文。该接口可以灵活使用,允许大脑从上次停止读取的地方继续,在特定时刻之前回退几个事件以查看前因,或在特定操作之前重新读取上下文。

任何获取的事件也可以在传递给 Claude 的上下文窗口之前在 harness 中进行转换。这些转换可以是 harness 编码的任何内容,包括上下文组织以实现高 prompt 缓存命中率和上下文工程。我们将 session 中可恢复的上下文存储与 harness 中任意的上下文管理分离开来,因为我们无法预测未来模型需要什么样的特定上下文工程。这些接口将上下文管理推入 harness,只保证 session 是持久的并且可供查询。

多个大脑。 将大脑与手解耦解决了我们最早的客户投诉之一。当团队希望 Claude 在他们自己的 VPC 中处理资源时,唯一的途径是将他们的网络与我们的网络对等,因为包含 harness 的容器假设每个资源都在它旁边。一旦 harness 不再在容器中,这个假设就消失了。同样的改变也带来了性能收益。当我们最初将大脑放在容器中时,这意味着多个大脑需要同样多的容器。对于每个大脑,在容器准备好之前无法进行推理;每个 session 都要预先支付完整的容器设置成本。每个 session,即使是那些永远不会触及 sandbox 的 session,都必须克隆仓库、启动进程、从我们的服务器获取待处理事件。

这段空闲时间体现在首令牌时间(TTFT)上,它衡量 session 在接受工作到产生第一个响应令牌之间的等待时间。TTFT 是用户最敏感地感受到的延迟。

将大脑与手解耦意味着容器仅在需要时由大脑通过工具调用(execute(name, input) → string)来准备。因此,不需要立即使用容器的 session 不会等待容器。一旦编排层从 session 日志中拉取待处理事件,推理就可以立即开始。使用这种架构,我们的 p50 TTFT 下降了约 60%,p95 下降了超过 90%。扩展到多个大脑只需启动多个无状态 harness,并在需要时将它们连接到手。

多只手。 我们还希望将每个大脑连接到多只手。在实践中,这意味着 Claude 必须推理多个执行环境并决定将工作发送到哪里——这是一项比在单个 shell 中操作更困难的认知任务。我们最初将大脑放在单个容器中,因为早期的模型不具备这种能力。随着智能的提升,单个容器反而成了限制:当该容器失败时,我们丢失了大脑正在访问的每只手的全部状态。

将大脑与手解耦使得每只手成为一个工具:execute(name, input) → string:输入名称和输入,返回一个字符串。该接口支持任何自定义工具、任何 MCP 服务器以及我们自己的工具。Harness 不知道 sandbox 是容器、手机还是宝可梦模拟器。并且由于没有手与任何大脑耦合,大脑可以互相传递手。

元设计。 我们面临的挑战是一个老问题:如何为"尚未想到的程序"设计系统。操作系统通过将硬件虚拟化为足够通用的抽象层来支持尚未存在的程序,从而持续了几十年。通过 Managed Agents,我们旨在设计一个能够容纳未来围绕 Claude 的 harness、sandbox 或其他组件的系统。

Managed Agents 是一个同样精神的元 harness(meta-harness),对 Claude 未来需要的具体 harness 没有意见。相反,它是一个具有通用接口的系统,允许许多不同的 harness。例如,Claude Code 是一个优秀的 harness,我们在各种任务中广泛使用它。我们还展示了特定任务的 agent harness 在狭窄领域表现出色。Managed Agents 可以容纳其中任何一个,随着时间推移匹配 Claude 的智能。

元 harness 设计意味着对围绕 Claude 的接口有明确主张:我们预计 Claude 将需要操作状态(session)和执行计算(sandbox)的能力。我们还预计 Claude 将需要扩展到多个大脑和多只手的能力。我们设计了这些接口,以便它们可以在长时间范围内可靠且安全地运行。但我们不对 Claude 需要的大脑或手的数量或位置做出任何假设。

由 Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。感谢 Nodir Turakulov 和 Jeremy Fox 在这些话题上的有益讨论。特别感谢 Agents API 团队和 Jake Eaton 的贡献。

译自 Anthropic · 工程博客 · 录于 二〇二六年七月三十日