规模化托管智能体:将大脑与双手解耦

原文:Scaling Managed Agents: Decoupling the brain from the hands 作者:Lance Martin, Gabe Cemaj, Michael Cohen 日期:2026-02-04 分类:Anthropic 工程博客 · 架构


Harness 编码的假设会随着模型进步而过时。Managed Agents——我们面向长时间智能体工作的托管服务——围绕一组在 harness 变化时仍保持稳定的接口来构建。

请参阅我们的文档开始使用 Claude Managed Agents。

工程博客一直在讨论的主题是如何构建有效的智能体,以及如何为长时运行的工作设计 harness。贯穿这些工作的一条共同主线是:harness 编码了关于 Claude 自身无法完成什么的假设。然而,这些假设需要被频繁质疑,因为随着模型的改进,它们可能会过时。

举一个例子:在之前的工作中,我们发现 Claude Sonnet 4.5 会在感知到上下文限制逼近时过早结束任务——这种行为有时被称为”上下文焦虑”(context anxiety)。我们通过在 harness 中添加上下文重置来解决这个问题。但当我们在 Claude Opus 4.5 上使用同一个 harness 时,发现这种行为已经消失了。那些重置变成了死重。

我们预期 harness 将持续演进。因此我们构建了 Managed Agents:Claude 平台中的一项托管服务,通过一组旨在超越任何特定实现(包括我们今天运行的那些)的小型接口集,代表你运行长时间智能体。

为未知未来而设计

构建 Managed Agents 意味着要解决计算领域的一个老问题:如何为”尚未被构思出的程序”设计系统。几十年前,操作系统通过将硬件虚拟化为抽象——进程(process)、文件(file)——解决了这个问题,这些抽象足够通用,可以服务于尚不存在的程序。抽象比硬件更长寿。 read() 命令不关心它访问的是 1970 年代的磁盘组还是现代 SSD。上层抽象保持稳定,而底层实现可以自由替换。

Managed Agents 遵循同样的模式。我们将智能体的组件虚拟化为:一个会话(session,记录所有发生事件的追加写日志)、一个 harness(调用 Claude 并将 Claude 的工具调用路由到相关基础设施的循环)、以及一个沙箱(sandbox,Claude 可以运行代码和编辑文件的执行环境)。这使得每个组件的实现都可以被替换,而不会干扰其他组件。我们对这些接口的形状有明确主张,但对接口背后运行什么不做限定。

别养宠物

我们最初将所有智能体组件放在一个容器中,这意味着会话、智能体 harness 和沙箱共享一个环境。这种方式有其好处,包括文件编辑是直接的系统调用,而且不需要设计服务边界。

但将所有东西耦合在一个容器中后,我们遇到了一个老生常谈的基础设施问题:我们养了一只”宠物”。在”宠物 vs 牲畜”(pets vs cattle)的类比中,宠物是一个有名字、需要精心照料、你承受不起失去的个体,而牲畜是可互换的。在我们的场景中,服务器成了那只宠物;如果容器故障,会话就丢失了。如果容器无响应,我们不得不把它”救活”。

“救活”容器意味着调试无响应的卡死会话。我们唯一的观察窗口是 WebSocket 事件流,但它无法告诉我们故障发生在哪里——这意味着 harness 中的 bug、事件流中的丢包、容器下线,呈现出的都是同一个症状。要弄清楚出了什么问题,工程师必须在容器内打开一个 shell,但因为那个容器通常也持有用户数据,这种方法本质上意味着我们丧失了调试能力。

第二个问题是,harness 假设 Claude 所操作的一切都和它在同一个容器中。当客户要求我们将 Claude 连接到他们的虚拟私有云(VPC)时,他们要么需要将他们的网络与我们的对等互联(peer),要么在他们自己的环境中运行我们的 harness。一个烙印在 harness 中的假设,在我们想将其连接到不同基础设施时就成了问题。

将大脑与双手解耦

