规模化托管 Agent:将大脑与双手解耦¶
原文发布于 2026 年 4 月 8 日,作者 Lance Martin、Gabe Cemaj 和 Michael Cohen
引言¶
Harness 中编码的假设会随着模型进化而过时,Managed Agents 围绕稳定接口构建,让实现可以自由更替。
本文与此前关于构建高效 Agent 和为长时间运行的 Agent 设计 harness 的文章一脉相承。共同的主线是:harness 编码了关于"Claude 无法独立完成什么"的假设,但这些假设需要经常被质疑,因为随着模型进步,它们会迅速过时。
一个实际案例:Claude Sonnet 4.5 表现出"上下文焦虑"——在接近上下文窗口极限时提前结束任务。为此,harness 中加入了上下文重置逻辑。但当 Claude Opus 4.5 发布后,这种行为消失了,重置逻辑反而成了无谓的开销。
既然 harness 会持续演化,Anthropic 构建了 Managed Agents:一个运行在 Claude Platform 上的托管服务,代替用户运行长周期 Agent 任务,其接口设计旨在超越任何特定的实现。
设计哲学:面向"尚未构思出的程序"¶
借鉴操作系统的抽象思路,将 Agent 组件虚拟化为可独立替换的接口。
这个问题并不新鲜——它与为"尚未构思出的程序"设计系统的经典计算问题异曲同工(参见 Art of Unix Programming)。操作系统通过将硬件虚拟化为 process 和 file 等抽象来解决这一问题。read() 命令无论是访问 1970 年代的磁盘包还是现代 SSD,都能正常工作。抽象的生命周期超越了硬件。
Managed Agents 采用相同的模式,将 Agent 组件虚拟化为三个核心抽象:
| 组件 | 职责 | 特点 |
|---|---|---|
| Session | 发生的所有事件的 append-only 日志 | 持久化的上下文存储 |
| Harness | 调用 Claude 并将工具调用路由到基础设施的循环 | 可无状态替换 |
| Sandbox | Claude 运行代码和编辑文件的执行环境 | 可独立扩缩 |
每个组件都可以在不影响其他组件的情况下独立替换。我们对这些接口的形状持有明确立场,但对接口背后运行什么保持开放。

不要养宠物¶
所有组件耦合在单一容器中会让系统变成需要精心呵护的"宠物",一旦出问题就无法恢复。
最初,所有 Agent 组件——Session、Harness、Sandbox——都放在同一个容器里。好处显而易见:文件编辑可以直接走系统调用,无需设计服务边界。
但将所有东西耦合到一个容器中,意味着我们养了一只"宠物"。这里引用了经典的 Pets vs. Cattle 类比:宠物是有名字、需要精心照料、不能失去的个体;而牛群是可互换的。 如果容器故障,Session 就丢失了。如果容器无响应,就得想办法把它救活。
调试极其困难。唯一的观察窗口是 WebSocket 事件流,但它无法区分故障来源——harness 的 bug、事件流的丢包、还是容器下线,表现出来的症状完全相同。 工程师需要进入容器内部的 shell 来排查问题,但因为容器中包含用户数据,这条路基本走不通。
第二个问题:harness 假定 Claude 所操作的资源就在容器内。当客户希望将 Claude 连接到自己的 VPC 时,要么让他们的网络与我们做 peering,要么在客户自己的环境中运行我们的 harness——两种方案都不理想。一个深植于 harness 中的假设,在面对不同基础设施需求时变成了阻碍。
将大脑与双手解耦¶
将"大脑"(Claude + Harness)从"双手"(Sandbox/工具)和"记忆"(Session 日志)中分离出来,让每个部分可以独立故障和替换。

解决方案是将三者解耦:每个组件变成一个接口,对其他组件做尽可能少的假设,可以独立地失败或被替换。
Harness 离开容器¶
Harness 不再驻留在容器内部,而是像调用任何其他工具一样调用容器:
execute(name, input) → string
容器变成了"牛群"——如果某个容器挂了,harness 将其视为工具调用错误并传递给 Claude。如果 Claude 决定重试,可以用标准配方重新初始化一个新容器:
provision({resources})
我们不再需要把故障容器救活。
从 Harness 故障中恢复¶
Harness 本身也变成了"牛群"。因为 Session 日志存储在 harness 之外,harness 中没有任何状态需要在崩溃中幸存。新的 harness 通过以下接口重启:
wake(sessionId) // 从 Session 重启 harness
getSession(id) // 获取事件日志
emitEvent(id, event) // 写入持久化事件
在 Agent 循环运行期间,harness 通过 emitEvent 持续向 Session 写入事件,维持持久化记录。崩溃后,新 harness 只需拉取事件日志,从最后一个事件处恢复。
安全边界¶
结构性隔离确保凭证永远无法从沙箱中被触达,而非依赖于 Claude "不够聪明"这个脆弱假设。
在耦合设计中,Claude 生成的不可信代码运行在与凭证相同的容器中——一次 prompt injection 只需说服 Claude 读取自己的环境变量。获得这些 token 的攻击者可以生成不受限制的新 session 并将工作委派给它们。缩小权限范围是一种明显的缓解措施,但它编码了关于"Claude 用有限 token 能做什么"的假设——而 Claude 正变得越来越聪明。
结构性修复:确保 token 永远无法从 Claude 生成的代码运行的沙箱中被触达。
| 模式 | 实现方式 | 效果 |
|---|---|---|
| 认证与资源绑定 | Git access token 在沙箱初始化时用于 clone 仓库并写入本地 git remote | push/pull 正常工作,Agent 从不接触 token |
| 凭证存储在保险库 | MCP 工具的 OAuth token 存于安全保险库;Claude 通过专用代理调用 MCP 工具,代理独立获取凭证 | Harness 从不感知任何凭证 |
Session 不等于 Claude 的上下文窗口¶
Session 是独立于上下文窗口之外的持久化上下文对象,支持按需检索和任意重组。

