乐于分享
好东西不私藏

OpenClaw 实现原理:从消息入口到 Agent Runtime 的完整拆解

OpenClaw 实现原理:从消息入口到 Agent Runtime 的完整拆解

文档概要

  • 字数:约 5,352 字(不含代码块)
  • 参考源码版本:openclaw-2026.5.7
  • 核心主旨:深入拆解 OpenClaw 如何通过分层架构(用户接口层 → Gateway 核心层 → 消息处理层 → 扩展与插件层 → 基础设施层)实现可治理的 Agent Runtime,包括消息路由、会话治理、Skills 注入、记忆系统、多 Agent 协作等核心机制。

目录

  • 一、OpenClaw 是什么:定位与核心架构
  • 二、Gateway 网关:消息的统一入口与路由
  • 三、会话治理:车道机制与并发控制
  • 四、上下文组装:从 Bootstrap 到 Skills 注入
  • 五、Skills 系统:Agent 的工程化能力单元
  • 六、记忆系统:上下文工程的核心
  • 七、Agent 执行生命周期
  • 八、会话持久化与资源管理
  • 九、多 Agent 协作
  • 十、OpenClaw vs Hermes:两种 Agent 架构的对比
  • 十一、安全与风险
  • 十二、工程启示与核心结论
  • 附录:关键源码文件索引

一、OpenClaw 是什么:定位与核心架构

OpenClaw 是一个开源数字员工框架,其核心定位可以概括为四个关键组件的组合:聊天入口 + Agent 编排 + 本地执行沙箱 + 可扩展技能包。这一定位决定了它与传统聊天机器人的本质区别——它不是单纯的自然语言对话系统,而是一个具备工程化执行能力的 Agent Runtime。

五层架构

OpenClaw 采用清晰的分层架构设计:

  1. 用户接口层:负责与各消息通道的协议适配,包括 Web 聊天、飞书、Telegram、Discord、Slack、WhatsApp 等
  2. Gateway 核心层:消息的统一入口与路由分发,实现消息去重、会话绑定、快速响应拦截
  3. 消息处理层:会话治理、上下文组装、Agent 执行调度、ContextEngine 委托分发
  4. 扩展与插件层:Skills 技能系统、Channel Plugins、Memory Providers、Memory Embedding Providers、RealtimeVoice Providers、Channel Adapter Types (Outbound/Config/Setup/Security)
  5. 基础设施层:配置管理(ConfigSchema)、持久化存储、沙箱隔离、审计日志、Secrets 密钥管理

核心数据流

一条消息从进入到响应的完整生命周期:

这个数据流的关键特征是可治理性——每个环节都有明确的边界、可观测的指标和可控的策略。源码中 src/gateway/server-methods/shared-types.ts 定义了 Gateway 请求上下文类型,而 src/agents/pi-embedded-runner/ 则是 Agent 执行的核心入口。

消息处理完整时序


二、Gateway 网关:消息的统一入口与路由

多通道适配

OpenClaw 通过 ChannelPlugin 机制实现多通道适配。每个通道(如 wechatfeishutelegramdiscord)都有对应的插件实现,这些插件在 src/channels/plugins/ 目录中通过 createChannelPluginBase 函数创建基础实例。

通道插件的核心职责:

  • 将外部消息协议转换为 OpenClaw 内部统一的 MsgContext 格式
  • 将 OpenClaw 的响应消息转换回外部通道的协议格式
  • 处理通道特有的功能(如飞书的卡片消息、Telegram 的 Inline Keyboard)

统一消息模型 MsgContext

MsgContext 是 OpenClaw 内部消息的统一表示,其关键字段包括:

interface MsgContext {  Body: string;           // 消息正文  SessionKey: string;     // 会话标识键  Provider: string;       // 消息来源通道(如 "webchat", "feishu")  ChatType: "direct" | "group" | "channel";  SenderId: string;       // 发送者 ID  PeerId: string;         // 对端标识(群聊为群 ID,私聊为对方 ID)  ThreadId?: string;      // 线程/话题 ID(用于支持话题功能的通道)  MessageId: string;      // 消息唯一 ID  Timestamp: number;      // 消息时间戳}

消息去重机制

Gateway 实现了基于幂等键的消息去重,防止网络重传或通道回调导致的重复处理:

idempotencyKey = provider|accountId|sessionKey|peerId|threadId|messageId

幂等检查基于 Gateway 请求上下文的内嵌 dedupe Map 实现,配合 15 秒错误重试宽限期AGENT_LIFECYCLE_ERROR_RETRY_GRACE_MS)。幂等键的生命周期与 Gateway 请求上下文绑定,非独立 TTL 机制。

源码实现位于 src/gateway/server-methods/chat.tschat.send 幂等检查)和 src/gateway/server-methods/agent.tsagent.run 幂等检查)。

路由系统

消息路由的核心逻辑在 src/gateway/server-methods/sessions-resolve.ts(session key 规范化)和 src/infra/outbound/session-binding-service.ts(会话绑定服务)中实现。

Web 通道:直接使用 sessionKey 进行路由,格式为 assistant:main

外部通道:通过 bindings 规则 进行匹配。绑定记录存储在 ~/.openclaw/state/bindings/current-conversations.json,包含以下信息:

{"version"1,"bindings": [{"bindingId""generic:feishu␟account1␟␟conv123","targetSessionKey""assistant:feishu:direct:ou_xxx","conversation": {"channel""feishu","accountId""account1","conversationId""conv123"    },"status""active","boundAt"1716123456789,"expiresAt"1716210000000  }]}

路由优先级(6 级):

  1. 精确匹配当前对话绑定
  2. 精确匹配会话键
  3. 模糊匹配通道 + 账号
  4. 默认主会话
  5. 新建会话
  6. 拒绝处理

Scope 路由机制:OpenClaw 支持多种会话 scope 类型:maindirectdmgroupchannelcronrunsubagentacpthreadtopic。路由时根据会话 scope 和客户端类型确定投递通道。

ACP/Plugin 会话绑定:外部通道绑定通过 getSessionBindingService().resolveByConversation() 解析,支持 ACP (Agent Communication Protocol) 会话的专门路由机制。

会话键格式

会话键(Session Key)是 OpenClaw 识别会话的唯一标识,格式设计遵循层级结构:

