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

日期:2026-03-17 19:43:58 / 人气:1



今天我们再深入一点,从源头开始,看看OpenClaw这个Agent系统是如何实现的,和其他Agent有啥不同。但单纯按模块介绍容易显得零散,所以我们回到最底层的问题:当用户真的发来一条消息时,OpenClaw这个Agent系统内部,到底是怎么跑起来的?

这件事其实比表面上看起来更重要。因为你只要真的把一条消息从头跟到尾,就会发现OpenClaw跟普通聊天机器人、传统工作流系统、以及很多只会调工具的Agent框架,差别并不在于它会不会聊天,而在于它背后有一整套完整的运行链路:消息接收、协议适配、路由分发、会话隔离、上下文组装、技能注入、流式执行、工具调用、持久化存储,以及在复杂任务下的多Agent协作。

为了让整篇文章更容易理解,我们先假设一个典型场景:在钉钉里发来一句:“帮我整理今天的重要邮件,提炼待办,并生成一份给老板的简报”。接下来,我们就沿着这条消息,看看它是如何从外部世界的一段文本,最终变成一套真正被Agent执行起来的任务链路的。

希望大家带着三个问题阅读此文:OpenClaw的整体架构设计到底是什么?一条消息在系统中的完整执行路径是什么?多Agent协作到底是怎么落地实现的?

一、OpenClaw怎么跑的?—— 五层架构与完整执行链路

很多人第一次看OpenClaw,很容易把它理解成一个能聊天、能调工具、还能帮你跨平台干活的智能助理。但如果从工程实现的角度看,OpenClaw更像是一个围绕Agent构建出来的运行时网关系统:Agent Runtime。

它不是简单地把一句用户输入丢给大模型,然后把输出再发回来,而是把整个过程拆成了一条清晰的执行链路,并在每个关键节点上做了工程治理。它的整体架构,基本可以抽象成五层,每层职责清晰、边界明确,共同支撑起整个系统的稳定运行。

1.1 五层架构详解

OpenClaw的五层架构从顶层到底层,依次为用户接口层、Gateway核心层、消息处理层、扩展与插件层、基础设施层,每层各司其职、相互配合,构成了完整的Agent运行体系。

第1层:用户接口层(入口层)

这一层是用户与系统交互的“窗口”,提供了多种接入方式,核心作用是把用户的各种操作,转化为系统能识别的统一内部请求。具体包括CLI(命令行接口)、Web UI(网页界面)、移动App、WebSocket API等入口,覆盖了不同用户的使用场景。

对用户来说,可能只是在网页里输入一句话,或者在钉钉、飞书里发一条消息;但对系统来说,这些来自不同平台的入口,最终都要收敛成统一的内部消息模型——这是后续所有流程能顺畅运行的基础,避免了因入口差异导致的核心逻辑混乱。

第2层:Gateway核心层(运行时核心)

这是OpenClaw的“心脏”,也是整个系统的核心运行时。它不直接处理具体的业务逻辑,而是负责系统的基础治理工作,确保整个系统能常驻运行、正常接收消息、及时响应消息、维持稳定状态。

具体来说,Gateway核心层的职责包括:连接管理(维护与各个接入通道的连接)、请求接入(接收来自用户接口层的所有请求)、配置热加载(无需重启系统,即可更新配置)、健康监控(实时监测系统各模块的运行状态)等。换句话说,真正让整个系统“活起来”的,不是单个Agent,而是这个Gateway。

第3层:消息处理层(业务核心层)

这是业务逻辑真正流转的核心,也是一条消息从进入系统到最终响应的“主战场”。所有与消息处理相关的核心动作,都发生在这一层,主要包括:

- Agent执行器:负责驱动Agent的具体执行,包括上下文组装、模型调用、工具调用等;

- 路由系统:负责将消息分发到对应的Agent,解决“这条消息该由谁处理”的问题;

- 会话管理:负责维护会话状态,实现会话隔离、并发控制,确保对话的连贯性;

- 媒体处理:处理消息中包含的附件、图片、音频等媒体内容;

- 出站投递:将Agent的执行结果,投递回用户所在的接入通道。

可以说,这一层是OpenClaw的“业务中枢”,决定了消息如何被处理、任务如何被执行。

第4层:扩展与插件层(可扩展层)

这一层是OpenClaw实现“可扩展”的关键,所有可插拔的扩展功能都集中在这里,让系统能够灵活适配不同的需求,无需修改核心代码即可实现功能升级。主要包括:

- 通道插件:对接钉钉、飞书、Telegram、WhatsApp、Slack等各类外部平台,实现不同平台消息的适配与流转;

- 技能工具系统:管理所有Agent可调用的技能和工具,是Agent实现“办事能力”的核心;

- sub Agent机制:支撑多Agent协作,允许主Agent根据任务需要,临时创建子Agent处理子任务。

也正因为有这一层,OpenClaw才能不断往上接新通道、往下接新工具、往内部接多Agent协作,具备了极强的灵活性和可扩展性。

第5层:基础设施层(支撑层)

这一层是整个系统的“基石”,为前面四层提供通用的基础能力,虽然平时不太显眼,但没有它,前面几层都无法稳定运行。主要包括:

- 配置与密钥管理:统一管理系统的所有配置和敏感密钥,确保配置可维护、密钥安全;

- 结构化日志:记录系统运行的所有日志,便于问题排查和系统监控;

- 定时任务:支撑周期性任务的执行,例如记忆索引的同步、过期会话的清理等;

- 事件总线:实现系统内部各模块之间的通信,解耦模块依赖;

- 记忆检索:为记忆系统提供检索能力,让Agent能够快速获取所需的历史记忆;

- 沙箱安全:隔离不同Agent的运行环境,防止恶意操作或错误操作影响整个系统。

1.2 一条消息的完整执行路径(数据流转视角)

从数据流转的视角看,一条用户消息从发出到得到响应,在OpenClaw系统中的完整路径其实很清晰,可概括为:消息源→协议适配→路由分发→会话构建→Agent执行→响应投递→状态持久化。

接下来,我们就结合前面假设的钉钉消息场景(“帮我整理今天的重要邮件,提炼待办,并生成一份给老板的简报”),一步步拆解这条路径的每一个环节,看看OpenClaw是如何一步步处理这条消息的。

二、消息进门:协议适配与前置治理—— Gateway的核心作用

从用户视角看,“帮我整理今天的重要邮件,提炼待办,并生成一份给老板的简报”只是一条普通消息。但从系统视角看,第一个问题就来了:不同平台的消息格式千差万别——钉钉的消息格式和飞书不一样,Discord和WhatsApp不一样,Telegram和内部WebSocket通道也不一样。

有的平台带message_id,有的平台叫thread_ts,有的消息体里还嵌着复杂的结构,附件、引用、线程信息各不相同。如果核心逻辑直接去处理这些异构数据,代码很快就会变成一团乱麻,后续的扩展和维护也会变得异常困难。

所以OpenClaw的第一步,不是让Agent去理解任务,而是先做“协议适配”——这也是Gateway核心层的重要工作,把异构的外部消息,转化为系统统一的内部对象。

2.1 协议适配:统一消息格式

OpenClaw为每个外部渠道都设计了专属的适配器插件,这些插件的核心作用,就是把来自不同平台的原始消息,清洗、转化成统一的内部对象——MsgContext。这个对象的大致结构如下(伪代码):

interface MsgContext {
  Body: string;          // 用户输入的核心文本
  BodyForAgent?: string; // 供Agent处理的标准化文本
  BodyForCommands?: string; // 供系统处理的命令文本
  RawBody?: string;      // 原始消息体(未清洗)
  SessionKey: string;    // 会话键(后续用于会话隔离)
  Provider: string;      // 消息来源平台(如dingtalk、wechat)
  Surface?: string;      // 接入表面(如web、app)
  ChatType?: "direct" | "group"; // 聊天类型(私聊/群聊)
  SenderId?: string;     // 发送者ID
  SenderName?: string;   // 发送者名称
  SenderUsername?: string; // 发送者用户名
  OriginatingChannel?: string; // 原始通道
  OriginatingTo?: string; // 接收目标
  AccountId?: string;    // 账户ID
  MessageThreadId?: string; // 消息线程ID
  CommandAuthorized?: boolean; // 是否授权命令
  MessageSid?: string;   // 消息唯一标识
  GatewayClientScopes?: string[]; // 网关客户端权限
}

这里最关键的是“统一抽象”——不管消息是从哪个犄角旮旯来的,进了网关之后,都会变成这个标准格式。后面的所有流程(路由、Agent执行等),只需要对着MsgContext干活,完全不用操心消息的来源平台。

这一步把平台差异隔离在了网关入口,没有污染到整个Agent执行链路。也正因为这种设计,OpenClaw后续要接入一个新通道时,不需要修改核心逻辑,通常只要做四件事:

- 在Registry(注册中心)中添加通道元数据;