长周期任务不可避免地超出上下文窗口长度。标准做法涉及"对保留什么做出不可逆的决策":压缩(保存摘要)、记忆工具(将上下文写入文件),以及删除旧工具结果或思考块的上下文裁剪。
但不可逆决策可能出错:很难预知未来的对话轮次需要哪些 token。 如果被压缩的消息从上下文中移除,它们"只有被存储起来才可恢复"。此前的研究探索了将上下文作为独立于上下文窗口的对象进行存储——例如作为 REPL 中的对象,LLM 通过编写代码来过滤或切片它。
在 Managed Agents 中,Session 承担了这个角色——持久化存储在 Session 日志中,而非沙箱内部。接口 getEvents() 允许"大脑"通过选取事件流的位置切片来检索上下文——从上次中断处继续、回溯到某个特定时刻、或重新阅读某个特定动作之前的上下文。
获取的事件可以在 harness 中进行变换后再传入 Claude 的上下文窗口——包括为了提高 prompt cache 命中率而进行的上下文组织,以及各种 effective context engineering。我们将可恢复的上下文存储(Session)与任意的上下文管理(Harness)分离,因为我们无法预测未来模型需要什么样的具体 context engineering。接口只保证一件事:Session 是持久的,且可被查询。
多大脑,多双手¶
多大脑¶
解耦消除了 VPC 连接的障碍,同时带来了显著的性能提升——TTFT p50 下降约 60%,p95 下降超过 90%。

解耦解决了早期客户的一个核心痛点。想让 Claude 操作 VPC 内资源的团队,此前必须做网络 peering,因为持有 harness 的容器假定所有资源都在旁边。Harness 离开容器后,这个假设就消失了。
解耦还带来了性能收益。原先,多个大脑需要同样多的容器,每个容器都要付出完整的启动成本——clone 代码仓库、启动进程、获取待处理事件——然后才能开始推理。
这段空闲时间用 time-to-first-token (TTFT) 来衡量:从接受任务到产生第一个响应 token 之间的等待时间。TTFT 是用户感知最敏锐的延迟。
解耦后,容器通过工具调用 execute(name, input) → string 按需创建。不需要容器的 session 无需等待容器就绪——推理在编排层从 Session 日志拉取待处理事件后即可开始。
| 指标 | 优化效果 |
|---|---|
| TTFT p50 | 下降约 60% |
| TTFT p95 | 下降超过 90% |
扩展到多大脑意味着"启动多个无状态 harness,仅在需要时才连接到双手"。
多双手¶
每只"手"都是一个遵循统一接口的工具,harness 不关心沙箱是容器、手机还是 Pokemon 模拟器。
我们还希望每个大脑能连接到多双手——这意味着 Claude 必须推理多个执行环境并决定将工作发往何处。这是比在单一 shell 中操作更困难的认知任务。早期模型尚不具备这种能力,但随着智能的提升,单一容器反而成了瓶颈。
解耦后,每只手就是一个工具:execute(name, input) → string ——一个名称和输入进去,一个字符串返回。这个接口支持任何自定义工具、任何 MCP server,以及 Anthropic 自己的工具。Harness 不知道沙箱是一个容器、一部手机还是一个 Pokemon 模拟器。 而且因为没有任何一只手与任何一个大脑耦合,大脑之间可以互相传递双手。
总结¶
Managed Agents 是一个"元编排系统"——不对 Claude 需要的具体 harness 持有意见,但对接口有明确主张。
挑战在于面向"尚未构思出的程序"进行设计。操作系统的抽象已经持续了数十年。Managed Agents 秉承同样的精神,是一个元编排系统——拥有通用接口,能够容纳多种不同的 harness。
我们对接口有明确的主张:
- Claude 需要操纵状态的能力(Session)
- Claude 需要执行计算的能力(Sandbox)
- Claude 需要扩展到多大脑和多双手的能力
接口为长期的可靠性和安全性而设计,但我们不对 Claude 需要的大脑或双手的数量和位置做任何假设。
致谢¶
感谢 Nodir Turakulov 和 Jeremy Fox 的启发性讨论,特别感谢 Agents API 团队和 Jake Eaton 的贡献。