assistant:main                           # 主会话assistant:whatsapp:direct:+1234567890    # WhatsApp 私聊assistant:feishu:group:oc_xxx            # 飞书群聊assistant:telegram:channel:@channelname  # Telegram 频道agent:workspace-id:session-xxx           # 子 Agent 会话

源码 src/routing/session-key.ts 实现了会话键的解析、验证和生成逻辑。

拦截与快速响应

Gateway 在消息进入 Agent 执行前会进行拦截检查:

  • /stop 指令:立即中断当前会话执行,释放车道锁
  • WebSocket started 状态:如果会话正在执行中,返回状态提示而非重复启动
  • 心跳消息:识别并跳过心跳消息的处理

三、会话治理:车道机制与并发控制

OpenClaw 的会话治理采用双层车道(Lane)机制,确保并发安全。

会话级车道

相同 sessionKey 的消息串行执行,防止同一会话的上下文错乱。源码 src/process/lanes.ts 定义了 CommandLane 枚举:

exportconstenum CommandLane {  Main = "main",  Cron = "cron",  CronNested = "cron-nested",  Subagent = "subagent",  Nested = "nested",}

src/agents/lanes.ts 中定义了 Agent 特有的车道解析逻辑:

exportconst AGENT_LANE_NESTED = CommandLane.Nested;exportconst AGENT_LANE_SUBAGENT = CommandLane.Subagent;const NESTED_LANE_PREFIX = `${NESTED_LANE}:`;exportfunctionresolveNestedAgentLaneForSession(sessionKey: string | undefined): string{const trimmed = sessionKey?.trim();if (!trimmed) return AGENT_LANE_NESTED;return`${NESTED_LANE_PREFIX}${trimmed}`;}

会话级车道的实现通过写锁(Write Lock) 机制,在 src/agents/session-write-lock.ts 中:

// acquireSessionWriteLock - 获取会话写锁,确保同一会话的串行执行// resolveSessionLockMaxHoldFromTimeout - 从超时计算最大持有时间// resolveSessionWriteLockAcquireTimeoutMs - 确定锁获取超时// 支持超时和最大持有时间限制

全局车道

限制系统总并发数,防止资源耗尽。全局车道通过信号量(Semaphore)实现,控制同时执行的会话数量上限。

双层节流的效果

层级
目的
实现机制
会话层
防止同一会话的消息乱序处理
写锁 + 消息队列
系统层
防止系统资源被打爆
信号量 + 并发配额

这种设计确保了:

  1. 单个会话的消息按顺序处理,上下文不会错乱
  2. 系统整体并发可控,不会因消息洪泛而崩溃
  3. 支持优先级调度,紧急消息可以插队

四、上下文组装:从 Bootstrap 到 Skills 注入

系统提示词文件体系

OpenClaw 的系统提示词(System Prompt)由多个文件组装而成,按优先级从高到低:

文件
作用
加载时机
BOOTSTRAP.md
首次运行引导流程
仅在首次启动或引导未完成时
AGENTS.md
工作区规则和约定
每次会话启动
SOUL.md
Agent 人格和说话风格
每次会话启动
IDENTITY.md
Agent 身份信息(名称、头像)
每次会话启动
USER.md
用户画像和偏好
每次会话启动
TOOLS.md
工具使用说明和技巧
每次会话启动
HEARTBEAT.md
心跳检查提示词
定期心跳检查时
MEMORY.md
长期记忆内容
主会话启动时

源码 src/agents/bootstrap-hooks.ts 实现了 Bootstrap 提示词的构建:

exportfunctionbuildFullBootstrapPromptLines(params: {  readLine: string;  firstReplyLine: string;}): string[] {return [    params.readLine,"If this run can complete the BOOTSTRAP.md workflow, do so.","If it cannot, explain the blocker briefly, continue with any bootstrap steps that are still possible here, and offer the simplest next step.","Do not pretend bootstrap is complete when it is not.","Do not use a generic first greeting or reply normally until after you have handled BOOTSTRAP.md.",    params.firstReplyLine,  ];}

Bootstrap Mode 判定逻辑

Bootstrap 有三种工作模式,由 src/agents/bootstrap-mode.ts 中的 resolveBootstrapMode() 函数确定:

exporttype BootstrapMode = "full" | "limited" | "none";exportfunctionresolveBootstrapMode(params: {  bootstrapPending: boolean;  runKind?: "default" | "heartbeat" | "cron";  isInteractiveUserFacing: boolean;  isPrimaryRun: boolean;  isCanonicalWorkspace: boolean;  hasBootstrapFileAccess: boolean;}): BootstrapMode

判定条件:

  • “none”:无 bootstrap 待处理,或 heartbeat/cron 运行,或非主要/非交互式运行
  • “limited”:有待处理但无文件访问权限或非规范工作区
  • “full”:有待处理且在规范工作区中有文件访问权限

记忆召回规则

OpenClaw 实现了先查记忆再回答的策略。当用户问题涉及历史决策、偏好、待办事项等需要跨会话记忆的内容时,Agent 会:

  1. 调用 memory_search 工具检索 MEMORY.md 和 memory/YYYY-MM-DD.md
  2. 根据检索结果决定是否需要补充上下文
  3. 在回答中引用相关记忆内容

这个规则在系统提示词中明确声明,确保 Agent 不会凭空回答需要历史上下文的问题。

Skills 注入流程

Skills 的注入是一个多阶段流程:

发现 → 配置继承 → 过滤 → 安全检查 → 提示词生成

1. 发现阶段:扫描多个目录寻找 SKILL.md 文件

源码 src/agents/skills/workspace.ts 中的技能加载逻辑(6 级优先级):

// 优先级顺序:extra < bundled < managed < agents-skills-personal < agents-skills-project < workspaceconst merged = new Map<string, LoadedSkillRecord>();for (const record of extraSkills) { merged.set(record.skill.name, record); }for (const record of bundledSkills) { merged.set(record.skill.name, record); }for (const record of managedSkills) { merged.set(record.skill.name, record); }for (const record of personalAgentsSkills) { merged.set(record.skill.name, record); }for (const record of projectAgentsSkills) { merged.set(record.skill.name, record); }for (const record of workspaceSkills) { merged.set(record.skill.name, record); }

技能加载常数(src/agents/skills/workspace.ts):

