规模化托管智能体:将决策核心与执行模块解耦

3
分类技术博客
作者Anthropic
来源跳转
发表时间

内容

开始使用 Claude 托管智能体(Claude Managed Agents),请参考我们的文档

在工程博客中,一个持续探讨的话题是如何构建高效的智能体以及为长时间运行的任务设计有效的“缰绳”机制。这些工作的共同点是:这些“缰绳”机制编码了对 Claude 自身能力边界的假设。然而,随着模型能力的提升,这些假设需要被频繁审视,因为它们可能会变得过时

举个简单的例子,在我们之前的工作中,我们发现 Claude Sonnet 4.5 会在感知到上下文即将达到上限时提前完成任务——这种行为有时被称为“上下文焦虑”。我们通过向“缰绳”中添加上下文重置机制来解决这一问题。但当我们将同样的“缰绳”应用于 Claude Opus 4.5 时,却发现该问题消失了。此时,那些重置机制反而成了冗余负担。

我们预期“缰绳”机制将持续演进。因此,我们构建了 Managed Agents:这是 Claude 平台上的一个托管服务,它通过一组简洁的接口,代表用户运行长周期智能体,而这些接口的设计目标是能够超越当前任何具体实现——包括我们现在正在使用的那些实现。

构建 Managed Agents 意味着要解决计算领域的一个老问题:如何为“尚未构思的程序”设计系统。几十年前,操作系统通过将硬件虚拟化为一组通用抽象(如 进程、文件)解决了这一问题——这些抽象足够通用,以至于适用于当时尚不存在的应用程序。这些抽象超越了底层硬件本身。read() 命令对它是访问 1970 年代的磁盘阵列还是现代 SSD 毫不知情。正是这些上层抽象保持稳定,而底层实现可以自由变化。

Managed Agents 遵循相同的模式。我们将智能体的组件进行了虚拟化:会话(记录所有事件的无追加日志)、“缰绳”(调用 Claude 并将其工具调用路由到相关基础设施的循环)和沙箱(Claude 可在此运行代码并编辑文件的执行环境)。这使得每个组件的实现都可以独立替换而不影响其他部分。我们对这些接口的形状有明确意见,但对它们背后的具体实现则没有。

image

不要养宠物

我们最初将所有智能体组件放入单个容器中,这意味着会话、“缰绳”和沙箱共享同一环境。这种做法有一定优势,例如文件编辑直接通过系统调用完成,且无需设计服务边界。

但将所有内容耦合进单一容器也带来了传统的基础设施问题:我们养了宠物。在“宠物 vs 牛群”的类比中,宠物是命名个体,需精心照料,不能承受丢失;而牛群则是可互换的普通资源。在我们的场景中,服务器就成了那只“宠物”——一旦容器崩溃,整个会话就会丢失。如果容器无响应,我们必须将其“治愈”才能恢复。

“治愈”容器意味着调试无响应或卡住的会话。我们唯一的入口是 WebSocket 事件流,但它无法告诉我们失败发生的具体位置——无论是“缰绳”中的 bug、事件流中的数据包丢失,还是容器离线,都会表现出相同的症状。为了找出问题所在,工程师必须进入容器内部打开 shell,但由于该容器通常还包含用户数据,这种方法实际上意味着我们缺乏有效的调试能力。

第二个问题是:“缰绳”假设 Claude 处理的所有任务都位于同一容器内。当客户请求将 Claude 连接到他们自己的虚拟私有云时,他们只能选择与我们网络对等,或将我们的“缰绳”部署在他们自己的环境中。这一内置于“缰绳”中的假设,在我们希望将其连接至不同基础设施时反而成了障碍。

我们最终的解决方案是将我们认为的“大脑”(Claude 及其“缰绳”)与“双手”(执行操作的工具和沙箱)以及“会话”(事件日志)解耦。三者各自成为对其他部分做出最少假设的接口,并可独立失败或被替换。

“缰绳”离开了容器。将大脑与双手解耦意味着“缰绳”不再位于容器内部。它像调用任何其他工具一样调用容器:execute(name, input) → string。容器变成了“牛群”。如果容器死亡,“缰绳”会捕获该错误作为工具调用异常并返回给 Claude。如果 Claude 决定重试,可以使用标准配方 provision({resources}) 重新初始化新容器。我们不再需要“治愈”失败的容器。

从“缰绳”故障中恢复。“缰绳”本身也成了“牛群”。由于会话日志位于“缰绳”之外,“缰绳”中没有任何内容需要在崩溃后存活。当它失败时,可通过 wake(sessionId) 重启新实例,使用 getSession(id) 获取事件日志,并从最后一个事件继续。在智能体循环过程中,“缰绳”通过 emitEvent(id, event) 写入会话,以保持事件的持久化记录。

image

安全边界。在耦合设计中,Claude 生成的任何不受信任的代码都在包含凭据的同一个容器中运行——因此只需说服 Claude 读取其自身环境即可实现提示注入攻击。一旦攻击者获得这些令牌,他们就可以创建全新的、无限制的会话,并将工作委托给它们。缩小作用域是一种明显的缓解措施,但这仍然基于对 Claude 在有限令牌下无法做什么的假设——而 Claude 正变得越来越聪明。结构性解决方案是确保凭据永远无法从 Claude 生成代码运行的沙箱中访问。

