可易

扩展托管智能体:把大脑和双手拆开

可易团队阅读约 9 分钟

Anthropic 把托管智能体拆成会话、编排循环和沙箱。容器失败不再丢会话,凭证留在沙箱外,按需创建执行环境后,首令牌时间中位数约降 60%。

目录

#扩展托管智能体:把大脑和双手拆开

发布于 2026年4月8日

编排循环(harness)写进了一些会随模型变强而过时的假设。托管智能体(Managed Agents)是我们为长时程智能体工作提供的托管服务,它围绕一组接口来建,这些接口在编排循环更换时仍保持稳定。

按照我们的文档开始使用 Claude 托管智能体(Claude Managed Agents)。

工程博客上持续讨论的题目,是如何构建有效的智能体,以及如何为长时运行的工作设计编排循环。这些工作有一条共同线索:编排循环编码了“Claude 自己还做不到什么”的假设。但这些假设必须经常被质疑,因为模型变强之后,它们会过时。

只举一个例子。在先前的工作里,我们发现 Claude Sonnet 4.5 会在感觉上下文接近上限时提前收尾,这种行为有时叫“上下文焦虑(context anxiety)”。我们的应对是在编排循环里加入上下文重置。但把同一套编排循环用到 Claude Opus 4.5 上时,这个行为消失了。重置变成了累赘。

我们预期编排循环还会继续变化。所以我们做了托管智能体:Claude 平台(Claude Platform)里的一项托管服务,通过一小套接口代你运行长时程智能体。这套接口要活得比任何一种具体实现都久,包括我们今天正在跑的实现。

做托管智能体,等于在解一个计算领域的老问题:怎样为一个“尚未被想到的程序”设计系统。几十年前,操作系统把硬件虚拟成抽象——进程(process)、文件(file)——这些抽象足够一般,能装下当时还不存在的程序。抽象比硬件活得更久。read() 并不在乎它读的是 1970 年代的磁盘组,还是现代固态硬盘(SSD)。上层抽象保持稳定,下层实现可以自由更换。

托管智能体沿用同一模式。我们把智能体的组成部分虚拟化了:会话(session,发生过的一切的只追加日志)、编排循环(调用 Claude,并把 Claude 的工具调用路由到相应基础设施的循环),以及沙箱(sandbox,Claude 可以在其中运行代码、编辑文件的执行环境)。这样,每一部分的实现都可以替换,而不打扰其余部分。我们对这些接口的形状有明确主张,对接口后面跑的是什么则没有。

不要收养宠物

一开始,我们把智能体的全部组件放进同一个容器。会话、智能体编排循环和沙箱共用一个环境。这样做有好处:文件编辑是直接的系统调用,也不需要设计服务边界。

但把一切耦合进一个容器之后,我们撞上一个老的基础设施问题:我们收养了一只宠物(pet)。在宠物与牲畜(pets-vs-cattle)的类比里,宠物是有名字、要亲手照料、丢不起的个体;牲畜则可以互换。在我们这里,服务器变成了那只宠物:容器一失败,会话就丢了。容器无响应时,我们得把它护理回健康状态。

护理容器,意味着调试无响应、卡住的会话。我们唯一的窗口是 WebSocket 事件流,但它说不出故障出在哪里。编排循环里的缺陷、事件流里的丢包、容器下线,表现都一样。要弄清出了什么问题,工程师得在容器里开一个 shell。而那个容器往往还装着用户数据,于是这条路实际上等于我们没有调试能力。

第二个问题是:编排循环假定 Claude 正在处理的东西就住在同一个容器里。客户要求把 Claude 接到他们的虚拟私有云(VPC)时,要么把他们的网络和我们的网络做对等连接(peer),要么把我们的编排循环跑在他们自己的环境里。写进编排循环的一条假设,在我们想把它接到不同基础设施时,变成了问题。

把大脑和双手拆开

