【转载】Scaling Managed Agents:将“大脑”和“手”解耦

发布日期:2026 年 4 月 8 日

harness 会编码一些假设,而这些假设会随着模型能力提升而变得过时。Managed Agents 是我们为长周期 agent 工作提供的托管服务,它围绕一组稳定接口构建,这些接口能够在 harness 变化时保持不变。

你可以按照我们的文档开始使用 Claude Managed Agents。

Anthropic 工程博客一直在讨论一个主题:如何构建有效的 agents,以及如何为长时间运行的任务设计 harness。贯穿这些讨论的一条主线是:harness 往往会编码一些关于"Claude 不能自己完成什么"的假设。然而,这些假设必须被持续重新审视,因为随着模型能力提升,它们可能很快过时。

举一个例子。在之前的工作中,我们发现 Claude Sonnet 4.5 在感觉上下文窗口快要接近限制时,会过早结束任务。这种行为有时被称为"上下文焦虑"(context anxiety)。我们通过在 harness 中加入上下文重置来解决这个问题。但当我们在 Claude Opus 4.5 上使用同样的 harness 时,发现这个行为已经消失了。之前加入的重置逻辑变成了无用负担。

我们预计 harness 会持续演进。因此我们构建了 Managed Agents:这是 Claude Platform 上的一项托管服务,可以代表你运行长周期 agents,并通过一组小而稳定的接口来工作。这些接口被设计成能够比任何特定实现都活得更久,包括我们今天正在运行的实现。

构建 Managed Agents,意味着要解决计算领域中的一个老问题:如何为"尚未被想象出来的程序"设计系统。几十年前,操作系统通过把硬件虚拟化为一些抽象概念,解决了这个问题,比如进程、文件。这些抽象足够通用,可以服务于当时还不存在的程序。抽象比底层硬件更长寿。read() 命令并不关心自己访问的是 20 世纪 70 年代的磁盘包,还是现代 SSD。上层抽象保持稳定,而底层实现可以自由变化。

Managed Agents 遵循同样的模式。我们将 agent 的组件进行了虚拟化:session,也就是发生过的一切事情的 append-only 日志;harness,也就是调用 Claude 并把 Claude 的工具调用路由到相关基础设施的循环;sandbox,也就是 Claude 可以运行代码、编辑文件的执行环境。这样,每个组件的实现都可以被替换,而不会打扰其他组件。我们对接口的形态有明确主张,但不对接口背后运行的具体实现作强绑定。

不要领养宠物

一开始,我们把所有 agent 组件都放进同一个容器里。这意味着 session、agent harness 和 sandbox 共享同一个环境。这种方式有一些好处,比如文件编辑可以直接通过系统调用完成,也不需要设计服务边界。

但把所有东西耦合进一个容器后,我们遇到了一个经典的基础设施问题:我们"领养了一只宠物"。在 pets-vs-cattle 的类比里,pet 是一个有名字、需要精心照料、不能轻易丢失的个体;而 cattle 则是可替换的。在我们的场景里,这台服务器就变成了那只"宠物":如果容器失败,session 就会丢失;如果容器无响应,我们必须想办法把它救回来。

"照料"这些容器意味着要调试无响应、卡住的 session。我们唯一的观察窗口是 WebSocket 事件流,但它无法告诉我们故障到底发生在哪里。于是,harness 中的 bug、事件流里的丢包、容器下线,看起来都一样。为了搞清楚发生了什么,工程师必须进入容器打开 shell。但这个容器往往也包含用户数据,所以这种方式本质上意味着我们缺乏真正可用的调试能力。

第二个问题是,harness 默认假设 Claude 正在处理的所有东西都和它一起存在于容器中。当客户希望我们把 Claude 连接到他们的虚拟私有云(VPC)时,他们要么必须把自己的网络和我们的网络做 peering,要么必须在他们自己的环境里运行我们的 harness。一个被写进 harness 的假设,在我们想要连接不同基础设施时,变成了问题。

将"大脑"和"手"解耦

我们最终采用的解决方案是:把我们称为"大脑"的部分,也就是 Claude 和它的 harness,与"手"以及"session"解耦。"手"指的是 sandbox 和执行动作的工具;"session"指的是 session 事件日志。每一部分都变成一个接口,对其他部分只做很少假设,并且每一部分都可以独立失败、独立替换。

harness 离开容器。将"大脑"和"手"解耦,意味着 harness 不再运行在容器内部。它像调用其他任何工具一样调用容器:

lua 复制代码
execute(name, input) → string

容器变成了 cattle。如果容器死掉,harness 会把这个失败捕获为一次工具调用错误,并把它传回给 Claude。如果 Claude 决定重试,可以用标准配方重新初始化一个新容器:

scss 复制代码
provision({resources})

我们不再需要把失败的容器救回健康状态。

从 harness 故障中恢复。harness 本身也变成了 cattle。因为 session log 位于 harness 外部,所以 harness 里没有任何东西必须在崩溃后存活。当某个 harness 失败时,一个新的 harness 可以通过以下方式重启:

scss 复制代码
wake(sessionId)

然后使用:

scss 复制代码
getSession(id)

取回事件日志,并从最后一个事件继续恢复。在 agent loop 运行过程中,harness 会通过:

scss 复制代码
emitEvent(id, event)

向 session 写入事件,从而保留一份持久的事件记录。

安全边界

在耦合设计中,Claude 生成的任何不受信任代码,都会和凭证运行在同一个容器里。因此,一次 prompt injection 只需要诱导 Claude 读取自己的环境变量就够了。一旦攻击者拿到这些 token,他们就可以启动新的、不受限制的 session,并把工作委派给这些 session。

缩小权限范围当然是一种显而易见的缓解方式,但它仍然编码了一个关于"Claude 不能用受限 token 做什么"的假设。而 Claude 正在变得越来越聪明。结构性修复方案是:确保 token 永远无法从 Claude 生成代码运行的 sandbox 中被访问到。

我们用了两种模式来做到这一点。认证信息可以和资源绑定在一起,也可以存放在 sandbox 外部的 vault 中。

对于 Git,我们使用每个代码仓库的 access token,在 sandbox 初始化期间 clone 仓库,并把它接入本地 git remote。这样,在 sandbox 内部可以执行 git push 和 pull,但 agent 自身永远不会直接处理 token。

对于自定义工具,我们支持 MCP,并把 OAuth token 存放在安全 vault 中。Claude 通过一个专用 proxy 调用 MCP 工具;这个 proxy 接收一个和 session 关联的 token。然后 proxy 可以从 vault 中取出对应凭证,并调用外部服务。harness 永远不会感知任何凭证。

session 不是 Claude 的上下文窗口

长周期任务往往会超过 Claude 上下文窗口的长度,而处理这个问题的标准方式,通常都涉及对"保留什么"做不可逆的决定。

我们在之前关于 context engineering 的工作中探索过这些技术。例如,compaction 可以让 Claude 保存其上下文窗口的摘要;memory tool 可以让 Claude 把上下文写入文件,从而支持跨 session 学习。这些技术也可以和 context trimming 结合使用,后者会有选择地移除一些 token,比如旧的工具结果或 thinking blocks。

但这种选择性保留或丢弃上下文的不可逆决策可能导致失败。很难提前知道未来的轮次到底需要哪些 token。如果消息经过 compaction 步骤被转换,harness 会把被压缩的消息从 Claude 的上下文窗口中移除,而这些消息只有在被存储的情况下才可以恢复。

之前的研究探索过一些办法,比如把上下文作为一个存在于上下文窗口外部的对象来存储。举例来说,上下文可以是 REPL 中的一个对象,LLM 可以通过写代码的方式对它进行过滤或切片,从而以编程方式访问它。

在 Managed Agents 中,session 提供了同样的好处:它是一个存在于 Claude 上下文窗口之外的上下文对象。但它不是存储在 sandbox 或 REPL 中,而是被持久化存储在 session log 中。

接口:

scss 复制代码
getEvents()

允许"大脑"通过选择事件流中的位置切片来查询上下文。这个接口可以灵活使用:大脑可以从上次停止阅读的位置继续,也可以回退到某个特定时刻之前的几个事件,查看前因后果;还可以在某个具体动作之前重新阅读相关上下文。

任何被取回的事件,也可以先在 harness 中进行转换,然后再传入 Claude 的上下文窗口。这些转换可以是 harness 编码的任何内容,包括为了提高 prompt cache 命中率而做的上下文组织,以及其他 context engineering 技术。

我们将两类关注点分离开:session 负责可恢复的上下文存储,harness 负责任意形式的上下文管理。之所以这样设计,是因为我们无法预测未来模型到底需要什么样的 context engineering。接口把上下文管理推给 harness,只保证 session 是持久的,并且可以被查询。

多个大脑,多个手

多个大脑。将"大脑"和"手"解耦,解决了我们最早遇到的客户抱怨之一。

当团队希望 Claude 在他们自己 VPC 中的资源上工作时,唯一方式是让他们的网络和我们的网络做 peering。原因是,当时包含 harness 的容器假设每个资源都在它旁边。一旦 harness 不再位于容器中,这个假设就消失了。

