2026年万字拆解OpenClaw:从Gateway、Memory、Skills、多Agent 到Runtime

万字拆解OpenClaw:从Gateway、Memory、Skills、多Agent 到Runtime本文来自微信公众号 叶小钗 作者 叶小钗 原文标题 万字拆解 OpenClaw 从 Gateway Memory Skills 多 Agent 到 Runtime 今天我们再深入一点 从源头开始 看看这个 Agent 是如何实现的 和其他 Agent 有啥不同 但单纯按模块介绍容易显得零散 所以我们回到最底层的问题 当用户真的发来一条消息时 OpenClaw 这个 Agent 系统内部

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



本文来自微信公众号: 叶小钗 ,作者:叶小钗,原文标题:《万字拆解 OpenClaw:从 Gateway、Memory、Skills、多 Agent 到 Runtime》

今天我们再深入一点,从源头开始,看看这个Agent是如何实现的,和其他Agent有啥不同。

但单纯按模块介绍容易显得零散,所以我们回到最底层的问题:

当用户真的发来一条消息时,OpenClaw这个Agent系统内部,到底是怎么跑起来的?

这件事其实比表面上看起来更重要。

因为你只要真的把一条消息从头跟到尾,就会发现OpenClaw跟普通聊天机器人、传统工作流系统、以及很多只会调工具的Agent框架,差别并不在于它会不会聊天,而在于它背后有一整套完整的运行链路:

消息接收、协议适配、路由分发、会话隔离、上下文组装、技能注入、流式执行、工具调用、持久化存储,以及在复杂任务下的多Agent协作。

为了让整篇文章更容易理解,我们先假设一个典型场景:

在钉钉里发来一句:

帮我整理今天的重要邮件,提炼待办

并生成一份给老板的简报

接下来,我们就沿着这条消息,看看它是如何从外部世界的一段文本,最终变成一套真正被Agent执行起来的任务链路的。

希望大家带着三个问题阅读此文:

  1. OpenClaw的整体架构设计到底是什么
  2. 一条消息在系统中的完整执行路径是什么
  3. 多Agent协作到底是怎么落地实现的

OpenClaw怎么跑的

很多人第一次看OpenClaw,很容易把它理解成一个能聊天、能调工具、还能帮你跨平台干活的智能助理。

如果从工程实现的角度看,OpenClaw更像是一个围绕Agent构建出来的运行时网关系统:Agent Runtime。

它不是简单地把一句用户输入丢给大模型,然后把输出再发回来,而是把整个过程拆成了一条清晰的执行链路,并在每个关键节点上做了工程治理。

它的整体架构,基本可以抽象成五层:

第1层:用户接口层

提供CLI、Web UI、移动App、WebSocket API等入口,把用户操作转成统一的内部请求。

对用户来说,可能只是网页里输入一句话,或者在钉钉、飞书里发一条消息;但对系统来说,这些入口最终都要收敛成统一的内部消息模型。

第2层:Gateway核心层

这是OpenClaw的核心运行时。它负责连接管理、请求接入、配置热加载、健康监控等基础治理工作。

换句话说,真正让整个系统常驻运行、能接消息、能回消息、能维持状态的,不是单个Agent,而是这个Gateway。

第3层:消息处理层

这是业务逻辑真正流转的核心,包括:

  1. Agent执行器
  2. 路由系统
  3. 会话管理
  4. 媒体处理
  5. 出站投递

一条消息从进入系统到最终响应,最核心的执行动作都发生在这一层。

第4层:扩展与插件层

所有可插拔的扩展都在这里:

  1. 通道插件,对接钉钉/飞书、Telegram、WhatsApp、Slack等
  2. 技能工具系统
  3. sub Agent机制

也正因为有这一层,OpenClaw才能不断往上接新通道、往下接新工具、往内部接多Agent协作。

第5层:基础设施层

这一层为整个系统提供通用能力,包括:

  1. 配置与密钥管理
  2. 结构化日志
  3. 定时任务
  4. 事件总线
  5. 记忆检索
  6. 沙箱安全

