本系列文章将从源码带大家学习最近大火的 Clawdbot 的架构设计,学完后有兴趣的可以参与开源项目。
前言
Clawdbot 是一个开源的「个人 AI 助手」——你可以把它跑在自己的设备上,通过 WhatsApp、Telegram、Slack、Discord 等渠道与它对话,它背后是统一的 Gateway 控制平面和 Pi Agent 运行时。本系列文章将带你从源码层面理解它的设计与实现。
本文是《Clawdbot 源码解读》系列的第一篇,我们会从整体架构入手,建立对项目的全局认知,为后续深入各个模块打基础。
学习目标
- 理解 Clawdbot 的项目定位和设计理念
- 掌握整体分层架构和核心组件
- 熟悉技术栈和源码目录结构
- 能够搭建开发环境并运行项目
前置知识
- 了解 Node.js / TypeScript 基础
- 对 WebSocket、CLI 工具有基本概念即可
一、项目背景与定位
1.1 它是什么?
Clawdbot 的官方描述是:Personal AI Assistant you run on your own devices。核心特点可以概括为:
- 本地优先:Gateway 和会话数据都在你自己控制的机器上
- 多渠道统一:同一套助手能力,接入 WhatsApp、Telegram、Slack、Discord、Google Chat、Signal、iMessage、WebChat 以及扩展渠道(Teams、Matrix、Zalo 等)
- 产品是助手,Gateway 是控制平面:用户感知到的是「一个会回消息的 AI」,而 Gateway 负责连接渠道、路由消息、调度 Agent、管理工具和事件
README 里有一句很关键的话:
The Gateway is just the control plane — the product is the assistant.
也就是说:Gateway 不是产品本身,而是让「助手」能在各个渠道里工作的控制层。
1.2 设计理念
从文档和代码结构可以看出几个明确的设计取向:
-
单一 Gateway,多客户端
一个主机上只跑一个 Gateway 进程,所有消息渠道由它统一管理;CLI、macOS 应用、Web UI、iOS/Android 节点都通过 WebSocket 连到同一个 Gateway。 -
类型安全与协议约束
使用 TypeScript 严格模式,协议用 TypeBox 定义 JSON Schema,既有运行时校验,也有类型推导和代码生成(如 Swift 端)。 -
可扩展但不臃肿
核心能力在src/,渠道和技能通过extensions/与skills/扩展,插件依赖与核心分离,避免把一切塞进主包。 -
安全默认值
默认 DM 需要配对、非 main 会话可进沙箱、Gateway 必须配置认证等,都体现「安全优先」的取舍。
二、整体架构:分层视角
我们可以用「从上到下」的视角把系统拆成几层:外部接口 → Gateway 控制平面 → 渠道与路由 → Agent 运行时 → 工具与存储。
2.1 架构全景图
下面这张图概括了各层之间的关系(箭头表示数据流或依赖关系):
graph TB
subgraph "外部接口层"
WA[WhatsApp]
TG[Telegram]
SL[Slack]
DC[Discord]
WC[WebChat]
EXT[扩展渠道]
end
subgraph "Gateway 控制平面"
WS[WebSocket Server :18789]
HTTP[HTTP Server]
AUTH[认证层]
end
subgraph "渠道与路由"
CM[Channel Manager]
RT[路由引擎]
SM[Session Manager]
end
subgraph "Agent 运行时"
AR[Agent Runtime]
AM[Agent 管理]
WS2[Workspace]
end
subgraph "工具与存储"
BR[Browser]
CV[Canvas]
ND[Nodes]
SK[Skills]
CFG[配置/会话/凭证]
end
subgraph "客户端"
CLI[CLI]
MAC[macOS App]
WEB[Web UI]
end
WA --> CM
TG --> CM
SL --> CM
DC --> CM
WC --> CM
EXT --> CM
CM --> RT
RT --> SM
SM --> AR
AR --> AM
AM --> WS2
AR --> BR
AR --> CV
AR --> ND
AR --> SK
WS --> AUTH
HTTP --> AUTH
AUTH --> CM
AUTH --> AR
CLI --> WS
MAC --> WS
WEB --> HTTP
CM --> CFG
SM --> CFG
各层职责简述:
| 层级 | 职责 |
|---|---|
| 外部接口层 | 各消息平台(WhatsApp、Telegram 等)及 WebChat,消息从这里进入和发出 |
| Gateway 控制平面 | WebSocket/HTTP 服务、认证;所有客户端和渠道都经由这里 |
| 渠道与路由 | 渠道管理、路由规则、Session Key、配对与白名单 |
| Agent 运行时 | Pi Agent RPC、多 Agent、工作空间与提示词 |
| 工具与存储 | Browser、Canvas、Nodes、Skills、配置与会话存储 |
后续文章会逐层展开,这里先建立「分层」和「数据流」的印象即可。
2.2 核心数据流(一句话版)
- 入站:某渠道收到用户消息 → Channel Manager → 路由解析出 Agent 和 Session Key → Session Manager 取/建会话 → Agent 执行 → 可选调用工具 → 生成回复。
- 出站:回复经格式化后交给对应渠道发送回用户。
- 控制:CLI / macOS / Web 等客户端通过 WebSocket 调用
agent、send、config等方法,并订阅agent、presence、health等事件。
三、技术栈与工程约束
3.1 技术选型
从 package.json 和工程配置可以看出:
- 语言与模块:TypeScript,ESM(
"type": "module"),Node 22+。 - 包管理:pnpm,monorepo(根目录 +
ui+extensions/*)。 - 构建:
tsc输出到dist/,另有 A2UI 打包、协议代码生成等脚本。 - 质量:Vitest 测试,oxlint/oxfmt 做 lint 和格式化。
关键依赖里能看到「渠道 + Agent + 工具」的痕迹,例如:
- 渠道:
@whiskeysockets/baileys(WhatsApp)、grammy(Telegram)、@slack/bolt、discord 相关等。 - Agent:
@mariozechner/pi-agent-core、pi-ai、pi-coding-agent等。 - 工具与基础设施:
playwright-core、express、hono、ws、croner等。
入口和二进制在 package.json 里是这样暴露的:
{
"main": "dist/index.js",
"bin": {
"clawdbot": "dist/entry.js"
},
"exports": {
".": "./dist/index.js",
"./plugin-sdk": "./dist/plugin-sdk/index.js",
"./plugin-sdk/*": "./dist/plugin-sdk/*"
}
}
也就是说:你执行 clawdbot 时,实际跑的是 dist/entry.js,而库入口和插件 SDK 通过 exports 对外提供。
3.2 源码目录结构(高层)
在仓库根目录下,和「读源码」最相关的目录大致如下(只列顶层和代表性子目录):
clawdbot/
├── src/ # 核心源码
│ ├── entry.ts # CLI 可执行入口(见下节)
│ ├── index.ts # 库入口
│ ├── cli/ # CLI 命令与程序结构
│ ├── config/ # 配置加载、类型、会话等
│ ├── gateway/ # Gateway 服务器、协议、认证
│ ├── channels/ # 渠道抽象、注册表、会话等
│ ├── routing/ # 路由解析
│ ├── agents/ # Agent 运行时、工具注册等
│ ├── commands/ # 具体命令实现(如 agent、send)
│ ├── auto-reply/ # 自动回复与消息分发
│ ├── browser/ # 浏览器工具
│ ├── plugins/ # 插件加载
│ ├── plugin-sdk/ # 插件 SDK
│ ├── infra/ # 环境、端口、二进制、日志等
│ ├── telegram/ # Telegram 实现
│ ├── discord/ # Discord 实现
│ ├── slack/ # Slack 实现
│ ├── web/ # Web 渠道 / WebChat 相关
│ └── ...
├── extensions/ # 渠道与能力扩展(如 msteams、matrix)
├── skills/ # 技能(SKILL.md + 脚本等)
├── apps/ # macOS / iOS / Android 应用
├── ui/ # Web Control UI
├── docs/ # 文档
└── scripts/ # 构建、发布等脚本
读源码时建议的「第一站」:
- 入口与 CLI:
src/entry.ts→src/cli/run-main.ts→src/cli/program/ - Gateway:
src/gateway/server.impl.ts、src/gateway/server/ - 渠道路由:
src/channels/registry.ts、src/routing/ - Agent:
src/agents/、src/commands/agent.js(或对应 TS)
四、程序入口:从 entry.ts 到 CLI
用户执行 clawdbot 时,系统实际执行的是编译后的 dist/entry.js。理解入口有助于后面理解「配置何时加载、Gateway 何时启动」。
4.1 入口文件做了什么?
src/entry.ts 做了几件「在真正跑 CLI 之前」的事:
-
设置进程标题
process.title = "clawdbot",方便在进程列表里识别。 -
抑制 Node 实验性警告
若未设置NODE_OPTIONS中的--disable-warning=ExperimentalWarning,会设置环境变量并 respawn 当前进程(用新的 Node 进程重新执行当前参数),这样用户无需自己配NODE_OPTIONS。若已设置或设置了CLAWDBOT_NO_RESPAWN,则不再 respawn。 -
处理
--no-color
若传入该参数,会设置NO_COLOR/FORCE_COLOR,影响后续输出是否带颜色。 -
Windows 下规范化 argv
normalizeWindowsArgv(process.argv)会去掉 Windows 下可能混入的node.exe路径等,保证 Commander 解析的是「用户输入的参数」。 -
Profile 解析
使用parseCliProfileArgs/applyCliProfileEnv处理 profile(如--profile dev),若有 profile 会改写环境变量并更新process.argv。 -
动态加载并运行 CLI
最后通过import("./cli/run-main.js")得到runCli,并执行runCli(process.argv)。真正的命令解析、子命令注册、插件注册都在run-main和后续的 program 里完成。
下面这段是入口里「不 respawn 时」的执行路径(保留原逻辑,仅作注释说明):
// src/entry.ts(节选)
process.title = "clawdbot";
installProcessWarningFilter();
if (process.argv.includes("--no-color")) {
process.env.NO_COLOR = "1";
process.env.FORCE_COLOR = "0";
}
// ... ensureExperimentalWarningSuppressed() / normalizeWindowsArgv ...
if (!ensureExperimentalWarningSuppressed()) {
const parsed = parseCliProfileArgs(process.argv);
if (!parsed.ok) {
console.error(`[clawdbot] ${parsed.error}`);
process.exit(2);
}
if (parsed.profile) {
applyCliProfileEnv({ profile: parsed.profile });
process.argv = parsed.argv;
}
import("./cli/run-main.js")
.then(({ runCli }) => runCli(process.argv))
.catch((error) => {
console.error("[clawdbot] Failed to start CLI:", ...);
process.exitCode = 1;
});
}
抓住这几点即可:入口只做环境与参数准备,不解析具体子命令;子命令和 Gateway 的启动都在 run-main 及其下游。
4.2 入口与 index 的区别
dist/entry.js:对应src/entry.ts,是bin.clawdbot的入口,面向「命令行使用」。dist/index.js:对应src/index.ts,是库入口(require/import('clawdbot')),会导出配置、会话、回复等能力,供其他模块或脚本使用。
写「源码解读」或做二次开发时,若关心「用户敲一条命令发生了什么」,就从 entry.ts 追起;若关心「如何在代码里调 Clawdbot 的能力」,就从 index.ts 和其导出看起。
五、快速搭建开发环境
5.1 环境要求
- Node.js:≥ 22.12.0(见
package.json的engines) - 包管理:pnpm(推荐,与仓库脚本一致)
5.2 克隆、安装、构建
git clone https://github.com/clawdbot/clawdbot.git
cd clawdbot
pnpm install
pnpm build
若需要 Control UI,可能还需执行:
pnpm ui:build
(具体以仓库根目录的 README 或脚本说明为准。)
5.3 运行 CLI
开发时常用:
# 使用编译后的 dist(需先 pnpm build)
node dist/entry.js --help
# 或使用仓库提供的脚本(可能通过 tsx 直接跑 TS)
pnpm clawdbot --help
验证 Gateway 时(需已配置认证等):
pnpm clawdbot gateway run --verbose
在另一终端可执行:
pnpm clawdbot gateway status
5.4 如何「顺着」源码读下去
-
从入口到子命令
src/entry.ts→src/cli/run-main.ts→src/cli/program/build-program.ts/command-registry,看一条命令(如gateway run)是如何被注册和调用的。 -
从文档到代码
docs/concepts/architecture.md描述 Gateway 与客户端、Node 的关系;docs/gateway/protocol.md描述协议。对照src/gateway/下的实现读,会更容易。 -
从测试到用法
很多模块配有*.test.ts,例如src/gateway/*.test.ts、src/channels/*.test.ts。测试里往往有「最小可运行」的调用示例,适合用来理解 API 和流程。 -
善用搜索
确定一个概念(如sessionKey、resolveAgentRoute)后,在src/里搜索,可以快速找到定义和使用点,再结合调用栈理解层次。
六、总结与下一篇预告
6.1 本文要点
- Clawdbot 是「个人 AI 助手」,产品是助手本身,Gateway 是控制平面,负责渠道、路由、Agent 和工具。
- 架构:外部接口 → Gateway(WS/HTTP + 认证)→ 渠道与路由 → Agent 运行时 → 工具与存储;客户端通过 WebSocket 连到同一 Gateway。
- 技术栈:TypeScript ESM、Node 22+、pnpm monorepo;入口二进制是
dist/entry.js,库入口是dist/index.js。 - 入口:
src/entry.ts负责进程标题、Node 警告抑制、argv 规范化、profile,然后加载run-main执行真正的 CLI。 - 读源码:先建立「分层 + 数据流」的图景,再按入口 → CLI → Gateway → 渠道/路由 → Agent 的顺序,结合文档和测试读。
6.2 下一步
下一篇将专门讲 CLI 系统:命令如何注册、如何解析、如何与 Gateway 和配置交互,并会跟踪一条具体命令(如 clawdbot gateway run)的完整执行路径。这样你就能把「用户输入」和「进程内调用」串起来,形成更清晰的代码地图。
参考资源
- 项目仓库:github.com/clawdbot/cl…
- 官方文档:docs.clawd.bot