OpenClaw 不是一个简单的聊天机器人或工具调用框架,而是一个集成了消息处理、会话治理、上下文管理、技能执行和多Agent协作的完整运行时网关系统(Agent Runtime),其工程设计的核心价值在于将用户请求转化为一条可治理、可扩展、可追踪的执行链路。 ## 1. 整体架构:五层运行时网关 OpenClaw的整体架构抽象为五层,从用户接口到基础设施,职责清晰。用户接口层将各种入口请求收敛为统一内部消息;Gateway核心层负责系统常驻运行与基础治理;消息处理层是业务逻辑流转的核心;扩展与插件层实现通道、技能和子Agent的可插拔;基础设施层提供配置、日志、记忆检索等通用能力,确保系统稳定。 ## 2. 消息的完整执行路径 一条消息的完整路径遵循“消息源→协议适配→路由分发→会话构建→Agent执行→响应投递→状态持久化”的清晰流程。协议适配器将异构平台消息清洗为标准`MsgContext`对象,隔离平台差异。随后,消息经过`dispatchInboundMessage`统一入口,进行去重(基于`idempotencyKey`,默认TTL 20分钟)、拦截和控制命令处理,确保系统安全。 ## 3. 路由与会话隔离 路由系统根据通道类型决定消息由哪个Agent处理:Web内部通道可直接指定`sessionKey`(格式如`{agentId}:{scope}`),而外部通道则依赖绑定规则匹配。系统通过“会话车道”机制保证同一`sessionKey`下的消息串行执行,防止上下文错乱,并结合全局并发控制进行资源治理。 ## 4. 上下文组装与记忆系统 Agent执行前,系统会为模型组装一套分层上下文,顺序为:系统提示词(通过Bootstrap文件系统注入)→ 技能提示 → 对话历史 → 当前消息。会话历史采用双层存储(轻量索引`sessions.json`和重度转录`JSONL`文件)。为防止上下文爆炸,系统实施了历史轮次限制、工具结果截断、自动压缩(生成摘要替换早期历史)等多级防爆机制。长期记忆(`MEMORY.md`)和每日记忆(`memory/YYYY-MM-DD.md`)则通过记忆召回规则和索引系统(`index.db`)支持按需检索。 ## 5. 技能(Skills)与执行 Skills不是简单的工具列表,而是让Agent知晓能力边界和使用方法的说明包。技能经过发现、过滤(权限、安全策略)后,其描述被格式化注入系统提示词。真正的执行是流式的,结合工具调用和错误回退策略(如限流、模型降级),并在任务完成后进行响应投递、会话持久化和资源清理。 ## 6. 多Agent协作的实现 对于复杂任务,OpenClaw支持多Agent协作。主Agent可通过`sessions_spawn`工具创建子Agent,并施加严格的嵌套深度、并发和安全性检查。子Agent拥有专门的系统提示词,强调其临时性和任务边界。子Agent的结果通过内部事件回传主Agent,形成层级化协作网络。Agent配置采用三级继承机制(Agent级 > 全局默认 > 代码默认),保证灵活性与可控性。 ## 7. 核心设计原则与价值 OpenClaw的核心在于其分层、运行时导向、可扩展并具备分布式协作雏形的设计。它通过工程治理(如去重、车道、压缩、回退)解决了Agent长期运行中的实际问题,将其从一个Prompt包装升级为一个真正的Agent Runtime+Gateway。
万字拆解OpenClaw:从Gateway、Memory、Skills、多Agent 到Runtime
2026-03-16 08:35

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

本文来自微信公众号: 叶小钗 ,作者:叶小钗,原文标题:《万字拆解 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. 出站投递


  6. ...


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


第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 provider=normalizeProvider(


ctx.OriginatingChannel??ctx.Provider??ctx.Surface


);


const messageId=ctx.MessageSid?.trim();


if(!provider||!messageId){


returnnull;


}


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}


比如:


  • whatsapp||main:+1234567890|msg_123


  • discord|default|agent:assistant:123|987654321||11223


  • slack|default|main:default|U12345678||C12345678


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


只要缓存里发现这个键已经处理过,系统就直接返回,避免重复调用昂贵的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":"+1234567890"}}


}


]


}


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


  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:+1234567890


assistant:discord:channel:987654321


support:telegram:group:-1001234567890


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


也就是说,用户看到的只是一句消息,但系统真正管理的,其实是:这句话属于哪个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机制自动触发。


触发条件:


  • 会话token数接近上下文窗口上限(默认软阈值4000 tokens)


  • 会话转录文件大小超过阈值(默认2MB)


当系统发现会话快接近压缩阈值时,会先发一个特殊提示给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,这也是我们正在做的。


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

AI创投日报频道: 前沿科技
本内容来源于网络 原文链接,观点仅代表作者本人,不代表虎嗅立场。
如涉及版权问题请联系 hezuo@huxiu.com,我们将及时核实并处理。
正在改变与想要改变世界的人,都在 虎嗅APP
赞赏
关闭赞赏 开启赞赏

支持一下   修改

确定