Managed Agents
Anthropic 在这篇文章中讨论的是 Managed Agents 的基础设施设计:当模型能力持续提升时,Harness 中对模型能力的假设会很快过时。Managed Agents 的目标不是绑定某一种当前 Harness,而是提供一组能跨越 Harness 演进周期的稳定接口。
这套设计来自一个很老的计算机系统问题:如何为“尚未被设想出来的程序”设计系统。操作系统通过把硬件虚拟化为进程、文件等抽象解决了这个问题;这些抽象能跨越具体硬件实现。Managed Agents 采用同样思路,把 Agent 的关键组件虚拟化为 Session、Harness 和 Sandbox。
Harness 假设会过时
Anthropic 工程博客此前持续讨论如何构建有效 Agent,以及如何为长程任务设计 Harness。贯穿这些工作的共同点是:Harness 会编码“Claude 自己做不到什么”的假设。
这些假设必须经常被重新审视,因为它们会随着模型进步而失效。
一个例子是 context anxiety。在此前工作中,Claude Sonnet 4.5 接近上下文窗口限制时,会过早收尾任务。Anthropic 通过在 Harness 中加入 context reset 来处理这个问题。但当同一套 Harness 用在 Claude Opus 4.5 上时,这种行为消失了,reset 逻辑就变成了负担。
因此,Anthropic 预期 Harness 会持续演进。Managed Agents 是 Claude Platform 上的托管服务,用少量接口替用户运行长程 Agent;这些接口被设计为能比任何具体实现活得更久,包括 Anthropic 当前运行的实现。
Managed Agents 虚拟化了三个组件:
| 抽象 | 含义 |
|---|---|
| Session | 所有已发生事件的 append-only 日志。 |
| Harness | 调用 Claude,并把 Claude 的工具调用路由到相关基础设施的循环。 |
| Sandbox | Claude 可以运行代码、编辑文件的执行环境。 |
这让每个组件都可以替换实现,而不干扰其他组件。系统对接口形状有主见,但不绑定接口背后的具体实现。
不要收养一只宠物
Managed Agents 的早期架构把所有 Agent 组件放进同一个容器:Session、Agent Harness 和 Sandbox 共享一个环境。
这种设计有明显好处。例如,模型生成的代码需要改文件时,可以直接发起系统调用,不需要设计额外服务边界和通信协议。
但把所有东西耦合在一个容器里,也引出了经典基础设施问题:系统变成了一只 pet,而不是 cattle。在 pets-vs-cattle 类比中,pet 是有名字、需要手工照料、不能轻易丢失的个体;cattle 是标准化、可替换的实例。
在这个早期架构里,容器就成了 pet:如果容器失败,Session 就丢失;如果容器无响应,工程师必须想办法把它救回来。
这带来几个问题:
- 失败难以区分:唯一可见窗口是 WebSocket 事件流,但它无法说明失败来自 Harness bug、事件流丢包,还是容器离线。
- 调试受隐私限制:排查问题需要 shell 进入容器,但容器通常包含用户数据,这让调试本身变得不可接受。
- 基础设施位置被写死:Harness 假设 Claude 要操作的一切都和它在同一个容器里。当客户希望 Claude 访问自己的 VPC 时,只能与 Anthropic 网络做对等互联,或把 Harness 跑在客户自己的环境中。
这些问题的根因是:Session、Harness 和 Sandbox 被迫共享同一个失败边界。
将大脑与双手解耦
Anthropic 的解决方案是把“大脑”和“双手”解耦:
- 大脑 (brain):Claude 和它的 Harness。
- 双手 (hands):Sandbox 和实际执行动作的工具。
- Session:记录 Session 事件的日志。
三者都成为接口,尽量少假设彼此,并且可以独立失败、独立替换。
Harness 离开容器
脑手解耦意味着 Harness 不再住在容器里。它像调用其他工具一样调用容器:
execute(name, input) -> string
容器因此变成 cattle,而不是 pet。如果容器死亡,Harness 会把失败当作一次普通工具调用错误捕获,并传回给 Claude。Claude 可以决定是否重试;如果重试,系统可以用标准 recipe 重新初始化一个容器:
provision({resources})
这样就不再需要照料失败容器。
从 Harness 失败中恢复
Harness 本身也变成 cattle。因为 Session 日志位于 Harness 外部,Harness 中没有任何状态必须跨崩溃存活。
当某个 Harness 失败后,一个新的 Harness 可以:
- 通过
wake(sessionId)启动。 - 用
getSession(id)取回事件日志。 - 从最后一个事件恢复。
- 在 Agent 循环中用
emitEvent(id, event)写入 Session,形成持久事件记录。
换句话说,持久状态被放在 Session 里,Harness 可以随时被替换。
安全边界
在耦合设计里,Claude 生成的不可信代码和凭证运行在同一个容器中。一次 prompt injection 只需要说服 Claude 读取自己的环境变量,就可能拿到 token。拿到 token 后,攻击者还可以创建新的、不受限制的 Session,并把任务委托给它们。
缩小 token 权限范围是显然的缓解方式,但这仍然编码了一个假设:Claude 无法用有限 token 做出意料之外的事。随着模型变聪明,这个假设越来越不稳。
结构性修复是:让 token 永远无法从 Claude 生成代码运行的 Sandbox 中访问。
Managed Agents 使用两种模式:
- Auth bundled with resource:例如 Git。系统用仓库 access token 在 Sandbox 初始化期间 clone repo,并把 token 接入本地 git remote。Sandbox 内部的
git push和git pull可以工作,但 Agent 永远不直接处理 token。 - Vault outside the sandbox:例如自定义工具和 MCP。OAuth token 存在安全 vault 中。Claude 通过专用代理调用 MCP 工具;代理接收与 Session 关联的 token,从 vault 获取对应凭证,调用外部服务,再把结果返回给 Harness。Harness 本身也不会看到真实凭证。
Session 不是 Claude 的上下文窗口
长程任务经常超过 Claude 的上下文窗口。标准处理方式通常涉及不可逆选择:哪些内容保留,哪些内容丢弃。
Anthropic 此前在 context engineering 中探索过这些技术。例如,compaction 让 Claude 保存上下文窗口摘要;memory tool 让 Claude 把上下文写入文件,以支持跨会话学习。这些方法还可以与 context trimming 搭配,选择性移除旧工具结果或 thinking blocks 等 token。
但不可逆地保留或丢弃上下文可能导致失败。未来轮次到底需要哪些 token,很难提前知道。如果消息被 compaction 转换,Harness 会从 Claude 的上下文窗口中移除原消息;除非这些原消息被存储,否则之后无法恢复。
Managed Agents 中,Session 提供类似“上下文对象”的能力,但它不是存放在 Sandbox 或 REPL 中,而是持久地存在 Session log 中。getEvents() 接口允许大脑通过位置切片查询事件流:
- 从上次停止读取的位置继续。
- 回退到某个事件之前,查看它的前因。
- 在某个动作之前重新读取相关上下文。
Harness 还可以在把事件传给 Claude 的上下文窗口之前转换它们。例如,为了更高 prompt cache 命中率而组织上下文,或实现特定 context engineering 策略。
这个设计把两件事分开:
- Session 负责可恢复的上下文存储,保证事件持久、可查询。
- Harness 负责任意上下文管理,决定哪些事件进入 Claude 的上下文窗口,以及如何组织。
Anthropic 这样设计,是因为他们无法预测未来模型需要什么具体 context engineering 策略。接口只保证 Session 是持久且可被查询的,把具体上下文管理留给 Harness。
多个大脑,多个双手
多个大脑
脑手解耦解决了一个早期客户问题:团队希望 Claude 访问自己 VPC 中的资源时,原来只能把客户网络和 Anthropic 网络做 peering,因为装着 Harness 的容器假设所有资源都在自己旁边。Harness 离开容器后,这个假设消失了。
同一变化也带来性能收益。
早期架构把大脑放在容器里,这意味着每个大脑都需要一个容器。每个 Session 在推理开始前,都要先付完整容器启动成本:clone repo、启动进程、从服务器获取 pending events。即使某个 Session 根本不会使用 Sandbox,也要等这一套过程完成。
这种等待体现在 time-to-first-token (TTFT) 上,也就是从系统接受任务到产出第一个响应 token 的延迟。TTFT 是用户最直接感受到的延迟。
解耦后,容器只在大脑通过工具调用需要它时才 provision。一个暂时不需要容器的 Session 不必等待容器启动。Orchestration layer 从 Session log 拉取 pending events 后,推理可以立刻开始。
结果是:p50 TTFT 下降约 60%,p95 下降超过 90%。
扩展多个大脑,也就变成启动多个无状态 Harness,并在需要时把它们连接到双手。
多个双手
Anthropic 还希望一个大脑能连接多双手。实践中,这意味着 Claude 要理解多个执行环境,并决定把工作发送到哪里。这比只在一个 shell 中操作更难。
早期把大脑放在单个容器里,是因为更早的模型还做不好这种判断。但随着模型智能提升,单容器反而成了限制:容器失败时,大脑正在触达的每一双手的状态都会一起丢失。
脑手解耦后,每个 hand 都是同一种工具接口:
execute(name, input) -> string
输入是名称和参数,返回是字符串。这个接口可以支持任何自定义工具、任何 MCP server,也可以支持 Anthropic 自己的工具。Harness 不需要知道 Sandbox 到底是容器、手机、浏览器,还是别的执行环境。
因为没有任何 hand 与任何 brain 强耦合,brains 也可以把 hands 传给彼此。
结论:Managed Agents 是 Meta-Harness
Managed Agents 面对的是一个老问题:如何为“尚未被设想出来的程序”设计系统。操作系统通过把硬件虚拟化成足够通用的抽象,让自己跨越了几十年的硬件变化。Managed Agents 试图用同样方式,为 Claude 周围未来可能出现的 Harness、Sandbox 和其他组件设计系统。
Managed Agents 不是某一个具体 Harness,而是一个 meta-harness:它不对未来 Claude 需要的具体 Harness 做假设,而是提供一组通用接口,容纳不同 Harness。
例如,Claude Code 是 Anthropic 在广泛任务中使用的优秀 Harness;特定任务 Harness 也能在窄领域中表现更好。Managed Agents 需要容纳这些不同形态,并随着 Claude 智能提升而一起演进。
Meta-harness 设计意味着:
- 对 Claude 周围的接口保持有主见:Claude 需要操纵状态,也就是 Session;也需要执行计算,也就是 Sandbox。
- 预期 Claude 需要扩展到多个大脑和多个双手。
- 设计接口,让这些组件能在长时间跨度上可靠、安全地运行。
- 不假设 Claude 未来需要多少个大脑、多少双手,或它们应该位于哪里。
来源
Scaling Managed Agents: Decoupling the Brain from the Hands — Lance Martin, Gabe Cemaj & Michael Cohen, Anthropic, 2026.04。