扩展托管智能体:让决策与执行解耦,各行其职
译者点评:这篇文章本质上是在论述 AI 原生基础设施的“POSIX 时刻”——通过定义一组足够通用、生命周期足够长的接口,让智能体应用不再与特定的模型版本或执行环境死死绑定,从而为未来十年模型智能的爆炸式增长留出兼容空间。
请参考我们的 文档 开始使用 Claude 托管智能体。
我们在工程博客中经常探讨的一个话题是,如何 构建高效智能体 并为 长期运行的任务 设计驾驭(Harnesses)。贯穿这项工作的一个共同主题是,驾驭(Harness)固化了对 Claude 独立能力边界的一系列预设。 然而,由于这些预设可能随着模型的改进而 过时,因此需要频繁地对其进行审视与质疑。
举个例子,在之前的工作中 我们发现,Claude Sonnet 4.5 在感知到即将触及上下文长度限制时会过早地结束任务——这种行为有时被称为“上下文焦虑”。我们通过在驾驭中添加上下文重置机制来解决这个问题。但是,当我们在 Claude Opus 4.5 上使用相同的驾驭时,我们发现这种行为已经消失。此时重置机制便成了多余的负担。
我们预计驾驭将继续演进。因此,我们构建了托管智能体(Managed Agents):这是 Claude 平台中的一项托管服务,旨在为您代管并运行长周期任务智能体,其通过一小组精心设计的接口来运行,这些接口的生命周期将长于任何特定的具体实现——包括我们当前正在运行的这些实现。
构建托管智能体意味着解决计算领域中的一个古老问题:如何为“尚未构想的程序”设计系统。几十年前,操作系统通过将硬件虚拟化为足够通用的抽象概念(如 进程、文件 )来解决这个问题,以便为当时不存在的程序提供支持。这些抽象超越了硬件本身。read() 命令并不关心它访问的究竟是 1970 年代的磁盘组还是现代的 SSD。顶层的抽象保持稳定,而底层的实现则自由变化。
托管智能体遵循相同的模式。我们将智能体的组件进行了虚拟化:会话(记录所有已发生事件的仅追加日志)、驾驭(调用 Claude 并将 Claude 的工具调用路由到相关基础设施的循环),以及沙箱(Claude 可以运行代码和编辑文件的执行环境)。这使得每一个组件的实现都可以被替换,而不会干扰其他组件。我们对这些接口的形态有自己的设计主张,但不关心它们背后运行的具体内容。

1 视基础设施为“牛群”,而非“宠物”
我们最初将所有智能体组件放入单个容器中,这意味着会话、智能体驾驭和沙箱共享一个环境。这种方法有其优势,例如文件编辑是直接的系统调用,而且不需要设计服务边界。
但是,通过将一切耦合在一个容器中,我们遇到了一个老旧的基础设施问题:我们收养了一只 宠物(pet)。在“宠物与牛群”的类比中,宠物是一个有名字、需要手工照料且你承受不起失去的个体,而牛群则是可互换的。在我们的案例中,服务器变成了那只宠物;如果容器发生故障,会话就会丢失。如果容器失去响应,我们就必须想办法让它恢复正常(“治疗”它)。
所谓的“照料”容器,实际上意味着我们需要去调试那些无响应且卡死的会话。我们唯一的观测窗口是 WebSocket 事件流,但这并不能告诉我们故障究竟发生 在何处 ——这意味着驾驭逻辑中的 Bug、事件流的丢包,或是容器离线,在表面上看起来都一模一样。为了查明出了什么问题,工程师必须进入容器内部打开 shell;然而,由于该容器往往还包含用户数据,这种做法无异于告诉我们:一旦出事,几乎束手无策。
第二个问题在于,驾驭本身包含了一个假设:Claude 正在处理的所有内容都与驾驭同处一个容器内。当客户要求我们将 Claude 连接到他们的虚拟私有云(VPC)时,他们要么必须将其网络与我们的网络进行对等连接,要么在他们自己的环境中运行我们的驾驭。当我们要将其连接到不同的基础设施时,驾驭中内置的假设就成了一个问题。
我们得出的解决方案是,将我们认为是“大脑”(Claude 及其驾驭)的部分与“双手”(执行操作的沙箱和工具)以及“会话”(会话事件的日志)解耦。它们各自成为独立的接口,彼此之间几乎不作任何预设,任何一个组件都可以独立发生故障或被替换。
驾驭离开容器。 将大脑与双手解耦意味着驾驭不再存在于容器内部。它像调用任何其他工具一样调用容器:execute(name, input) → string。容器变成了“牛群”。如果容器死亡,驾驭会将此故障作为工具调用错误捕获并将其传回给 Claude。如果 Claude 决定重试,则可以使用标准配置(recipe)重新初始化一个新容器:provision({resources})。我们再也不需要费心去“治疗”发生故障的容器了。
从驾驭故障中恢复。 驾驭也变成了牛群。由于会话日志位于驾驭外部,因此驾驭中没有任何内容需要在一个崩溃中存活下来。当其中一个发生故障时,可以通过 wake(sessionId) 重新启动一个新的驾驭,使用 getSession(id) 取回事件日志,并从上一个事件恢复。在智能体循环期间,驾驭通过 emitEvent(id, event) 写入会话,以保留事件的持久记录。

