第二章 整體架構解析
在第一章,我們從設計哲學的角度理解了 OpenClaw 的核心定位:本地優先、智能體循環、工具驅動。這些選擇回答了"爲什麼"的問題。
現在我們要回答"是什麼"的問題——這些設計是如何在代碼層面落地的?當你啓動 openclaw gateway 後,系統內部到底在發生什麼?
1. 從一個問題開始
想象一下這個場景:
你正在使用 OpenClaw,突然遇到了奇怪的行爲。你讓它"查找項目裏所有 TODO 註釋",它卻回覆"我找不到任何文件"。你知道項目裏明明有幾十個 TODO,問題出在哪裏?
作爲用戶,你只能換個說法再試一次。但如果你理解架構,你就能定位問題:
- 是 Gateway 沒有正確接收你的消息?
- 是 Command Queue 把消息路由到了錯誤的會話?
- 是 Agent 調用了 Glob 但模式匹配失敗?
- 還是 LLM 返回的結果解析出錯?
這就是本章的目標:從"再試一次"到"我知道爲什麼"。
2. 架構全景:WebSocket Gateway
OpenClaw 採用 WebSocket Gateway 架構,核心組件包括:
┌─────────────────────────────────────────────────────────────┐
│ Clients │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Control UI │ │ CLI │ │ Web Dashboard │ │
│ │ (macOS) │ │ (Terminal) │ │ (Browser) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ HTTP / WebSocket
▼
┌─────────────────────────────────────────────────────────────┐
│ Gateway (WebSocket) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Message Router & Session Mgr │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ │ │
┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐
│ Telegram │ │ Discord │ │ WhatsApp (Baileys) │
│ Channel │ │ Channel │ │ Channel │
│ (extension)│ │ (extension)│ │ (built-in) │
└─────────────┘ └─────────────┘ └─────────────────────┘
▲ ▲ ▲
│ │ │
Telegram Discord API WhatsApp Web
Bot API (HTTP/WebSocket) (WebSocket)
│
│ node.invoke (WebSocket)
▼
┌─────────────────────────────────────────────────────────────┐
│ Nodes (Device Agents) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ macOS Node │ │ iOS Node │ │ Android Node │ │
│ │(system.run) │ │(camera.snap)│ │ (screen.record) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘一條消息的典型生命週期:
- 用戶通過 Telegram/Discord/Slack 等渠道發送消息
- Gateway 接收消息,通過對應平臺的 SDK 處理
- 消息進入 Command Queue,按會話排隊
- Agent 取出消息,進入 Agent Loop 進行決策
- 需要時調用 LLM,需要時調用工具(read、write、edit、exec)
- 結果通過 Gateway 返回給用戶
3. Gateway:統一入口與多平臺接入
3.1 爲什麼需要 Gateway
Gateway 是 OpenClaw 的唯一入口點,負責管理所有消息渠道和控制平面客戶端。
如果沒有 Gateway,每個渠道都需要獨立的連接管理、身份驗證和消息處理邏輯,這會導致:
- 複雜度爆炸:每加一個渠道,核心代碼都要改一次
- 狀態分散:WhatsApp 會話、Telegram 連接分散管理
- 安全風險:多個入口點意味着多個攻擊面
Gateway 通過統一 WebSocket 接口解決了這些問題。
3.2 多平臺接入
OpenClaw 直接使用各平臺的 SDK 接入,而非自行實現協議:
| 平臺 | 接入方式 |
|---|---|
| Telegram | Webhook / 長輪詢 |
| Discord | WebSocket |
| Slack | Events API / WebSocket |
| WebSocket | |
| WebChat | WebSocket |
這些 SDK 負責處理平臺特定的協議細節,Gateway 專注於消息路由和會話管理。
3.3 連接模式與內網部署
不同渠道的接入方式差異很大:
| 模式 | 原理 | 實時性 | 公網要求 |
|---|---|---|---|
| Webhook | 平臺主動 HTTP 請求 | 高 | 需要公網地址 |
| WebSocket | Gateway 主動保持長連接 | 高 | 不需要 |
| 長輪詢 | Gateway 定期查詢 | 中 | 不需要 |
對於 Webhook 模式,大多數個人用戶沒有公網服務器。解決方案包括:
- Tailscale:推薦方案,建立加密 mesh 網絡
- SSH 隧道:建立端口轉發隧道
這讓 OpenClaw 可以安全地運行在內網服務器甚至筆記本上,而不需要公網 IP。
3.4 身份識別與會話隔離
Gateway 負責進行路由尋址。不同平臺的用戶 ID 格式各異,但都遵循統一規範:
- agent:main:telegram:123456789🧵42 - Telegram 話題線程
- agent:main:discord:channel:987654321 - Discord 頻道
- agent:main:whatsapp:dm:+15551234567 - WhatsApp 私聊
- agent:ops:slack:cron:job:daily-report:run:uuid - Cron 任務會話
這個統一的身份標識決定了消息進入哪個會話(Session),是實現多用戶隔離的基礎。不同用戶的消息被路由到獨立的處理隊列中,不會混淆。
4. Command Queue:命令隊列與併發控制
4.1 爲什麼需要 Command Queue
直接讓 Gateway 調用 Agent 會有三個問題:
| 問題 | 後果 | Command Queue 的解決 |
|---|---|---|
| 併發衝突 | 多個消息同時處理導致資源競爭 | 按會話序列化處理 |
| 無緩衝 | 高峯期消息湧入導致崩潰 | 提供隊列緩衝 |
| 難以控制 | 無法控制並行度 | 可配置併發上限 |
Command Queue 是一個進程內隊列,基於"泳道"模型實現併發控制,純 TypeScript 實現,無外部依賴。
4.2 泳道模型:併發與順序的平衡
Command Queue 的核心設計是 泳道模型:
泳道內:串行保證(按 Session Key) 泳道間:並行處理
┌──────────────────────────┐ ┌──────────┐ ┌──────────┐
│ Session A 的消息隊列 │ │ 泳道 A │ │ 泳道 B │
│ ──────────────────────── │ │(SessionA)│ │(SessionB)│
│ 1. 創建 report │ │ ──────── │ │ ──────── │
│ 2. 寫入內容 ←─────────────┼── 排隊等待 │ 長任務 │ │ 短查詢 │
└──────────────────────────┘ │ (5分鐘) │ │ (1秒) │
│ ──────── │ │ ──────── │
│ │ │ 立即響應 │
└──────────┘ └──────────┘泳道內的串行保證:同一用戶的消息按順序處理。比如用戶連續發送"創建 report.txt"和"寫入內容",第二條會等待第一條完成後再執行,防止因文件不存在而失敗。
泳道間的並行處理:不同用戶互不等待。Session A 的 5 分鐘代碼分析不會阻塞Session B 的天氣查詢。
4.3 消息處理方式
Command Queue 支持多種消息處理方式,可通過配置或命令切換:
| 方式 | 行爲 | 適用場景 |
|---|---|---|
| collect(默認) | 合併所有隊列消息爲單條跟進 | 大多數場景 |
| steer | 立即注入當前運行 | 需要實時干預 |
| followup | 排隊等待下一輪 | 順序處理 |
| steer-backlog | 立即注入 + 保留跟進 | 複雜交互 |
| steer+backlog | 與 steer-backlog 類似 | 複雜交互 |
| queue | 標準隊列模式 | 順序處理 |
| interrupt | 中斷當前運行 | 緊急干預 |
配置示例(openclaw.json):
{
messages: {
queue: {
mode: "collect",
debounceMs: 等待毫秒數,
cap: 隊列長度上限,
drop: "summarize",
byChannel: { discord: "collect" },
},
},
}會話內臨時切換:發送 /queue steer 或 /queue collect
4.4 隊列選項
- debounceMs:等待安靜期後開始處理(防止"繼續、繼續"的連續消息)
- cap:每會話最大隊列長度
- drop:溢出策略(
old丟棄舊消息 /new丟棄新消息 /summarize生成摘要)
5. Agent:決策核心與 Agent Loop
5.1 不是腳本,是循環
消息通過 Command Queue 後,到達系統的"大腦"——Agent。Agent 是 OpenClaw 最核心的組件,負責理解用戶需求、規劃任務、選擇工具、協調執行。
傳統的程序是線性的:輸入、處理、輸出。Agent 不是線性的,它是循環的。這個循環被稱爲 Agent Loop(智能體循環)。
Agent Loop 的工作流程:
┌──────────┐
│ 觀察 │◄──────────────────────────┐
│ (Observe)│ │
└────┬─────┘ │
│ 接收當前狀態 │
│ (用戶輸入、工具結果、環境信息) │
▼ │
┌──────────┐ ┌──────────┐ │
│ 思考 │────►│ 行動 │ │
│ (Think) │ │ (Act) │ │
└──────────┘ └────┬─────┘ │
│ │
│ 執行決策 │
│ (調用工具/回覆) │
└────────────────┘循環持續進行,直到:
- 任務完成 → 生成最終回覆
- 遇到錯誤 → 無法繼續
- 達到最大循環次數 → 強制終止
這就是 OpenClaw 能處理複雜任務的原因。它不是一次性生成答案,而是像人類一樣,一步步探索、驗證、調整,直到達成目標。
5.2 工具選擇:如何讓 LLM 做出正確決策
在每次思考階段,Agent 需要讓 LLM 選擇工具。Agent 會把當前狀態、可用工具列表(包含每個工具的名稱、描述和參數格式)發送給 LLM,LLM 根據任務需求選擇最合適的工具並生成參數。
這個過程面臨的三個挑戰:
| 挑戰 | 問題描述 | 解決方案 |
|---|---|---|
| 描述質量 | LLM 根據描述理解工具,模糊描述導致錯誤選擇 | 精心設計工具描述 |
| 上下文限制 | 工具多時佔用大量上下文空間 | 智能選擇展示哪些工具 |
| 幻覺錯誤 | LLM "幻覺"出不存在的工具或格式錯誤 | 驗證白名單,錯誤反饋 |
5.3 狀態管理:記憶的維護
Agent 需要維護狀態才能進行多輪推理。狀態包括:
- 對話歷史:用戶和 Agent 之前的交互記錄,作爲短期記憶讓 Agent 理解上下文
- 工具結果:最近調用的工具及其返回值,成爲下一次思考的依據
- 環境信息:當前工作目錄、系統狀態、用戶偏好等
狀態管理的一個核心挑戰是上下文窗口限制。LLM 能處理的文本長度是有限的(比如 4K、8K、128K Token)。當對話變長或任務變複雜時,狀態可能超出這個限制。
Agent 採用了幾種策略來應對:
- 截斷:丟棄或壓縮最舊的對話歷史,重點保留最近的若干輪
- 摘要:對早期的對話進行壓縮,用更短的文本保留關鍵信息
- 提取:將重要的事實(如用戶偏好)提取出來,存儲到長期記憶(MEMORY.md)中
5.4 安全邊界
Agent 具有調用工具執行任意操作的能力,這既是優勢也是風險。OpenClaw 設計了三層安全機制:
- Security 模式:
deny/allowlist/full三級控制 - Ask 模式:
off/on-miss/always確認級別 - Safe Bins:stdin-only 工具限制
6. LLM 集成與錯誤處理
6.1 多模型支持
Agent 做出了決策,但它需要調用外部的 LLM 來獲得推理能力。
OpenClaw 支持接入多種 LLM,包括 OpenAI 的 GPT 系列、Anthropic 的 Claude、Google 的 Gemini,以及本地模型。系統負責屏蔽它們的差異:
- 封裝不同的 API 格式
- 適配不同的能力(有的支持函數調用,有的不支持)
- 管理故障轉移(當主模型不可用時自動切換到備用模型)
6.2 錯誤處理與自愈
LLM 的輸出是不確定的,經常會遇到各種問題。系統實現三層錯誤自愈機制:
| 問題 | 處理方式 | 示例 |
|---|---|---|
| 格式錯誤 | 自動修復 | { "tool": "bash" → 補全 } |
| 模型故障 | 切換認證檔案或使用備用模型 | 換一個Key或換一個模型 |
| 網絡超時 | 指數退避重試 | 自動重試 |
這種機制讓 OpenClaw 在面對不穩定的模型輸出時,依然能保持極高的任務成功率。
6.3 Token 管理與成本控制
每次調用 LLM 都會產生 Token 消耗,系統從四個方面管理成本:
| 策略 | 作用 |
|---|---|
| 用量統計 | 記錄每次調用的 input/output Token |
| 預算控制 | 設置上限,接近時發出警告 |
| 提示詞壓縮 | 壓縮舊對話,保留新消息 |
| 響應緩存 | 緩存重複查詢,避免重複調用 |
7. 場景化理解:當 things go wrong
理解了架構之後,讓我們看看幾個真實場景中如何定位問題。
場景一:消息沒有響應
你發送了一條消息,Agent 完全沒有反應。
排查思路:
- 檢查 Gateway:日誌中是否收到了消息?如果沒有,檢查網絡連接和渠道配置。
- 檢查 Command Queue:消息是否進入了正確的泳道?隊列是否堆積?
- 檢查 Agent:是否正在處理其他消息?是否陷入了長時間循環?
常見原因:
- Gateway 的 WebSocket 連接斷開,沒有重連成功
- Command Queue 已滿,新消息被延遲處理
- Agent 正在處理一個耗時任務,你的消息在泳道中排隊等待
場景二:響應非常慢
一條簡單的查詢等了十幾秒纔回復。
排查思路:
- Gateway 到 Queue:通常是毫秒級,不會是瓶頸
- Queue 排隊:檢查是否有其他消息在隊列中等待
- Agent 處理:檢查 Agent Loop 輪數,是否在反覆嘗試
- LLM 調用:檢查 LLM 的響應時間,是否模型過載
常見原因:
- LLM 服務響應慢(尤其是高峯期)
- Agent 陷入工具調用的循環,反覆嘗試失敗的命令
- 上下文過長,每次調用都攜帶大量歷史記錄
場景三:工具選擇錯誤
你讓它查找文件,它卻一直用 Bash 運行 find 命令,而不是更適合的 Glob。
排查思路:
- 工具描述:系統提示詞中 Glob 的描述是否清晰?
- 上下文限制:是否因爲工具太多,Glob 沒有被展示給 LLM?
- 歷史慣性:是否之前的對話讓 LLM 形成了使用 Bash 的習慣?
解決方案:
- 在 TOOLS.md 中明確描述 Glob 的適用場景
- 如果工具太多,考慮精簡可用工具列表
- 在對話中明確提示"請使用 Glob 工具"
8. 組件協作示例
消息旅程(用戶發送:"查找所有包含 TODO 的 Python 文件"):
Step 1: Gateway Step 2: Command Queue Step 3: Agent (Loop)
─────────────── ─────────────────── ─────────────────────
Telegram SDK ─────▶ 確定會話標識 第1輪:觀察 → 思考 → 行動
提取消息內容 ───▶ 放入隊列 ─────────▶ (調用Glob) 得50個文件
調度給Agent 第2輪:觀察 → 思考 → 行動
(調用Grep) 得12個匹配
第3輪:觀察 → 思考 → 行動
任務完成,生成回覆
Step 4: Command Queue Step 5: Gateway
─────────────────── ───────────────
回覆放入隊列 ─────────▶ 通過 Telegram SDK
發送給用戶異常處理:
- Gateway 網絡問題 → 重試或返回錯誤
- Queue 溢出 → 根據 drop 策略處理
- Agent 死循環 → Tool Loop Detection 終止
- LLM 拒絕 → 錯誤自愈重試
9. 消息通信格式
Gateway 與客戶端之間的通信使用標準的消息格式:
type 消息類型 = "lifecycle" | "tool" | "assistant" | "error" | (string & {});
type 消息內容 = {
runId: string; // 運行ID
seq: number; // 序列號(嚴格遞增)
stream: 消息類型; // 消息類型
ts: number; // 時間戳(毫秒)
data: Record<string, unknown>; // 消息數據
sessionKey?: string; // 會話標識
};消息類型說明:
- lifecycle:運行生命週期事件(開始、結束)
- tool:工具調用事件
- assistant:助手回覆事件
- error:錯誤事件
- compaction:狀態壓縮事件
- (string & {}):允許擴展其他自定義消息類型
10. 本章小結
通過這一章,我們從架構層面理解了 OpenClaw 的核心組件:
| 組件 | 核心職責 | 解決的關鍵問題 |
|---|---|---|
| Gateway | WebSocket 網關,管理所有渠道連接 | 統一入口、多平臺接入、身份識別 |
| Command Queue | 命令隊列,按會話序列化處理 | 併發控制、順序保證、流量緩衝 |
| Agent | 理解需求、規劃任務、選擇工具 | Agent Loop 決策、狀態管理、安全邊界 |
| LLM 集成 | 連接多種模型,處理錯誤與重試 | 多模型適配、錯誤自愈、Token 管理 |
這些組件通過定義良好的協議協同工作,構成了 OpenClaw 的"數字大腦"。
架構設計的核心洞察:
OpenClaw 的架構體現了"關注點分離"的設計原則。每個組件只負責一件事,並且把這件事做好:
- Gateway 只關心如何接收和路由消息,不關心消息如何處理
- Command Queue 只關心如何排隊和調度,不關心消息內容是什麼
- Agent 只關心如何決策和行動,不關心消息從哪裏來
- LLM 集成 只關心如何調用模型,不關心調用的結果如何使用
這種分離帶來了極大的靈活性。你可以:
- 接入新的渠道,而不影響其他組件
- 使用不同的 LLM,Agent 的邏輯完全不用修改
- 在一臺機器上運行多個實例,共享同一個 Gateway
理解了這個架構,你就能診斷問題發生在哪個環節:
- 消息沒收到是 Gateway 的問題
- 消息沒處理是 Queue 或 Agent 的問題
- 回覆質量差是 LLM 或 Agent 的問題
在下一章,我們將深入 Agent 的"靈魂"——提示詞系統。你會看到 SOUL.md、USER.md、TOOLS.md 等文件如何被組合成系統提示詞,熱更新機制如何讓修改立即生效,以及如何通過調整提示詞精細控制 Agent 行爲。