Moltbot/OpenClaw 架构解读与二次开发完全指南
文档版本 : 2026年1月 | 基于 OpenClaw v3.0+ / Moltbot v2.0+
目标读者: AI Agent 开发者、全栈工程师、系统架构师
目录
- 核心架构概览
- Gateway 深度解析
- Agent Runtime 运行时
- Skill 系统详解
- WebSocket 协议与通信
- 二次开发完整指南
- 实战案例与最佳实践
- 安全与性能优化
核心架构概览
1.1 整体设计哲学
Moltbot(现已更名为 OpenClaw)采用去中心化事件总线架构,核心理念是将一个本地人工智能助手分解为多个解耦的、职责单一的组件。这种设计避免了传统云服务的隐私泄露风险,同时保留了强大的可扩展性。
架构的四个支柱
┌─────────────────────────────────────────────────────────────┐
│ Moltbot System Design │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. Gateway (WebSocket Event Bus) │ │
│ │ (Port 18789, localhost) │ │
│ │ ├─ 会话管理 (Session Management) │ │
│ │ ├─ 认证授权 (Authentication & Authorization) │ │
│ │ ├─ 设备配对 (Device Pairing) │ │
│ │ └─ 消息路由 (Message Routing & Dispatch) │ │
│ └──────────────────────────────────────────────────────┘ │
│ △ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ 2. Agent Runtime│ │ 3. Adapters │ │ 4. Nodes │ │
│ │ │ │ │ │ │ │
│ │ ├─ LLM Engine │ │ ├─ WhatsApp │ │ ├─ Peekaboo │ │
│ │ ├─ Tool Exec │ │ ├─ Telegram │ │ ├─ Shell │ │
│ │ ├─ Memory Mgmt │ │ ├─ Discord │ │ └─ Browser │ │
│ │ └─ Skill Loader │ │ └─ Slack │ │ │ │
│ └─────────────────┘ └──────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 信息流动拓扑
当用户在 WhatsApp 发送消息时,从消息进入到 AI 执行的完整流程如下:
用户 → WhatsApp 消息
↓
WhatsApp 网页会话(Baileys Protocol)
↓
WhatsApp Adapter(JSON 序列化)
↓
Gateway WebSocket(消息验证、路由)
↓
Agent Runtime(LLM 调用、工具选择)
↓
执行工具集(Shell/Browser/Files)
↓
节点(Nodes)执行实际操作(如 Peekaboo 截图)
↓
结果聚合 → Gateway → Adapter → WhatsApp 用户界面
这种分层设计的优势:
- 隔离性(Isolation):每个层级可独立失败而不影响整体
- 可观测性(Observability):每一步都可在 Gateway 的 WebSocket 流中观察
- 扩展性(Extensibility):可轻松添加新的 Adapter、Node 或 Skill
- 安全性(Security):清晰的权限边界和认证点
Gateway 深度解析
2.1 Gateway 的核心职责
Gateway 是一个常驻后台守护进程(Daemon) ,通常运行在 localhost:18789。它的职责不是执行 AI 推理,而是:
职责清单
| 职责 | 具体功能 | 优先级 |
|---|---|---|
| 消息路由 | 将 WhatsApp/Telegram 消息转发给 Agent Runtime | P0 |
| 会话管理 | 维护用户-设备-Agent 的三元组映射 | P0 |
| 身份认证 | Token 验证、设备配对、权限检查 | P0 |
| 状态持久化 | 工作区配置、凭证存储、会话历史 | P1 |
| 事件广播 | 将 Agent 执行状态推送给所有连接的客户端 | P1 |
| 资源管理 | 管理并发连接、内存占用、子进程生命周期 | P2 |
2.2 Gateway 的存储结构
Gateway 的所有数据存储在 ~/.openclaw/(或 ~/.moltbot/ 用于遗留安装):
~/.openclaw/
├── config.yml # 全局配置(端口、认证、模型等)
├── gateway.db # SQLite 数据库(会话、凭证、设备令牌)
├── workspaces/
│ ├── default/
│ │ ├── conversations/ # 对话历史(Markdown 格式)
│ │ │ ├── whatsapp-chat-1.md
│ │ │ ├── telegram-chat-2.md
│ │ │ └── discord-chat-3.md
│ │ ├── memory/ # Agent 长期记忆
│ │ │ ├── user-preferences.md
│ │ │ ├── learned-patterns.md
│ │ │ └── task-history.md
│ │ └── agent-state.json # Agent 的即时状态快照
├── skills/
│ ├── bundled/ # 内置技能(官方提供)
│ │ ├── shell/
│ │ ├── files/
│ │ ├── peekaboo/
│ │ └── ...
│ └── custom/ # 用户自定义技能
│ ├── wine-cellar/SKILL.md
│ ├── github-pr-reviewer/SKILL.md
│ └── ...
├── credentials/
│ ├── encrypted/ # 加密存储的 API 密钥
│ │ ├── claude-api-key.enc
│ │ ├── github-token.enc
│ │ └── ...
│ └── metadata.json # 凭证清单(不含密钥本身)
├── logs/
│ ├── gateway.log # Gateway 守护进程日志
│ └── agent.log # Agent 运行时日志
└── tls/
├── cert.pem # TLS 证书(仅当启用远程访问时)
└── key.pem # 私钥(仅当启用远程访问时)
2.3 Gateway 的通信机制
Gateway 使用 WebSocket 作为统一的通信协议。所有连接(包括 CLI、Web UI、聊天适配器、外部节点)都通过 WebSocket 进行通信。
连接生命周期
1. 客户端(Client)启动 WebSocket 连接到 ws://localhost:18789
2. 客户端发送 CONNECT 请求,声明自己的身份:
{
"type": "connect",
"id": "uuid-1",
"params": {
"role": "operator|node|adapter",
"deviceId": "mac-mini-001",
"auth": {
"token": "CLAWDBOT_GATEWAY_TOKEN 的值" // 可选
},
"scopes": ["operator.read", "operator.write"],
"caps": ["peekaboo.screenshot", "shell.exec"], // 声明能力
"commands": ["shell", "files", "browser"] // 允许的命令
}
}
3. Gateway 验证 token、检查权限、验证设备身份
- 如果是新设备,可能需要配对确认(Pairing)
- 配对码通过 DM 或 UI 显示给用户
4. 认证成功,Gateway 返回 hello-ok:
{
"type": "res",
"id": "uuid-1",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 3,
"auth": {
"deviceToken": "新签发的设备令牌",
"role": "operator",
"scopes": ["operator.read", "operator.write"]
},
"policy": {
"tickIntervalMs": 15000 // 心跳间隔
}
}
}
5. 连接进入就绪状态,可开始交换消息
6. 客户端定期发送心跳(TICK)以保持连接活跃
{
"type": "tick",
"id": "uuid-N"
}
7. 当需要断开时,客户端发送 DISCONNECT,Gateway 清理相关资源
2.4 Gateway 的权限模型
Moltbot 采用基于作用(Role)和权限(Scopes)的访问控制(RBAC):
作用(Roles)
- operator:本地人类操作员(通过 CLI、Web UI、设备连接),拥有最高权限
- node :执行节点(如 Peekaboo、远程工作机),声明特定能力,受限于声明的
caps - adapter:聊天平台适配器(WhatsApp、Telegram 等),只能接收/发送消息,无法访问本地系统
权限范围(Scopes)
权限采用点符号(Dot-Notation)表示,形成树状结构:
operator.*
├── operator.read # 读取会话、配置、日志
├── operator.write # 修改配置、发起任务
├── operator.pairing # 批准新设备配对
└── operator.admin # 管理其他操作员
node.*
├── node.caps # 声明能力的权利
├── node.execute # 执行工具调用
└── node.report # 向 Gateway 报告状态
adapter.*
├── adapter.message.read # 接收来自 Gateway 的消息
├── adapter.message.write # 向用户发送消息
└── adapter.channel.bind # 将频道绑定到 Agent
权限检查流程
当 Agent 尝试执行工具时:
python
# 伪代码示例
def execute_tool(agent_id, tool_name, tool_args):
# 1. 检查 Agent 是否声明了该工具的能力
if tool_name not in agent.declared_caps:
raise PermissionDenied(f"Agent {agent_id} 未声明 {tool_name} 能力")
# 2. 检查该工具是否在 Agent 的执行白名单中
if tool_name not in agent.commands:
raise PermissionDenied(f"Tool {tool_name} 不在执行白名单中")
# 3. 对于敏感操作(如文件删除、命令执行),进行额外权限检查
if tool_name == "files.delete":
if "files.delete" not in agent.scopes:
raise PermissionDenied("删除文件需要 files.delete 权限")
# 4. 记录审计日志
audit_log(action="tool_exec", agent=agent_id, tool=tool_name, args=tool_args)
# 5. 转发给对应的节点或本地执行
return execute_on_node(agent_id, tool_name, tool_args)
Agent Runtime 运行时
3.1 Agent 的核心循环(Agentic Loop)
Agent Runtime 是执行 AI 推理的中心。它实现了经典的 ReAct(Reasoning + Acting)循环:
┌─────────────────────────────────────────────────────────────┐
│ Agent Agentic Loop │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户输入(来自 Gateway) │
│ ↓ │
│ 1. OBSERVE 观察 │
│ ├─ 读取上下文窗口(最近 N 条消息) │
│ ├─ 加载长期记忆(~/.openclaw/memory) │
│ ├─ 检查系统状态(可用工具、已加载技能) │
│ └─ 构建 Prompt │
│ ↓ │
│ 2. LLM 调用(Thinking) │
│ ├─ 发送 Prompt 到 Claude/GPT 等 │
│ ├─ LLM 返回 CoT(Chain-of-Thought) │
│ └─ LLM 返回工具调用决策 │
│ ↓ │
│ 3. DECIDE 决策 │
│ ├─ 解析 LLM 的工具调用(Tool Call) │
│ ├─ 检查权限(是否允许调用) │
│ ├─ 验证工具参数类型 │
│ └─ 选择执行策略(本地/远程节点) │
│ ↓ │
│ 4. ACT 执行 │
│ ├─ 本地执行:直接调用 shell/files 等 │
│ ├─ 远程执行:通过 Gateway 转发给 Peekaboo/节点 │
│ └─ 流式返回结果给 LLM │
│ ↓ │
│ 5. EVALUATE 评估 │
│ ├─ 检查工具执行是否成功 │
│ ├─ 收集错误或异常 │
│ ├─ 更新内部状态和记忆 │
│ └─ 判断是否完成了用户目标 │
│ ↓ │
│ 6. 条件判断 │
│ ├─ 目标达成?→ 生成回复,结束 │
│ ├─ 需要更多工具?→ 回到步骤 2 │
│ ├─ 遇到错误需重试?→ 调整参数,回到步骤 2 │
│ └─ 达到最大迭代次数?→ 返回部分结果 │
│ ↓ │
│ 回复发送至用户(通过 Gateway 和 Adapter) │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 上下文窗口与令牌预算
OpenClaw 使用 Claude 模型的 128K 令牌(Token)上下文窗口进行长期记忆和对话保留。
令牌分配策略
总上下文:128,000 tokens
│
├─ 系统 Prompt(固定):2,000 tokens
│ ├─ Agent 身份和能力说明
│ ├─ 可用技能列表
│ ├─ 用户偏好和指示
│ └─ 安全策略约束
│
├─ 长期记忆(动态):20,000 tokens
│ ├─ 用户偏好(~5KB Markdown)
│ ├─ 学到的模式(~8KB)
│ ├─ 重要决策历史(~7KB)
│ └─ 上下文摘要(由 LLM 生成,用于压缩)
│
├─ 对话历史(动态,滚动窗口):60,000 tokens
│ ├─ 最近 N 条消息(保留对话连贯性)
│ ├─ 相关的历史消息(通过向量搜索检索)
│ └─ 工具执行结果的详细日志
│
├─ 当前请求(动态):30,000 tokens
│ ├─ 用户新消息:1-2K tokens
│ ├─ 工具调用结果:5-15K tokens
│ └─ 思考过程的 CoT 输出:5-10K tokens
│
└─ 预留缓冲(用于响应生成):15,000 tokens
└─ 确保 LLM 有足够空间生成完整回复
令牌管理算法
当上下文接近限制时,Agent 采用以下策略(按优先级):
python
def manage_context_tokens():
total_tokens = count_tokens(system_prompt + memory + history + current_request)
if total_tokens < 100000:
return # 还有余量,继续
# 策略 1:压缩最旧的对话消息
if len(conversation_history) > 20:
oldest_messages = conversation_history[:10]
summary = llm_summarize(oldest_messages) # LLM 生成摘要
conversation_history = [summary] + conversation_history[10:]
# 策略 2:删除不相关的长期记忆
if memory_tokens > 15000:
similarity_scores = compute_similarity(current_query, memory_entries)
irrelevant = [m for m, score in zip(memory_entries, similarity_scores) if score < 0.3]
for m in irrelevant:
archive_to_disk(m) # 存档到磁盘
# 策略 3:截断工具执行结果
if len(tool_results) > 5:
for result in tool_results[:-3]:
result['output'] = result['output'][:500] # 保留前 500 字符
# 策略 4:触发强制压缩
if total_tokens > 120000:
invoke_emergency_compression()
3.3 记忆系统(Memory System)
Moltbot 维护两种记忆:
A. 短期记忆(Episodic Memory)
存储在 ~/.openclaw/workspaces/default/conversations/ 下:
markdown
# WhatsApp Conversation - 2026-01-28
## Context
- Started: 2026-01-28T10:30:00Z
- Participants: User, Agent
- Total Messages: 127
- Total Tokens: ~45,000
## Messages
### Message 1 (User, 10:30)
用户: "帮我分析上周的销售数据"
### Message 2 (Agent, 10:31)
Agent 思考过程:
用户要求分析销售数据。我需要:
-
找到销售数据文件的位置
-
读取并解析数据
-
进行统计分析
-
生成可视化图表
-
总结关键见解
执行工具: files.find
参数: {"pattern": "sales*.csv", "path": "~/data"}Message 3 (Node, 10:32)
工具结果:
找到的文件:
-
~/data/sales_2026_01.csv
-
~/data/sales_2026_02.csv
...(省略中间步骤)
Message 50 (Agent, 10:45)
最终回复:
"根据我对上周数据的分析:- 总销售额:$125,000(增长 12%)
- 最畅销产品:产品 A(占比 35%)
- 区域表现:北方市场表现最好(+18%)
[图表已生成]"
B. 长期记忆(Semantic Memory)
存储在 ~/.openclaw/workspaces/default/memory/ 下,采用 Markdown 格式:
markdown
# User Preferences
- 时区: Asia/Shanghai (UTC+8)
- 语言偏好: 中文优先,英文补充
- 工作时间: 9AM-6PM
- 报告格式: 总是包括数字、图表、关键见解
# Learned Patterns
## Pattern 1: Weekly Report Generation
用户在每个周一上午 10 点要求生成周报。
自动化建议:
- 预留 Friday EOD 至 Monday 10AM 的数据收集时间
- 自动拉取上周的销售、项目进度数据
- 使用模板:执行摘要 → 关键指标 → 风险/机遇 → 下周计划
## Pattern 2: Market Analysis Requests
用户频繁要求竞争对手分析。已学到:
- 用户关注: 定价策略、新产品发布、客户评价
- 信息源: G2、Crunchbase、官网、新闻
- 输出格式: 表格对比 + SWOT 分析
# Decision History
## 2026-01-25: API Integration Decision
用户问: "应该用 REST 还是 GraphQL?"
Agent 决策过程:
1. 询问了数据量预期和查询复杂度
2. 建议 GraphQL(基于用户的高查询多样性)
3. 用户采纳
决议: 应在类似情景下继续推荐 GraphQL
## 2026-01-20: Tool Selection
用户实施了 Agent 建议的 Notion 数据库方案。
反馈: 提高了信息组织效率 40%
推论: 在 PKM(个人知识管理)方面,倾向推荐 Notion 而非其他工具
# Important Context
## User's Company
- 名称: TechCorp
- 规模: 50 人
- 行业: SaaS
- 产品: 数据分析平台
- 工程团队规模: 15 人
## User's Role
- 职位: VP of Product
- 责任: 产品战略、市场分析、工程协调
- 优先级: 用户获取成本(CAC)控制、产品-市场契合
3.4 多智能体管理
当启用多个 Agent 时,每个 Agent 通过以下机制隔离:
yaml
# config.yml 示例
agents:
- id: main-agent
name: "主助手"
model: "claude-opus-4"
workspace: "~/.openclaw/workspaces/default"
bindings:
- "whatsapp:*" # 处理所有 WhatsApp 消息
- "telegram:@main_bot"
skills: ["bundled/*", "custom/wine-cellar", "custom/analytics"]
memory:
ttl_days: 90 # 90 天后自动清理旧记忆
max_size_mb: 500 # 单个工作区最大 500MB
capabilities:
- "shell.exec"
- "files.read"
- "files.write"
- "peekaboo.screenshot"
- "browser.control"
- id: coding-agent
name: "编码助手"
model: "claude-3.5-sonnet"
workspace: "~/.openclaw/workspaces/coding"
bindings:
- "discord:dev-channel"
- "slack:engineering"
skills: ["bundled/*", "custom/github-pr-reviewer", "custom/code-analyzer"]
capabilities:
- "shell.exec"
- "files.read"
- "files.write"
restrictions:
- "peekaboo.*" # 禁止截图
- "browser.*" # 禁止浏览器控制
- id: analytics-agent
name: "数据分析师"
model: "claude-3.5-sonnet"
workspace: "~/.openclaw/workspaces/analytics"
bindings:
- "email:analytics-bot@company.com"
skills: ["bundled/*", "custom/data-processor", "custom/visualization"]
capabilities:
- "files.read"
- "shell.exec" # 仅允许 Python、R 脚本
Skill 系统详解
4.1 Skill 的架构(SKILL.md 规范)
每个 Skill 都是一个包含 SKILL.md 文件的目录。SKILL.md 是纯 Markdown 格式的技能定义,LLM 通过自然语言指令理解如何使用该技能。
标准目录结构
custom-skills/
└── wine-cellar/ # Skill 的唯一标识符
├── SKILL.md # 必需:技能定义
├── implementation.js # 可选:实现脚本
├── api-docs.md # 可选:API 文档
├── examples.md # 可选:使用示例
└── config.json # 可选:运行时配置
SKILL.md 的完整结构
markdown
---
emoji: 🍷
requires:
bins:
- "curl" # 依赖的命令行工具
- "jq"
env:
- "WINE_DB_PATH" # 依赖的环境变量
- "TASTING_API_KEY"
config:
- "wine_database_file" # 运行时配置项
install: |
npm install wine-api --save
pip install sommelier --upgrade
version: "1.2.3"
---
# Wine Cellar Management(一级标题:技能名称)
## 描述(Description)
该技能管理个人葡萄酒收藏库。用户可以添加新葡萄酒、搜索现有葡萄酒、获取配餐建议,
以及跟踪葡萄酒的年份和评分。
**适用场景**:
- 葡萄酒收藏管理
- 尝酒笔记记录
- 餐酒搭配推荐
- 库存追踪
## 工具(Tools)
### add_wine
添加新葡萄酒到收藏库。
**参数**:
- `name` (string, required): 葡萄酒名称,如 "Château Margaux 2018"
- `region` (string, required): 产地,如 "Bordeaux"
- `vintage` (number, required): 年份
- `type` (enum): 红、白、起泡
- `tasting_notes` (string): 品酒笔记
- `rating` (number): 个人评分(1-5)
- `price_paid` (number): 购买价格
- `purchase_date` (string): 购买日期 YYYY-MM-DD
- `location` (string): 存储位置,如 "酒柜第二层"
**返回**:
```json
{
"success": true,
"wine_id": "wine-12345",
"message": "Château Margaux 2018 已添加"
}
search_wine
搜索收藏库中的葡萄酒。
参数:
query(string): 搜索词,可以是名称、产地、年份filters(object, optional):type: 葡萄酒类型vintage_from/vintage_to: 年份范围min_rating: 最低评分
limit(number): 返回结果数量,默认 10
返回:
json
{
"results": [
{
"wine_id": "wine-12345",
"name": "Château Margaux 2018",
"region": "Bordeaux",
"vintage": 2018,
"rating": 4.8,
"tasting_notes": "深色浆果,樱桃,柔软的单宁..."
}
],
"total": 1
}
get_pairing_suggestion
获取食物配餐建议。
参数:
food_item(string): 食物名称,如 "红烧牛肉"wine_id(string, optional): 如果提供,检查该葡萄酒是否匹配cuisine(string, optional): 菜系,如 "中文"、"法式"
返回:
json
{
"food": "红烧牛肉",
"top_pairings": [
{
"wine_name": "Château Margaux 2018",
"region": "Bordeaux",
"pairing_score": 0.92,
"reason": "丰厚的红色水果和结构化的单宁与牛肉的脂肪和香料完美平衡"
}
]
}
说明(Instructions)
当用户提到葡萄酒管理或品酒相关的请求时,优先使用该技能。
使用流程
-
添加葡萄酒:当用户说"我买了一瓶 Château Margaux 2018"时
- 调用
add_wine,填充名称、产地、年份 - 询问用户是否想添加品酒笔记
- 确认添加成功
- 调用
-
搜索和查询:当用户问"我有什么红葡萄酒"时
- 调用
search_wine,使用filters.type = "red" - 返回按评分排序的结果
- 提供库存摘要(如"总共 23 瓶")
- 调用
-
配餐推荐:当用户说"晚餐我有牛肉,推荐个葡萄酒"时
- 调用
get_pairing_suggestion - 返回收藏库中的最佳匹配,以及购买建议
- 调用
边界情况(Edge Cases)
-
重复添加 :如果尝试添加相同的葡萄酒(名称+年份+产地完全相同),
提醒用户是否确实有多瓶,还是更新现有记录?
-
缺少必需参数:如果用户未提供年份或产地,主动询问而非拒绝。
-
API 失败:如果 Tasting API 不可用,回退到本地数据库查询。
示例(Examples)
示例 1:添加和搜索
用户: "我刚买了两瓶 Château Margaux 2018,我想看看我还有什么 Bordeaux"
Agent 行为:
- 调用
add_wine两次(确认数量) - 调用
search_wine(query="Bordeaux", limit=10) - 返回所有 Bordeaux 葡萄酒的列表
示例 2:配餐建议
用户: "明天我要做红烧羊肉,推荐个酒"
Agent 行为:
- 调用
get_pairing_suggestion(food_item="红烧羊肉") - 检查返回的配对中哪些葡萄酒在用户的收藏库中
- 优先推荐用户已有的葡萄酒,其次推荐需要购买的葡萄酒
失败处理(Failure Handling)
如果 add_wine 返回错误:
├─ "Duplicate wine": 提醒用户该葡萄酒已存在,询问是否更新
├─ "Invalid vintage": 验证年份是否有效(1800-当前年份)
└─ "Storage location full": 提示用户清理空间或选择其他位置
如果 search_wine 返回空结果:
├─ 提示用户收藏库中没有匹配项
├─ 建议用户尝试不同的搜索词
└─ 可选:询问用户是否想添加新葡萄酒
权限要求(Permissions)
files.read: 读取本地葡萄酒数据库文件files.write: 更新葡萄酒收藏记录http: 调用外部 Tasting API(可选)
配置示例(config.json)
json
{
"wine_database_file": "~/.openclaw/workspaces/default/data/wines.json",
"tasting_api_url": "https://api.tasting.example.com",
"tasting_api_key": "${TASTING_API_KEY}",
"currency": "USD",
"temperature_unit": "C",
"locale": "zh_CN"
}
### 4.2 Skill 的发现与加载机制
Agent 启动时,会执行以下 Skill 发现和加载流程:
```python
def discover_and_load_skills():
"""
Skill 发现算法:系统启动时的一次性扫描
"""
skill_directories = [
f"{OPENCLAW_HOME}/skills/bundled", # 内置技能
f"{OPENCLAW_HOME}/skills/custom", # 用户自定义
f"{CURRENT_WORKSPACE}/skills", # 工作区特定技能
]
loaded_skills = {}
for skill_dir in skill_directories:
for entry in os.listdir(skill_dir):
skill_path = os.path.join(skill_dir, entry)
if not os.path.isdir(skill_path):
continue
# 查找 SKILL.md
skill_md = os.path.join(skill_path, "SKILL.md")
if not os.path.exists(skill_md):
continue
try:
# 解析 SKILL.md 的 YAML 前置信息
metadata = parse_yaml_frontmatter(skill_md)
# 检查依赖
if not check_dependencies(metadata.get("requires", {})):
logger.warning(f"Skill {entry} 缺失依赖,已跳过")
continue
# 加载 Skill 定义
skill_content = read_markdown_content(skill_md)
skill_id = entry # 目录名作为唯一 ID
loaded_skills[skill_id] = {
"id": skill_id,
"name": extract_h1_title(skill_content),
"description": extract_description_section(skill_content),
"tools": parse_tool_definitions(skill_content),
"instructions": extract_instructions_section(skill_content),
"examples": parse_examples_section(skill_content),
"metadata": metadata,
"path": skill_path,
"content": skill_content, # 完整 Markdown,作为上下文
}
logger.info(f"✅ 加载 Skill: {skill_id}")
except Exception as e:
logger.error(f"❌ 加载 Skill {entry} 失败: {e}")
continue
return loaded_skills
def select_skills_for_task(user_query, loaded_skills):
"""
当 Agent 收到用户请求时,使用向量相似度搜索选择相关 Skill
"""
# 1. 构建 Skill 索引(基于描述、工具名称、示例)
skill_embeddings = {}
for skill_id, skill in loaded_skills.items():
# 合并多个文本字段用于向量化
combined_text = (
f"{skill['name']} {skill['description']} "
f"{' '.join(t['name'] for t in skill['tools'])} "
f"{skill['instructions']}"
)
skill_embeddings[skill_id] = embed(combined_text)
# 2. 计算用户查询的向量
query_embedding = embed(user_query)
# 3. 计算相似度
similarities = {}
for skill_id, skill_embedding in skill_embeddings.items():
similarity = cosine_similarity(query_embedding, skill_embedding)
similarities[skill_id] = similarity
# 4. 选择相似度高于阈值的 Skill(通常 > 0.5)
selected_skills = [
skill_id for skill_id, score in similarities.items()
if score > 0.5
]
# 5. 按相似度排序,最相关的优先
selected_skills.sort(key=lambda s: similarities[s], reverse=True)
# 6. 将选中的 Skill 的完整 Markdown 添加到 LLM 上下文
skills_context = "\n\n---\n\n".join([
f"# Skill: {loaded_skills[sid]['name']}\n{loaded_skills[sid]['content']}"
for sid in selected_skills[:5] # 限制最多 5 个 Skill 进入上下文
])
return selected_skills, skills_context
4.3 社区 Skill 注册表(ClawdHub)
ClawdHub 是 Moltbot 社区托管的 Skill 包管理平台,类似 npm。用户可以:
bash
# 搜索 Skill
openclaw skill search wine
# 安装社区 Skill
openclaw skill install @community/wine-cellar
# 发布自己的 Skill
openclaw skill publish ./wine-cellar \
--name "Wine Cellar Manager" \
--version "1.0.0" \
--description "管理你的葡萄酒收藏" \
--license MIT
WebSocket 协议与通信
5.1 协议规范(Protocol v3)
消息帧格式
所有 WebSocket 消息都是 JSON 格式,包含以下通用字段:
json
{
"type": "req|res|event|tick|disconnect",
"id": "唯一消息 ID (UUID v4)",
"timestamp": "ISO 8601 时间戳",
"payload": { /* 类型特定的数据 */ },
"metadata": {
"source": "whatsapp|telegram|cli|web|node",
"agent_id": "main-agent",
"device_id": "mac-mini-001"
}
}
消息类型详解
1. CONNECT(连接建立)
json
{
"type": "req",
"id": "conn-001",
"timestamp": "2026-01-28T10:30:00Z",
"payload": {
"type": "connect",
"params": {
"role": "operator",
"deviceId": "mac-mini-001",
"auth": {
"token": "CLAWDBOT_GATEWAY_TOKEN"
},
"scopes": ["operator.read", "operator.write"],
"caps": ["peekaboo.screenshot", "shell.exec"],
"commands": ["shell", "files", "browser"]
}
}
}
响应:
json
{
"type": "res",
"id": "conn-001",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 3,
"auth": {
"deviceToken": "eyJhbGci...", // JWT 令牌
"role": "operator",
"scopes": ["operator.read", "operator.write"]
},
"policy": {
"tickIntervalMs": 15000,
"maxMessageSize": 1048576, // 1MB
"connectionTimeout": 300000 // 5 分钟无活动自动断开
}
}
}
2. CHAT.MESSAGE(聊天消息)
json
{
"type": "event",
"id": "msg-001",
"timestamp": "2026-01-28T10:30:15Z",
"payload": {
"type": "chat.message",
"channel": {
"platform": "whatsapp",
"id": "120363012345678-1234567890",
"name": "Personal Chat"
},
"message": {
"id": "wamid.xxx",
"sender": {
"id": "60123456789",
"name": "John Doe"
},
"timestamp": "2026-01-28T10:30:10Z",
"text": "帮我分析销售数据",
"attachments": [] // 可包含图片、文件等
}
}
}
3. AGENT.INVOKE(调用工具)
json
{
"type": "req",
"id": "invoke-001",
"timestamp": "2026-01-28T10:30:20Z",
"payload": {
"type": "agent.invoke",
"agent_id": "main-agent",
"tool": "files.read",
"args": {
"path": "~/data/sales.csv"
}
}
}
响应(流式):
json
{
"type": "res",
"id": "invoke-001",
"stream": true,
"chunk": 0,
"payload": {
"status": "executing",
"progress": 0.3
}
}
// ... 后续块 ...
{
"type": "res",
"id": "invoke-001",
"stream": true,
"chunk": 5,
"stream_done": true,
"ok": true,
"payload": {
"status": "completed",
"result": "sales_data_csv_content"
}
}
4. AGENT.THINKING(Agent 思考过程)
json
{
"type": "event",
"id": "thinking-001",
"timestamp": "2026-01-28T10:30:25Z",
"payload": {
"type": "agent.thinking",
"agent_id": "main-agent",
"step": 1,
"message": "用户要求分析销售数据。我将首先定位数据文件。",
"tools_available": ["files.read", "shell.exec", "browser.control"]
}
}
5. TICK(心跳)
json
{
"type": "tick",
"id": "tick-001",
"timestamp": "2026-01-28T10:30:30Z"
}
5.2 错误处理与重试策略
当 WebSocket 消息失败时,客户端应实现指数退避重试:
python
class GatewayClient:
def __init__(self, max_retries=5):
self.max_retries = max_retries
self.retry_delays = [1, 2, 4, 8, 16] # 秒
async def send_with_retry(self, message):
"""
发送消息,失败时自动重试
"""
for attempt in range(self.max_retries):
try:
response = await self.send(message)
# 检查响应中的错误
if not response.get("ok"):
error_code = response.get("error_code")
# 某些错误不应重试
if error_code in ["auth.invalid_token", "auth.unauthorized"]:
raise AuthenticationError(response.get("error"))
# 其他错误应重试
if attempt < self.max_retries - 1:
delay = self.retry_delays[attempt]
logger.warning(f"消息发送失败,{delay}秒后重试")
await asyncio.sleep(delay)
continue
return response
except (ConnectionError, TimeoutError) as e:
if attempt < self.max_retries - 1:
delay = self.retry_delays[attempt]
logger.warning(f"连接错误,{delay}秒后重试: {e}")
await asyncio.sleep(delay)
continue
else:
raise
raise MaxRetriesExceededError("消息发送失败,已达最大重试次数")
二次开发完整指南
6.1 开发环境搭建
前置要求
bash
# 系统要求
- macOS 11+ 或 Linux(Debian/Ubuntu/Fedora)或 Windows 11(需 WSL2)
- Node.js 22+ LTS
- npm 或 yarn
- 至少 4GB RAM
- 可选:Docker(用于容器化部署)
# 验证环境
node --version # v22.0.0+
npm --version # 10.0.0+
克隆和安装
bash
# 1. 克隆官方仓库
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# 2. 安装依赖
npm install
# 3. 配置开发环境
cp .env.example .env
# 编辑 .env,配置 API 密钥等
# 4. 启动开发 Gateway
npm run dev:gateway
# 5. 在另一个终端启动 Agent
npm run dev:agent
# 6. (可选)启动 Web UI
npm run dev:ui
# 访问 http://localhost:3000
6.2 创建自定义 Skill
Step 1: 创建 Skill 目录
bash
mkdir -p ~/.openclaw/skills/custom/my-awesome-skill
cd ~/.openclaw/skills/custom/my-awesome-skill
Step 2: 编写 SKILL.md
创建 SKILL.md 文件(参照第 4 章的完整 Markdown 格式):
markdown
---
emoji: ⚡
requires:
bins:
- "python3"
env:
- "MY_API_KEY"
config:
- "api_endpoint"
---
# My Awesome Skill
## 描述
...
Step 3: 创建实现脚本(可选)
如果 Skill 需要执行复杂逻辑,可以创建 implementation.js 或 implementation.py:
javascript
// implementation.js
module.exports = {
async execute_tool(tool_name, args) {
switch(tool_name) {
case "process_data":
return await processData(args);
case "generate_report":
return await generateReport(args);
default:
throw new Error(`未知工具: ${tool_name}`);
}
},
async processData(args) {
const { input_file, output_format } = args;
// 实现数据处理逻辑
return {
success: true,
output: "处理完成"
};
},
async generateReport(args) {
// 实现报告生成逻辑
return { report: "..." };
}
};
Step 4: 测试 Skill
bash
# 1. 启动 Moltbot(如上所述)
# 2. 在 Web UI 或 CLI 中测试
# 在聊天中输入相关消息,Agent 应自动选择你的 Skill
# 3. 查看日志
tail -f ~/.openclaw/logs/agent.log
# 4. 调试
# 在 Agent 的思考过程中可以看到是否选择了你的 Skill
Step 5: 发布到社区
bash
# 1. 准备文件
# - SKILL.md (必需)
# - README.md (描述、安装说明、示例)
# - LICENSE (选择 MIT、Apache 2.0 等)
# - examples/ (目录,包含使用示例)
# 2. 登录 ClawdHub
openclaw auth login
# 3. 发布
openclaw skill publish ./my-awesome-skill \
--name "My Awesome Skill" \
--description "做一些很棒的事情" \
--license MIT \
--tags "automation,data-processing"
# 3. 验证发布
# https://clawd-hub.org/@your-username/my-awesome-skill
6.3 扩展 Gateway
创建自定义 Adapter(聊天渠道)
如果想添加新的聊天平台(如微信、钉钉),需要创建 Adapter:
typescript
// src/adapters/custom-platform/adapter.ts
import { GatewayAdapter, Message, Channel } from "@openclaw/gateway";
export class CustomPlatformAdapter extends GatewayAdapter {
private client: CustomPlatformClient;
async initialize(config: AdapterConfig): Promise<void> {
// 初始化连接到自定义平台
this.client = new CustomPlatformClient(config.api_key);
// 监听消息事件
this.client.on("message", (msg) => this.onMessage(msg));
this.client.on("status", (status) => this.onStatusChange(status));
}
private async onMessage(platformMessage: any): Promise<void> {
// 将平台特定的消息格式转换为 Gateway 的标准格式
const message: Message = {
id: platformMessage.msg_id,
text: platformMessage.content,
sender: {
id: platformMessage.from_user_id,
name: platformMessage.from_user_name
},
channel: {
platform: "custom-platform",
id: platformMessage.conversation_id,
name: platformMessage.conversation_name
},
timestamp: new Date(platformMessage.timestamp),
attachments: platformMessage.attachments?.map(att => ({
type: att.type,
url: att.url,
size: att.size
}))
};
// 将消息转发给 Gateway
await this.gateway.emit("chat.message", message);
}
async sendMessage(channel: Channel, text: string): Promise<void> {
// 将 Gateway 的消息格式转换为平台特定格式,然后发送
await this.client.send({
conversation_id: channel.id,
content: text
});
}
}
注册 Adapter
typescript
// src/gateway/registry.ts
import { CustomPlatformAdapter } from "./adapters/custom-platform/adapter";
GatewayRegistry.registerAdapter("custom-platform", CustomPlatformAdapter);
配置 Adapter
yaml
# config.yml
adapters:
custom-platform:
enabled: true
api_key: ${CUSTOM_PLATFORM_API_KEY}
webhook_url: "http://localhost:18789/webhooks/custom-platform"
auto_reconnect: true
reconnect_delay_ms: 5000
6.4 扩展 Agent Runtime
添加自定义工具(Tool)
工具是 Agent 可以直接调用的原子操作。与 Skill 不同,Tool 是编程式的,Skill 是声明式的。
typescript
// src/agent/tools/custom-tool.ts
import { Tool, ToolArgument } from "@openclaw/agent";
export class CustomTool extends Tool {
name = "custom.process";
description = "处理自定义数据格式";
arguments: ToolArgument[] = [
{
name: "input_data",
type: "string",
description: "要处理的数据",
required: true
},
{
name: "format",
type: "enum",
enum: ["json", "csv", "xml"],
description: "输出格式",
default: "json"
}
];
async execute(args: Record<string, any>): Promise<any> {
const { input_data, format } = args;
try {
// 数据验证
if (!input_data || typeof input_data !== "string") {
return {
success: false,
error: "input_data 必须是字符串"
};
}
// 处理逻辑
let processed = this.processData(input_data);
// 格式转换
const output = this.convertFormat(processed, format);
return {
success: true,
output: output,
format: format
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
private processData(data: string): any {
// 实现处理逻辑
return { processed: true };
}
private convertFormat(data: any, format: string): string {
switch(format) {
case "json":
return JSON.stringify(data, null, 2);
case "csv":
return this.toCSV(data);
case "xml":
return this.toXML(data);
default:
return JSON.stringify(data);
}
}
// ... 辅助方法
}
注册工具
typescript
// src/agent/tool-registry.ts
import { CustomTool } from "./tools/custom-tool";
ToolRegistry.register(new CustomTool());
6.5 构建 Node 节点(远程执行器)
Node 是执行特定工具的远程进程,如 Peekaboo(用于 macOS UI 自动化)。
typescript
// src/nodes/custom-node/index.ts
import { MoltbotNode } from "@openclaw/node";
class CustomNode extends MoltbotNode {
constructor() {
super({
id: "custom-node",
name: "自定义执行节点",
version: "1.0.0",
capabilities: ["custom.process", "custom.analyze"]
});
}
async initialize(): Promise<void> {
await super.initialize();
// 初始化环境、加载依赖等
console.log("✅ 自定义 Node 已初始化");
}
async executeCommand(
capability: string,
args: Record<string, any>
): Promise<any> {
switch(capability) {
case "custom.process":
return await this.processData(args);
case "custom.analyze":
return await this.analyzeData(args);
default:
throw new Error(`未知能力: ${capability}`);
}
}
private async processData(args: any): Promise<any> {
// 实现处理逻辑
return { status: "processed" };
}
private async analyzeData(args: any): Promise<any> {
// 实现分析逻辑
return { analysis: "..." };
}
}
// 启动 Node
const node = new CustomNode();
node.connect("ws://localhost:18789");
实战案例与最佳实践
7.1 案例 1: 企业级智能工单系统
场景:一个 SaaS 公司希望用 Moltbot 自动分配和跟踪客户支持工单。
架构设计
用户(Slack)
↓
Slack Adapter
↓
Gateway
↓
Support Agent
├─ Skill: JIRA Integration
│ ├─ Tool: create_issue
│ ├─ Tool: search_issues
│ └─ Tool: update_issue_status
│
├─ Skill: Knowledge Base Search
│ ├─ Tool: search_knowledge_base
│ └─ Tool: get_article_content
│
└─ Skill: Email Notifier
├─ Tool: send_email
└─ Tool: add_to_distribution_list
Nodes
├─ JIRA Node(远程 JIRA 服务器)
└─ Email Node(远程 SMTP 服务器)
工作流程
客户消息:"我的 API key 过期了,无法连接你们的服务"
↓
Support Agent 思考:
1. 这是一个登录/认证问题
2. 应该首先搜索知识库中是否有解决方案
3. 如果没有现成方案,创建工单并通知相关技术人员
↓
执行 Skill: Knowledge Base Search
结果:找到"API Key 过期问题"的知识库文章
↓
Agent 回复客户:
"根据我们的知识库,API key 过期可以通过以下方式解决...
如果这不解决你的问题,我会为你创建一个工单并通知技术团队。"
↓
创建 JIRA 工单
↓
通过 Email 通知技术团队负责人
7.2 案例 2: 多智能体数据分析管道
场景:一个市场研究公司需要每周自动收集、分析和报告竞争对手数据。
多智能体架构
yaml
agents:
- id: orchestrator-agent
role: "协调者"
model: "claude-opus-4" # 高端模型用于策略决策
responsibilities:
- 分解任务为子任务
- 协调多个工作者 Agent
- 综合最终报告
- id: research-agent-1
role: "竞争对手 A 研究员"
model: "claude-3.5-sonnet"
focus: ["Company A 网站监控", "新闻追踪", "社交媒体分析"]
skills:
- web-scraping
- news-aggregation
- sentiment-analysis
- id: research-agent-2
role: "竞争对手 B 研究员"
model: "claude-3.5-sonnet"
focus: ["Company B 产品更新", "招聘信息", "融资新闻"]
- id: research-agent-3
role: "竞争对手 C 研究员"
model: "claude-3.5-sonnet"
focus: ["Company C 财务数据", "专利申请", "高管变更"]
- id: analyst-agent
role: "数据分析师"
model: "claude-3.5-sonnet"
focus: ["数据清洗", "趋势分析", "对比分析"]
skills:
- data-processing
- statistics
- visualization
- id: writer-agent
role: "报告撰写员"
model: "claude-3.5-sonnet"
focus: ["报告撰写", "编辑排版", "格式化"]
并行任务编排
python
async def weekly_competitor_analysis():
"""
周报生成工作流
"""
# 步骤 1:编排器分配任务
tasks = {
"research_a": research_agent_1.task("分析 Company A 本周变化"),
"research_b": research_agent_2.task("分析 Company B 本周变化"),
"research_c": research_agent_3.task("分析 Company C 本周变化"),
}
# 步骤 2:并行执行(3 个研究 Agent 同时工作)
research_results = await asyncio.gather(
tasks["research_a"],
tasks["research_b"],
tasks["research_c"]
)
# 步骤 3:数据分析
analysis_result = await analyst_agent.analyze(research_results)
# 步骤 4:报告撰写
final_report = await writer_agent.write_report(analysis_result)
# 步骤 5:发送报告
await send_email(
to="stakeholders@company.com",
subject=f"周报:竞争对手分析 {date.today()}",
body=final_report
)
安全与性能优化
8.1 安全加固清单
| 安全项 | 默认值 | 建议配置 | 重要性 |
|---|---|---|---|
| Gateway 绑定地址 | 127.0.0.1 | 仅本地 | 严重 |
| 认证模式 | token 必需 | 启用 CLAWDBOT_GATEWAY_TOKEN | 严重 |
| 设备配对 | 需要批准 | 启用 DM 确认 | 严重 |
| 文件访问权限 | 无限制 | 配置白名单 | 高 |
| Shell 执行 | 无限制 | 禁用危险命令(rm -rf 等) | 高 |
| 记忆文件加密 | 否 | 启用 ~/.openclaw/credentials 加密 | 中 |
| 日志包含凭证 | 否 | 确保日志中不包含 API 密钥 | 中 |
| 定期备份 | 无 | 每周备份 ~/.openclaw | 中 |
配置示例
yaml
# config.yml - 安全强化配置
gateway:
# 网络安全
bind: "127.0.0.1" # 仅本地
port: 18789
auth: "required" # 必须提供 token
# 设备认证
device_pairing:
require_approval: true # 新设备需批准
pairing_code_ttl: 600 # 配对码 10 分钟有效
auto_approve_localhost: false # 禁止自动批准
# TLS(启用远程访问时)
tls:
enabled: true
cert_path: "~/.openclaw/tls/cert.pem"
key_path: "~/.openclaw/tls/key.pem"
require_client_cert: false # 可选:需要客户端证书
# 速率限制
rate_limit:
max_messages_per_minute: 60
max_tool_calls_per_hour: 1000
agent:
# 权限控制
permissions:
file:
allowed_paths: # 白名单
- "~/"
- "/tmp"
blocked_paths: # 黑名单
- "/System"
- "/Library"
- "/.ssh"
shell:
dangerous_commands: # 禁止的命令
- "rm -rf"
- "sudo"
- "shutdown"
- "reboot"
allowed_interpreters: # 允许的解释器
- "bash"
- "python3"
- "node"
# 资源限制
limits:
max_memory_mb: 500 # 单次任务最大内存
max_execution_time_s: 300 # 单次任务最大耗时 5 分钟
max_file_size_mb: 100 # 单个文件最大 100MB
credentials:
# 凭证存储
encryption_method: "aes-256-gcm"
key_derivation: "pbkdf2" # 密钥衍生函数
storage:
# 数据备份
backup:
enabled: true
frequency: "weekly" # 每周备份
retention_days: 90 # 保留 90 天
destination: "/backup/openclaw"
8.2 性能优化
内存优化
python
# 定期清理旧的对话记忆
def prune_old_conversations():
"""
定期清理超过 TTL 的旧对话
"""
import os
from datetime import datetime, timedelta
conv_dir = os.path.expanduser("~/.openclaw/workspaces/default/conversations")
ttl_days = 90
cutoff_date = datetime.now() - timedelta(days=ttl_days)
for file in os.listdir(conv_dir):
file_path = os.path.join(conv_dir, file)
mod_time = datetime.fromtimestamp(os.path.getmtime(file_path))
if mod_time < cutoff_date:
# 先存档到冷存储
archive_to_s3(file_path)
# 再删除本地文件
os.remove(file_path)
print(f"📦 已存档对话: {file}")
Token 预算管理
python
# 在发送 LLM 请求前,评估 token 成本
def estimate_and_optimize_tokens():
"""
估算本次请求的 token 成本,如果超过预算则裁剪
"""
context = {
"system_prompt": system_prompt,
"memory": long_term_memory,
"history": conversation_history,
"current_query": user_query
}
# 估算 token 数
estimated_tokens = sum(count_tokens(v) for v in context.values())
# 定义 token 预算
budget = 100000 # 100K token 预算
if estimated_tokens > budget * 0.8: # 超过 80% 预算
# 策略 1:删除最旧的历史
context["history"] = context["history"][-20:] # 只保留最近 20 条
# 策略 2:压缩长期记忆
relevant_memory = filter_irrelevant_memory(
context["memory"],
user_query,
threshold=0.5
)
context["memory"] = relevant_memory
# 重新估算
estimated_tokens = sum(count_tokens(v) for v in context.values())
# 添加成本信息
cost_usd = (estimated_tokens / 1000) * 0.003 # $3 per 1M tokens 的估算
print(f"📊 预计 Token: {estimated_tokens:,} (成本: ${cost_usd:.3f})")
return context, estimated_tokens
总结与展望
Moltbot/OpenClaw 通过精心设计的分层架构,将强大的 AI 能力与本地隐私保护、灵活的可扩展性结合在一起。其核心贡献包括:
- Gateway 事件总线:统一的通信骨干,使所有组件可插拔
- WebSocket 协议 v3:类型安全、流式优化、支持认证和权限
- Skill 系统:声明式、自然语言友好的能力扩展机制
- 多智能体编排:从单 Agent 到复杂的分布式 Agent 网络
- Peekaboo 视觉桥接:弥合 AI 和图形用户界面之间的鸿沟
对于二次开发者,最佳实践包括:
- 从简单开始:先构建单个 Skill,理解基础机制
- 充分测试:在生产部署前进行全面的集成测试
- 记录你的工作:编写清晰的 SKILL.md 和 README
- 加入社区:在 ClawdHub 发布你的成果,获得反馈
- 关注安全:遵循安全加固清单,定期审计权限
- 优化成本:监控 Token 消耗,使用合理的模型组合
Moltbot 代表了 AI Agent 从概念到可用产品的一个重要进展------一个真正运行在用户控制的硬件上、尊重隐私、可深度定制的个人助手。
参考资源:
- 官方文档:https://docs.openclaw.ai
- GitHub 仓库:https://github.com/openclaw/openclaw
- 社区 Skill 市场:https://clawd-hub.org
- 讨论论坛:https://github.com/openclaw/openclaw/discussions
更新时间 :2026年1月28日
版本:1.0(基于 OpenClaw v3.0)