安全边界。 在耦合设计中,Claude 生成的任何不受信任的代码都与凭据在同一容器中运行——因此提示注入攻击只需诱使 Claude 读取其自身运行环境中的变量即可得手。一旦攻击者获得了这些令牌,他们就可以生成全新的、不受限制的会话并将工作委派给它们。缩小作用域是一个明显的缓解措施,但这固化了一个假设:即 Claude 无法利用有限的权限令牌(tokens)执行恶意操作——而 Claude 正在变得越来越聪明。结构性的修复方案是确保从 Claude 运行生成代码的沙箱中永远无法触及这些令牌。
我们使用了两种模式来确保这一点。授权可以与资源捆绑在一起,也可以保存在沙箱外部的保险库中。对于 Git,我们在沙箱初始化期间使用每个存储库的访问令牌来克隆仓库,并将其连接到本地 git 远程。Git push 和 pull 可以从沙箱内部运行,而智能体本身无需处理令牌。对于自定义工具,我们支持 MCP(模型上下文协议)并将 OAuth 令牌存储在安全的保险库中。Claude 通过专用代理调用 MCP 工具;该代理接收与会话关联的令牌。然后,代理可以从保险库中获取相应的凭据并向外部服务发起调用。驾驭永远不会知晓任何凭据。
2 会话不是 Claude 的上下文窗口
长周期任务通常会超过 Claude 上下文窗口的长度,而解决这一问题的标准方法往往都涉及不可逆的决策:即到底该保留哪些内容。我们在之前的关于上下文工程 工作 中探索了这些技术。例如,压缩允许 Claude 保存其上下文窗口的摘要,而记忆工具允许 Claude 将上下文写入文件,从而实现跨会话的学习。这可以与上下文修剪配对使用,后者会选择性地删除旧工具结果或思考块等令牌。
但是,选择性保留或丢弃上下文的不可逆决定可能会导致失败。很难知道未来的轮次将需要哪些令牌。如果消息被压缩步骤转换,驾驭就会从 Claude 的上下文窗口中删除被压缩的消息,而且只有在它们被存储的情况下才能恢复。之前的工作 已经探索了 通过将上下文作为 位于 上下文窗口外部的对象存储来解决这个问题的方法。例如,上下文可以是 REPL(交互式解释器)中的一个对象,LLM 通过编写代码来过滤或切片它来进行编程式访问。

