Managed Agents 里,Harness 到底升级了什么?

0 阅读6分钟

Anthropic 最新一篇文章《Scaling Managed Agents: Decoupling the brain from the hands》,讲的是将“大脑(Claude+Harness)”和“手脚(Sandbox/Tools)”、“会话(Session)”、“调度(Orchestration)”拆开解耦,彼此通过接口协作,每个模块独立可替换,互不干扰。读完感受,不在于“发布了什么新能力”,而在一个更底层的工程常识:Agent 系统要长期可用,关键是接口稳定,而不是某一版 Harness 是否足够聪明。


解耦边界:先把系统故障域切小

图片

主题图:解耦边界与故障域切分

如图,Managed Agents 将整个系统拆分解耦成几个组件:

  • Session:会话事件账本。记录会话过程所有发生过的 append-only 日志,包括这次任务做过什么、做到哪一步,支持中断后续跑。
  • Harness:中枢编排层。负责调用 Claude、串起会话进度,并把工具调用路由到对应执行层。
  • Sandbox:执行环境。Claude 需要跑代码、改文件、做命令操作时,都在这里动手。
  • Tools + Resources / MCP:外部能力和外部数据入口。比如调用第三方系统、读取仓库资源,都是通过这层接入。
  • Orchestration:调度层。负责“什么时候唤醒任务、失败后怎么重试、谁先谁后执行”这类运行编排。

那么,抛个问题:如果某个模块挂了,系统能不能沿着既定链路自动续跑?

Anthropic 的答案是“能”,而且恢复链路比较清晰:

  1. 先由调度层唤醒会话wake(session_id) 把这次任务重新拉起。
  2. 再由 Harness 重建现场:通过 getSession/getEvents 读取事件日志,定位上次停在什么位置。
  3. 执行层故障按工具错误返回:如果某个 sandbox 掉线,会以 execute(...) 调用失败的形式返回给 Harness,而不是把整个会话拖死。
  4. 按需重建执行环境并继续:Harness 可触发 provision({resources}) 初始化新 sandbox,然后从已记录的进度继续往下跑。
  5. 持续写回进度:每一步都通过 emitEvent(...) 追加到 session 日志,保证下一次恢复还有可追溯现场。

换句话说,它不是“尽量别出错”的设计,而是“默认会出错,但出错后可以按协议恢复”的设计。

过去很多人常见做法是:
把 agent loop、上下文、工具执行、文件系统全塞进一个运行体,开发会很快。
但一到长期运行,问题会一起冒出来:容器卡死、上下文污染、恢复困难,debug 还得进活体系统。

所以它的核心价值不是“组件多了”,而是故障域变小了

图片

主题图:接口协议与可替换实现

顺着上面的恢复链路,再看这张接口配图会更清楚:
真正决定系统可恢复、可替换、可演进的,是这三组接口。

1) Session

这组接口的作用:把会话进度做成可查询、可续跑、可追溯的事件账本。

  • getSession(session_id):先拿会话元信息与历史概览(比如会话状态、已记录事件范围),用于判断“从哪里继续”。
  • getEvents(session_id):再拿待处理或指定区间事件,恢复上下文现场;等于把“上次停在第几步”具体读出来。
  • emitEvent(id, event):每推进一步就追加写回,保证后续失败时有最新检查点可恢复。

2) Orchestration

这组接口的作用:把“何时启动、何时重试、如何续跑”从业务逻辑里抽成调度能力。

  • wake(session_id):当检测到会话有待处理事件,或某次执行失败需要重试时,调度层通过它重新唤醒会话,让 Harness 继续跑。

3) Sandbox

这组接口的作用:把执行环境标准化成“先初始化、再执行动作”的统一调用面。

  • provision({resources}):按资源清单初始化执行环境(如代码仓、依赖、凭证代理等运行前条件)。
  • execute(name, input) -> String:把具体动作交给执行层(命令、脚本、工具调用),返回结果字符串给 Harness 做下一步决策。

可恢复记忆层:Session 不等于上下文窗口

图片

主题图:Session 作为可恢复记忆层

这组设计的作用:把“可恢复历史”与“模型当前工作台”拆开,避免长任务越跑越脆。

核心拆分是两层:

  • context window:当前上下文窗口,快,但会有记忆压缩和内容裁剪
  • session log:完整会话日志,慢一点,但可追溯

这样做主要是为了解决三类长任务问题:

  • 历史太长,要压缩
  • 历史被重排,语义出现偏差
  • 某一步输出后来证明关键,但已经被截掉

所以 Session 放在窗口外,不是“多一份日志”这么简单。
它的意义是:Harness 可以按需 getEvents() 回看现场,而不是把希望押在一次压缩策略上。


按需接入执行层:性能和稳定性一起变好

图片

主题图:按需接入执行层的解耦关系

这组机制的作用:把“先起环境再推理”改成“先推理、按需接入执行层”。

最直接的结果有两点:

  • TTFT 下降:不再让所有会话都先等容器冷启动。
    原文数据是 p50 TTFT 约降 60%,p95 超过 90%。
  • 故障域缩小:sandbox 掉线时表现为一次 execute 失败,
    Harness 可选择重试、换执行层或降级,而不是整会话一起挂。

安全边界怎么做成结构能力

这组设计的作用:把“凭证安全”从提示词约束,升级为结构隔离。

原文的判断很直接:
如果凭证在模型可触达环境里,本质上是在赌“模型暂时拿不到”。

Anthropic 的处理是:

  • 凭证不直接暴露在 Sandbox
  • Git 访问在初始化阶段注入 remote 能力,而不是裸 token 到处传
  • MCP/OAuth 走代理和 vault,按 session 取最小所需凭证

这套边界的现实价值是:
模型能力继续变强时,系统不需要依赖“提示词防线”硬扛。


对 Harness 有什么可落地的启发

这部分我只留三条可直接执行的动作:

  1. 先写接口协议:状态、调度、执行三层先分开,再谈实现细节。
  2. 把恢复当主路径:别把 crash recovery 当“可选补丁”。
  3. 把不可达当安全目标:不是“不要拿凭证”,而是“拿不到凭证”。

如果现在还是“一个容器包天下”,先做最小拆分就够:
先把事件日志抽出去,再把执行器工具化。通常这两步就能见效。


参考链接

  • 《Scaling Managed Agents: Decoupling the brain from the hands》