我们得到的解法,是把我们视为“大脑”(Claude 及其编排循环)的部分,与“双手”(执行动作的沙箱和工具)以及“会话”(会话事件的日志)拆开。每一块都变成一个对其他块假设很少的接口,每一块都可以独立失败或被替换。

编排循环离开容器。 把大脑和双手拆开,意味着编排循环不再住在容器里面。它像调用任何其他工具一样调用容器:execute(name, input) → string。容器变成了牲畜。容器死了,编排循环把失败当成一次工具调用错误接住,再交回给 Claude。如果 Claude 决定重试,可以用一条标准配方重新初始化新容器:provision({resources})。我们不再需要把失败的容器护理回健康状态。

从编排循环故障中恢复。 编排循环也变成了牲畜。因为会话日志放在编排循环外面,编排循环里没有任何东西需要在崩溃后存活。一个编排循环失败时,可以用 wake(sessionId) 启动一个新的,用 getSession(id) 取回事件日志,并从最后一个事件继续。在智能体循环期间,编排循环用 emitEvent(id, event) 写入会话,以便留下可持久的事件记录。

安全边界。 在耦合设计里,Claude 生成的任何不受信任代码,都和凭证跑在同一个容器里。于是一次提示注入只需要说服 Claude 去读它自己的环境。攻击者拿到这些令牌之后,就可以派生出新的、不受限制的会话,并把工作委派给它们。把令牌范围收窄是明显的缓解办法,但这又编码了一条关于“Claude 拿着有限令牌做不到什么”的假设,而 Claude 正在变得越来越聪明。结构性的修复,是确保令牌永远无法从 Claude 所生成代码运行的沙箱里被触及。

我们用两种模式来保证这一点。认证可以和资源绑在一起,也可以放在沙箱外的保管库里。对 Git,我们用每个仓库的访问令牌,在沙箱初始化时克隆仓库,并把它接到本地 git remote。沙箱内部可以做 Git push 和 pull,智能体自己却从不经手令牌。对自定义工具,我们支持模型上下文协议(MCP),并把 OAuth 令牌存在安全保管库里。Claude 通过一个专用代理调用 MCP 工具;这个代理接收与会话关联的令牌,再从保管库取出对应凭证,向外部服务发起调用。编排循环始终不知道任何凭证。

会话不是 Claude 的上下文窗口

长时程任务经常超出 Claude 上下文窗口的长度。应对这件事的常规办法,都涉及“留下什么”的不可逆决定。我们在关于上下文工程的先前工作里讨论过这些技术。例如,压缩(compaction)让 Claude 保存上下文窗口的一份摘要,记忆工具让 Claude 把上下文写到文件里,从而跨会话学习。这可以和上下文修剪(context trimming)搭配,有选择地去掉旧工具结果或思考块之类的令牌。

但有选择地保留或丢弃上下文的不可逆决定,会导致失败。很难预先知道未来的回合需要哪些令牌。如果消息被压缩步骤改写,编排循环会把被压缩的消息从 Claude 的上下文窗口里移除;只有被存下来,它们才可恢复。先前的工作探讨过把上下文存成一个活在上下文窗口之外的对象。例如,上下文可以是读-求值-打印循环(REPL)里的一个对象,大语言模型通过写代码去过滤或切片来程序化地访问它。

在托管智能体里,会话提供同样的好处:它是一个活在 Claude 上下文窗口之外的上下文对象。但上下文不是存在沙箱或 REPL 里面,而是持久地存在会话日志里。接口 getEvents() 允许大脑通过选取事件流的位置切片来询问上下文。这个接口可以灵活使用:大脑可以从上次停止阅读的地方接着读,可以在某个时刻之前倒回几个事件以看到前因,也可以在某个具体动作之前重读上下文。

