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 本地模型

核心设计理念

  1. 单进程网关 (Gateway) — 所有状态的中心,拥有 session、routing、channel 连接
  2. Pi SDK 嵌入 — 不spawn子进程,直接 import Pi 的 createAgentSession()
  3. 插件化渠道 — 每个聊天平台是独立的 channel provider
  4. Session 隔离 — 每个 sessionKey 独立会话,支持分支/压缩
  5. 多 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 文件位置
WhatsApp @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 源码和官方文档

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