乐于分享
好东西不私藏

OpenClaw源码解析(二):整体架构

OpenClaw源码解析(二):整体架构

上一篇回顾

OpenClaw源码解析(一)开篇:项目定位与愿景


这一篇怎么读

这篇不是“背目录树”,而是要完成两件事

  1. 把上一篇的产品边界映射到真实目录
  2. 建立一个足够准确、但不过度简化的架构地图

这里的 5 层模型是学习时用的心智模型,不是运行时严格的分层图。真正执行链在很多地方会跨层,例如:

  • channel ingress 直接把消息推入 routing / agent
  • agent runner 会反过来调用 tools、memory、context engine
  • plugin 体系横切多个子系统

所以你要一边看“分层”,一边记住“真实控制流会跨层”。


第一步:先从根目录判断项目形态

根目录里最重要的不是文件数量,而是这些分区:

  • src/:主运行时代码
  • extensions/:工作区扩展包
  • apps/:客户端和平台应用
  • docs/:文档与产品说明
  • scripts/:构建、发布、生成脚本

这已经说明 OpenClaw 不是单体 CLI,而是:

  • 一个主 runtime
  • 一组可安装扩展
  • 若干平台客户端

架构全景(5 层模型)

┌─────────────────────────────────────────────────┐│  CLI / Commands                                 ││  src/cli/  src/commands/                        ││  用户直接交互层:命令解析、参数处理                    │├─────────────────────────────────────────────────┤│  Gateway(控制平面)                              ││  src/gateway/                                   ││  WebSocket 服务器、协议帧、设备配对、认证             │├─────────────────────────────────────────────────┤│  Agent / AI 层                                  ││  src/agents/  src/plugins/  src/plugin-sdk/     ││  LLM 调用、工具执行、子 Agent、认证轮换              │├─────────────────────────────────────────────────┤│  Channels(消息通道)                             ││  src/channels/  src/telegram/  src/discord/ ... ││  20+ 平台的统一适配器,消息入站/出站                  │├─────────────────────────────────────────────────┤│  Infrastructure                                 ││  src/infra/  src/config/  src/security/         ││  配置、文件系统、设备信任、环境变量                    │└─────────────────────────────────────────────────┘

这张图的价值在于先给你一个入口排序:

  • 先理解 CLI
  • 再理解 channels + routing
  • 然后进入 agents
  • 最后再补 gatewaypluginsinfra

如果一上来直接钻 src/gateway/,很容易把控制平面误当成主执行链。


第二步:把 src/ 映射成几个真正重要的模块簇

1. 启动与命令层

  • src/entry.ts
  • src/cli/
  • src/commands/

这一层解决的是:

  • 程序怎么启动
  • 命令怎么注册
  • CLI 怎么把请求送入 runtime

2. 消息接入与通道层

  • src/channels/
  • src/telegram/
  • src/discord/
  • src/slack/
  • src/signal/
  • src/imessage/
  • src/web/
  • src/whatsapp/

      这一层解决的是:

  • 不同平台的消息如何被标准化
  • 哪些能力由统一接口表达
  • 哪些仍保留平台差异

3. 路由与 Agent 运行层

  • src/routing/
  • src/agents/
  • src/agents/pi-embedded-runner/
  • src/agents/tools/

这一层才是“消息变成一次 agent turn”的核心。

4. 上下文与记忆层

  • src/context-engine/
  • src/memory/
  • src/sessions/

这一层解释:

  • 当前会话上下文如何控制
  • 长期记忆如何检索
  • session 文件如何参与运行时