我们得出的解决方案是将我们所认为的”大脑”(Brain,即 Claude 及其 harness)与”双手”(Hands,即执行操作的沙箱和工具)以及”会话”(记录会话事件的日志)解耦。每个组件都成为一个对其他组件做很少假设的接口,每个组件都可以独立地故障或被替换。

Harness 离开容器。 将大脑从双手中解耦,意味着 harness 不再住在容器内部。它像调用其他任何工具一样调用容器:execute(name, input) → string。容器变成了牲畜。如果容器挂了,harness 将故障捕获为工具调用错误,并传递回 Claude。如果 Claude 决定重试,可以用标准配方重新初始化一个新容器:provision({resources})。我们不再需要费力把故障容器救活。

从 harness 故障中恢复。 Harness 本身也变成了牲畜。因为会话日志存在于 harness 之外,harness 中没有任何东西需要在崩溃中存活。当一个 harness 故障时,新的可以通过 wake(sessionId) 重启,使用 getSession(id) 获取事件日志,然后从最后一个事件恢复。在智能体循环中,harness 通过 emitEvent(id, event) 写入会话,以维持持久化的事件记录。

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

我们使用了两种模式来确保这一点。认证信息可以与资源捆绑,或保存在沙箱外部的保险库(vault)中。对于 Git,我们使用每个仓库的访问令牌在沙箱初始化期间克隆仓库并将其接入本地 git remote。Git 的 pushpull 操作在沙箱内部就能工作,智能体自身永远不需要接触令牌。对于自定义工具,我们支持 MCP 并将 OAuth 令牌存储在安全保险库中。Claude 通过专用代理(proxy)调用 MCP 工具;该代理接收一个与会话关联的令牌,然后可以从保险库获取相应的凭证并调用外部服务。Harness 永远不会接触到任何凭证。

会话不是 Claude 的上下文窗口

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

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

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

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

多个大脑,多双手

多个大脑。 将大脑与双手解耦解决了我们最早期的客户投诉之一。当团队希望 Claude 操作他们自己 VPC 中的资源时,唯一的途径是将他们的网络与我们的对等互联,因为持有 harness 的容器假设每个资源都在它旁边。一旦 harness 不再在容器中,这个假设就消失了。同样的变化也带来了性能回报。当我们最初将大脑放在容器中时,这意味着多个大脑需要同等数量的容器。对于每个大脑,在容器完成配置之前无法进行推理;每个会话都要在一开始就支付完整的容器设置成本。每个会话——即使是那些永远不会接触沙箱的——都必须克隆仓库、启动进程、从我们的服务器获取待处理事件。

这些等待时间体现在首 token 时间(TTFT,time-to-first-token)上,它衡量的是会话从接受工作到产生第一个响应 token 之间的等待时长。TTFT 是用户感受最敏锐的延迟。

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

多双手。 我们还希望能够将每个大脑连接到多双手。在实践中,这意味着 Claude 必须推理多个执行环境并决定将工作发送到哪里——这是一个比在单个 shell 中操作更难的认知任务。我们从将大脑放在单个容器中开始,因为早期的模型不具备这种能力。随着智能的扩展,单个容器反而成了限制:当那个容器故障时,我们会丢失大脑正在操作的每一双手的状态。

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

结论

我们面临的挑战是一个老问题:如何为”尚未被构思出的程序”设计系统。操作系统通过将硬件虚拟化为足够通用的抽象来服务于尚不存在的程序,已经持续了数十年。通过 Managed Agents,我们旨在设计一个能够容纳未来围绕 Claude 的 harness、沙箱或其他组件的系统。

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

元 harness 设计意味着对 Claude 周围的接口持明确主张:我们预期 Claude 将需要操作状态的能力(会话)和执行计算的能力(沙箱)。我们也预期 Claude 将需要扩展到多个大脑和多双手的能力。我们设计接口使这些能够在长时间范围内可靠且安全地运行。但我们不对 Claude 将需要的大脑或双手的数量或位置做任何假设。


致谢:Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。特别感谢 Agents API 团队和 Jake Eaton 的贡献。