AI读码, OpenClaw 架构深度解析
·
OpenClaw 架构深度解析
基于源码和官方文档整理,版本 2026.2.25
一、全局架构总览
┌─────────────────────────────────────────────────────────────────┐
│ 用户 / 开发者 │
└──────┬──────────┬──────────┬──────────┬──────────┬──────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
WhatsApp Telegram Discord iMessage 飞书/Lark
(Baileys) (Grammy) (Discord.js) (Signal-CLI/BB)
│ │ │ │ │
└──────────┴──────────┴──────────┴──────────┘
│
▼
┌───────────────────────┐
│ Gateway 网关 │ ← 单进程,所有状态的中心
│ (Express 5 + WS) │
└───────────┬───────────┘
│
┌───────────┼───────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌────────┐ ┌──────────┐
│ Pi Agent │ │ Plugins│ │ Control │
│ Runner │ │ 扩展 │ │ UI │
│(SDK嵌入) │ │ │ │(Web仪表盘)│
└────┬─────┘ └────────┘ └──────────┘
│
┌─────┼──────┬──────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌──────┐┌──────┐┌──────┐ ┌──────────┐
│ LLM ││Tools ││Memory│ │Sessions │
│调用 ││系统 ││系统 │ │持久化 │
└──┬───┘└──────┘└──────┘ └──────────┘
│
┌───┼────────────────────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
Anthro OpenAI Gemini Bedrock Ollama 本地模型
核心设计理念
- 单进程网关 (Gateway) — 所有状态的中心,拥有 session、routing、channel 连接
- Pi SDK 嵌入 — 不spawn子进程,直接 import Pi 的
createAgentSession() - 插件化渠道 — 每个聊天平台是独立的 channel provider
- Session 隔离 — 每个 sessionKey 独立会话,支持分支/压缩
- 多 Agent 路由 — 可配置多个 agent,按 workspace/sender 隔离
二、核心模块详解
2.1 Gateway 网关
位置: src/gateway/ (核心入口)
技术栈: Express 5 + WebSocket + SSE
职责:
- 启动 HTTP 服务器 (默认端口 18789)
- 加载 channel providers(各平台连接器)
- 管理所有 channel 的生命周期
- 接收来自各渠道的 inbound 消息
- 调度到 Pi Agent Runner
- 托管 Web Control UI
- 处理 cron 任务、heartbeat 心跳
- WebSocket 供 UI 实时通信
配置文件: ~/.openclaw/openclaw.json
{
channels: {
whatsapp: { allowFrom: ["+15555550123"] },
telegram: { token: "xxx" },
discord: { token: "xxx" }
},
messages: {
groupChat: { mentionPatterns: ["@openclaw"] }
}
}
2.2 Channel Providers (渠道连接器)
每个渠道是一个独立的模块,负责:
- 与平台 SDK 通信(收发消息)
- 消息格式标准化(统一为 OpenClaw 内部格式)
- 媒体处理(图片/音频/文件)
- 群聊 @提及检测
| 渠道 | SDK | 文件位置 |
|---|---|---|
| @whiskeysockets/baileys | src/channels/whatsapp/ |
|
| Telegram | grammy | src/channels/telegram/ |
| Discord | discord.js | src/channels/discord/ |
| iMessage | signal-cli (RPC) | src/channels/signal/ |
| 飞书/Lark | @larksuiteoapi/node-sdk | src/channels/lark/ |
| Slack | @slack/bolt | plugin |
| Line | @line/bot-sdk | plugin |
| WebChat | 内置 HTTP | src/channels/webchat/ |
2.3 Pi Agent Runner (AI 代理运行器)
位置: src/agents/pi-embedded-runner/
这是 OpenClaw 的大脑
核心入口: runEmbeddedPiAgent() → runEmbeddedAttempt()
关键文件结构:
pi-embedded-runner/
├── run.ts # 主入口 runEmbeddedPiAgent()
├── run/
│ ├── attempt.ts # 单次 attempt 逻辑
│ ├── params.ts # 参数类型定义
│ ├── payloads.ts # 构建响应 payload
│ ├── images.ts # 图片注入处理
│ └── types.ts # 类型定义
├── model.ts # 模型解析 via ModelRegistry
├── system-prompt.ts # 系统提示词构建
├── history.ts # 历史消息裁剪
├── compact.ts # 压缩逻辑
├── lanes.ts # 命令通道(session/global)
├── runs.ts # 活跃 run 追踪、abort、队列
├── sandbox-info.ts # 沙箱信息注入
└── session-manager-*.ts # Session 管理器缓存与初始化
2.4 Tool 系统 (工具系统)
位置: src/agents/tools/
工具是 Agent 的"手脚",分层架构:
Layer 1: Pi SDK 基础工具 (read, bash, edit, write)
↓ 被 OpenClaw 替换/扩展
Layer 2: OpenClaw 核心工具
├── exec / process → 命令执行
├── read / edit / write → 文件操作(沙箱版)
├── browser → 浏览器自动化 (Playwright)
├── canvas → 画布展示
├── message → 消息发送
├── sessions_* → 会话管理
├── cron-tool → 定时任务
├── gateway-tool → 网关控制
├── image-tool → 图片分析
├── nodes-tool → 移动节点控制
└── web-* → 网页抓取
↓
Layer 3: 渠道特定工具
├── telegram-actions.ts
├── discord-actions*.ts
├── whatsapp-actions.ts
└── slack-actions.ts
↓
Layer 4: Policy 过滤
├── tool-policy.ts → 按角色/沙箱/群组过滤
├── pi-tools.policy.ts → allowlist/denylist
└── schema 规范化 → 适配不同 LLM 的 schema 要求
2.5 Session 系统 (会话系统)
两层持久化:
Layer 1: sessions.json (会话元数据)
├── sessionKey → SessionEntry 映射
├── 当前 sessionId、模型、token 计数
├── thinkingLevel、verboseLevel 等设置
└── 路径: ~/.openclaw/agents/<agentId>/sessions/sessions.json
Layer 2: <sessionId>.jsonl (对话记录)
├── JSONL 格式,树结构 (id + parentId)
├── 第一行: session header (id, cwd, timestamp)
├── 后续: message / custom_message / custom / compaction
└── 路径: ~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
Session Key 路由规则:
| 场景 | sessionKey 格式 |
|---|---|
| 直接聊天 | agent:<agentId>:<mainKey> |
| 群聊 | agent:<agentId>:<channel>:group:<id> |
| 频道/房间 | agent:<agentId>:<channel>:channel:<id> |
| Cron 任务 | cron:<job.id> |
| Webhook | hook:<uuid> |
2.6 Memory 系统 (记忆系统)
三层记忆架构:
1. MEMORY.md — 精选长期记忆(核心事实/偏好)
├── 只在主会话(直接聊天)加载
├── 群聊中不加载(安全考虑)
└── 定期从 daily notes 蒸馏更新
2. memory/YYYY-MM-DD.md — 每日记忆日志 (append-only)
├── 当天事件的原始记录
└── 不自动注入 prompt,按需 read 读取
3. Workspace 文件 — 上下文注入到 system prompt
├── AGENTS.md, SOUL.md, USER.md, IDENTITY.md
├── TOOLS.md, HEARTBEAT.md, MEMORY.md
└── 大文件截断: bootstrapMaxChars (默认 20000)
2.7 Skill 系统 (技能系统)
位置: ~/.agents/skills/ 和 skills/
# 技能结构
skills/
├── code-1.0.4/
│ ├── SKILL.md # 技能定义(触发条件 + 指令)
│ └── (辅助文件)
└── feishu-doc-1.2.7/
├── SKILL.md
└── scripts/
工作方式:
- 每个技能是一个 SKILL.md 文件
- System prompt 中只注入技能的
<description>和<location> - Agent 按需用
read工具加载具体 SKILL.md - 触发匹配:description 中包含的触发关键词
三、一次请求的完整生命周期 ⭐
场景:用户在 Telegram 发送 “帮我查一下天气”
时间线从左到右 ──────────────────────────────────────────────►
Phase 0: 系统启动(Gateway 启动时)
[Gateway 启动]
│
├─1. 读取 openclaw.json 配置
├─2. 初始化 AuthProfileStore (多 API Key 管理)
├─3. 初始化 ModelRegistry (模型注册表)
├─4. 加载所有 channel providers
├─5. 启动 Express HTTP 服务器 (端口 18789)
├─6. 启动 WebSocket 服务 (供 Control UI)
├─7. 建立 Telegram Bot 连接 (grammy)
├─8. 建立 WhatsApp 连接 (baileys)
├─9. 建立 Discord 连接 (discord.js)
├─10. 启动 cron 调度器
├─11. 启动 heartbeat 心跳
└─12. 加载 sessions.json (恢复会话状态)
Phase 1: 消息接收 (Inbound)
[Telegram SDK 收到消息]
│
├─1. Grammy on('message') 回调触发
│
├─2. Telegram Channel Provider 处理:
│ ├── 解析消息: text="帮我查一下天气"
│ ├── 提取元数据:
│ │ ├── from: { id, username, name }
│ │ ├── chat: { id, type }
│ │ └── messageId
│ ├── 检查是否 @提及 (群聊模式)
│ └── 标准化为内部 InboundMessage 格式
│
└─3. 传递给 Gateway 的 auto-reply 系统
Phase 2: 路由与预处理 (Routing & Pre-processing)
[Gateway Auto-Reply]
│
├─1. Session 路由:
│ ├── 计算 sessionKey:
│ │ → "agent:main:telegram:user:123456"
│ ├── 查找 sessions.json 中对应的 SessionEntry
│ └── 如果不存在,创建新的 session
│
├─2. 消息预处理:
│ ├── 检查是否是斜杠命令 (/status, /new, /usage 等)
│ │ └─ 如果是 → 直接处理,不走 Agent
│ ├── 检查 allowFrom 白名单
│ ├── 检查发送频率限制
│ └── 检查是否正在运行中 (防止并发)
│
├─3. Agent 解析:
│ ├── 确定使用哪个 agent (默认 "main")
│ ├── 解析模型: provider="openai", model="gpt-4o"
│ └── 解析 auth profile (API Key 选择)
│
└─4. 构建 runEmbeddedPiAgent() 参数
Phase 3: Agent 会话初始化 (Session Init)
[runEmbeddedAttempt()]
│
├─1. 打开/恢复 Session:
│ ├── sessionManager = SessionManager.open(sessionFile)
│ │ └── 读取 <sessionId>.jsonl,恢复对话树
│ └── 检查是否需要 daily reset / idle reset
│ └── 如果需要 → 创建新 sessionId
│
├─2. 加载 Workspace 文件:
│ ├── 读取 AGENTS.md, SOUL.md, USER.md 等
│ ├── 大文件截断 (bootstrapMaxChars)
│ └── 计算总注入大小 (bootstrapTotalMaxChars)
│
├─3. 构建 System Prompt:
│ ├── 工具列表 + 描述
│ ├── 技能列表 (仅 description,按需加载)
│ ├── Workspace 文件内容
│ ├── 运行时元数据 (OS/模型/时间)
│ ├── 安全规则 (Safety guardrails)
│ ├── 回复标签 + 心跳行为
│ └── 渠道特定指令 (群聊行为、平台格式)
│
├─4. 准备工具集:
│ ├── 加载基础工具 (exec, read, edit, write, browser...)
│ ├── 加载渠道特定工具 (telegram-actions 等)
│ ├── 应用 Tool Policy 过滤
│ │ ├── 按角色过滤 (群聊中隐藏某些工具)
│ │ └── 按沙箱过滤
│ ├── Schema 规范化 (适配目标 LLM)
│ └── splitSdkTools() → { builtIn: [], custom: [...] }
│
├─5. 创建 Pi Agent Session:
│ ├── resourceLoader = new DefaultResourceLoader({ cwd, agentDir })
│ ├── { session } = await createAgentSession({
│ │ model, tools, customTools,
│ │ sessionManager, settingsManager,
│ │ resourceLoader, authStorage
│ │ })
│ └── applySystemPromptOverrideToSession(session, systemPrompt)
│
└─6. 加载 Pi Extensions:
├── compaction-safeguard (压缩保护)
└── context-pruning (上下文裁剪,如果启用)
Phase 4: LLM 调用循环 (Agent Loop) ⭐核心⭐
[session.prompt("帮我查一下天气")]
│
│ ┌──────────────────────────────────────────┐
│ │ Pi Agent Loop (循环) │
│ │ │
│ │ while (agent 未完成) { │
│ │ │
│ │ ┌──── Step A: 构建 LLM 请求 ────┐ │
│ │ │ │ │
│ │ │ 1. 收集上下文: │ │
│ │ │ ├── system prompt │ │
│ │ │ ├── 历史对话 (从 jsonl) │ │
│ │ │ ├── 之前的 tool 结果 │ │
│ │ │ └── 当前用户消息 │ │
│ │ │ │ │
│ │ │ 2. 应用历史限制: │ │
│ │ │ └── limitHistoryTurns() │ │
│ │ │ │ │
│ │ │ 3. 序列化为 provider 格式 │ │
│ │ │ └── Anthropic/OpenAI/Gemini │ │
│ │ │ │ │
│ │ │ 4. 发送 HTTP 请求到 LLM API │ │
│ │ │ └── streamSimple(model, msgs) │ │
│ │ └────────────────────────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──── Step B: 处理 LLM 响应 ────┐ │
│ │ │ │ │
│ │ │ LLM 返回 (流式): │ │
│ │ │ ├── text: "我来帮你查天气..." │ │
│ │ │ └── tool_calls: [{ │ │
│ │ │ name: "web_fetch", │ │
│ │ │ params: { │ │
│ │ │ url: "wttr.in/..." │ │
│ │ │ } │ │
│ │ │ }] │ │
│ │ └────────────────────────────────┘ │
│ │ │ │
│ │ ┌───────┴───────┐ │
│ │ ▼ ▼ │
│ │ [有工具调用] [纯文本回复] │
│ │ │ │ │
│ │ ┌───┴──────┐ [Step E: 流式返回] │
│ │ ▼ ▼ │ │
│ │ │ │
│ │ ┌──── Step C: 执行工具 ─────┐ │ │
│ │ │ │ │ │
│ │ │ 对每个 tool_call: │ │ │
│ │ │ 1. 触发事件: │ │ │
│ │ │ tool_execution_start │ │ │
│ │ │ 2. 执行工具: │ │ │
│ │ │ tool.execute(id, params)│ │ │
│ │ │ 3. web_fetch("wttr.in/") │ │ │
│ │ │ → 抓取网页内容 │ │ │
│ │ │ → 提取天气数据 │ │ │
│ │ │ 4. 返回 tool_result │ │ │
│ │ │ 5. 触发事件: │ │ │
│ │ │ tool_execution_end │ │ │
│ │ │ 6. 将结果追加到对话历史 │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──── Step D: 循环继续 ─────┐ │ │
│ │ │ │ │ │
│ │ │ 带着 tool_result 回到 │ │ │
│ │ │ Step A,重新调用 LLM │ │ │
│ │ │ │ │ │
│ │ │ LLM 看到天气数据后,生成 │ │ │
│ │ │ 最终文本回复: │ │ │
│ │ │ "今天北京晴,15-25°C..." │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────┘ │ │
│ │ │ │ │
│ │ └───────────┬───────────────┘ │
│ │ │ │
│ │ } // 循环结束 │
│ └──────────────────────────────────────────┘
Phase 5: 流式响应与事件分发 (Streaming & Events)
[subscribeEmbeddedPiSession() 监听事件]
│
├─ message_start → 通知 UI 开始新消息
├─ message_update → 流式文本块到达
│ └─ BlockChunker 分块:
│ ├── 累积文本到有意义块
│ ├── 处理 [[media:url]] 指令
│ ├── 处理 [[voice]] 指令
│ ├── 处理 [[reply_to:id]] 指令
│ └─ stripBlockTags():
│ ├── 剥离 🤔... 内容 (thinking)
│ └── 提取 <final>...</final> (如果 enforceFinalTag)
│
├─ onBlockReply() → 发送完整文本块到渠道
│ └─ Telegram Channel Provider:
│ ├── 格式化消息 (Markdown → Telegram HTML)
│ └── bot.api.sendMessage(chatId, text)
│
├─ tool_execution_start → 通知 UI 工具开始执行
├─ tool_execution_end → 通知 UI 工具执行完毕
│
├─ turn_end → 一轮结束
└─ agent_end → 整个 agent 运行结束
Phase 6: 后处理 (Post-processing)
[运行结束后]
│
├─1. 更新 sessions.json:
│ ├── inputTokens, outputTokens, totalTokens
│ ├── contextTokens
│ ├── updatedAt
│ └── compactionCount (如果发生了压缩)
│
├─2. 持久化对话记录:
│ └── SessionManager 将新消息追加到 .jsonl 文件
│
├─3. 检查是否需要 auto-compaction:
│ ├── 如果 contextTokens 接近上限
│ └── 触发 compactEmbeddedPiSessionDirect()
│ ├── 用 LLM 生成对话摘要
│ ├── 替换旧消息为摘要
│ └── 保存 compaction entry 到 jsonl
│
├─4. 释放 SessionManager 缓存
│
└─5. 标记 run 为完成
四、并发与队列控制
[并发请求处理]
每个 sessionKey 同时只允许一个 run:
│
├── 如果当前 session 有 run 在执行:
│ ├── 新请求进入队列 (runs.ts 管理)
│ └── run 完成后自动处理下一个
│
├── Abort 支持:
│ ├── 用户发 /stop → AbortSignal
│ └── 工具执行检查 signal,中断
│
└── 超时控制:
└── timeoutMs (默认 120s)
五、Auth Profile 与 Failover (多 Key 轮换)
[多 API Key 管理]
AuthProfileStore:
├── 每个 provider 可配置多个 profile
├── profile 有优先级顺序
│
├── 正常流程:
│ └── 使用第一个可用 profile
│
├── 失败处理:
│ ├── markAuthProfileFailure() → 标记失败,进入冷却
│ ├── advanceAuthProfile() → 轮换到下一个
│ └── 冷却结束后恢复可用
│
└── FailoverError:
├── 触发条件: auth 失败 / rate limit / quota / timeout
├── 自动重试 + 模型降级
└── 可配置: models.providers.<p>.failover
六、完整数据流图 (简化版)
用户消息
│
▼
Channel Provider (Telegram/Discord/WhatsApp...)
│ 标准化消息
▼
Gateway Auto-Reply
│ ├─ 斜杠命令? → 直接处理
│ ├─ allowFrom 检查
│ ├─ Session 路由 (sessionKey)
│ └─ 并发控制 (队列)
▼
Pi Agent Runner
│ ├─ Session 恢复 (jsonl)
│ ├─ System Prompt 构建
│ ├─ 工具集准备 + Policy 过滤
│ └─ 创建 Pi AgentSession
▼
Agent Loop (循环)
│
├─► 构建 LLM 请求 (system + history + user msg)
│ ▼
│ 调用 LLM API (streamSimple)
│ │
│ ├── 纯文本 → 流式返回
│ │
│ └── tool_calls → 执行工具 → 结果追加到 history ─┐
│ │
│ ◄──────────────────────────────────────────────────┘
│
▼
流式响应
│ ├─ BlockChunker 分块
│ ├─ 剥离 thinking 标签
│ └─ 解析回复指令 (media/voice/reply)
▼
Channel Provider 发送回复
│
▼
用户收到消息
七、关键设计决策总结
| 决策 | 选择 | 原因 |
|---|---|---|
| 运行模式 | Pi SDK 嵌入 (非子进程) | 完全控制生命周期、自定义工具、事件处理 |
| 进程模型 | 单进程网关 | 简化状态管理,所有 session 在同一进程 |
| 会话格式 | JSONL 树结构 | 支持分支、压缩,可追加 |
| 工具架构 | 全部走 customTools | 统一 policy 过滤,绕过 pi 内置工具 |
| System Prompt | 动态构建 | 按渠道/角色/群聊定制行为 |
| 认证 | 多 profile + 轮换 | 高可用,避免单 key 耗尽 |
| 模型 | Provider 无关 | 通过 ModelRegistry 抽象,支持 failover |
| 记忆 | 文件系统 | 简单、透明、人类可读可编辑 |
八、技术栈一览
| 层 | 技术 |
|---|---|
| 运行时 | Node.js 22+ (ESM) |
| 语言 | TypeScript 5.9 |
| Web 框架 | Express 5 |
| WebSocket | ws |
| AI SDK | @mariozechner/pi-coding-agent 0.55.1 |
| LLM 抽象 | @mariozechner/pi-ai |
| Agent 核心 | @mariozechner/pi-agent-core |
| 浏览器自动化 | Playwright Core |
| 数据验证 | Zod + TypeBox |
| 日志 | tslog |
| 定时任务 | croner |
| 配置 | JSON5 + YAML |
| 包管理 | pnpm 10 |
整理时间: 2026-03-10
基于 OpenClaw v2026.2.25 源码和官方文档
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)