OpenClaw 科普系列(23):源码架构——深入理解 OpenClaw 内部
前几期我们聊了 Agent Workspace,了解了如何组织工作空间。现在让我们深入 OpenClaw 的内部——它的源码架构、核心模块和设计思想。
本期我们来解析 OpenClaw 的源码结构,帮助你理解它是如何工作的。
项目概览
OpenClaw 是一个基于 Node.js/TypeScript 的开源项目,采用模块化架构设计。
仓库结构
openclaw/
├── apps/ # 应用程序
│ ├── mac/ # macOS 菜单栏应用
│ ├── ios/ # iOS 应用
│ └── android/ # Android 应用
├── packages/ # 核心包
│ ├── gateway/ # Gateway 核心
│ ├── cli/ # 命令行工具
│ ├── agent/ # Agent 运行时
│ ├── tools/ # 工具实现
│ └── protocol/ # 通信协议
├── extensions/ # 扩展插件
│ └── plugins/ # 官方插件
├── docs/ # 文档
└── scripts/ # 构建脚本
核心架构
Gateway 架构
Gateway 是 OpenClaw 的核心,采用分层架构:
┌─────────────────────────────────────┐
│ API Layer │
│ (HTTP REST / WebSocket / CLI) │
├─────────────────────────────────────┤
│ Service Layer │
│ (Auth / Routing / Session / Cron) │
├─────────────────────────────────────┤
│ Agent Runtime │
│ (Model / Tools / Memory / Skills) │
├─────────────────────────────────────┤
│ Channel Adapters │
│ (WhatsApp / Telegram / Discord) │
├─────────────────────────────────────┤
│ Data Layer │
│ (File System / State / Logs) │
└─────────────────────────────────────┘
模块职责
| 模块 | 职责 |
|---|---|
gateway |
WebSocket/HTTP 服务器,消息路由 |
agent |
LLM 调用,工具编排,提示词管理 |
channels |
各消息渠道的适配器 |
tools |
工具实现(exec, fs, browser 等) |
skills |
Skill 加载和管理 |
memory |
记忆存储和检索 |
cron |
定时任务调度 |
sessions |
会话管理 |
数据流
消息处理流程
用户消息
↓
Channel Adapter (解析消息格式)
↓
Gateway (路由到对应 Agent)
↓
Agent Runtime (构建提示词)
↓
LLM (生成响应)
↓
Tool Calls (如需执行工具)
↓
Channel Adapter (发送回复)
↓
用户
会话生命周期
1. 消息到达 → 创建/恢复 Session
2. 构建上下文 → 加载历史 + 系统提示
3. LLM 调用 → 生成响应或工具请求
4. 工具执行 → 获取结果
5. 继续对话 → 或结束会话
6. 保存状态 → 更新 Session 存储
核心组件详解
Gateway 服务
// 简化的 Gateway 结构
class Gateway {
private wss: WebSocketServer;
private httpServer: HTTPServer;
private agents: Map<string, Agent>;
private channels: Map<string, Channel>;
async start() {
// 初始化 WebSocket 服务器
this.wss = new WebSocketServer({ port: this.config.port });
// 设置连接处理
this.wss.on('connection', this.handleConnection);
// 初始化频道
await this.initChannels();
}
private handleConnection(ws: WebSocket, req: Request) {
// 认证检查
// 路由到对应处理器
}
}
Agent 运行时
// 简化的 Agent 结构
class Agent {
private model: LLMProvider;
private tools: ToolRegistry;
private memory: MemoryStore;
private skills: SkillManager;
async processMessage(message: string, session: Session) {
// 1. 构建系统提示词
const systemPrompt = this.buildSystemPrompt(session);
// 2. 获取历史上下文
const history = await this.memory.getHistory(session.key);
// 3. 调用 LLM
const response = await this.model.generate({
system: systemPrompt,
messages: [...history, { role: 'user', content: message }],
tools: this.tools.getAvailableTools(),
});
// 4. 处理工具调用
if (response.toolCalls) {
const results = await this.executeTools(response.toolCalls);
// 继续对话...
}
// 5. 保存到记忆
await this.memory.save(session.key, message, response);
return response.content;
}
}
工具系统
// 工具注册表
class ToolRegistry {
private tools: Map<string, Tool>;
register(tool: Tool) {
this.tools.set(tool.name, tool);
}
async execute(name: string, params: any) {
const tool = this.tools.get(name);
if (!tool) throw new Error(`Tool ${name} not found`);
return tool.execute(params);
}
}
// 工具定义示例
const execTool: Tool = {
name: 'exec',
description: 'Execute shell commands',
parameters: {
type: 'object',
properties: {
command: { type: 'string' },
cwd: { type: 'string' },
},
required: ['command'],
},
async execute(params) {
// 执行命令并返回结果
},
};
通信协议
WebSocket 协议
Gateway 使用 WebSocket 进行实时通信:
// 消息格式
interface WSMessage {
id: string; // 消息 ID
type: 'request' | 'response' | 'event';
method: string; // 方法名
params?: any; // 参数
result?: any; // 结果
error?: ErrorInfo; // 错误信息
}
// 示例:发送消息
{
id: "msg-123",
type: "request",
method: "chat.send",
params: {
sessionKey: "agent:main:main",
message: "Hello"
}
}
内部事件系统
// 事件总线
class EventBus {
private handlers: Map<string, Function[]>;
on(event: string, handler: Function) {
if (!this.handlers.has(event)) {
this.handlers.set(event, []);
}
this.handlers.get(event).push(handler);
}
emit(event: string, data: any) {
const handlers = this.handlers.get(event) || [];
handlers.forEach(h => h(data));
}
}
// 使用示例
eventBus.on('message.received', (msg) => {
console.log(`New message: ${msg.content}`);
});
扩展机制
插件架构
// 插件接口
interface Plugin {
id: string;
version: string;
activate(context: PluginContext): void;
deactivate(): void;
}
// 插件上下文
interface PluginContext {
registerTool(tool: Tool): void;
registerCommand(command: Command): void;
on(event: string, handler: Function): void;
}
// 示例插件
const myPlugin: Plugin = {
id: 'my-plugin',
version: '1.0.0',
activate(context) {
context.registerTool(myCustomTool);
context.on('message.received', handleMessage);
},
deactivate() {
// 清理资源
},
};
Skill 加载机制
class SkillManager {
private skills: Map<string, Skill>;
async loadFromDirectory(path: string) {
const files = await fs.readdir(path);
for (const file of files) {
if (file.endsWith('SKILL.md')) {
const content = await fs.readFile(`${path}/${file}`, 'utf-8');
const skill = this.parseSkill(content);
this.skills.set(skill.name, skill);
}
}
}
parseSkill(content: string): Skill {
// 解析 YAML frontmatter + Markdown
const { frontmatter, body } = parseFrontmatter(content);
return {
name: frontmatter.name,
description: frontmatter.description,
instructions: body,
metadata: frontmatter.metadata,
};
}
}
数据存储
文件结构
~/.openclaw/
├── openclaw.json # 主配置
├── credentials/ # 凭证存储
│ ├── whatsapp/
│ └── oauth.json
├── agents/
│ └── <agentId>/
│ ├── agent/
│ │ └── auth-profiles.json
│ └── sessions/
│ ├── sessions.json
│ └── *.jsonl
├── cron/
│ ├── jobs.json
│ └── runs/
└── logs/
└── openclaw-YYYY-MM-DD.log
会话存储格式
sessions.json:
{
"agent:main:main": {
"sessionId": "sess-abc123",
"updatedAt": 1712487600000,
"model": "anthropic/claude-sonnet-4",
"contextTokens": 2048,
"totalTokens": 10240
}
}
*.jsonl(会话记录):
{"role":"system","content":"You are a helpful assistant...","timestamp":"2024-04-07T10:00:00Z"}
{"role":"user","content":"Hello","timestamp":"2024-04-07T10:00:05Z"}
{"role":"assistant","content":"Hi there!","timestamp":"2024-04-07T10:00:06Z"}
构建和开发
本地开发
# 克隆仓库
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# 安装依赖
npm install
# 构建
npm run build
# 运行测试
npm test
# 本地运行 Gateway
npm run dev:gateway
调试技巧
# 启用详细日志
DEBUG=openclaw:* npm run dev:gateway
# 特定模块日志
DEBUG=openclaw:agent npm run dev:gateway
# TypeScript 调试
npm run build:watch
贡献代码
提交 PR 流程
- 1. Fork 仓库
- 2. 创建分支:
git checkout -b feature/my-feature - 3. 编写代码:遵循现有代码风格
- 4. 添加测试:确保代码有测试覆盖
- 5. 提交更改:
git commit -m "feat: add new feature" - 6. 推送分支:
git push origin feature/my-feature - 7. 创建 PR:描述更改内容和测试方法
代码规范
- • TypeScript:严格类型检查
- • ESLint:统一代码风格
- • Prettier:自动格式化
- • 测试:单元测试 + 集成测试
总结
OpenClaw 的架构设计遵循以下原则:
- • 模块化:各组件职责清晰,易于扩展
- • 插件化:通过插件机制支持自定义功能
- • 协议化:标准通信协议,支持多客户端
- • 文件化:数据以文件形式存储,便于管理和备份
理解源码架构有助于:
- • 更好地使用和配置 OpenClaw
- • 开发自定义插件和 Skill
- • 排查问题和贡献代码
- • 构建基于 OpenClaw 的应用
OpenClaw 是开源项目,欢迎参与贡献!
下一期,我们将探讨 贡献开源——如何为 OpenClaw 社区做出贡献。
夜雨聆风