我们采用了两种模式来确保这一点。认证可以与资源绑定,或存储在沙箱外部的金库中。对于 Git,我们在沙箱初始化期间使用每个仓库的访问令牌克隆仓库,并将其嵌入本地 git remote。Git 的 pushpull 操作可在沙箱内部正常工作,而无需代理直接处理令牌。对于自定义工具,我们支持 MCP,并将 OAuth 令牌存储在安全金库中。Claude 通过专用代理调用 MCP 工具;该代理接收与会话关联的令牌,然后从金库获取相应凭据并向外部服务发起调用。“缰绳”本身对这些凭据一无所知。

会话不是 Claude 的上下文窗口

长周期任务常常超出 Claude 上下文窗口的长度,而解决这一问题的常规方法都涉及对保留内容做出不可逆的决定。我们在之前的工作中已经探讨过这些技术。例如,压缩(compaction)让 Claude 保存其上下文窗口的摘要,而记忆工具(memory tool)允许 Claude 将上下文写入文件,从而实现跨会话的学习。这可以结合上下文修剪(context trimming),有选择地移除旧工具结果或思考块等 token。

但选择性保留或丢弃上下文的不可逆决策可能导致失败。很难预知未来所需的 token 究竟是哪一部分。如果消息经过压缩步骤,“缰绳”会从 Claude 的上下文窗口中移除压缩后的消息,只有当这些消息被存储时才可恢复。先前的工作已探索将上下文作为对象存储在上下文窗口之外的方法。例如,上下文可以是 REPL 中的一个对象,LLM 通过编写代码来过滤或切片该对象以编程方式访问它。

image

在 Managed Agents 中,会话提供了相同的好处,作为存在于 Claude 上下文窗口之外的上下文对象。但与存储在沙箱或 REPL 中不同,上下文被持久化存储在会话日志中。getEvents() 接口允许大脑通过选择事件流的位置切片来查询上下文。该接口具有高度灵活性,大脑可以从上次停止读取的地方继续,或在特定时刻前回退几个事件查看前因后果,或重新读取特定动作前的上下文。

获取的任何事件也可以在“缰绳”中被转换后再传递给 Claude 的上下文窗口。这些转换可以是“缰绳”所编码的任何内容,包括上下文组织以实现高缓存命中率,以及上下文工程优化。我们将会话中的可恢复上下文存储与“缰绳”中的任意上下文管理分离,因为我们无法预测未来模型需要什么样的具体上下文工程。接口将这些上下文管理责任推入“缰绳”,仅保证会话是持久化且可查询的。

多脑多手

多脑。将大脑与双手解耦解决了我们早期客户投诉的核心问题之一。当团队希望 Claude 在其自有 VPC 资源上工作时,唯一路径是将其网络与我们的网络对等,因为承载“缰绳”的容器假设所有资源都与其相邻。一旦“缰绳”离开容器,这一假设便不复存在。这一变更还带来了性能收益。最初我们将大脑放入容器时,意味着每个大脑都需要一个容器。每次推理都必须等待容器就绪;每个会话都要预先支付完整的容器设置成本。即使是那些永远不会接触沙箱的会话,也必须克隆仓库、启动进程、从我们的服务器获取待处理事件。

这种延迟体现在“首字响应时间”(TTFT)中,它衡量会话在接受任务到产生首个响应 token 之间的等待时间。TTFT 是用户最直观感受到的延迟。

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

多手。我们还希望每个大脑能连接多个“手”。在实践中,这意味着 Claude 必须对不同执行环境进行推理并决定工作流向何处——这比在单个 shell 中操作更具认知挑战。我们最初将大脑放在单个容器中,是因为早期模型还不具备这种能力。随着智能水平提升,单个容器反而成了瓶颈:一旦该容器失败,我们就失去了大脑所连接的所有“手”的状态。

将大脑与“手”解耦后,每只手都成为一个工具:execute(name, input) → string——输入名称和参数,返回字符串结果。该接口支持任何自定义工具、任何 MCP 服务器以及我们自己的工具。“缰绳”不知道沙箱是容器、手机还是宝可梦模拟器。由于没有“手”与任何“脑”耦合,“脑”之间可以互相传递“手”。

image

结论

我们所面临的挑战是一个古老的问题:如何为“尚未构思的程序”设计系统。操作系统之所以能持续数十年,正是因为它将硬件虚拟化为一组足够通用的抽象,适用于当时尚不存在的程序。通过 Managed Agents,我们的目标是构建一个能容纳未来“缰绳”、沙箱或其他围绕 Claude 组件的系统。

Managed Agents 本质上是一个元“缰绳”(meta-harness),它对 Claude 未来需要的具体“缰绳”类型不持特定观点。相反,它是一个具有一般性接口的系统,可容纳多种不同的“缰绳”。例如,Claude Code 是一个非常优秀的“缰绳”,我们在各类任务中广泛使用它。我们也发现,针对特定任务的专用智能体“缰绳”在狭窄领域内表现出色。Managed Agents 能够适应这些变化,随 Claude 智能水平的提升而匹配其需求。

元“缰绳”设计的核心在于对围绕 Claude 的接口持有明确观点:我们预期 Claude 需要操控状态(会话)和执行计算(沙箱)的能力。我们还预期 Claude 需要扩展至多脑多手的场景。我们设计了这些接口,使其能够在长时间内可靠且安全地运行。但我们不对 Claude 未来需要多少或何种“脑”或“手”做任何假设。

致谢

由 Lance Martin、Gabe Cemaj 和 Michael Cohen 撰写。特别感谢 Agents API 团队的贡献,以及 Jake Eaton 的支持。

评论

(0)
未配置登录方式
暂无评论