取出的事件也可以在送进 Claude 的上下文窗口之前,先在编排循环里被变换。这些变换可以是编排循环所编码的任何东西,包括为了提高提示缓存命中率而做的上下文组织,以及上下文工程。我们把“会话里可恢复的上下文存储”和“编排循环里任意的上下文管理”分开,是因为我们无法预测未来的模型需要哪一种具体的上下文工程。这些接口把上下文管理推到编排循环里,只保证会话是持久的、可以被询问的。

很多大脑,很多双手

很多大脑。 把大脑和双手拆开,解决了我们最早的一批客户抱怨之一。团队希望 Claude 针对他们自己 VPC 里的资源工作时,唯一的路径是把他们的网络和我们的网络做对等连接,因为装着编排循环的容器假定每个资源都坐在它旁边。编排循环一旦不再在容器里,这条假设就消失了。同一处改动还有性能上的回报。最初把大脑放进容器,意味着有多少大脑就要有多少容器。对每一个大脑,容器配好之前不能做推理;每个会话都要预先付完整的容器搭建成本。每个会话,哪怕永远不会碰到沙箱,也必须克隆仓库、启动进程、从我们的服务器拉取待处理事件。

这段空等表现在首令牌时间(time-to-first-token,TTFT)上。它度量一个会话从接受工作到产出第一个响应令牌要等多久。TTFT 是用户最直接感觉到的延迟。

把大脑和双手拆开之后,容器只在需要时由大脑通过一次工具调用 (execute(name, input) → string) 来配给。于是一个暂时不需要容器的会话就不必等容器。编排层一旦从会话日志拉出待处理事件,推理就可以开始。用这套架构,我们的 p50 TTFT 大约下降 60%,p95 下降超过 90%。扩展到很多大脑,只是启动很多无状态的编排循环,并且只在需要时把它们接到双手上。

很多双手。 我们也希望每个大脑能连上很多双手。实际含义是:Claude 必须对很多执行环境做推理,并决定把工作送到哪里。这比在单个 shell 里操作更难。我们一开始把大脑放在单个容器里,是因为更早的模型做不到这件事。随着智能提升,单个容器反而成了限制:那个容器一失败,大脑所伸到的每一只手的状态都丢了。

把大脑和双手拆开,使每一只手都成为一个工具,execute(name, input) → string:进去的是名字和输入,出来的是一个字符串。这个接口支持任何自定义工具、任何 MCP 服务器,以及我们自己的工具。编排循环并不知道沙箱是一个容器、一部手机,还是一个宝可梦模拟器。而且因为没有任何一只手耦合到任何一个大脑,大脑之间可以互相传递双手。

结论

我们面对的挑战是一个老问题:怎样为一个“尚未被想到的程序”设计系统。操作系统能活几十年,是因为它们把硬件虚拟成足够一般的抽象,能装下当时还不存在的程序。对托管智能体,我们的目标是设计一个系统,能容纳未来围绕 Claude 出现的编排循环、沙箱或其他组件。

托管智能体是同一精神下的元编排循环(meta-harness)。它对 Claude 将来需要的某一种具体编排循环不持立场。它是一套带有一般接口、能装下许多不同编排循环的系统。例如,Claude Code 是我们在大量任务上广泛使用的优秀编排循环。我们也表明,针对任务的智能体编排循环在窄领域里表现很好。托管智能体可以容纳其中任何一种,并随时间匹配 Claude 的智能。

元编排循环的设计,意味着对 Claude 周围的接口持明确主张:我们预期 Claude 需要能够操作状态(会话)和执行计算(沙箱)。我们也预期 Claude 需要能够扩展到很多大脑和很多双手。我们把接口设计成可以在很长的时间跨度上可靠、安全地运行这些东西。但我们对 Claude 需要的大脑或双手的数量和位置不做假设。 PRIMARY_SOURCE: https://www.anthropic.com/engineering/managed-agents

  • #托管智能体
  • #沙箱
  • #会话
  • #权限边界
  • #编排循环