这部分平时不太显眼,但没有它,前面几层都跑不稳。

从数据流转的视角看,一条消息的完整路径其实很清楚:

消息源→协议适配→路由分发→会话构建→Agent执行→响应投递→状态持久化

所以接下来,我们就沿着这条路径往下看。

消息进门

先回到刚才那个例子。

帮我整理今天的重要邮件,提炼待办

并生成一份给老板的简报

从用户视角看,这就是一条普通消息。但从系统视角看,问题马上就来了:

钉钉的消息格式和飞书不一样,Discord和WhatsApp不一样,Telegram和内部WebSocket通道也不一样。

有的平台带message_id,有的平台叫thread_ts,有的消息体里还嵌着复杂的结构,附件、引用、线程信息各不相同。

如果核心逻辑直接去处理这些异构数据,代码很快就会变成一团乱麻。

所以OpenClaw的第一步,不是让Agent去理解任务,而是先做协议适配。

每个外部渠道都有一个专属适配器插件,把原始消息清洗成统一的内部对象,MsgContext。大概长这样:

interfaceMsgContext{

Body:string;

BodyForAgent?:string;

BodyForCommands?:string;

RawBody?:string;

SessionKey:string;

Provider:string;

Surface?:string;

ChatType?:“direct”|“group”;

SenderId?:string;

SenderName?:string;

SenderUsername?:string;

OriginatingChannel?:string;

OriginatingTo?:string;

AccountId?:string;

MessageThreadId?:string;

CommandAuthorized?:boolean;

MessageSid?:string;

GatewayClientScopes?:string[];

}

这里最关键是统一抽象。

也就是说,不管消息是从哪个犄角旮旯来的,进了网关之后,都会变成这个标准格式。后面的流程只需要对着MsgContext干活,完全不用操心来源平台。

web端消息适配

这一步把平台差异隔离在了网关入口,而不是污染到整个Agent执行链路里。

所以如果以后要接一个新通道,也不需要改核心逻辑,通常只要做四件事:

  1. 在Registry中添加通道元数据
  2. 实现对应的ChannelPlugin
  3. 在插件加载器里注册新插件
  4. 更新配置Schema和文档测试

整个过程无需改核心系统代码,这就是插件化设计真正有价值的地方。

PS:这里要特别提一句,OpenClaw很多代码都是工程产物,如果大家真的想了解他的设计核心,只需要做一个渠道就好了,就会少很多工程策略,比如这里的收束信息

信息处理

所有封装好的MsgContext,最后都会流入一个统一关卡:dispatchInboundMessage。

这一步的作用,不是做复杂业务,而是把所有入站消息的处理入口收敛成同一个总开关。源码大概是这样:

exportasyncfunctiondispatchInboundMessage(params){

constfinalized=finalizeInboundContext(params.ctx);

returnawaitwithReplyDispatcher({

dispatcher:params.dispatcher,

run:()=>dispatchReplyFromConfig({

ctx:finalized,

cfg:params.cfg,

dispatcher:params.dispatcher,

replyOptions:params.replyOptions,

replyResolver:params.replyResolver,

}),

});

}

从结构上看,它先做了两件事:

1.最终化入站上下文

也就是finalizeInboundContext。这个步骤主要负责:

  1. 补全缺失字段
  2. 标准化格式
  3. 统一上下文表示

它的意义在于,前面虽然已经做了通道适配,但不同通道在细节上仍可能有不一致的地方,所以在真正进入核心处理逻辑前,还要再做一次最终收束。

2.交给回复分发器继续往下跑

这一步之后,消息才真正开始进入OpenClaw的处理主干。

也就是说,到这里为止,系统干的还不是“理解任务”,而是先确保:

这条消息在格式上是可被系统安全处理的。

路由系统

消息一进入主链路,OpenClaw不会立刻把它扔给模型,而是先做三类关键判断:

  1. 要不要处理
  2. 有没有重复
  3. 该交给哪个Agent

这一步非常像真实系统里的“前置治理层”。

