Multi-Agent 系统的"操作系统时刻":如何设计穿越变化的稳定抽象?

模型在变,harness 在变,执行环境也在变。一个 agent 系统里,到底哪一层应该保持稳定?

这是每个构建多智能体系统的工程师都会遇到的核心难题:你今天为了弥补模型短板而写的一层逻辑,明天模型升级后可能就成了历史包袱。真正困难的,不是写代码,而是先找到一层能穿越这些变化的稳定抽象

这个问题,其实很像操作系统当年解决的问题:底层硬件会不断变化,但你要先定义一层长期有效的接口,让未来还没出现的软件也能在上面稳定运行。

Anthropic 最近在 Multi-agent coordination patterns 一文中,给出了他们的答案。


🎯 核心目标:解耦"大脑"与"手"

当模型(brain)和编排层(harness)都会持续演进时,怎么给 agent 的"执行力"(agency)做一个不用反复重写的运行底盘?

Anthropic 的思路很清晰:

真正要稳住的,不是具体实现,而是模块之间的职责和接口。

  • 模型可以升级 ✅
  • Harness 可以改写 ✅
  • 执行环境可以替换 ✅
  • 存储和调度方式可以重做 ✅
  • 但这些部分之间怎么协作,接口尽量不要跟着反复改 ✅✅✅

这正是操作系统的设计哲学:长期稳定的是 processfile 这一层抽象,像 read() 这样的接口就是稳定抽象向上暴露的典范。


🧩 先定义清楚四个核心组件

在深入架构之前,先把后面会反复出现的几个部件"定住":

组件 职责 类比
Session 外部任务记录,保存"这个任务到底发生过什么" 📋 项目日志/档案柜
Harness 编排逻辑:组织上下文、安排工具调用、路由执行请求 🧠 总指挥室
Sandbox/Tools 真正动手的一层:跑代码、改文件、调外部能力 ✋ 施工队/手
Resources 任务用到的仓库、文件、资料(按需接入,非执行能力) 📦 原材料

简单理解:Cloud + Harness 负责思考和编排,Sandbox + Tools 负责真正动手。


⚠️ 旧架构的陷阱:当容器变成"宠物"

早期设计中,Session、Harness、Sandbox 共享同一个容器。这样做的好处很明显:边界少、文件操作直接、启动快。

但规模上来后,问题也随之而来。Anthropic 用了一个经典类比:

🐕 Pets vs 🐄 Cattle(宠物 vs 牛群)

  • 宠物坏了不能轻易换,只能精心照顾
  • 牛群是标准可替换的,一头不行换一头

他们的容器慢慢变成了"宠物",因为它承载了太多职责:

  • 任务状态
  • 执行现场
  • 工具运行时
  • 甚至"恢复的希望"

这带来了三层麻烦:

  1. 脆弱性增加:容器卡住不能随手重来,因为任务历史和进度都绑在一起
  2. 调试失真:从外部看,网络问题、Harness bug、容器故障表现相似,难以快速定位
  3. 环境假设写死:Harness 默认"要操作的东西就在旁边容器里",限制了接入私有网络/内部资源的能力

核心问题:把会变的东西(模型、环境)直接写进核心结构,系统就会越来越难演进。


🔪 两刀结构改造:建立稳定边界

第一刀:思考编排层 ⇄ 执行层 分离

改造前:Harness 住在容器里,思考+执行+状态 耦合在一起
改造后:Harness 离开容器,留在平台侧,与 Cloud 组成"思考编排层"

┌─────────────────────────┐
│  思考编排层 (Platform)   │
│  • Cloud (推理)         │
│  • Harness (编排)       │
└─────────┬───────────────┘
          │ 稳定接口: provision / execute
          ▼
┌─────────────────────────┐
│   执行层 (Execution)    │
│  • Sandbox / Tools      │
│  • 按需创建的执行单元    │
└─────────────────────────┘

这层接口压得很薄,核心就两件事

  1. provision:准备好本次任务需要的执行环境和资源
  2. execute:执行某个动作(名称+输入),返回结果

上面不需要知道下面是什么实现,只需要知道"要不要动手"和"调用哪个能力"。

带来的直接收益

  • 容器出错时,上层看到的只是一次普通工具调用失败
  • 可重试:重新 provision 一个新执行单元即可
  • 执行层实现可自由替换:本地/远程容器、MCP Server、其他运行时

第二刀:Session 从运行进程抽离到持久层

光把"脑子"和"手"分开还不够。如果任务历史还压在 Harness 进程里,进程一挂任务就没了。

改造核心:Session 不再依附于某个活着的进程,而是作为持续追加的任务事件流独立存在。

┌─────────────────┐
│   Session (持久层)  │
│  • 任务事件流      │
│  • 独立存储/调度   │
└────────┬────────┘
         │ 稳定接口
         ▼
┌─────────────────┐
│   Harness (无状态) │
│  • 读历史做决策   │
│  • 写新事件持久化 │
└─────────────────┘

这层接口也很清晰,分四类