同样的改变也带来了性能收益。最初我们把大脑放进容器里,这意味着多个大脑就需要多个容器。对于每一个大脑,只有容器完成 provision 之后,推理才能开始;每个 session 都必须预先支付完整的容器启动成本。即使是永远不会碰 sandbox 的 session,也必须 clone repo、启动进程、从我们的服务器获取待处理事件。

这些无效等待时间会体现在 time-to-first-token,也就是 TTFT 上。TTFT 衡量的是一个 session 从接受工作到产出第一个响应 token 之间等待了多久。这是用户最明显感受到的延迟。

将"大脑"和"手"解耦后,容器只有在需要时才由大脑通过工具调用来 provision:

lua 复制代码
execute(name, input) → string

因此,如果一个 session 一开始并不需要容器,它就不需要等容器启动。只要编排层从 session log 拉取了待处理事件,推理就可以开始。

采用这种架构后,我们的 p50 TTFT 下降了大约 60%,p95 TTFT 下降超过 90%。扩展到多个大脑,只意味着启动多个无状态 harness,并且只在需要时把它们连接到"手"。

多个手。我们也希望每个大脑都可以连接多个手。在实践中,这意味着 Claude 必须能够理解多个执行环境,并决定把工作发送到哪里。这比只在单一 shell 中操作,对认知能力的要求更高。

我们最初让大脑运行在单个容器中,是因为早期模型还不具备这种能力。随着模型智能提升,单容器反而变成了限制:当这个容器失败时,我们会失去大脑连接到的每一个手的状态。

将"大脑"和"手"解耦后,每一个手都变成一个工具:

lua 复制代码
execute(name, input) → string

输入是一个 name 和 input,输出是一个 string。这个接口可以支持任何自定义工具、任何 MCP server,以及我们自己的工具。

harness 不需要知道 sandbox 到底是一个容器、一部手机,还是一个 Pokémon 模拟器。并且,因为没有任何一个手和任何一个大脑强耦合,大脑之间还可以把手传递给彼此。

结论

我们面对的挑战是一个老问题:如何为"尚未被想象出来的程序"设计系统。操作系统之所以能够延续几十年,是因为它们把硬件虚拟化为足够通用的抽象,从而支持当时还不存在的程序。

通过 Managed Agents,我们试图设计一个系统,让 Claude 周围未来可能出现的 harness、sandbox 或其他组件都能够被容纳进来。

Managed Agents 是一种 meta-harness,精神上与上述理念一致。它不对 Claude 未来需要什么具体 harness 作出强预设。相反,它是一个拥有通用接口的系统,可以容纳许多不同的 harness。

例如,Claude Code 是一个非常优秀的 harness,我们在大量任务中广泛使用它。我们也已经看到,面向特定任务的 agent harness 在狭窄领域中表现出色。Managed Agents 可以容纳这些不同形态,并随着 Claude 智能水平的发展与之匹配。

meta-harness 设计意味着,我们对 Claude 周围的接口保持明确主张:我们预计 Claude 需要操作状态的能力,也就是 session;需要执行计算的能力,也就是 sandbox。我们也预计 Claude 需要扩展到多个大脑和多个手的能力。

我们设计这些接口,是为了让这些能力可以在长时间范围内可靠、安全地运行。但我们不对 Claude 未来需要多少个大脑、多少个手,或者这些大脑和手位于哪里,作任何假设。

相关推荐
xcLeigh3 小时前
聚合AI工具KULAAI:GPT、Claude、Gemini、DeepSeek热门模型一键使用
人工智能·gpt·claude·gemini·deepseek·聚合ai·kulaai
DO_Community5 小时前
为AI编程降本!OpenCode 原生支持 DigitalOcean 推理路由器
智能路由器·ai编程·claude
Bigger6 小时前
mini-cc 的 MCP 协议:给 AI 装个 USB-C 接口
人工智能·ai编程·claude
ZzT6 小时前
Claude Code 把编排写进代码:Dynamic Workflows 详解
claude
创世宇图6 小时前
Claude Opus 4.8 深度实测:动态多 Agent 协同、Effort Control 与幻觉抑制的工程化解析
ai·llm·agent·claude·ai工程化
m0_535817558 小时前
macOS上Claude Code安装配置保姆级教程:国内直连API,从0到1跑通(附避坑指南)
gpt·macos·ai·node.js·claude·claudecode·88api
沉默王二10 小时前
每月13亿免费Token,14家AI大厂的API任你用,包括Gemini
github·claude·gemini
AI悦创Python辅导12 小时前
一份 CLAUDE.md,让 Claude Code 不再每次从零开始
claude
空空潍13 小时前
解锁ClaudeCode浏览器访问,极速效率-Playwright Cli
ai·claude·playwright-cli