去重:先防止消息被重复处理

真实生产环境里,消息重复投递是非常常见的。

Webhook可能重试,平台可能重复推送,网络抖动也可能导致同一条消息被系统收两次。如果不做幂等控制,最坏的结果不是“多回复一句”,而是:

  1. 同一任务被执行两次
  2. 同一工具被调用两次
  3. 同一个外部API被重复触发
  4. 同一笔成本被重复消耗

所以OpenClaw会为每条消息生成一个幂等键,也就是idempotencyKey。核心逻辑由buildInboundDedupeKey控制:

exportfunctionbuildInboundDedupeKey(ctx:MsgContext):string|null

const peerId=resolveInboundPeerId(ctx);

if(!peerId){

returnnull;

}

const sessionKey=ctx.SessionKey?.trim()??“”;

const accountId=ctx.AccountId?.trim()??“”;

const threadId=ctx.MessageThreadId?String(ctx.MessageThreadId):“”;

return[provider,accountId,sessionKey,peerId,threadId,messageId]

.filter(Boolean)

.join(“|”);

}

生成格式大概是:

{provider}|{accountId}|{sessionKey}|{peerId}|{threadId}|{messageId}

比如:

都会变成自己的唯一标识。

只要缓存里发现这个键已经处理过,系统就直接返回,避免重复调用昂贵的LLM API,默认TTL是20分钟。

拦截

有些消息不是给Agent干活的,而是给系统发控制命令的,所以除了去重,系统还会做一些快速拦截。

例如用户输入/stop,那它就不该继续往下跑任务理解和工具调用,而应该立刻中断对应的AbortController,强行停止正在执行的Agent任务。

这说明OpenClaw不是一个单纯的“问一句答一句”的壳,它本质上还是一个长期运行的任务系统,所以控制命令是它必须支持的一类特殊输入。

快速响应

对于Web请求,系统通常还会先通过WebSocket返回一个started状态,然后再异步执行后续处理。

这一步看起来小,但很重要。

因为模型思考、工具调用、网络请求都可能很慢,如果前端一直等最终结果,很容易超时,也很容易让用户产生“卡死了”的感觉。

所以从体验上看,OpenClaw会先让你知道:任务已经进入执行状态了。

Agent登场

去重和拦截只是前置治理,真正进入业务之前,系统还得回答一个根本问题:这条消息,应该由谁来处理?这就是路由系统的工作。

OpenClaw的路由根据通道类型,采用两套不同策略。

Web内部通道

Web客户端通常可以直接传sessionKey,格式一般是:

{agentId}:{scope}

例如:

assistant:main

在这种情况下,系统可以直接使用这个会话键,不需要再查绑定规则。

外部通道

但像Slack、WhatsApp、Discord这类外部通道,客户端本身并不知道系统内部的会话组织方式,所以必须通过配置里的绑定规则来决定该交给哪个Agent。

例如:

{

“bindings”:[

{

“agentId”:“assistant”,

“match”:{“channel”:“whatsapp”,“accountId”:“my_bot”}

},

{

“agentId”:“vip-assistant”,

“match”:{“channel”:“whatsapp”,“peer”:{“id”:“+”}}

}

]

}

每个绑定规则都可以根据不同维度匹配:

  1. 通道标识
  2. 账户ID
  3. 对等体用户或群组
  4. Discord服务器ID
  5. 团队ID
  6. 角色列表

系统会按优先级从高到低匹配:

  1. 精确对等体匹配
  2. Discord服务器+角色匹配
  3. Discord服务器匹配
  4. 通道账户级匹配
  5. 通道级匹配
  6. 默认Agent

所以回到我们的例子:

这条钉钉消息先被识别为来自哪个通道、哪个账号、哪个用户、哪个频道,然后系统根据配置决定,最终是由assistant来处理,还是由某个专门的support-agent、vip-assistant、coder来处理。

唯一会话键

一旦Agent确定下来,系统就会为这次对话构建sessionKey,格式一般是:

{agentId}:{scope}