在托管智能体中,会话提供了相同的好处,充当位于 Claude 上下文窗口外部的上下文对象。但是,上下文并不是存储在沙箱或 REPL 中,而是持久地存储在会话日志中。接口 getEvents() 允许大脑通过选择事件流的基于位置的切片来询问上下文。该接口可以灵活使用,允许大脑从上次停止阅读的任何位置继续阅读,在特定时刻之前倒回几个事件以查看前文,或者在特定操作之前重新阅读上下文。
任何获取的事件在被传递到 Claude 的上下文窗口之前,也可以在驾驭中进行转换。这些转换可以是驾驭中编写的任何逻辑,包括旨在提高提示缓存命中率的上下文组织与上下文工程。我们将“会话中可恢复的上下文存储”与“驾驭中任意的上下文管理”这两个关注点解耦,因为我们无法预测未来模型将需要哪些具体的上下文工程。这些接口将该上下文管理推入驾驭,仅保证会话是持久的并且可供询问。
3 多个大脑,多双双手
多个大脑。 将大脑与双手解耦解决了我们最早的客户投诉之一。当团队希望 Claude 对其自己 VPC 中的资源进行操作时,唯一的途径是将其网络与我们的网络进行对等连接,因为容纳驾驭的容器假设每个资源都位于其旁边。一旦驾驭不再位于容器中,这个假设就消失了。相同的变化也带来了性能上的回报。当我们最初将大脑放入容器中时,这意味着多个大脑需要同样多的容器。在容器配置完成前,大脑无法进行任何推理;每一个会话都需要预先承担完整的容器启动成本。每个会话,甚至是那些永远不会接触沙箱的会话,都必须克隆存储库、启动进程,并从我们的服务器获取待处理的事件(pending events)。
这种空转期表现为首词元时间(Time To First Token, TTFT),它测量会话在接受工作和生成其第一个响应词元之间等待的时间。TTFT 是用户最敏锐的 体感延迟。
将大脑与双手解耦意味着大脑仅在需要时通过工具调用 (execute(name, input) → string) 来配置容器。因此,不需要立即使用容器的会话就不会等待容器。一旦编排层从会话日志中提取出待处理的事件,推理就可以开始了。使用这种架构,我们的 p50 TTFT 下降了约 60%,p95 下降了超过 90%。扩展到多个大脑仅仅意味着启动许多无状态驾驭,并仅在需要时将它们连接到双手。
多双双手。 我们还希望能够将每个大脑连接到多双双手。在实践中,这意味着 Claude 必须对许多执行环境进行推理并决定将工作发送到哪里——这是一项比在单一 shell 中操作困难得多的认知任务。我们从单个容器中的大脑开始,因为早期的模型无法做到这一点。随着智能的扩展,单个容器反而成为了限制:当这个容器发生故障时,我们就丢失了大脑正在操控的所有“双手”的状态。
将大脑与双手解耦使每双手都成为一个工具,execute(name, input) → string:输入名称和输入内容,然后返回一个字符串。该接口支持任何自定义工具、任何 MCP 服务器以及我们自己的工具。驾驭并不关心沙箱究竟是一个容器、一部手机还是一个 Pokémon 模拟器。而且因为没有手耦合到任何大脑,大脑可以将手传递给彼此。

4 结论
我们面临的挑战是一个古老的挑战:如何为“尚未构想的程序”设计系统。操作系统通过将硬件虚拟化为足够通用的抽象概念,为当时尚未出现的程序提供了支持,从而在几十年的时间里始终保持着生命力。通过托管智能体,我们旨在设计一个能够容纳 Claude 周围未来驾驭、沙箱或其他组件的系统。
托管智能体正是秉持着同样的设计精神,成为了一个元驾驭(Meta-harness)。它在设计上保持中立(Unopinionated),并不预设 Claude 未来具体需要哪一种特定的驾驭。相反,它是一个具有通用接口的系统,允许多种不同的驾驭同时存在。例如,Claude Code 是一个出色的驾驭,我们在各项任务中广泛使用它。我们还证明了特定任务的智能体驾驭在狭窄领域中表现出色。托管智能体可以容纳所有这些内容,并随着时间的推移匹配 Claude 的智能。
元驾驭设计意味着对 Claude 周围的接口有自己的主张:我们预计 Claude 将需要操作状态(会话)和执行计算(沙箱)的能力。我们还预计 Claude 将需要扩展到多个大脑和多双双手的能力。我们设计了这些接口,以便可以在较长的时间跨度内可靠且安全地运行它们。但是,我们没有对 Claude 将需要的大脑或双手的数量或位置做出任何假设。
5 致谢
由 Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。感谢 Nodir Turakulov 和 Jeremy Fox 在这些主题上提供的有益对话。特别感谢 Agents API 团队和 Jake Eaton 所做的贡献。