接口 职责 说明
wake 唤醒任务 根据 session ID 重新拉起任务,调度方式是实现细节
get_session 读取历史 新 Harness 接手时,知道前面做到哪一步
get_events 按需切片 不必全量加载,可读未处理部分或回溯特定步骤
emit_event 追加新事件 每往前走一步,新动作持续写回 Session

类比:把项目日志从"工头脑子里"拿出来,放进"外面的档案柜"。工头换了,工程还在。


🔄 新架构下的请求流转

一次用户请求现在会变成这样:

1. 用户请求进入
       ↓
2. 创建/更新 Session,调度层 wake 任务
       ↓
3. Harness 读取 Session 历史,结合 Cloud 做判断
       ↓
4. 分支决策:
   ├─ 仅需分析规划 → 平台侧继续推进,无需执行环境
   └─ 需要执行动作 → 调用执行层
          ├─ provision: 准备执行单元+资源
          ├─ execute: 执行动作,返回结果
          └─ emit_event: 结果写回 Session
       ↓
5. 进入下一轮循环...

主轴关系非常清晰

  • Cloud + Harness:思考与编排
  • Sandbox + Tools + Resources:执行与资源
  • Session:任务真相的持久化

各部分内部怎么实现,后面都可以继续变;但它们之间怎么协作,接口尽量不要跟着反复改。


🎁 四重收益:为什么这套拆法值得?

1️⃣ 故障被重新定义:从"灾难"到"可处理事件"

旧架构 新架构
容器挂 = 整个任务现场丢失 执行单元挂 = 一次普通执行失败
Harness 挂 = 任务中断 Harness 挂 = 新实例读 Session 继续跑
故障会把任务拖死 故障被系统吸收为可重试事件

关键:任务真相不在进程内存里,而在独立的 Session 中。

2️⃣ 安全边界终于清晰:能力可以给,秘密不要让它看见

旧架构中,Claude 生成的不可信代码和敏感凭证可能在同一个执行环境,风险被放大。

Anthropic 的原则

✅ 可以给 agent 调用 Git 的能力
❌ 但不要让它直接看到 Git Token

✅ 可以让 agent 发起 API 调用
❌ 但 Token 放在执行环境外的安全保险库

实现方式:Cloud 发起调用时先经过代理,由代理代为完成鉴权。放在结构上拿不到,比放在"希望模型克制"上稳得多。

3️⃣ Session 与 Context Window 彻底分离

很多人会把"聊天历史"和"上下文窗口"混为一谈,但这篇文章专门把它们拆开:

Session Context Window
定位 任务外部持久化的真实历史 模型本轮推理临时能看到的内容
容量 理论上无限 有 token 上限
用途 完整记录,支持回溯 当前决策的输入材料

🗄️ 档案室(Session)负责保存原始记录
🪑 会议桌(Context)摆什么材料,Harness 每轮可以动态调整

收益:原始细节不会因为摘要压缩被永久丢掉,未来新模型/新 Harness 还能回头拿到完整信息。

4️⃣ 系统更快、更易扩展

更快

  • 思考层从重执行环境中解放,请求进来先分析再决定是否动手
  • 实测:某些 Session 的 TTFT(Time To First Token)P50 下降约 60%,P95 下降超 90%
  • 注意:不是模型变快了,是调度方式变了

更易扩展

┌─────────────────┐     ┌─────────────────┐
│  思考编排层      │     │   执行层         │
│  • 可独立扩容    │◄───►│  • 可独立扩容    │
│  • 逻辑迭代快   │     │  • 资源弹性调度  │
└─────────────────┘     └─────────────────┘
         ▲
         │ 通过稳定接口协作
         ▼
┌─────────────────┐
│   Session 层    │
│  • 存储可替换   │
│  • 调度策略可演进│
└─────────────────┘
  • 执行层统一抽象:可以是容器、MCP Server、其他运行时
  • 一个 Harness 可调度多个执行单元,多个 Brain 可共享同一批 Hands
  • Brain 之间甚至可以移交"手"的使用权

💡 总结:穿越变化的设计哲学

当模型在变、harness 在变、执行环境也在变时,一个 agent 系统到底该把什么先稳定下来?

Anthropic 给出的答案是:

先把职责和接口确定下来。每个部分内部可以继续变,但这些部分之间怎么协作,尽量不要一代一代跟着重写。

只要这些边界还在,整个 agent 系统就不用每变一次就从底下重搭一次。

这本质上是一种面向演进的设计

  • 不追求"一次做对",而是追求"容易改对"
  • 不绑定具体技术选型,而是定义清晰协作契约
  • 不为今天的最优解牺牲明天的可能性

正如操作系统通过稳定的系统调用接口,支撑了半个世纪的软件演进;多 agent 系统也需要找到自己的"系统调用层"。

真正的工程智慧,不在于写出多复杂的代码,而在于知道哪些东西应该保持不变。


📚 延伸阅读

如果你也在构建 agent 系统,欢迎在评论区分享:你遇到的"变化"主要来自哪一层?你是如何设计稳定边界的? 🚀

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