比如:

assistant:main

assistant:whatsapp:direct:+

assistant:discord:channel:

support:telegram:group:-90

这个键非常重要,因为它后面承担两件事:会话隔离与并发控制。

也就是说,用户看到的只是一句消息,但系统真正管理的,其实是:这句话属于哪个Agent的哪一条会话。

PS:这一段处理逻辑其实挺复杂的,我们这里不展开

车道机制

到这里,消息已经完成了路由,知道该交给哪个Agent,也知道自己属于哪个会话。

但系统仍然不会直接开跑。原因很简单:如果同一会话里两条消息同时跑,很容易出现上下文错乱。

比如用户前一秒说:

帮我整理今天的重要邮件

下一秒又说:

顺便把待办改成按优先级排序

如果两条消息并行处理,就可能出现:

  1. 第二条先完成
  2. 第一条后完成
  3. 上下文互相污染
  4. 输出顺序颠倒
  5. 工具调用状态不一致

所以OpenClaw设计了会话车道机制。

会话级车道

相同sessionKey的消息必须串行执行,确保上下文连贯。

这一步本质上是在保证:一个会话在任意时刻,只有一条消息真正占用它的执行上下文。

这样你才能把同一条对话当成“连续对话”来理解,而不是并发乱流。

全局级车道

除了会话级串行,系统还可以配置全局最大并发数。

如果整个系统同时进来太多消息,超出容量,就先进入等待队列。

这相当于两层节流:

  1. 会话层防止同一条对话乱序
  2. 系统层防止整个运行时被打爆

这一步很有OpenClaw的工程味道。它不是单纯依赖模型能力,而是在运行时层面主动治理资源。

开始执行

当消息终于排到自己,真正进入Agent执行阶段时,最重要的一件事来了:系统要为模型组装完整上下文。

大家马上会发现,要“爆炸”了…

很多人理解Agent时,容易想成这样:

  1. 用户输入一句话
  2. 模型理解一下
  3. 调用几个工具
  4. 结束

但实际在OpenClaw里,模型看到的不是单独一句用户输入,而是一整套被拼装好的上下文环境。组装顺序大致是:

系统提示词→技能提示→对话历史→当前消息

这个顺序很重要,因为它实际上定义了模型的认知层级:

  1. 先知道自己是谁
  2. 再知道自己能做什么
  3. 再知道之前发生了什么
  4. 最后才看用户刚刚说了什么

系统提示词

系统提示词的职责,是定义Agent的角色、行为规则和安全边界。

OpenClaw这里做得很工程化,它不是把一整段prompt硬写死,而是通过Bootstrap文件系统来注入。

大概会把这些文件装进上下文:

AGENTS.md:定义Agent行为规则和工具使用指南

SOUL.md:定义个性和人格

TOOLS.md:工具使用说明

IDENTITY.md:身份标识信息

USER.md:用户偏好

HEARTBEAT.md:心跳检测提示

BOOTSTRAP.md:初始化引导

MEMORY.md/memory.md:长期记忆

这些文件位于工作区目录,默认是/.openclaw/workspace。


也就是说,在真正回答你“整理邮件”之前,模型先会被告知:


我是谁


我该遵守什么规则


我能用哪些工具


这个用户有什么偏好


这个系统有哪些长期记忆


这和普通聊天机器人有个本质区别:它不是每次都从零开始聊,而是从一个被预先塑形过的Agent身份出发。


记忆召回规则


在src/agents/system-prompt.ts里,系统提示词里还会显式包含记忆召回规则:


Memory Recall


Before answering anything about prior work,decisions,dates,people,preferences,or todos:


run memory_search on MEMORY.md+memory/*.md;thenuse memory_get to pull only needed lines.


If low confidence after search,say you checked.


这段话很有意思。


它不是告诉模型“你要尽量记住”,而是告诉模型:


碰到和历史决策、偏好、待办、日期等相关的问题时,先查记忆,再说话。


这就把“记忆”从模型模糊的上下文残留,升级成了一种显式检索机制。


大小限制


因为这些文件每次运行都会消耗tokens,所以系统会限制:


  1. 单文件最大字符数


  2. 总注入字符数上限


  3. 是否显示截断警告


这说明OpenClaw的思路不是把所有东西都喂给模型,原因很简单:系统提示词本身也要受到上下文预算约束。


核心:Skills载入


系统提示词组装完之后,接下来要做的就是技能提示注入。这部分很关键,因为很多人谈OpenClaw时,最容易误解Skills。



从源码实现上看,Skills并不是简单的一堆函数列表。它更像是:


先把一组可用能力的使用说明、调用边界、适用场景告诉模型,再在模型决定调用时,去连接真实的工具实现


也就是说,Skills首先是让Agent知道自己该怎么用工具的方法包。


技能加载流程大致分四步:


发现


系统会从多个来源扫描技能文件:


  1. 工作区


  2. 用户全局目录


  3. 内置目录


  4. 插件目录


过滤


不是所有发现到的技能都能直接用,系统会根据多个条件过滤:


  1. 平台


  2. 消息通道


  3. 发送者权限


  4. 黑白名单配置


安全检查


OpenClaw对Skills做了三层策略管道:


  1. Profile过滤


  2. Sandbox隔离


  3. Subagent继承


也就是说,一个技能能不能被调用,不只是看它存不存在,还要看当前Agent有没有权限、安全边界允不允许、子Agent是否继承到对应能力。


生成提示词


最后系统会把可用技能描述格式化为文本,注入系统提示词,供LLM在需要时调用。


所以在我们的案例里,当用户说“帮我整理今天的重要邮件,提炼待办,并生成给老板的简报”时,模型不是凭空想象自己能做什么,而是会在这套技能描述中判断:


  1. 有没有邮件处理相关能力


  2. 有没有摘要生成能力


  3. 有没有文档组织能力


  4. 是否需要进一步调用子Agent


这才是Skills真正的作用。


记忆系统


系统提示词和Skills搞定后,接下来才轮到对话历史和当前消息。


会话历史


OpenClaw采用双层存储管理会话历史。


一、轻量索引:sessions.json


里面存的是元数据,例如:


会话ID


会话键


转录文件路径


最后更新时间


模型覆盖配置


技能快照


位置通常在:


/.openclaw/agents/{agentId}/sessions/sessions.json

内容如下

二、重度转录:{sessionId}.jsonl

记录完整的对话历史,采用JSON Lines格式,每行一个JSON对象,便于流式读取和追加。文件同样位于agents目录下的sessions文件夹中。

历史消息加载

系统从转录文件中读取历史时,会做几件事:

  1. 从最新消息向前读取指定token数量的轮次
  2. 过滤掉不需要的消息类型
  3. 保证时间顺序正确
  4. 估算历史消息占用的token

所以在真正调用模型前,系统已经在做一件很现实的事情:这次上下文预算里,到底还能放多少历史。

当前消息

当前消息作为上下文最后一部分注入,包括:

用户输入文本

发送者信息

时间戳

元数据

通道信息

会话键

这也意味着,模型看到这次请求时,不是只看到一句新消息,而是站在一整条会话上下文的尾部,来理解它的语义。

记忆压缩

一旦把系统提示词、技能提示、历史记录、当前消息全塞进去,另一个问题马上就来了:上下文总有一天会爆。

所以OpenClaw这里专门做了一整套防爆机制。

历史轮次限制

系统会根据通道配置限制保留的历史轮数,从最新消息开始往前扫描,丢弃更早的部分。

这是一层最简单也最直接的防线。

工具结果截断

工具调用结果可能非常大,例如:

  1. 长文本
  2. 大JSON
  3. 多页日志
  4. 大段网页内容

如果一股脑全塞回上下文,很容易直接撑爆窗口。

所以系统会自动截断工具输出,并判断是否要保留尾部关键信息。如果尾部有错误信息或JSON结构,就会采取“头尾保留”策略,否则只保留开头。

自动压缩

当上下文窗口接近模型限制时,系统会把早期历史分块,然后为每块生成摘要,用摘要替换早期历史,同时保留最近几轮完整对话。

摘要生成时要求保留:

  1. 活跃任务
  2. 操作进度
  3. 用户最后请求
  4. 已做决策
  5. 后续依赖信息

所以这不是机械裁剪,而是一种语义压缩。

容错与降级

如果压缩后仍然超限,系统还会继续尝试:

  1. 切换到上下文更大的模型
  2. 降低Agent的thinking级别
  3. 最终回退为提示用户重置会话

这说明OpenClaw不是把上下文管理交给模型自己,而是把它视为运行时层面的硬约束问题。

PS:从这里大家就可以看出,如果没有最近一年的模型上下文极速增长,根本不可能有OpenClaw这类Agent啥事

记忆系统怎么工作的

除了会话历史,OpenClaw还单独维护两类记忆:

一、长期记忆

文件名通常是:

MEMORY.md

memory.md

用于存常青知识,例如:

  1. 项目规则
  2. API文档
  3. 设计决策
  4. 长期偏好

这类记忆在Agent启动时通过Bootstrap系统直接注入系统提示词。

二、每日记忆

存放在:

memory/YYYY-MM-DD.md

用于记录时效性内容,例如:

  1. 每日纪要
  2. 当天待办
  3. 临时决策
  4. 会议记录

这类记忆不直接注入提示词,而是通过记忆搜索工具按需检索,并且会带时间衰减权重,越新的内容权重越高。

三、记忆什么时候写入

长期记忆通常由用户或Agent通过编辑工具手动维护。

而每日记忆则会通过Memory Flush机制自动触发。

触发条件:

当系统发现会话快接近压缩阈值时,会先发一个特殊提示给Agent:

Pre-compaction memory flush.

Store durable memories now(use memory/YYYY-MM-DD.md;create memory/ifneeded).

IMPORTANT:If file already exists,APPEND new content only anddonot overwrite existing entries.

也就是说,在压缩发生前,系统会先提醒Agent:把这轮对话中值得长期保留的信息,先沉淀进每日记忆。

这相当于在上下文压缩前,先打一层记忆护城河。

真的执行了

到这里,上下文已经准备好,Agent才真正开始运行。

在我们的例子里,这时候模型可能会判断:

当前任务不是普通问答

它是一个执行型任务

里面包含邮件整理、待办提炼、简报生成三类需求

需要调用相应技能和工具

如果任务过于复杂,可能还要拆给子Agent

这个阶段最核心的三件事是:

流式响应

OpenClaw使用SSE或WebSocket做流式输出。

当LLM开始生成内容时,系统会把内容块实时推送给客户端,让用户立即看到输出开始,而不是等全部完成后一次性返回。

这能明显降低感知延迟,也更适合长任务。

工具调用

当LLM判断需要调用工具时,系统会暂停文本流,执行对应工具,例如:

  1. 读取文件
  2. 调API
  3. 搜索记忆
  4. 访问邮件
  5. 执行脚本

执行完后,再把结果反馈给LLM,让它继续推理。

对用户来说,看到的可能只是正在执行工具或者一段中断后的继续输出;但对系统来说,这其实是一次完整的推理—执行—再推理循环。

错误处理与模型回退

真实生产环境里,出错是常态。所以OpenClaw做了多级回退策略:

  1. 限流:指数退避或切换备用模型
  2. 认证错误:轮换多个API Key
  3. 超时错误:降低thinking级别或换更快模型
  4. 上下文溢出:触发压缩或换更大上下文模型

这部分很能说明OpenClaw的工程取向:它不假设模型永远稳定,而是假设运行过程随时可能失败,所以提前把兜底路径都铺好。

任务完成

当Agent终于处理完

整理今天的重要邮件,提炼待办

并生成给老板的简报

这件事后,系统仍然不能立刻算结束。它还要做三类收尾动作:

响应投递

回复分发器会根据消息上下文中的来源通道和目标地址,调用对应通道的出站适配器发送消息。

如果配置了跨通道回复,也可以覆盖原始目标。例如:

  1. 在钉钉发起任务
  2. 最终把结果发回WhatsApp

这一点非常像智能网关的思路,而不只是聊天框里的模型。

但还是那句话,OpenClaw这块的源码不适合初学者,初学者直接搞一个IM渠道就好,OpenClaw很多工程代码,就是为了兜底,这些代码量全部加剧了学习成本。

会话持久化

系统会把这次交互的完整记录写下来:

  1. 更新sessions.json里的元数据
  2. 把用户消息、AI回复、工具调用等内容追加到JSONL转录文件

这样下一次会话继续时,系统才能知道之前发生过什么。

资源释放与清理

执行结束后,还要做:

  1. 释放会话车道锁
  2. 释放全局并发配额
  3. 标记幂等键为已处理
  4. 定期清理过期会话
  5. 归档旧转录
  6. 轮换大文件

这一步很像一个长期运行系统的善后逻辑,没有它,系统迟早会越来越重。

记忆索引

既然记忆是Markdown文件,系统就还要解决一个问题:怎么让Agent高效查它们。

OpenClaw的做法是给记忆系统单独建索引,索引数据库通常位于:

~/.openclaw/memory/index.db

里面大致会有这些表:

files

chunks

chunks_vec

chunks_fts

embedding_cache

也就是同时支持:

  1. 文件元数据管理
  2. 文本分块
  3. 向量检索
  4. 全文搜索
  5. 向量缓存

为了保证文件和索引同步,系统还会做三种同步机制:

  1. 文件监视器自动触发
  2. 定期同步
  3. 增量同步

必要时还会全量重建索引:

  1. 创建临时数据库
  2. 遍历记忆文件
  3. 分块
  4. 生成embedding
  5. 建全文索引
  6. 最后原子替换旧索引

综上,OpenClaw的记忆并不是把Markdown当备忘录丢在那,而是真把它做成了一层可检索、可维护的知识基座。

多Agent

前面讲到这里,其实已经是一条完整的单Agent执行链路了:

消息进门

协议适配

去重拦截

路由分发

会话排队

上下文组装

技能注入

流式执行

响应投递

状态持久化

如果任务简单,到这里就闭环了。

但现实问题是,很多复杂任务并不适合由一个Agent单独完成。还是刚才那个例子:

帮我整理今天的重要邮件,提炼待办

并生成一份给老板的简报

看起来一句话,实际上可能包含至少三块工作:

  1. 筛选和归类邮件
  2. 提炼关键待办
  3. 组织成适合老板阅读的简报格式

如果全让一个Agent一把抓,它当然也能硬做,但往往会出现:

上下文太重

推理链过长

工具调用太杂

专业能力混在一起

中间状态难管理

所以OpenClaw在单Agent之上,又做了多Agent协作系统。

多Agent的核心能力

它实现了几件关键事情:

  1. Agent隔离
  2. 动态任务分发
  3. 层级协作
  4. 生命周期管理
  5. 安全边界控制

也就是说,主Agent可以根据任务需要,临时创建一个或多个子Agent,把某些子任务交出去,自己做总控和汇总。

一个典型的多Agent流程

在我们的案例里,主Agent完全可能这么干:

  1. 先创建一个research-agent去筛重要邮件
  2. 再创建一个analysis-agent去提炼待办和风险点
  3. 最后由主Agent自己把这些结果整合成给老板的简报

这时候,多Agent就不再是抽象概念,而是变成了一种很具体的任务拆解策略。

创建subAgent

主Agent决定创建子Agent时,通常会走sessions_spawn工具。

系统先做一轮严格校验:

  1. 嵌套深度检查,防止无限递归
  2. 并发限制检查,防止资源耗尽
  3. 允许列表检查
  4. 沙箱状态检查

通过后,系统会:

生成唯一子会话键

例如agent:{agentId}:subagent:{uuid}

应用模型配置和thinking级别

处理附件和上下文传递

为子Agent生成专门的系统提示词

例如:

#Subagent Context

You are a subagent spawned by main agentfora specific task.

Your Role

-You were created to handle:${taskDescription}

-Complete this task.That’s your entire purpose.

-You are NOT main agent.Don’t try to be.

Rules

1.Stay focused

2.Complete task

3.Don’t initiate

4.Be ephemeral

这个提示词不是在强化子Agent的人格,而是在强调它的边界:你不是主Agent,你就是来做这一个子任务的。

结果返回

子Agent完成任务后,系统会:

  1. 触发生命周期事件
  2. 读取输出结果
  3. 经过通知队列
  4. 判断目标是谁
  5. 决定注入主Agent会话,还是直接发给用户

如果请求者是主Agent,那么结果通常会以内部事件的方式重新注入主Agent会话,供主Agent下一轮推理时使用。

于是整条协作链路就变成了:

主Agent接收用户任务

主Agent拆子任务

子Agent各自执行

子Agent把结果回传

主Agent汇总结果

主Agent最终回复用户

这个过程本质上就是一个层级化协作网络。

配置继承

OpenClaw在Agent配置上采用三级继承机制:

  1. Agent级配置优先级最高
  2. 全局默认配置次之
  3. 代码默认值兜底

例如某个coding-agent可以有自己的:

模型

工作区

可用工具

子Agent策略

沙箱模式

而没有单独定义的部分,再回落到全局默认值。这种设计让多Agent系统既灵活,又不至于配置爆炸。

OpenClaw强在哪

再回到最初那句话:

帮我整理今天的重要邮件,提炼待办

并生成一份给老板的简报

那么它在OpenClaw里真正经历的大致过程,其实是这样的:

钉钉原始消息进入系统

通道插件把它适配成统一的MsgContext

网关做最终化处理

系统检查去重、拦截控制命令、快速响应started状态

路由系统根据绑定规则找到目标Agent

生成sessionKey

进入会话车道排队,确保同一会话不乱序

组装完整上下文:系统提示词、Bootstrap文件、Skills、历史记录、当前消息

模型在技能描述和规则约束下开始推理

过程中可能调用工具,也可能spawn子Agent

子Agent完成后把结果回流给主Agent

主Agent生成最终答复

回复分发器把结果投递回目标通道

会话和转录被持久化

记忆被更新,索引同步

资源释放,执行闭环结束

这样一看你就会发现,OpenClaw真正有价值的地方,不是它能不能回答一句话,而是:

它把一条消息从进入系统到完成执行,做成了一条可治理、可扩展、可追踪、可恢复的Agent Runtime链路

结语

通过完整追踪一条消息的旅程,我们可以更清楚地看到OpenClaw的几个核心设计原则。

第一,它是分层的。

各层职责清晰,从通道适配到执行治理再到基础设施,边界都比较明确。

第二,它是运行时导向的。

去重、会话车道、上下文压缩、错误回退、资源清理,这些都说明它不是一个把prompt包一层UI的玩具,而是真在认真处理长期运行中的工程问题。

第三,它是可扩展的。

通道插件、技能系统、子Agent机制、记忆索引,都让它更像一个开放的Agent网关,而不是一个封闭应用。

第四,它开始具备分布式协作的雏形。

多Agent的出现,意味着OpenClaw已经不满足于“一个模型处理一切”,而是在朝任务拆解、层级协作、并行执行的方向发展。

所以如果你问,OpenClaw到底是什么。

我的回答会是:

它不是一个更会聊天的机器人,也不只是一个会调工具的Agent壳。它更像是一个把消息入口、会话治理、上下文管理、技能调用、持久化存储和多Agent协作缝合在一起的Agent Runtime+Gateway

而理解它最好的方式,就是去开发一个Mini-OpenClaw,这也是我们正在做的。

所以,后续我们再基于一个更简单版本,没有那么多工程控制的系统做讨论…

小讯
上一篇 2026-03-19 07:16
下一篇 2026-03-19 07:14

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/241917.html