- 实现对应的ChannelPlugin(通道插件);

- 在插件加载器里注册新插件;

- 更新配置Schema和文档测试。

整个过程无需改动核心系统代码,这就是插件化设计真正有价值的地方——降低扩展成本,保证核心逻辑的稳定性。

补充一句:OpenClaw很多代码都是工程产物,如果大家真的想了解它的设计核心,只需要实现一个渠道插件就好,这样能避开很多复杂的工程策略,更专注于核心的消息处理逻辑,比如这里的消息格式收束。

2.2 信息处理:收敛入口与标准化

所有封装好的MsgContext,最后都会流入一个统一的关卡:dispatchInboundMessage(入站消息分发函数)。这一步的作用,不是做复杂的业务逻辑,而是把所有入站消息的处理入口,收敛成同一个总开关,便于统一治理。其核心源码大致如下(伪代码):

export async function dispatchInboundMessage(params) {
  // 1. 最终化入站上下文,补全缺失字段、标准化格式
  const finalized = finalizeInboundContext(params.ctx);
  // 2. 交给回复分发器,继续往下执行
  return await withReplyDispatcher({
    dispatcher: params.dispatcher,
    run: () => dispatchReplyFromConfig({
      ctx: finalized,
      cfg: params.cfg,
      dispatcher: params.dispatcher,
      replyOptions: params.replyOptions,
      replyResolver: params.replyResolver,
    }),
  });
}

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

第一,最终化入站上下文(finalizeInboundContext)。前面虽然已经做了通道适配,但不同通道在细节上仍可能有不一致的地方(比如某些字段的缺失、格式的细微差异),所以在真正进入核心处理逻辑前,还要再做一次最终收束——补全缺失字段、标准化格式、统一上下文表示,确保后续流程能“无差别”处理所有消息。

第二,交给回复分发器继续往下跑。这一步之后,消息才真正开始进入OpenClaw的处理主干。也就是说,到这里为止,系统干的还不是“理解任务”,而是先确保:这条消息在格式上是可被系统安全处理的。

2.3 路由系统:前置治理与Agent分发

消息一进入主链路,OpenClaw不会立刻把它扔给模型,而是先做三类关键判断:要不要处理、有没有重复、该交给哪个Agent。这一步非常像真实系统里的“前置治理层”,目的是避免无效处理、重复处理,确保消息能准确分发到对应的Agent。

(1)去重:防止消息重复处理

真实生产环境里,消息重复投递是非常常见的——Webhook可能重试,平台可能重复推送,网络抖动也可能导致同一条消息被系统接收两次。如果不做幂等控制,最坏的结果不是“多回复一句”,而是:同一任务被执行两次、同一工具被调用两次、同一个外部API被重复触发、同一笔成本被重复消耗(比如重复调用LLM API产生额外费用)。

所以OpenClaw会为每条消息生成一个幂等键(idempotencyKey),核心逻辑由buildInboundDedupeKey函数控制,其伪代码如下:

export function buildInboundDedupeKey(ctx: MsgContext): string | null {
  // 标准化消息来源平台
  const provider = normalizeProvider(
    ctx.OriginatingChannel ?? ctx.Provider ?? ctx.Surface
  );
  // 获取消息唯一标识
  const messageId = ctx.MessageSid?.trim();
  if (!provider || !messageId) {
    return null; // 无法生成幂等键,不做去重
  }
  // 获取对等体ID(发送者/接收者相关标识)
  const peerId = resolveInboundPeerId(ctx);
  if (!peerId) {
    return null;
  }
  // 获取会话键、账户ID、线程ID
  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

每条消息都会生成一个唯一的幂等键,系统会将这个键缓存起来(默认TTL为20分钟),只要缓存里发现这个键已经处理过,系统就直接返回,避免重复调用昂贵的LLM API,降低系统成本,同时避免重复执行任务。

(2)拦截:快速处理控制命令

有些消息不是给Agent干活的,而是给系统发的控制命令,比如用户输入“/stop”,目的是中断正在执行的Agent任务。对于这类消息,系统会做快速拦截,不继续往下跑任务理解和工具调用,而是立刻中断对应的AbortController,强行停止正在执行的Agent任务。

这说明OpenClaw不是一个单纯的“问一句答一句”的聊天壳,它本质上还是一个长期运行的任务系统,所以控制命令是它必须支持的一类特殊输入,这也是工程化设计的体现——考虑到了真实使用场景中的各种异常和控制需求。

(3)快速响应:提升用户体验

对于Web请求,系统通常还会先通过WebSocket返回一个“started”状态,然后再异步执行后续的模型推理、工具调用等处理。这一步看起来很小,但对用户体验的提升非常大。

因为模型思考、工具调用、网络请求都可能很慢(比如整理邮件需要调用邮件API,生成简报需要模型长时间推理),如果前端一直等最终结果,很容易超时,也很容易让用户产生“系统卡死了”的感觉。所以OpenClaw会先让用户知道:“任务已经进入执行状态了”,降低用户的等待焦虑。

2.4 Agent登场:路由分发与会话隔离

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

OpenClaw的路由系统,根据通道类型,采用了两套不同的分发策略,确保消息能准确匹配到目标Agent。

(1)Web内部通道:直接通过sessionKey匹配

Web客户端通常可以直接传递sessionKey,这个键的格式一般是:{agentId}:{scope},例如:assistant:main、coder:dev。在这种情况下,系统可以直接使用这个会话键,不需要再查询绑定规则,直接将消息分发到对应的Agent(如assistant、coder)。

(2)外部通道:通过绑定规则匹配

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

{
  "bindings": [
    {
      "agentId": "assistant",
      "match": {"channel": "whatsapp", "accountId": "my_bot"}
    },
    {
      "agentId": "vip-assistant",
      "match": {"channel": "whatsapp", "peer": {"id": "+1234567890"}}
    }
  ]
}

每个绑定规则都可以根据不同维度进行匹配,优先级从高到低依次为:

1. 精确对等体匹配(比如指定某个具体用户的ID);

2. Discord服务器+角色匹配;

3. Discord服务器匹配;

4. 通道账户级匹配(比如某个平台的特定机器人账户);

5. 通道级匹配(比如所有来自WhatsApp的消息);

6. 默认Agent(如果没有任何规则匹配,就交给默认Agent处理)。

回到我们的案例:这条钉钉消息会先被识别为“来自钉钉通道、某个具体账户、某个具体用户”,然后系统根据配置的绑定规则,决定最终是由assistant(通用助理)来处理,还是由某个专门的support-agent(客服助理)、vip-assistant(VIP用户助理)、coder(代码助理)来处理。

(3)唯一会话键:会话隔离与并发控制的核心

一旦Agent确定下来,系统就会为这次对话构建一个唯一的sessionKey(会话键),格式一般是:{agentId}:{scope},例如:

- assistant:main(通用助理的主会话);

- assistant:whatsapp:direct:+1234567890(WhatsApp私聊的通用助理会话);

- assistant:discord:channel:987654321(Discord某频道的通用助理会话);

- support:telegram:group:-1001234567890(Telegram某群的客服助理会话)。

这个sessionKey非常重要,因为它承担着两件核心工作:会话隔离与并发控制。也就是说,用户看到的只是一句消息,但系统真正管理的,其实是:这句话属于哪个Agent的哪一条会话。通过sessionKey,系统可以确保不同会话之间互不干扰,同时控制同一会话的消息执行顺序。

2.5 车道机制:避免上下文错乱的工程设计

到这里,消息已经完成了路由,知道该交给哪个Agent,也知道自己属于哪个会话。但系统仍然不会直接开跑——原因很简单:如果同一会话里两条消息同时跑,很容易出现上下文错乱。

举个例子:用户前一秒说“帮我整理今天的重要邮件”,下一秒又说“顺便把待办改成按优先级排序”。如果两条消息并行处理,就可能出现:第二条消息先完成、第一条消息后完成,导致上下文互相污染、输出顺序颠倒、工具调用状态不一致等问题,严重影响任务执行效果。

为了解决这个问题,OpenClaw设计了“会话车道机制”,本质上是两层节流控制,确保任务执行的有序性和稳定性。

(1)会话级车道:同一会话串行执行

相同sessionKey的消息,必须串行执行——也就是说,一个会话在任意时刻,只有一条消息真正占用它的执行上下文。这样一来,用户才能把同一条对话当成“连续对话”来理解,而不是一堆并发乱流的消息。

比如前面的例子,用户的两条消息会按顺序排队,先执行“整理邮件”,再执行“待办排序”,确保上下文连贯,不会出现逻辑混乱。

(2)全局级车道:控制系统总并发

除了会话级的串行控制,系统还可以配置全局最大并发数。如果整个系统同时进来太多消息,超出了系统的处理容量,多余的消息就会先进入等待队列,按顺序等待执行。

这相当于两层节流:会话层防止同一条对话乱序,系统层防止整个运行时被打爆。这一步非常有OpenClaw的工程味道——它不是单纯依赖模型的能力,而是在运行时层面主动治理资源,确保系统的稳定运行。

三、核心能力拆解:Skills、Memory与Agent执行

当消息终于排到自己,真正进入Agent执行阶段时,最重要的一件事来了:系统要为模型组装完整的上下文。很多人理解Agent时,容易想成“用户输入一句话→模型理解→调用工具→结束”,但实际在OpenClaw里,模型看到的不是单独一句用户输入,而是一整套被拼装好的上下文环境——这也是OpenClaw与普通聊天机器人的核心区别之一。

上下文的组装顺序大致是:系统提示词→技能提示→对话历史→当前消息。这个顺序很重要,因为它实际上定义了模型的认知层级:先知道自己是谁,再知道自己能做什么,再知道之前发生了什么,最后才看用户刚刚说了什么。

接下来,我们分别拆解上下文组装的核心模块:系统提示词、Skills(技能系统)、Memory(记忆系统),以及Agent的具体执行过程。

3.1 系统提示词:Agent的“身份与规则”定义

系统提示词的核心职责,是定义Agent的角色、行为规则和安全边界——它决定了Agent“是谁”“该做什么”“不该做什么”。OpenClaw这里做得非常工程化,它不是把一整段prompt硬写死在代码里,而是通过Bootstrap文件系统来注入,让Agent的配置更灵活、更可维护。

在OpenClaw中,系统提示词会自动加载工作区目录下的一系列文件(默认工作区目录是~/.openclaw/workspace),这些文件共同构成了Agent的“身份认知”,主要包括:

- AGENTS.md:定义Agent的行为规则和工具使用指南,告诉Agent该如何规范地执行任务、调用工具;

- SOUL.md:定义Agent的个性和人格,比如是严谨的办公助理,还是活泼的聊天伙伴;

- TOOLS.md:工具使用说明,详细介绍每个工具的功能、调用方式、参数要求;

- IDENTITY.md:身份标识信息,告诉Agent它的名字、身份、所属组织等;

- USER.md:用户偏好,记录用户的习惯(比如喜欢的简报格式、待办优先级规则等);

- HEARTBEAT.md:心跳检测提示,用于系统监测Agent的运行状态;

- BOOTSTRAP.md:初始化引导,告诉Agent启动时该做什么、如何加载配置;

- MEMORY.md/memory.md:长期记忆,记录需要Agent长期记住的信息(如项目规则、API文档等)。

也就是说,在真正回答用户“整理邮件”的请求之前,模型先会被告知:我是谁、我该遵守什么规则、我能用哪些工具、这个用户有什么偏好、这个系统有哪些长期记忆。这和普通聊天机器人有个本质区别:它不是每次都从零开始聊,而是从一个被预先塑形过的Agent身份出发,执行任务更具针对性和规范性。

记忆召回规则:显式检索,而非模糊记忆

在OpenClaw的源码(src/agents/system-prompt.ts)中,系统提示词里还会显式包含记忆召回规则,这段规则非常关键,它定义了Agent如何使用记忆:

## Memory Recall
Before answering anything about prior work, decisions, dates, people, preferences, or todos:
run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only needed lines.
If low confidence after search, say you checked.

这段话的核心意思是:当Agent遇到与历史工作、决策、日期、人物、偏好或待办相关的问题时, 必须先通过memory_search工具检索MEMORY.md和memory目录下的所有文件,然后用memory_get工具提取需要的内容;如果检索后信心不足,要告诉用户“已经检查过记忆”。

这就把“记忆”从模型模糊的上下文残留,升级成了一种显式的检索机制——Agent不会靠“记不住”来敷衍用户,而是会主动去查询记忆,确保回答的准确性,这也是OpenClaw记忆系统的核心设计之一。

大小限制:控制上下文成本

因为这些Bootstrap文件每次运行都会消耗tokens(模型的上下文token是有限的,且消耗越多成本越高),所以OpenClaw会对这些文件的大小做严格限制:

- 单文件最大字符数限制;

- 所有文件注入的总字符数上限;

- 当文件超过限制时,显示截断警告。

这说明OpenClaw的思路不是“把所有东西都喂给模型”,而是在“功能完整性”和“成本控制”之间做了平衡——系统提示词本身也要受到上下文预算的约束,避免不必要的token浪费。

3.2 Skills:Agent的“办事能力”核心—— 不是工具列表,而是使用指南

系统提示词组装完之后,接下来要做的就是技能提示注入。这部分非常关键,因为很多人谈OpenClaw时,最容易误解Skills——它不是简单的一堆函数列表,而是让Agent知道“自己能做什么、该怎么用工具”的方法包。

从源码实现上看,Skills的核心逻辑是:先把一组可用能力的使用说明、调用边界、适用场景告诉模型,再在模型决定调用时,去连接真实的工具实现。也就是说,Skills首先是“使用指南”,其次才是“工具入口”。

OpenClaw的技能加载流程,大致分为四步,每一步都做了严格的治理,确保技能的可用性和安全性。

(1)发现:多来源扫描技能文件

系统会从多个来源自动扫描技能文件,确保所有可用的技能都能被识别,主要来源包括:

- 工作区目录(用户自定义的技能);

- 用户全局目录(用户所有Agent共享的技能);

- 内置目录(OpenClaw自带的基础技能,如记忆检索、文件读取等);

- 插件目录(通过扩展插件引入的技能)。

(2)过滤:筛选可用技能

不是所有发现到的技能都能直接被Agent使用,系统会根据多个条件进行过滤,确保技能的适用性和安全性:

- 平台过滤:根据当前接入的通道(如钉钉、飞书),筛选出适配该平台的技能;

- 消息通道过滤:根据消息的类型(私聊/群聊),筛选出适合当前场景的技能;

- 发送者权限过滤:根据用户的权限等级,筛选出用户有权限使用的技能(如普通用户不能使用系统管理类技能);

- 黑白名单配置:根据系统配置的技能黑白名单,过滤掉禁止使用的技能。

(3)安全检查:三层策略管道

OpenClaw对Skills做了严格的安全控制,采用三层策略管道,确保技能调用不会影响系统安全:

- Profile过滤:根据Agent的配置文件(Profile),筛选出该Agent允许使用的技能;

- Sandbox隔离:将技能的执行环境隔离在沙箱中,防止技能调用时执行恶意代码、访问敏感资源;

- Subagent继承:子Agent只能继承主Agent的部分技能,不能使用主Agent的所有技能,避免权限滥用。

也就是说,一个技能能不能被调用,不只是看它存不存在,还要看当前Agent有没有权限、安全边界允不允许、子Agent是否继承到对应能力——这体现了OpenClaw工程化设计中的“安全优先”原则。

(4)生成提示词:注入Agent上下文

最后,系统会把所有可用的技能描述,格式化为统一的文本,注入到系统提示词中,供LLM(大模型)在需要时调用。技能描述会详细说明:技能的功能、适用场景、调用参数、返回格式等,让模型能清晰地知道“该怎么用这个技能”。

回到我们的案例:当用户说“帮我整理今天的重要邮件,提炼待办,并生成给老板的简报”时,模型不是凭空想象自己能做什么,而是会在这套技能描述中判断:有没有邮件处理相关的能力(如读取邮件、筛选邮件)、有没有摘要生成能力(如提炼待办、生成简报)、有没有文档组织能力(如格式化简报),以及是否需要进一步调用子Agent来协助完成任务。

这才是Skills真正的作用——它为Agent提供了“办事的能力和方法”,让Agent不是只会聊天,而是能真正落地执行具体任务。

3.3 Memory:Agent的“长期记忆”—— 可检索、可维护的知识基座

系统提示词和Skills搞定后,接下来才轮到对话历史和当前消息——这两部分共同构成了Agent的“短期记忆”,而OpenClaw除此之外,还单独维护了两类“长期记忆”,形成了一套完整的记忆系统。

很多Agent框架的记忆,只是简单地把对话历史存起来,而OpenClaw的记忆系统,更像是一个可检索、可维护的知识基座,不仅能存,还能快速查、自动更新,这也是它的核心优势之一。

(1)记忆的分类:短期记忆与长期记忆

OpenClaw的记忆分为三类,分别对应不同的使用场景,各司其职:

① 短期记忆:会话历史

短期记忆主要是指当前会话的对话历史,包括用户输入、Agent回复、工具调用记录等,用于支撑当前对话的连贯性。OpenClaw采用双层存储管理会话历史,兼顾性能和完整性:

- 轻量索引:sessions.json,存储会话的元数据,包括会话ID、会话键、转录文件路径、最后更新时间、模型覆盖配置、技能快照等,位置通常在~/.openclaw/agents/{agentId}/sessions/sessions.json;

- 重度转录:{sessionId}.jsonl,记录完整的对话历史,采用JSON Lines格式(每行一个JSON对象),便于流式读取和追加,同样位于agents目录下的sessions文件夹中。

系统从转录文件中读取历史时,会做几件关键事情,确保上下文的合理性和经济性:

- 从最新消息向前读取指定token数量的轮次,避免加载过多历史导致上下文溢出;

- 过滤掉不需要的消息类型(如系统提示、控制命令);

- 保证时间顺序正确,确保对话的连贯性;

- 估算历史消息占用的token,确保不超过模型的上下文限制。

② 长期记忆:常青知识

长期记忆的文件名通常是MEMORY.md或memory.md,用于存储“常青知识”——也就是长期不变、需要Agent一直记住的信息,例如:项目规则、API文档、设计决策、用户长期偏好等。这类记忆在Agent启动时,会通过Bootstrap系统直接注入系统提示词,让Agent在执行任何任务时,都能随时调用这些知识。

③ 每日记忆:时效性内容

每日记忆存放在memory/YYYY-MM-DD.md(如memory/2026-03-17.md),用于记录时效性内容——也就是当天有效的信息,例如:每日纪要、当天待办、临时决策、会议记录等。这类记忆不直接注入提示词,而是通过记忆搜索工具按需检索,并且会带时间衰减权重:越新的内容权重越高,越旧的内容权重越低,确保Agent优先使用最新的信息。

(2)记忆的写入:手动维护与自动沉淀

OpenClaw的记忆写入,分为手动维护和自动沉淀两种方式,确保记忆的准确性和完整性:

- 长期记忆:通常由用户或Agent通过编辑工具手动维护,比如用户更新项目规则后,手动修改MEMORY.md文件;

- 每日记忆:通过Memory Flush(记忆刷新)机制自动触发写入,触发条件有两个:会话token数接近上下文窗口上限(默认软阈值4000 tokens)、会话转录文件大小超过阈值(默认2MB)。

当系统发现会话快接近压缩阈值时,会先发一个特殊提示给Agent:“Pre-compaction memory flush. Store durable memories now (use memory/YYYY-MM-DD.md; create memory/ if needed). IMPORTANT: If file already exists, APPEND new content only and do not overwrite existing entries.”

这句话的意思是:“压缩前的记忆刷新,请现在存储需要长期保留的记忆(使用memory/YYYY-MM-DD.md文件,必要时创建memory目录)。重要提示:如果文件已存在,只追加新内容,不要覆盖现有条目。”

这相当于在上下文压缩前,先打一层“记忆护城河”——把当前会话中值得长期保留的信息,提前沉淀到每日记忆中,避免因上下文压缩而丢失重要信息。

(3)记忆压缩:防止上下文溢出的防爆机制

一旦把系统提示词、技能提示、历史记录、当前消息全塞进去,很容易出现“上下文溢出”的问题——模型的上下文窗口是有限的,超出限制就会导致任务执行失败。所以OpenClaw专门做了一整套防爆机制,也就是“记忆压缩”,确保上下文始终在模型的限制范围内。

记忆压缩的策略分为四层,从简单到复杂,逐步兜底:

① 历史轮次限制:直接裁剪早期历史

系统会根据通道配置,限制保留的历史轮次(比如只保留最近10轮对话),从最新消息开始往前扫描,丢弃更早的部分。这是一层最简单也最直接的防线,能快速减少上下文占用的token。

② 工具结果截断:精简工具输出

工具调用结果可能非常大,比如长文本、大JSON、多页日志、大段网页内容,如果一股脑全塞回上下文,很容易直接撑爆窗口。所以系统会自动截断工具输出,并判断是否要保留尾部关键信息:如果尾部有错误信息或JSON结构,就采取“头尾保留”策略(保留开头和结尾的关键内容),否则只保留开头的核心信息。

③ 自动压缩:语义级摘要替换

当上下文窗口接近模型限制时,系统会把早期历史分块,然后为每块生成摘要,用摘要替换早期历史,同时保留最近几轮完整对话。摘要生成时,会严格要求保留以下关键信息:活跃任务、操作进度、用户最后请求、已做决策、后续依赖信息——这不是机械裁剪,而是一种语义压缩,确保Agent能通过摘要,快速回忆起早期对话的核心内容,而不会丢失关键信息。

④ 容错与降级:最后的兜底策略

如果压缩后仍然超限,系统还会继续尝试以下兜底策略,确保任务能正常执行:

- 切换到上下文更大的模型(如从GPT-4 Turbo 128k切换到GPT-4 Turbo 256k);

- 降低Agent的thinking级别(减少模型的推理深度,优先保证任务完成);

- 最终回退为提示用户重置会话(告知用户“会话过长,请重置会话后再尝试”)。

这说明OpenClaw不是把上下文管理交给模型自己,而是把它视为运行时层面的硬约束问题,提前做好了各种兜底方案,确保系统的稳定性。

补充一句:从这里大家就可以看出,如果没有最近一年大模型上下文窗口的极速增长(从几万token到几十万token),根本不可能有OpenClaw这类复杂Agent系统的出现——上下文窗口的扩大,为多轮对话、复杂任务执行、记忆系统的落地,提供了基础条件。

(4)记忆索引:高效检索的核心

既然记忆是以Markdown文件的形式存储的,那么系统就还要解决一个核心问题:怎么让Agent高效地检索这些记忆?如果只是简单地遍历文件,效率会非常低,尤其是当记忆文件数量多、内容量大的时候。

OpenClaw的做法是:给记忆系统单独建立索引,索引数据库通常位于~/.openclaw/memory/index.db,里面大致包含以下几张表,同时支持多种检索方式:

- files:存储记忆文件的元数据(如文件名、创建时间、修改时间、大小等);

- chunks:存储文本分块(将记忆文件拆分成小块,便于检索);

- chunks_vec:存储文本分块的向量表示,支持向量检索(快速匹配语义相似的内容);

- chunks_fts:存储文本分块的全文索引,支持全文搜索(快速匹配关键词);

- embedding_cache:缓存向量嵌入结果,避免重复生成,提升检索效率。

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

- 文件监视器自动触发:当记忆文件被修改、新增、删除时,自动更新索引;

- 定期同步:每隔一段时间,系统自动检查文件和索引的一致性,同步更新;

- 增量同步:只同步新增和修改的内容,减少同步成本。

必要时,系统还会全量重建索引,确保索引的准确性,重建流程如下:

1. 创建临时数据库,避免影响当前索引的使用;

2. 遍历所有记忆文件,读取文件内容;

3. 将文件内容分块,生成文本分块;

4. 为每个文本分块生成embedding(向量表示);

5. 建立全文索引和向量索引;

6. 原子替换旧索引,完成重建。

综上,OpenClaw的记忆并不是把Markdown文件当备忘录丢在那里,而是真的把它做成了一层可检索、可维护、高效的知识基座,为Agent的准确执行提供了有力支撑。

3.4 Agent执行:流式响应、工具调用与错误处理

到这里,上下文已经准备好(系统提示词+技能提示+记忆+当前消息),Agent才真正开始运行。在我们的案例里,这时候模型会先做一轮推理,判断当前任务的性质:这不是普通的问答任务,而是一个执行型任务,包含邮件整理、待办提炼、简报生成三类需求,需要调用相应的技能和工具,甚至可能需要拆分给子Agent处理。

这个阶段最核心的三件事,是流式响应、工具调用、错误处理——这三件事直接决定了用户体验和任务执行的稳定性。

(1)流式响应:降低感知延迟

OpenClaw使用SSE(服务器向客户端推送事件)或WebSocket做流式输出。当LLM开始生成内容时,系统会把内容块(比如一句话、一个段落)实时推送给客户端,让用户立即看到输出开始,而不是等全部完成后一次性返回。

比如生成给老板的简报时,Agent会先推送“正在整理今日重要邮件...”,然后逐步推送邮件摘要、待办列表、简报内容,用户能实时看到任务的执行进度,大大降低了等待焦虑,也更适合长任务的执行场景。

(2)工具调用:推理—执行—再推理的循环

当LLM判断需要调用工具时,系统会暂停文本流,执行对应的工具,执行完成后,再把结果反馈给LLM,让它继续推理——这是一个完整的“推理—执行—再推理”循环,也是Agent能落地执行具体任务的核心。

在我们的案例里,Agent可能会调用以下工具:

- 邮件读取工具:连接用户的邮箱,读取今天的所有邮件;

- 邮件筛选工具:筛选出重要邮件(根据发件人、主题、内容关键词);

- 待办提炼工具:从重要邮件中提取关键待办事项,按优先级排序;

- 简报生成工具:将邮件摘要、待办列表,组织成适合老板阅读的简报格式(简洁、重点突出)。

对用户来说,看到的可能只是“正在执行工具”或者一段中断后的继续输出;但对系统来说

作者:耀世娱乐-耀世注册登录平台




现在致电 8888910 OR 查看更多联系方式 →

COPYRIGHT © 耀世娱乐 版权所有