const DEFAULT_MAX_CANDIDATES_PER_ROOT = 300;const DEFAULT_MAX_SKILLS_LOADED_PER_SOURCE = 200;const DEFAULT_MAX_SKILLS_IN_PROMPT = 150;const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 18_000;const DEFAULT_MAX_SKILL_FILE_BYTES = 256_000;

2. 配置继承:Skills 可以继承全局配置,支持环境变量覆盖

3. 过滤阶段:根据技能过滤器(skillFilter)筛选需要注入的 Skills

4. 安全检查:验证 Skills 路径是否在允许范围内,防止路径穿越攻击

5. 提示词生成:将 Skills 信息格式化为系统提示词的一部分

三级预算降级策略

当 Skills 数量过多导致提示词超长时,采用三级降级策略:

级别
格式
Token 消耗
完整格式
包含 name + description + location + 完整 SKILL.md 内容
紧凑格式
仅包含 name + location
二分截断
保留一半 Skills,丢弃另一半
动态

源码中的配置:

const DEFAULT_MAX_SKILLS_IN_PROMPT = 150;const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 18000;

compactSkillPaths 路径压缩

为了节省 token,OpenClaw 会将技能文件路径中的用户主目录替换为 ~。源码 src/agents/skills/workspace.ts

functioncompactSkillPaths(skills: Skill[]): Skill[] {const homes = resolveCompactHomePrefixes();if (homes.length === 0return skills;return skills.map((s) => ({    ...s,    filePath: compactHomePath(s.filePath, homes),  }));}functionresolveCompactHomePrefixes(): string[] {const homes = [resolveHomeDir(), resolveUserHomeDir(), resolveNativeUserHomeDir()].filter(Boolean);const resolvedHomes = homes.map((home) => path.resolve(home));const realHomes = resolvedHomes.map((home) => tryRealpath(home)).filter(Boolean);return [...resolvedHomes, ...realHomes]    .filter((home, index, all) => all.indexOf(home) === index)    .sort((a, b) => b.length - a.length); // longest first for proper prefix matching}

示例:/Users/alice/.bun/.../skills/github/SKILL.md → ~/.bun/.../skills/github/SKILL.md

每个路径节省约 5-6 个 token,对于 100+ 技能可节省 500-600 token。


五、Skills 系统:Agent 的工程化能力单元

Skills 的本质

Skills 不是高级 Prompt,而是 Workflow 的迁移。这是理解 OpenClaw Skills 系统的关键。

一个 Skill 包含:

  • 元数据namedescription、触发条件
  • 核心指令SKILL.md 文件中的详细指导
  • 代码与资源scripts/ 目录下的可执行脚本

当 Agent 调用 Skill 时,它不是在”使用一个更好的提示词”,而是在”执行一个预定义的工作流”。这个工作流可能包含:

  • 多个工具的协调调用
  • 特定的错误处理逻辑
  • 结果的格式化和验证

三层渐进式披露

Skills 的加载遵循渐进式披露原则,按需加载更深层次的内容:

层级
内容
加载时机
元数据层
name + description
系统提示词注入时
核心指令层
SKILL.md 完整内容
Agent 决定使用该 Skill 时
代码资源层
scripts/ 下的脚本
实际执行脚本时

这种设计确保:

  • 系统提示词不会因为 Skills 过多而爆炸
  • Agent 可以根据 description 快速筛选相关 Skills
  • 只有真正需要的 Skill 才会加载完整内容

六级优先级覆盖

Skills 的加载来源有六个层级,优先级从低到高:

具体路径映射:

来源
路径变量
示例路径
内置 bundled
{bundled_skill_dir} ~/Library/Application Support/QClaw/openclaw/config/skills
用户 managed
{managed_skill_dir} ~/.qclaw/skills
个人 Agent
{openclaw_skill_dir} ~/.openclaw/workspace/skills
工作区
{workspace_skill_dir} ~/.openclaw/workspace/skills

高优先级的 Skill 可以覆盖低优先级的同名 Skill,实现定制化需求。

SkillInvocationPolicy

每个 Skill 可以定义其调用策略。源码 src/agents/skills/frontmatter.ts 中的 resolveSkillInvocationPolicy() 函数解析 frontmatter:

---name:dangerous-skilldescription:需要用户显式调用的危险操作invocation:user-invocable:true# 用户可以通过 /skill 命令调用disable-model-invocation:true# 禁止 Agent 自主调用---

Metadata 完整 Schema

---metadata:skillKey:string# 自定义查找键(默认为 skill name)primaryEnv:string# 首选环境变量emoji:stringhomepage:stringos:[linux,darwin,win32]always:boolean# 即使没有显式过滤匹配也始终包含requires:bins:[string]# 必需的可执行文件anyBins:[string]# 任意一个存在的二进制文件env:[string]# 必需的环境变量config:[string]# 必需的配置文件install:-kind:brew|node|go|uv|downloadformula:string# brewpackage:string# npm/pnpm/bunmodule:string# gourl:string# download---

这个策略由 loadSingleSkillDirectory 函数解析,返回时携带 disableModelInvocation 标志。

四种触发方式

  1. 语义触发:Agent 根据用户问题和 Skill description 的语义匹配自主选择
  2. /skill 指令触发:用户显式调用特定 Skill
  3. Cron 定时触发:通过 qclaw-cron-skill 配置定时任务
  4. Webhook 事件驱动:外部系统通过 Webhook 触发 Skill 执行

安全扫描

源码 src/security/skill-scanner.ts 实现了对 Skill 代码的安全扫描。扫描规则分为 Line Rules(逐行扫描)和 Source Rules(全文扫描):

Line Rules(逐行扫描):

const LINE_RULES = [  {    ruleId: "dangerous-exec",    severity: "critical",    message: "Shell command execution detected (child_process)",    pattern: /\b(exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(/,    requiresContext: /child_process/  },  {    ruleId: "dynamic-code-execution",    severity: "critical",    message: "Dynamic code execution detected",    pattern: /\beval\s*\(|new\s+Function\s*\(/  },  {    ruleId: "crypto-mining",    severity: "critical",    message: "Possible crypto-mining reference detected",    pattern: /stratum\+tcp|stratum\+ssl|coinhive|cryptonight|xmrig/i  },  {    ruleId: "suspicious-network",    severity: "warn",    message: "WebSocket connection to non-standard port",    pattern: /new\s+WebSocket\s*\(\s*["']wss?:\/\/[^"']*:(\d+)/  }];

Source Rules(全文扫描,8 行窗口上下文):

const SOURCE_RULES = [  {    ruleId: "potential-exfiltration",    severity: "warn",    message: "Possible data exfiltration (file read + network send)",    pattern: /fs\.readFile|fs\.readFileSync|fs\.createReadStream/,    requiresContext: NETWORK_SEND_CONTEXT_PATTERN,    requiresContextWindowLines: 8  },  {    ruleId: "obfuscated-code",    severity: "warn",    message: "Hex-encoded string detected",    pattern: /(\\x[0-9a-fA-F]{2}){6,}/  },  {    ruleId: "env-harvesting",    severity: "critical",    message: "Environment variable access combined with network send — possible credential harvesting",    pattern: /process\.env/,    requiresContext: NETWORK_SEND_CONTEXT_PATTERN,    requiresContextWindowLines: 8  }];

Comment Stripping:启发式分析前会剥离字符串字面量和注释(stripCommentsForHeuristics 函数,第 239-299 行),用于在应用源规则前减少误报。

扫描结果分为 critical(严重)和 warn(警告)两个级别,严重问题会阻止 Skill 加载。

文件监听热更新

Skills 支持热更新,当 Skill 文件变化时自动重新加载:

  • 使用 Chokidar 库监听文件变化
  • 250ms 去抖(debounce)防止频繁重载
  • 每次重载递增版本号,用于缓存失效

环境变量安全注入

Skills 可以定义需要的环境变量,但注入过程有严格的安全控制:

源码 env-overrides-USlLhNL_.js

const SKILL_ALWAYS_BLOCKED_ENV_PATTERNS = [/^OPENSSL_CONF$/i];functionisAlwaysBlockedSkillEnvKey(key{return isDangerousHostEnvVarName(key) ||          isDangerousHostEnvOverrideVarName(key) ||          matchesAnyPattern(key, SKILL_ALWAYS_BLOCKED_ENV_PATTERNS);}

源码 host-env-security-BmNeVswf.js 定义了完整的危险环境变量列表:

var host_env_security_policy_default = {blockedEverywhereKeys: ["NODE_OPTIONS""NODE_PATH""PYTHONHOME""PYTHONPATH","PERL5LIB""PERL5OPT""RUBYLIB""RUBYOPT","BASH_ENV""ENV""BROWSER""GIT_EDITOR","SHELL""SHELLOPTS""PS4""GCONV_PATH""IFS","SSLKEYLOGFILE""JAVA_OPTS""JAVA_TOOL_OPTIONS",// ... 更多危险变量  ],blockedOverrideOnlyKeys: ["HOME""GRADLE_USER_HOME""ZDOTDIR","OPENSSL_CONF""OPENSSL_ENGINES","HTTP_PROXY""HTTPS_PROXY""ALL_PROXY","NODE_TLS_REJECT_UNAUTHORIZED""NODE_EXTRA_CA_CERTS",// ... 更多敏感变量  ]};

环境变量注入采用引用计数机制:

const activeSkillEnvEntries = newMap();functionacquireActiveSkillEnvKey(key, value{const active = activeSkillEnvEntries.get(key);if (active) {    active.count += 1;returntrue;  }// 首次注入  activeSkillEnvEntries.set(key, { baseline: process.env[key], value, count1 });returntrue;}functionreleaseActiveSkillEnvKey(key{const active = activeSkillEnvEntries.get(key);if (!active) return;  active.count -= 1;if (active.count > 0return// 还有引用// 释放:恢复基线值或删除  activeSkillEnvEntries.delete(key);if (active.baseline === undefineddelete process.env[key];else process.env[key] = active.baseline;}

ClawHub 技能市场

OpenClaw 内置 ClawHub(小写)技能市场,提供 38000+ 可安装技能。源码 src/agents/skills/clawhub.ts 实现了与 ClawHub 的交互:

asyncfunctionsearchSkillsFromClawHub(params{returnawait searchClawHubSkills({query: params.query?.trim() || "*",limit: params.limit,baseUrl: params.baseUrl  });}asyncfunctioninstallSkillFromClawHub(params{// 1. 获取技能详情和版本信息const { detail, version } = await resolveInstallVersion({slug: params.slug,version: params.version,baseUrl: params.baseUrl  });// 2. 下载技能压缩包const archive = await downloadClawHubSkillArchive({slug: params.slug,    version,baseUrl: params.baseUrl  });// 3. 解压并安装const install = await withExtractedArchiveRoot({archivePath: archive.archivePath,rootMarkers: ["SKILL.md"],onExtractedasync (rootDir) => await installExtractedSkill({ ... })  });// 4. 写入来源信息和锁文件await writeClawHubSkillOrigin(install.targetDir, { ... });await writeClawHubSkillsLockfile(params.workspaceDir, lock);return { oktrue, slug, version, targetDir: install.targetDir };}

安装后的技能信息存储在:

  • 来源信息skills/{slug}/.clawhub/origin.json
  • 锁文件.clawhub/lock.json

Skill 写作实践

一个成熟的 Skill 开发流程:

  1. 裸模型先跑:先用基础模型验证任务可行性
  2. 定标准:明确输入输出格式、成功失败条件
  3. 先有再优:先实现基本功能,再优化提示词和脚本
  4. 补边界:处理异常情况、添加错误恢复逻辑
  5. 持续观察:监控 Skill 使用情况,收集失败案例
  6. 稳定脚本:将高频操作固化为脚本,减少提示词长度

六、记忆系统:上下文工程的核心

大模型的三大短板

短板
表现
OpenClaw 的解决方案
信息不够
胡说八道(幻觉)
Skills 注入领域知识
信息太多
上下文撑爆
分层压缩 + 按需加载
记忆不可靠
跨会话遗忘
持久化记忆系统

长期记忆 MEMORY.md

MEMORY.md 存储需要跨会话保留的重要信息:

  • 用户的偏好和习惯
  • 重要的决策和结论
  • 项目相关的约定和规则
  • 需要记住的事实

主会话启动时,MEMORY.md 通过 Bootstrap 流程注入系统提示词。在共享上下文(如 Discord 群聊、多用户会话)中,MEMORY.md 不会被加载,以保护隐私。

每日记忆 memory/YYYY-MM-DD.md

每日记忆按日期组织,记录当天发生的事件:

  • 执行的任务和结果
  • 遇到的问题和解决方案
  • 临时性的备忘

检索时带有时间衰减权重,近期记忆权重更高。

记忆索引

OpenClaw 维护记忆索引以支持高效检索,索引存储在 ~/.openclaw/memory/index.db,包含五张表:

表名
用途
files
文件元数据(路径、修改时间、大小)
chunks
文本分块记录
chunks_vec
向量嵌入(用于语义检索)
chunks_fts
全文检索索引(用于关键词检索)
embedding_cache
嵌入缓存(避免重复计算)

混合检索

记忆检索采用向量检索 + 全文检索的混合策略:

  1. 向量检索:语义相似度匹配,适合模糊查询
  2. 全文检索:关键词精确匹配,适合特定术语

两种检索结果通过加权融合,兼顾语义理解和精确匹配。

Memory Embedding Providers

src/plugins/memory-embedding-providers.ts 定义了嵌入提供者接口:

exporttype MemoryEmbeddingProviderAdapter = {  id: string;  defaultModel?: string;  transport?: "local" | "remote";  // local=内置, remote=外部服务  create(options: MemoryEmbeddingProviderCreateOptions): Promise<MemoryEmbeddingProviderCreateResult>;};

支持 local 和 remote 两种传输模式。

Hooks 内部钩子系统

src/hooks/internal-hooks.ts 中的 AgentBootstrapHookContext 系统允许在 bootstrap 流程中修改行为:

exporttype AgentBootstrapHookContext = {  workspaceDir: string;  bootstrapFiles: WorkspaceBootstrapFile[];  cfg?: OpenClawConfig;  sessionKey?: string;  sessionId?: string;  agentId?: string;};

applyBootstrapHookOverrides() 在 src/agents/bootstrap-hooks.ts 中触发 agent:bootstrap 钩子,可以在 Agent 看到 bootstrap 文件之前修改其内容。

Memory Flush 机制

在上下文压缩前,OpenClaw 会提醒 Agent 沉淀重要信息

Memory Flush: 即将进行上下文压缩,请将重要信息写入 MEMORY.md 或 memory/YYYY-MM-DD.md

这确保关键信息不会因为压缩而丢失。

上下文压缩策略

当上下文接近模型限制时,OpenClaw 启动压缩流程:

源码 src/agents/compaction.ts 实现了核心压缩逻辑和常量:

// 压缩算法核心常量exportconst BASE_CHUNK_RATIO = 0.4;      // 40% 消息在每个 chunkexportconst MIN_CHUNK_RATIO = 0.15;       // 大消息时最低 15%exportconst SAFETY_MARGIN = 1.2;          // 20% buffer for estimateTokens() inaccuracyconst MERGE_SUMMARIES_INSTRUCTIONS = ["Merge these partial summaries into a single cohesive summary.","","MUST PRESERVE:","- Active tasks and their current status (in-progress, blocked, pending)","- Batch operation progress (e.g., '5/17 items completed')","- The last thing the user requested and what was being done about it","- Decisions made and their rationale","- TODOs, open questions, and constraints","- Any commitments or follow-ups promised","","PRIORITIZE recent context over older history. The agent needs to know","what it was doing, not just what was discussed.",].join("\n");const IDENTIFIER_PRESERVATION_INSTRUCTIONS ="Preserve all opaque identifiers exactly as written (no shortening or reconstruction), " +"including UUIDs, hashes, IDs, hostnames, IPs, ports, URLs, and file names.";

压缩策略的优先级:

策略
操作
效果
历史截断
丢弃最早的消息
节省大量 token,但丢失早期上下文
工具结果截断
保留头尾,截断中间
节省 token,保留关键信息
自动摘要
生成摘要替换原文
保留语义,大幅压缩
容错降级
切换更大模型 / 降低 thinking / 重置会话
应急处理

ContextEngine 委托机制src/context-engine/types.ts 定义了 ContextEngine 接口,src/context-engine/delegate.ts 提供了 delegateCompactionToRuntime() 实现,允许外部上下文引擎委托给内置压缩运行时。

MemoryProvider 接口设计

记忆系统通过 MemoryProvider 接口实现可扩展性:

interface MemoryProvider {// 生命周期钩子  onSessionStart?(session: Session): Promise<void>;  onBeforeCall?(session: Session): Promise<void>;  onAfterCall?(session: Session, result: CallResult): Promise<void>;  onContextRetrieval?(query: string): Promise<MemoryEntry[]>;  onCompact?(session: Session): Promise<void>;  onAfterTurn?(session: Session): Promise<void>;  onSessionEnd?(session: Session): Promise<void>;// 子 Agent 支持  prepareSubagentSpawn?(parent: Session, child: Session): Promise<void>;  onSubagentEnded?(child: Session, result: any): Promise<void>;// 资源释放  dispose?(): Promise<void>;}

这个设计允许实现各种记忆后端:本地文件、数据库、向量存储等。


七、Agent 执行生命周期

ReAct 循环

OpenClaw 的 Agent 执行遵循 ReAct(Reasoning + Acting) 范式:

每个循环称为一个 Turn,包含:

  1. 接收用户消息或工具结果
  2. 构建完整上下文(系统提示词 + 历史 + 当前消息)
  3. 调用 LLM 获取响应
  4. 解析响应中的工具调用请求
  5. 执行工具并将结果加入上下文
  6. 判断是否需要继续(有未完成的工具调用或未回答的问题)

流式响应

OpenClaw 支持 SSE(Server-Sent Events) 和 WebSocket 两种流式传输方式:

  • SSE:单向推送,适合文本流
  • WebSocket:双向通信,支持中断和状态同步

流式响应的关键实现:

// 注册 Provider 流const providerStreamFn = registerProviderStreamForModel({model: params.effectiveModel,cfg: params.config,agentDir: params.agentDir,workspaceDir: params.effectiveWorkspace});// 应用文本变换(如 Provider 特有的格式转换)const providerTextTransforms = resolveProviderTextTransforms({provider: params.provider,config: params.config,workspaceDir: params.effectiveWorkspace});if (providerTextTransforms) {  session.agent.streamFn = wrapStreamFnTextTransforms({streamFn: session.agent.streamFn,input: providerTextTransforms.input,output: providerTextTransforms.output,transformSystemPromptfalse  });}

工具调用流程

当 LLM 返回工具调用请求时:

  1. 暂停文本流:停止向用户推送文本
  2. 执行工具:调用对应的工具处理函数
  3. 结果反馈:将工具结果加入上下文
  4. 继续推理:重新调用 LLM,让它基于工具结果继续生成

Before Tool Call Hookssrc/agents/pi-tools.before-tool-call.ts):

  • HookContext 包含 agentIdconfigsessionKeysessionIdrunIdtraceloopDetectiononToolOutcome
  • BeforeToolCallBlockedError – 钩子否决时抛出
  • requestPluginToolApproval() – 向插件请求异步批准
  • 钩子可通过 adjustedParamsByToolCallId Map 修改参数

工具循环检测src/tools/tool-loop-detection.ts):

  • 配置 max invocations per tool 和 observation window
  • 通过 config.tools.loopDetection 配置
  • 防止 ReAct 执行中的工具调用循环

工具策略管道

源码 src/agents/tool-policy-pipeline.ts 实现了工具过滤的多层管道:

```typescript// src/agents/tool-policy-pipeline.tsexportfunction buildDefaultToolPolicyPipelineSteps(params: {  profilePolicy?: ToolPolicyLike;  profile?: string;  providerProfilePolicy?: ToolPolicyLike;  providerProfile?: string;  globalPolicy?: ToolPolicyLike;  globalProviderPolicy?: ToolPolicyLike;  agentPolicy?: ToolPolicyLike;  agentProviderPolicy?: ToolPolicyLike;  groupPolicy?: ToolPolicyLike;  agentId?: string;}): ToolPolicyPipelineStep[]

Subagent 工具拒绝列表src/agents/pi-tools.policy.ts):

// 始终拒绝的工具(所有 subagent)const SUBAGENT_TOOL_DENY_ALWAYS = ["gateway""agents_list""session_status""cron""sessions_send"];// 叶子 subagent(达到最大深度)额外拒绝的工具const SUBAGENT_TOOL_DENY_LEAF = ["subagents""sessions_list""sessions_history""sessions_spawn"];functionresolveSubagentDenyList(depth: number, maxSpawnDepth: number): string[] {const isLeaf = depth >= Math.max(1Math.floor(maxSpawnDepth));if (isLeaf) {return [...SUBAGENT_TOOL_DENY_ALWAYS, ...SUBAGENT_TOOL_DENY_LEAF];  }return [...SUBAGENT_TOOL_DENY_ALWAYS];}

Owner-Only Tool Policy:某些工具仅限于所有者发送者使用:

const OWNER_ONLY_TOOL_APPROVAL_CLASS_FALLBACKS = new Map([  ["cron""control_plane"],  ["gateway""control_plane"],  ["nodes""exec_capable"],]);// applyOwnerOnlyToolPolicy() - 基于发送者授权过滤或包装 owner-only 工具// 违规时抛出 "Tool restricted to owner senders"

工具策略的层级:

层级
配置位置
用途
Profile 过滤
tools.profile
预定义的工具集配置
Sandbox 隔离
运行时沙箱
限制危险工具
Subagent 继承
父 Agent 配置
子 Agent 继承父 Agent 的工具策略
包装器层
运行时包装
参数验证、输入清理
Owner-Only
工具级别
限制为所有者发送者

多级错误回退

当 LLM 调用失败时,OpenClaw 实现了多级回退策略:

错误类型
回退策略
限流(429)
指数退避重试 / 切换模型
认证错误(401/403)
轮换 API Key / 切换 Provider
超时
降低 thinking 级别 / 切换快速模型
上下文溢出
压缩上下文 / 切换更大模型

三级配置继承

配置的优先级:

Agent 级配置 > 全局默认配置 > 代码默认值

示例:

# 全局默认agents:defaults:model:gpt-4thinking:medium# Agent 级覆盖agents:my-agent:model:claude-3-opusthinking:high

八、会话持久化与资源管理

双层存储

OpenClaw 采用双层存储架构:

存储层
文件
内容
用途
轻量索引
sessions.json
会话元数据
快速查找、状态同步
重度转录
{sessionId}.jsonl
完整消息历史
上下文重建、审计追溯

jsonl 格式的优势:

  • 追加写入:新消息直接追加,无需重写整个文件
  • 流式读取:可以逐行读取,支持大文件
  • 容错性:单行损坏不影响其他行

会话持久化流程

源码 src/config/sessions/transcript.ts 实现了会话转录管理:

asyncfunctionupdateSessionStoreAfterAgentRun(params{const { cfg, sessionId, sessionKey, storePath, sessionStore, result } = params;const usage = result.meta.agentMeta?.usage;const modelUsed = result.meta.agentMeta?.model ?? fallbackModel ?? defaultModel;const providerUsed = result.meta.agentMeta?.provider ?? fallbackProvider ?? defaultProvider;const entry = sessionStore[sessionKey] ?? { sessionId, updatedAtDate.now() };const next = {    ...entry,    sessionId,updatedAtDate.now(),    contextTokens,inputTokens: usage.input ?? 0,outputTokens: usage.output ?? 0,totalTokens: deriveSessionTotalTokens({ usage, contextTokens, promptTokens }),cacheRead: usage.cacheRead ?? 0,cacheWrite: usage.cacheWrite ?? 0,estimatedCostUsd: estimateUsageCost({ usage, cost: modelCostConfig })  };  sessionStore[sessionKey] = await updateSessionStore(storePath, (store) => {const merged = mergeSessionEntry(store[sessionKey], next);    store[sessionKey] = merged;return merged;  });}

持久化步骤:

  1. 更新元数据:更新 sessions.json 中的会话条目
  2. 追加 JSONL:将新消息追加到 {sessionId}.jsonl
  3. 加载历史:根据 token 预算加载历史消息

历史消息加载

// 从最新向前读取指定 token 预算的消息// 过滤不需要的消息类型(如心跳)// 保证时间顺序

加载策略:

  • Token 预算:根据模型上下文窗口限制计算可加载的消息量
  • 消息过滤:跳过心跳、系统消息等不需要的内容
  • 顺序保证:确保消息按时间顺序排列

响应投递

响应投递根据消息来源和目标地址选择出站适配器。源码 src/infra/outbound/deliver.ts 中的 ChannelHandler 实现了核心投递逻辑:

// 投递核心方法sendText(), sendMedia(), sendFormattedText(), sendFormattedMedia()// 支持 deliveryMode: "direct" | "gateway" | "hybrid"

媒体处理管道:图片解析和 offloaded refs 分阶段到 sandbox,ENOSPC/EPERM 映射到可重试的 5xx 错误。

投递队列系统src/infra/outbound/delivery-queue-storage.ts):

// 失败投递持久化到 {stateDir}/delivery-queue/// 恢复时使用指数退避重试

资源释放

会话结束时的资源释放:

  1. 释放车道锁:允许后续消息进入该会话
  2. 释放并发配额:归还全局并发信号量
  3. 标记幂等键:标记消息已处理完成
  4. 清理过期会话:清理长时间未活动的会话
  5. 归档旧转录:将过大的 JSONL 文件归档或压缩

九、多 Agent 协作

核心能力

OpenClaw 的多 Agent 协作能力包括:

  • Agent 隔离:每个 Agent 有独立的上下文和工具集
  • 动态任务分发:主 Agent 可以根据任务类型动态创建子 Agent
  • 层级协作:支持多层嵌套的 Agent 树
  • 生命周期管理:完整的子 Agent 创建、执行、终止流程
  • 安全边界控制:子 Agent 的权限受主 Agent 约束

SubAgent 创建流程

源码 src/agents/subagent-spawn.ts 实现了子 Agent 的创建,src/agents/subagent-registry.ts 管理子 Agent 的生命周期:

// SpawnSubagentParams 关键参数exporttype SpawnSubagentParams = {  task: string;  agentId?: string;  model?: string;  thinking?: string;  runTimeoutSeconds?: number;  mode?: SpawnSubagentMode;       // session mode retains child session  cleanup?: "delete" | "keep";  sandbox?: SpawnSubagentSandboxMode;  // none, read-only, full  context?: SpawnSubagentContextMode;   // standard, isolated, forked  lightContext?: boolean;  expectsCompletionMessage?: boolean;  attachments?: Array<{    name: string;    content: string;    encoding?: "utf8" | "base64";    mimeType?: string;  }>;};

Subagent Registry

  • 使用 subagentRuns Map 跟踪活跃运行
  • scheduleSubagentOrphanRecovery() – 防抖 orphan 恢复,1s 最小间隔
  • Sweeper 每 60 秒运行一次清理 stale runs
  • 生命周期错误宽限期 15s,超时宽限期 15s
  • 待处理生命周期条目 5 分钟绝对 TTL

子 Agent 提示词

源码 src/agents/subagent-system-prompt.ts 中 buildSubagentSystemPrompt() 函数:

exportfunctionbuildSubagentSystemPrompt(params: {  task: string;  childDepth: number;  maxSpawnDepth: number;  childSessionKey: string;  requesterSessionKey: string;}): string{const taskText = params.task ?? "{{TASK_DESCRIPTION}}";const childDepth = params.childDepth ?? 1;const maxSpawnDepth = params.maxSpawnDepth ?? 1;const canSpawn = childDepth < maxSpawnDepth;const parentLabel = childDepth >= 2 ? "parent orchestrator" : "main agent";const lines = ["# Subagent Context","",`You are a **subagent** spawned by the ${parentLabel} for a specific task.`,"","## Your Role",`- You were created to handle: ${taskText}`,"- Complete this task. That's your entire purpose.",`- You are NOT the ${parentLabel}. Don't try to be.`,"","## Rules","1. **Stay focused** - Do your assigned task, nothing else","2. **Complete the task** - Your final message will be automatically reported to the ${parentLabel}","3. **Don't initiate** - No heartbeats, no proactive actions, no side quests","4. **Be ephemeral** - You may be terminated after task completion. That's fine.","5. **Trust push-based completion** - Descendant results are auto-announced back to you; do not busy-poll for status.",// ...  ];if (canSpawn) {    lines.push("## Sub-Agent Spawning","You CAN spawn your own sub-agents for parallel or complex work using `sessions_spawn`.","Auto-announce is push-based. After spawning children, do NOT call sessions_list, sessions_history, exec sleep, or any polling tool.","Wait for completion events to arrive as user messages.",// ...    );  } elseif (childDepth >= 2) {    lines.push("## Sub-Agent Spawning","You are a leaf worker and CANNOT spawn further sub-agents. Focus on your assigned task."    );  }return lines.join("\n");}

关键设计点:

  • 强调边界:明确告诉子 Agent 它不是主 Agent
  • 禁止主动行为:不允许心跳、主动消息等
  • 信任推送:子 Agent 不应轮询结果,而是等待推送通知

结果回传

子 Agent 完成任务后:

  1. 触发生命周期事件onSubagentEnded 钩子
  2. 读取输出:从子 Agent 的会话文件读取最终响应
  3. 经通知队列:将结果放入主 Agent 的通知队列
  4. 判断目标
    • 如果主 Agent 还在运行:注入主 Agent 上下文
    • 如果主 Agent 已结束:直接发送给用户

典型协作流程

主 Agent 接收任务    ↓拆解为子任务(任务 A、任务 B、任务 C)    ↓并行创建子 Agent A、B、C    ↓子 Agent 各自执行    ↓结果自动回传到主 Agent    ↓主 Agent 汇总结果    ↓生成最终回复给用户

十、OpenClaw vs Hermes:两种 Agent 架构的对比

OpenClaw:平台治理型

特征:

  • 持久生命周期:Agent 会话可以长期存在
  • 会话系统管理:完整的会话状态、历史、绑定管理
  • 多通道支持:统一的消息入口和路由
  • 持久化存储:会话历史、记忆、配置的持久化

适用场景:

  • 平台型应用(客服系统、企业助手)
  • 多会话并发(同时服务多个用户)
  • 需要持久化状态的任务(长期项目跟踪)

Hermes:高效委派型

特征:

  • 线程池并行:子 Agent 在线程池中执行
  • 短生命周期:子 Agent 完成任务即销毁
  • 轻量级:没有复杂的会话管理开销

适用场景:

  • 本地工具型应用(CLI 工具、IDE 插件)
  • 编程助手(代码生成、重构)
  • 短链路委派(快速任务分发)

关键差异对比

维度
OpenClaw
Hermes
执行单元
进程外子 Agent(独立进程)
线程内子 Agent(同进程线程)
生命周期
持久,可跨多轮对话
临时,任务完成即销毁
结果回传
通过消息队列异步推送
通过 Promise 同步等待
隔离级别
进程级隔离,更安全
线程级隔离,共享内存
启动开销
较高(进程创建)
较低(线程创建)
上下文共享
需要显式传递
可以共享引用

选型建议

  • 做平台治理 → 靠近 OpenClaw

    • 需要多用户、多会话管理
    • 需要长期记忆和状态
    • 需要多通道接入
  • 做代码/工具型高效委派 → 靠近 Hermes

    • 单用户、单会话
    • 快速响应优先
    • 不需要持久化状态

十一、安全与风险

Skills 供应链风险

Skills 作为可扩展组件,存在供应链风险:

  1. 写得很烂:Skill 代码质量差,可能引入 bug 或性能问题
  2. 藏私货:Skill 可能包含隐藏的恶意行为
  3. 被投毒:Skill 来源被攻击,植入恶意代码

三道防线

OpenClaw 通过三道防线降低风险:

第一道:权限分级与用户授权

  • 敏感操作需要用户显式授权
  • /approve 命令机制,用户必须手动批准危险命令
  • 权限一次一授权,不自动继承

第二道:命令与路径策略

源码 host-env-security-BmNeVswf.js 定义了危险环境变量黑名单:

blockedEverywhereKeys: ["NODE_OPTIONS",    // 可注入任意代码"PYTHONPATH",      // 可劫持 Python 模块"BASH_ENV",        // 可注入 shell 命令"SHELL",           // 可改变 shell 解释器"OPENSSL_CONF",    // 可劫持 TLS 配置// ...]

第三道:沙箱隔离 + 审计日志

  • 危险命令在沙箱中执行
  • 所有命令执行记录审计日志
  • 支持事后追溯和分析

上下文窗口策略

系统提示词的分层管理:

系统提示(最高优先级)    ↓Skills 提示    ↓当前消息(最低优先级)

初始提示词约 11k input tokens,包含:

  • 系统规则和约束
  • 可用工具描述
  • Skills 列表
  • 用户画像

多轮对话后 token 爆炸风险:

  • 工具调用结果可能很长
  • 历史消息累积
  • 需要压缩或截断

十二、工程启示与核心结论

Skills 的本质

Skills 不是提示词增强,而是 Workflow 的迁移

当你把一个工作流固化为 Skill 时,你做的是:

  • 将经验编码为可复用的流程
  • 将试错过程固化为确定性的步骤
  • 将隐性知识显性化

这比单纯的”更好的提示词”有本质区别——你在构建的是可组合的能力单元,而不是更聪明的对话。

Agent 范式

Agent 范式并未升级,模型能力也未突破,但市场选择了它。

为什么?

  • 工程化需求:企业需要的是可预测、可控制、可观测的系统
  • 组合性:Agent 可以组合多个能力,处理复杂任务
  • 可扩展性:通过 Skills 可以无限扩展能力边界
  • 人机协作:Agent 作为中间层,平衡自动化和人类控制

关键设计原则

  1. 分层:清晰的层次边界,每层职责单一
  2. 运行时导向:配置驱动行为,而非代码分支
  3. 可扩展:插件机制支持无限扩展
  4. 分布式协作雏形:多 Agent 架构为分布式协作奠定基础

OpenClaw 的真正价值

OpenClaw = Agent Runtime + Gateway

它不是”更会聊天的机器人”,而是:

把消息入口、会话治理、上下文管理、技能调用、持久化存储和多 Agent 协作缝合在一起的可治理执行链路。

这个执行链路的价值在于:

  • 可观测:每个环节都有日志、指标、追踪
  • 可控:通过策略配置控制行为
  • 可扩展:通过插件机制扩展能力
  • 可靠:通过分层隔离降低故障影响

当你在构建一个需要长期运行、服务多用户、集成多通道、执行复杂任务的 AI 系统时,OpenClaw 提供了一个经过验证的架构模板。


附录:关键源码文件索引

功能模块
源码文件
关键函数/类
Gateway 核心
src/gateway/server-methods/shared-types.ts GatewayRequestContext
消息去重
src/gateway/server-methods/chat.ts agent.run

 / chat.send 幂等检查
会话路由
src/gateway/server-methods/sessions-resolve.ts
session key 规范化
会话绑定
src/infra/outbound/session-binding-service.ts resolveByConversation()
车道机制
src/process/lanes.ts

 + src/agents/lanes.ts
CommandLane

 枚举
会话存储
src/config/sessions/transcript.ts readLatestAssistantTextFromSessionTranscript()
会话写锁
src/agents/session-write-lock.ts acquireSessionWriteLock()
技能扫描
src/security/skill-scanner.ts scanSource()

LINE_RULESSOURCE_RULES
子 Agent 创建
src/agents/subagent-spawn.ts spawnSubagent()
子 Agent 提示词
src/agents/subagent-system-prompt.ts buildSubagentSystemPrompt()
工具策略
src/agents/tool-policy-pipeline.ts buildDefaultToolPolicyPipelineSteps()
Bootstrap
src/agents/bootstrap-hooks.ts buildFullBootstrapPromptLines()
Bootstrap Mode
src/agents/bootstrap-mode.ts resolveBootstrapMode()
上下文压缩
src/agents/compaction.ts BASE_CHUNK_RATIO

SAFETY_MARGIN
ClawHub
src/agents/skills/clawhub.ts installSkillFromClawHub()
环境变量安全
src/agents/env-overrides.ts applySkillEnvOverrides()
危险变量黑名单
src/security/host-env-security.ts host_env_security_policy_default
Skills 加载
src/agents/skills/workspace.ts loadSkillsFromDirSafe()
技能 Frontmatter
src/agents/skills/frontmatter.ts resolveSkillInvocationPolicy()
Subagent Registry
src/agents/subagent-registry.ts scheduleSubagentOrphanRecovery()
工具策略 (Subagent)
src/agents/pi-tools.policy.ts SUBAGENT_TOOL_DENY_ALWAYS
ContextEngine
src/context-engine/types.ts ContextEngine

 接口
RealtimeVoice
src/realtime-voice/session-runtime.ts RealtimeVoiceBridgeSession
Secrets
src/secrets/resolve.ts SecretRefSource