5. 横切扩展层

  • src/plugins/
  • src/plugin-sdk/
  • extensions/*

这是整个仓库最容易被低估的一层,因为它不是单独一个功能,而是很多能力的扩展边界。


目录职责速查表

目录
职责
src/entry.ts
CLI 入口,进程初始化,respawn 逻辑
src/cli/
Commander.js 程序构建,命令懒加载注册
src/commands/
各 CLI 子命令的具体实现
src/gateway/
WebSocket 服务器、协议帧定义、client.ts(核心)
src/agents/
LLM 调用、工具系统、子 Agent 注册表
src/plugins/
插件运行时、加载器、Plugin SDK
src/plugin-sdk/
插件作者公共 API(52 个导出入口)
src/channels/
通道插件系统、dock(注册/生命周期)、routing
src/telegram/
Telegram 通道实现(使用 grammy)
src/discord/
Discord 通道实现
src/slack/
Slack 通道实现(@slack/bolt)
src/signal/
Signal 通道实现
src/imessage/
iMessage 通道实现
src/web/
WhatsApp Web 通道实现(@whiskeysockets/baileys)
src/config/
配置加载/验证(Zod v4)、session 管理
src/infra/
文件系统边界、设备配对、环境工具
src/security/
安全审计、权限检查
src/media/
媒体管道(图片/视频处理)
src/browser/
Chrome/Chromium 管理
src/auto-reply/
自动回复模板引擎
src/routing/
消息路由(allowlist、命令门控)
src/context-engine/
可插拔上下文引擎接口与默认实现
src/memory/
长期记忆索引、检索、embedding、后端选择
src/sessions/
会话状态与相关运行时支撑

第三步:看 extensions/,确认插件不是装饰层

extensions/ 不是 demo 目录,而是正式工作区的一部分。

从现有结构看,里面至少有几类插件:

  • channel plugins:如 msteamsmatrixzalo
  • memory plugins:如 memory-corememory-lancedb
  • tool / runtime plugins:如 llm-taskvoice-call
  • shared / infra plugins:如 shared

这说明 OpenClaw 的“可扩展”不是营销说法,而是仓库结构级事实。


扩展系统(workspace packages)

extensions/├── msteams/          MS Teams 通道├── matrix/           Matrix 通道├── feishu/           飞书通道├── zalo/             Zalo 通道├── voice-call/       语音通话工具├── memory-core/      内存后端(基础)├── memory-lancedb/   内存后端(LanceDB 向量存储)├── llm-task/         LLM 任务 Agent 工具└── ...(共 40+ 个)

插件机制:

  • 从 npm 安装:openclaw plugins add @openclaw/msteams
  • 本地开发:~/.openclaw/plugins/ 目录
  • 运行时依赖注入:插件接收 PluginRuntime 上下文

第四步:从 package.json exports 判断公开边界

package.json 的 exports 很值得看,因为它告诉你:

  • 哪些模块被当成正式 API
  • 哪些只是内部实现

最关键的事实有两个:

1. ./plugin-sdk/* 出口非常多

这说明插件生态不是补充件,而是设计目标之一。

2. ./cli-entry 单独导出

这说明 CLI 入口本身也是被刻意包装和稳定暴露的。


npm 包导出结构

json

{  ".": "./dist/index.js",                    // 主入口  "./plugin-sdk": "./dist/plugin-sdk/index.js",  // 完整 SDK  "./plugin-sdk/core": "...",               // 核心类型  "./plugin-sdk/telegram": "...",           // Telegram 专用  "./plugin-sdk/discord": "...",            // Discord 专用  // ... 每个通道都有独立入口}

为什么这样设计? → 按需加载,不同通道的依赖互不影响


技术栈

领域
技术
原因
语言
TypeScript 5.9(ESM-only)
可 hack,类型安全
运行时
Node ≥22 + Bun 双支持
向前兼容 + 开发效率
CLI
Commander.js v14
成熟、可组合
网关协议
WebSocket(ws v8)
双向实时通信
构建
tsdown(基于 Rolldown)
快速多入口打包
Lint
Oxlint + Oxfmt(Rust)
极速
测试
Vitest
快速,Jest 兼容
配置验证
Zod v4
类型安全的 schema 验证
Telegram
grammy
最活跃的 Telegram Bot 框架
WhatsApp
@whiskeysockets/baileys
WhatsApp Web 逆向协议
Slack
@slack/bolt
Slack 官方 SDK

关键设计模式

1. Channel Plugin Adapter(最重要)

每个消息平台实现同一套 ChannelPlugin 接口。核心层不了解 Telegram/Slack 的细节,只调用抽象接口。

2. 懒加载命令注册

CLI 命令不在启动时全部加载,只有被调用时才 await import()

3. 依赖注入(PluginRuntime)

插件通过 PluginRuntime 上下文获取能力,而不是直接 import 内部模块。→ 实现了外部插件与内部实现的解耦

4. .runtime.ts 边界

需要懒加载的通道使用 *.runtime.ts 文件作为边界。→ 避免混用 await import() 和静态 import 导致的打包问题

5. Config-Driven Policies

白名单、命令门控、线程策略全部通过 YAML 配置驱动。→ 运行时行为可配置,不需要改代码

6. Slot-based replacement

memory、context engine 这类能力开始通过 slot 机制变成“同类能力只激活一个实现”。

这会成为你后面理解 src/plugins/slots.tssrc/context-engine/src/memory/ 的关键前提。


第五步:修正一个常见误解

很多人看完目录会形成一个误解:

gateway 是中心,其他一切都围着 gateway 转

更接近事实的说法是:

  • gateway 是控制平面
  • channel + routing 是消息入口面
  • agent runner 是实际执行面
  • memory / context 是状态与检索面
  • plugins 是横切扩展面

所以读代码时,主线应当优先追:

ingress->routing->agent runner-> tools/context/memory -> outbound

而不是只追:

cli -> gateway -> ws


消息流简图

入站(用户发消息给 AI)

Telegram 消息    ↓ grammy 接收src/telegram/handler.ts    ↓ 格式化为内部 MsgContextsrc/channels/* / ingress adapter    ↓src/routing/ (命令门控、agent 路由)    ↓src/agents/pi-embedded-runner/    ↓ 生成回复src/auto-reply/ (格式化、分块)    ↓Channel outbound adapter (发回 Telegram)

出站(openclaw message send

CLI: openclaw message send --to "+1234" "Hello"    ↓src/gateway/call.ts (执行引擎)    ↓ 解析目标通道src/channels/ (通道分发)    ↓ 媒体处理(如需要)src/media/    ↓Channel outbound adapter.sendText()

这个图先作为学习地图,下一阶段会开始逐步把这条线落到具体文件和调用点上。


这一篇读完后你应该得到什么

你应该至少形成下面 5 个判断:

  1. src/agents/
     才是核心执行层,不只是辅助目录
  2. src/context-engine/
     和 src/memory/ 是独立系统,不是零散工具
  3. extensions/
     代表真实扩展机制,而不是示例代码
  4. 5 层模型是学习地图,不是严格运行时调用图
  5. 后面读代码时,要优先沿执行链读,而不是沿目录名字平均扫

下一篇预告

开始进入真正的“逐段源码拆解”模式,从 src/entry.ts 这个最具体的入口开始,了解 CLI 启动过程

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » OpenClaw源码解析(二):整体架构

猜你喜欢

  • 暂无文章