diff --git a/articles/002/diagram-01.mmd b/articles/002/diagram-01.mmd new file mode 100644 index 0000000..af4b72e --- /dev/null +++ b/articles/002/diagram-01.mmd @@ -0,0 +1,95 @@ +graph TB + subgraph "外部消息平台" + TG[Telegram] + SL[Slack] + DC[Discord] + WA[WhatsApp] + SG[Signal] + FS[飞书] + MORE[30+ 其他平台...] + end + + subgraph "OpenClaw 核心运行时" + subgraph "Gateway 网关层" + WS[WebSocket Server
端口 18789] + AUTH[认证中间件
Ed25519 设备身份] + PROTO[协议处理器
AJV Schema 校验] + RL[速率限制] + end + + subgraph "通道管理" + CM[Channel Manager] + CP1[Telegram Plugin] + CP2[Slack Plugin] + CP3[Discord Plugin] + CPN[... N 个通道插件] + end + + subgraph "路由层" + RT[Route Resolver
消息 → Agent 映射] + SK[Session Key Parser
会话键解析] + end + + subgraph "ACP 协议层" + ACP_SVR[ACP Server] + ACP_TR[ACP Translator
协议翻译器] + ACP_SM[ACP Session Manager
会话管理器 + Actor 队列] + end + + subgraph "Agent 运行时" + PI[Pi Agent Runner
LLM 推理引擎] + TC[Tool Catalog
工具注册表] + SKL[Skills System
技能加载器] + SA[Subagent Registry
子 Agent 注册] + AP[Auth Profiles
模型凭证管理] + end + + subgraph "记忆系统" + MM[Memory Index Manager] + VEC[向量检索
sqlite-vec] + FTS[全文搜索
SQLite FTS5] + EMB[Embedding Providers
OpenAI/Gemini/Voyage/Ollama] + end + + subgraph "心跳系统" + HB[Heartbeat Runner
定时唤醒] + HMD[HEARTBEAT.md
任务配置文件] + end + + subgraph "存储层" + SS[Session Store
JSON/JSONL 文件] + CF[Config Store
config.json] + DI[Device Identity
密钥对存储] + end + end + + subgraph "LLM 提供商" + CLAUDE[Claude / Anthropic] + GPT[GPT / OpenAI] + GEM[Gemini / Google] + QWEN[Qwen / 通义] + OL[Ollama 本地模型] + MORE_LLM[Grok/Groq/Azure/...] + end + + TG & SL & DC & WA & SG & FS & MORE --> CM + CM --> CP1 & CP2 & CP3 & CPN + CP1 & CP2 & CP3 & CPN --> RT + RT --> SK --> ACP_SM + WS --> AUTH --> PROTO --> ACP_SVR + ACP_SVR --> ACP_TR --> ACP_SM + ACP_SM --> PI + PI --> TC & SKL & SA + PI --> MM + MM --> VEC & FTS + VEC & FTS --> EMB + PI --> AP --> CLAUDE & GPT & GEM & QWEN & OL & MORE_LLM + PI --> SS + HB --> HMD --> PI + ACP_SM --> SS + + style WS fill:#4A90D9,color:#fff + style PI fill:#E74C3C,color:#fff + style MM fill:#27AE60,color:#fff + style HB fill:#F39C12,color:#fff + style CM fill:#8E44AD,color:#fff \ No newline at end of file diff --git a/articles/002/diagram-01.png b/articles/002/diagram-01.png new file mode 100644 index 0000000..b46a4e6 Binary files /dev/null and b/articles/002/diagram-01.png differ diff --git a/articles/002/diagram-02-test.png b/articles/002/diagram-02-test.png new file mode 100644 index 0000000..bbe4983 Binary files /dev/null and b/articles/002/diagram-02-test.png differ diff --git a/articles/002/diagram-02.mmd b/articles/002/diagram-02.mmd new file mode 100644 index 0000000..4672afe --- /dev/null +++ b/articles/002/diagram-02.mmd @@ -0,0 +1,21 @@ +sequenceDiagram + participant Client as 客户端/CLI + participant GW as Gateway Server + participant Auth as 认证中间件 + participant Proto as 协议处理器 + participant CM as Channel Manager + participant RT as Route Resolver + + Client->>GW: WebSocket 连接 (ws://localhost:18789) + GW->>Auth: 设备认证 (Ed25519 签名) + Auth-->>GW: 认证通过 + 设备 Token + GW->>Proto: Hello 握手 (PROTOCOL_VERSION) + Proto-->>Client: HelloOk (capabilities, scopes) + + Note over Client,RT: 消息流 + + Client->>GW: RequestFrame: chat.send + GW->>Proto: AJV Schema 校验 + Proto->>RT: 路由解析 (channel + peer → agent) + RT-->>GW: sessionKey + agentId + GW->>CM: 分发到对应通道 \ No newline at end of file diff --git a/articles/002/diagram-02.png b/articles/002/diagram-02.png new file mode 100644 index 0000000..bbe4983 Binary files /dev/null and b/articles/002/diagram-02.png differ diff --git a/articles/002/diagram-03.mmd b/articles/002/diagram-03.mmd new file mode 100644 index 0000000..401e8d7 --- /dev/null +++ b/articles/002/diagram-03.mmd @@ -0,0 +1,11 @@ +graph LR + A[加载配置] --> B[校验认证配置] + B --> C[初始化 Secrets] + C --> D[加载插件] + D --> E[创建 Channel Manager] + E --> F[初始化 Memory Manager] + F --> G[启动 Heartbeat Runner] + G --> H[启动 WebSocket Server] + H --> I[挂载协议处理器] + I --> J[启动 Sidecars
发现/健康监控/Cron] + J --> K[✅ Ready] \ No newline at end of file diff --git a/articles/002/diagram-03.png b/articles/002/diagram-03.png new file mode 100644 index 0000000..894518d Binary files /dev/null and b/articles/002/diagram-03.png differ diff --git a/articles/002/diagram-04.mmd b/articles/002/diagram-04.mmd new file mode 100644 index 0000000..6d0be85 --- /dev/null +++ b/articles/002/diagram-04.mmd @@ -0,0 +1,19 @@ +graph TB + subgraph "ACP 协议栈" + EXT[外部 ACP 客户端
stdin/stdout] + CONN[AgentSideConnection
ndJSON 流] + AGENT[AcpGatewayAgent
协议翻译器] + CLIENT[GatewayClient
WebSocket] + MGR[AcpSessionManager
会话管理单例] + end + + subgraph "运行时缓存" + CACHE[RuntimeCache
sessionKey → Handle] + QUEUE[ActorQueue
每会话串行化] + EVICT[空闲驱逐
TTL 超时清理] + end + + EXT --> CONN --> AGENT --> CLIENT + AGENT --> MGR + MGR --> CACHE & QUEUE + CACHE --> EVICT \ No newline at end of file diff --git a/articles/002/diagram-04.png b/articles/002/diagram-04.png new file mode 100644 index 0000000..e0c8641 Binary files /dev/null and b/articles/002/diagram-04.png differ diff --git a/articles/002/diagram-05.mmd b/articles/002/diagram-05.mmd new file mode 100644 index 0000000..c240dc2 --- /dev/null +++ b/articles/002/diagram-05.mmd @@ -0,0 +1,48 @@ +graph TB + subgraph "Agent 运行时" + PI[Pi Agent Runner
核心推理引擎] + + subgraph "模型集成" + MC[Model Catalog
模型目录] + AP[Auth Profiles
凭证管理] + FB[Fallback Chains
模型降级链] + TH[Thinking Mode
思考/推理模式] + end + + subgraph "工具系统" + TC[Tool Catalog] + T1[web_search 网络搜索] + T2[browser_tool 浏览器] + T3[system.run 终端执行] + T4[system.spawn_acp 子Agent] + T5[channel actions 通道操作] + end + + subgraph "技能系统" + SL[Skill Loader
jiti 动态导入] + SF[skill.json 元数据] + SH[skill.ts 运行时处理器] + end + + subgraph "子 Agent" + SR[Subagent Registry] + SP[Spawn ACP
隔离会话派生] + end + end + + PI --> MC --> AP --> FB + PI --> TC --> T1 & T2 & T3 & T4 & T5 + PI --> SL --> SF & SH + PI --> SR --> SP + MC --> TH + + subgraph "LLM 提供商" + L1[Anthropic Claude] + L2[OpenAI GPT] + L3[Google Gemini] + L4[Qwen 通义千问] + L5[Ollama 本地] + L6[Groq / Grok / Azure / Minimax / 火山引擎] + end + + FB --> L1 & L2 & L3 & L4 & L5 & L6 \ No newline at end of file diff --git a/articles/002/diagram-05.png b/articles/002/diagram-05.png new file mode 100644 index 0000000..1068dde Binary files /dev/null and b/articles/002/diagram-05.png differ diff --git a/articles/002/diagram-06.mmd b/articles/002/diagram-06.mmd new file mode 100644 index 0000000..61a36f8 --- /dev/null +++ b/articles/002/diagram-06.mmd @@ -0,0 +1,16 @@ +sequenceDiagram + participant Agent as Agent Runtime + participant EA as ExecApprovalManager + participant Admin as 管理员 + participant CMD as 命令执行 + + Agent->>EA: system.run("rm -rf /tmp/data") + EA->>EA: 检查是否为危险操作 + EA->>Admin: 显示审批请求 + Admin-->>EA: ✅ 批准 / ❌ 拒绝 + alt 批准 + EA->>CMD: 执行命令 + CMD-->>Agent: 返回结果 + else 拒绝 + EA-->>Agent: 操作被拒绝 + end \ No newline at end of file diff --git a/articles/002/diagram-06.png b/articles/002/diagram-06.png new file mode 100644 index 0000000..e9d706e Binary files /dev/null and b/articles/002/diagram-06.png differ diff --git a/articles/002/diagram-07.mmd b/articles/002/diagram-07.mmd new file mode 100644 index 0000000..62a399b --- /dev/null +++ b/articles/002/diagram-07.mmd @@ -0,0 +1,19 @@ +graph TB + subgraph "插件生命周期" + D[1. Discovery 发现
扫描 extensions/ 目录] + M[2. Manifest 加载
读取 openclaw.plugin.json] + R[3. Runtime 创建
初始化 Hook Runner] + REG[4. Registration 注册
setActivePluginRegistry] + end + + D --> M --> R --> REG + + subgraph "插件类型" + CH[通道插件 ×34
Telegram/Slack/Discord/...] + MEM[记忆插件
memory-core / memory-lancedb] + AUTH_P[认证插件
google-gemini-cli-auth 等] + DIAG[诊断插件
diagnostics-otel] + SPEC[特殊插件
thread-ownership / llm-task] + end + + REG --> CH & MEM & AUTH_P & DIAG & SPEC \ No newline at end of file diff --git a/articles/002/diagram-07.png b/articles/002/diagram-07.png new file mode 100644 index 0000000..de91eae Binary files /dev/null and b/articles/002/diagram-07.png differ diff --git a/articles/002/diagram-08.mmd b/articles/002/diagram-08.mmd new file mode 100644 index 0000000..1a11c87 --- /dev/null +++ b/articles/002/diagram-08.mmd @@ -0,0 +1,39 @@ +graph TB + subgraph "记忆系统架构" + Q[搜索查询] + + subgraph "混合搜索引擎" + VS[向量检索
sqlite-vec] + BM[BM25 全文搜索
SQLite FTS5] + HY[混合排序
加权合并 + 时间衰减] + end + + subgraph "Embedding 提供商" + E1[OpenAI
text-embedding-3-small/large] + E2[Gemini
Generalist Multimodal] + E3[Voyage
voyage-large-2-instruct] + E4[Mistral
mistral-embed] + E5[Ollama
本地模型] + end + + subgraph "存储层" + DB[(SQLite 数据库)] + CV[chunks_vec 表
向量嵌入] + CF[chunks_fts 表
FTS5 全文索引] + EC[embedding_cache 表
查询缓存 + TTL] + end + + RES[搜索结果
相关度排序] + end + + Q --> VS & BM + VS --> HY + BM --> HY + VS --> E1 & E2 & E3 & E4 & E5 + E1 & E2 & E3 & E4 & E5 --> CV + BM --> CF + HY --> RES + DB --> CV & CF & EC + + style HY fill:#27AE60,color:#fff + style DB fill:#3498DB,color:#fff \ No newline at end of file diff --git a/articles/002/diagram-08.png b/articles/002/diagram-08.png new file mode 100644 index 0000000..e0eb26e Binary files /dev/null and b/articles/002/diagram-08.png differ diff --git a/articles/002/diagram-09.mmd b/articles/002/diagram-09.mmd new file mode 100644 index 0000000..2a58b82 --- /dev/null +++ b/articles/002/diagram-09.mmd @@ -0,0 +1,25 @@ +sequenceDiagram + participant Timer as 定时器 (默认 30 分钟) + participant HB as Heartbeat Runner + participant FS as 文件系统 + participant Agent as Agent Runtime + participant LLM as LLM 提供商 + participant Channel as 消息通道 + + loop 每 30 分钟 + Timer->>HB: 触发心跳 + HB->>FS: 读取 HEARTBEAT.md + alt HEARTBEAT.md 为空或不存在 + HB->>HB: 跳过,不调用 API + else HEARTBEAT.md 有任务内容 + HB->>Agent: 发送 HEARTBEAT_PROMPT + Agent->>LLM: 推理 + 任务执行 + LLM-->>Agent: 执行结果 + alt 返回 HEARTBEAT_OK + Agent-->>HB: 无需操作,静默 + else 有实际输出 + Agent->>Channel: 发送结果到通道 + Channel-->>HB: 显示 ACK 反应 (👀) + end + end + end \ No newline at end of file diff --git a/articles/002/diagram-09.png b/articles/002/diagram-09.png new file mode 100644 index 0000000..b15042a Binary files /dev/null and b/articles/002/diagram-09.png differ diff --git a/articles/002/diagram-10.mmd b/articles/002/diagram-10.mmd new file mode 100644 index 0000000..00bd068 --- /dev/null +++ b/articles/002/diagram-10.mmd @@ -0,0 +1,45 @@ +graph TB + subgraph "① 消息接收" + MSG[用户消息
Telegram/Slack/...] + SDK[平台 SDK
原生协议接收] + NORM[消息标准化
统一格式] + end + + subgraph "② 路由决策" + RT[Route Resolver
channel + peer → agentId] + BIND[Session Binding
会话绑定服务] + SK[Session Key
生成会话键] + end + + subgraph "③ 会话管理" + ACP[ACP Session Manager] + LOCK[Session Write Lock
防并发写入] + LOAD[加载会话历史
JSONL 转录] + end + + subgraph "④ Agent 推理" + PI[Pi Agent Runner] + LLM[LLM API 调用
含 Thinking Mode] + TOOL[工具调用
审批 → 执行] + MEM[记忆检索
混合搜索] + end + + subgraph "⑤ 响应投递" + STREAM[流式响应
逐 Token 返回] + TRANS[格式转换
适配目标平台] + DELIVER[投递到通道
支持线程/回复] + PERSIST[持久化
会话 + 记忆写入] + end + + MSG --> SDK --> NORM + NORM --> RT --> BIND --> SK + SK --> ACP --> LOCK --> LOAD + LOAD --> PI --> LLM + LLM --> TOOL + TOOL --> MEM + LLM --> STREAM --> TRANS --> DELIVER + DELIVER --> PERSIST + + style MSG fill:#9B59B6,color:#fff + style PI fill:#E74C3C,color:#fff + style DELIVER fill:#2ECC71,color:#fff \ No newline at end of file diff --git a/articles/002/diagram-10.png b/articles/002/diagram-10.png new file mode 100644 index 0000000..22ad579 Binary files /dev/null and b/articles/002/diagram-10.png differ diff --git a/articles/002/diagram-11.mmd b/articles/002/diagram-11.mmd new file mode 100644 index 0000000..7f079d1 --- /dev/null +++ b/articles/002/diagram-11.mmd @@ -0,0 +1,27 @@ +graph LR + subgraph "~/.openclaw/ 存储结构" + subgraph "设备层" + DI[device-identity.json
Ed25519 密钥对] + CRED[credentials/
设备 Token] + end + + subgraph "配置层" + CONFIG[config.json
全局配置] + SECRETS[secrets/
API Key 引用] + end + + subgraph "Agent 层 (per-agent)" + subgraph "agents/main/" + SESS[sessions.json
会话元数据] + TRANS[sessions/*.jsonl
会话转录] + WS[workspace/
工作区文件] + HMD2[HEARTBEAT.md] + MEMDB[memory.sqlite
向量+全文索引] + end + end + end + + style DI fill:#E67E22,color:#fff + style CONFIG fill:#3498DB,color:#fff + style SESS fill:#2ECC71,color:#fff + style MEMDB fill:#9B59B6,color:#fff \ No newline at end of file diff --git a/articles/002/diagram-11.png b/articles/002/diagram-11.png new file mode 100644 index 0000000..511a888 Binary files /dev/null and b/articles/002/diagram-11.png differ diff --git a/articles/002/diagram-12.mmd b/articles/002/diagram-12.mmd new file mode 100644 index 0000000..954e184 --- /dev/null +++ b/articles/002/diagram-12.mmd @@ -0,0 +1,33 @@ +graph TB + subgraph "安全层级" + subgraph "L1: 网络层" + TLS[TLS/mTLS 加密] + FP[证书指纹验证] + CWE[CWE-319 防护
禁止明文WS到非回环] + end + + subgraph "L2: 认证层" + DEV[设备认证
Ed25519 签名] + TOKEN[设备 Token
长期访问令牌] + OAUTH[OAuth 集成
Google/Discord 等] + PWD[密码认证
本地网关备选] + end + + subgraph "L3: 授权层" + SCOPE[操作域 Scopes] + PAIR[设备配对审批] + ROLE[Owner vs User 角色] + end + + subgraph "L4: 执行层" + APPROVE[工具审批门
危险操作拦截] + SANDBOX[沙箱策略
inherit/require/forbidden] + AUDIT[审计日志] + SCAN[危险工具扫描] + end + end + + TLS --> DEV --> SCOPE --> APPROVE + FP --> TOKEN --> PAIR --> SANDBOX + CWE --> OAUTH --> ROLE --> AUDIT + PWD --> SCAN \ No newline at end of file diff --git a/articles/002/diagram-12.png b/articles/002/diagram-12.png new file mode 100644 index 0000000..e6e9c6e Binary files /dev/null and b/articles/002/diagram-12.png differ diff --git a/articles/002/diagram-13.mmd b/articles/002/diagram-13.mmd new file mode 100644 index 0000000..c68eaa0 --- /dev/null +++ b/articles/002/diagram-13.mmd @@ -0,0 +1,24 @@ +graph TB + subgraph "通道插件架构" + IF[ChannelPlugin 接口] + + subgraph "插件实现" + TG[Telegram
Bot API] + SL[Slack
Bolt SDK] + DC[Discord
discord.js] + WA[WhatsApp
Baileys] + SIG[Signal
signal-cli] + FS[飞书
Open API] + end + + subgraph "生命周期" + INIT[initialize
认证 + 连接] + LISTEN[listen
消息监听] + ROUTE[route
路由到 Agent] + DELIVER[deliver
回传响应] + SHUTDOWN[shutdown
优雅断开] + end + end + + IF --> TG & SL & DC & WA & SIG & FS + TG & SL & DC & WA & SIG & FS --> INIT --> LISTEN --> ROUTE --> DELIVER --> SHUTDOWN \ No newline at end of file diff --git a/articles/002/diagram-13.png b/articles/002/diagram-13.png new file mode 100644 index 0000000..4145537 Binary files /dev/null and b/articles/002/diagram-13.png differ diff --git a/articles/002/diagram-14.mmd b/articles/002/diagram-14.mmd new file mode 100644 index 0000000..675ab89 --- /dev/null +++ b/articles/002/diagram-14.mmd @@ -0,0 +1,24 @@ +graph TB + subgraph "Docker Compose" + GW_SVC[gateway 服务
Node.js 运行时] + CLI_SVC[cli 服务
命令行交互] + HC[健康检查
端口 18789] + end + + subgraph "Docker 镜像" + BASE[Dockerfile
多阶段构建] + SB[Dockerfile.sandbox
沙箱镜像] + SBB[Dockerfile.sandbox-browser
浏览器沙箱] + SBC[Dockerfile.sandbox-common
公共沙箱基础] + end + + subgraph "其他部署" + FLY[fly.toml
Fly.io 部署] + RENDER[render.yaml
Render 部署] + PODMAN[Podman
无 root 容器] + end + + GW_SVC --> BASE + CLI_SVC --> BASE + GW_SVC --> HC + BASE --> SB & SBB & SBC \ No newline at end of file diff --git a/articles/002/diagram-14.png b/articles/002/diagram-14.png new file mode 100644 index 0000000..1be835f Binary files /dev/null and b/articles/002/diagram-14.png differ diff --git a/articles/002/diagram-15-test.png b/articles/002/diagram-15-test.png new file mode 100644 index 0000000..bf1077c Binary files /dev/null and b/articles/002/diagram-15-test.png differ diff --git a/articles/002/diagram-15.mmd b/articles/002/diagram-15.mmd new file mode 100644 index 0000000..ac353bb --- /dev/null +++ b/articles/002/diagram-15.mmd @@ -0,0 +1,23 @@ +mindmap + root((OpenClaw
设计模式)) + 消息驱动 + WebSocket 双向通信 + EventFrame 事件流 + RequestFrame 请求/响应 + 插件化 + 34 个通道插件 + Hook 系统 7 个钩子 + 动态加载 jiti + 会话隔离 + Actor 队列串行化 + Session Write Lock + Per-agent 存储隔离 + 安全纵深 + L1 网络加密 + L2 设备认证 + L3 域控授权 + L4 执行审批 + 韧性设计 + 模型降级链 + 连接重试退避 + 嵌入失败恢复 \ No newline at end of file diff --git a/articles/002/diagram-15.png b/articles/002/diagram-15.png new file mode 100644 index 0000000..2616d70 Binary files /dev/null and b/articles/002/diagram-15.png differ diff --git a/articles/002/mermaid-config.json b/articles/002/mermaid-config.json new file mode 100644 index 0000000..14c54a7 --- /dev/null +++ b/articles/002/mermaid-config.json @@ -0,0 +1,37 @@ +{ + "theme": "base", + "themeVariables": { + "primaryColor": "#0d2137", + "primaryTextColor": "#e0f7fa", + "primaryBorderColor": "#00e5ff", + "lineColor": "#00b8d4", + "secondaryColor": "#0a1628", + "secondaryTextColor": "#b2ebf2", + "secondaryBorderColor": "#00bcd4", + "tertiaryColor": "#112240", + "tertiaryTextColor": "#e0f7fa", + "tertiaryBorderColor": "#26c6da", + "noteBkgColor": "#0d2137", + "noteTextColor": "#e0f7fa", + "noteBorderColor": "#00e5ff", + "edgeLabelBackground": "#0a1628", + "clusterBkg": "#0a1a2e", + "clusterBorder": "#1a5276", + "titleColor": "#00e5ff", + "actorBkg": "#0d2137", + "actorBorder": "#00e5ff", + "actorTextColor": "#e0f7fa", + "actorLineColor": "#00b8d4", + "signalColor": "#00e5ff", + "signalTextColor": "#e0f7fa", + "labelBoxBkgColor": "#0d2137", + "labelBoxBorderColor": "#00e5ff", + "labelTextColor": "#e0f7fa", + "loopTextColor": "#80deea", + "activationBorderColor": "#00e5ff", + "activationBkgColor": "#112240", + "sequenceNumberColor": "#00e5ff", + "fontFamily": "SF Pro Display, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica Neue, Arial, sans-serif", + "fontSize": "15px" + } +} diff --git a/articles/002/mermaid-fix.css b/articles/002/mermaid-fix.css new file mode 100644 index 0000000..0d2e48e --- /dev/null +++ b/articles/002/mermaid-fix.css @@ -0,0 +1,7 @@ +.mindmap-node text, .mindmap-node tspan, +.node text, .node tspan, +.label text, .label tspan, +text, tspan { + fill: #ffffff !important; + color: #ffffff !important; +} diff --git a/articles/002/mermaid-mindmap.css b/articles/002/mermaid-mindmap.css new file mode 100644 index 0000000..26265ce --- /dev/null +++ b/articles/002/mermaid-mindmap.css @@ -0,0 +1,44 @@ +/* Mindmap sci-fi style - force visible node backgrounds */ + +text, tspan { + fill: #e0f7fa !important; + font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif !important; +} + +/* Root node */ +.mindmap-node:first-of-type circle { + fill: #0d2137 !important; + stroke: #00e5ff !important; + stroke-width: 3px !important; + filter: drop-shadow(0 0 12px rgba(0, 229, 255, 0.6)) !important; +} + +/* All mindmap section/node shapes */ +.mindmap-node rect, +.mindmap-node polygon, +.mindmap-node circle, +.mindmap-node ellipse, +.mindmap-node path { + stroke: #00b8d4 !important; + stroke-width: 2px !important; + filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.35)) !important; +} + +/* Force different section colors instead of black */ +.section-0 rect, .section-0 path { fill: #0d3b66 !important; stroke: #00e5ff !important; } +.section-1 rect, .section-1 path { fill: #1a3a4a !important; stroke: #26c6da !important; } +.section-2 rect, .section-2 path { fill: #1b3044 !important; stroke: #4dd0e1 !important; } +.section-3 rect, .section-3 path { fill: #14293d !important; stroke: #00bcd4 !important; } +.section-4 rect, .section-4 path { fill: #0f2b3d !important; stroke: #80deea !important; } + +/* Generic fallback: any rect/path that ends up black */ +rect[fill="#000"], rect[fill="#000000"], rect[fill="black"], +path[fill="#000"], path[fill="#000000"], path[fill="black"] { + fill: #0d3b66 !important; +} + +/* Lines between nodes */ +line, path.edge { + stroke: #00b8d4 !important; + stroke-width: 2px !important; +} diff --git a/articles/002/mermaid-tech.css b/articles/002/mermaid-tech.css new file mode 100644 index 0000000..c01c606 --- /dev/null +++ b/articles/002/mermaid-tech.css @@ -0,0 +1,115 @@ +/* Sci-fi / Tech style for mermaid diagrams */ + +/* Node styling */ +.node rect, .node polygon, .node circle, .node ellipse { + stroke-width: 2px !important; + filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.4)) !important; + rx: 8 !important; + ry: 8 !important; +} + +/* All text white/cyan */ +text, tspan { + fill: #e0f7fa !important; + font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif !important; +} + +/* Cluster/subgraph borders */ +.cluster rect { + stroke: #1a6a8a !important; + stroke-width: 2px !important; + stroke-dasharray: 6 3 !important; + fill: rgba(10, 26, 46, 0.7) !important; + rx: 12 !important; + ry: 12 !important; + filter: drop-shadow(0 0 8px rgba(0, 184, 212, 0.2)) !important; +} + +/* Cluster labels */ +.cluster text, .cluster tspan { + fill: #4dd0e1 !important; + font-weight: 600 !important; + font-size: 14px !important; +} + +/* Edge/arrow lines */ +.edge-pattern-solid, .flowchart-link { + stroke: #00b8d4 !important; + stroke-width: 2px !important; +} + +/* Arrow markers */ +marker path { + fill: #00e5ff !important; +} + +/* Sequence diagram lines */ +.messageLine0, .messageLine1 { + stroke: #00b8d4 !important; + stroke-width: 2px !important; +} + +/* Sequence diagram actors */ +.actor { + stroke: #00e5ff !important; + fill: #0d2137 !important; + stroke-width: 2px !important; + filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.3)) !important; +} + +/* Labels on edges */ +.edgeLabel rect { + fill: #0a1628 !important; + opacity: 0.9 !important; +} + +.edgeLabel span, .edgeLabel text, .edgeLabel tspan { + fill: #80deea !important; + color: #80deea !important; + font-size: 12px !important; +} + +/* Note boxes */ +.note { + fill: #112240 !important; + stroke: #00e5ff !important; +} + +/* Mindmap specific */ +.mindmap-node rect, .mindmap-node circle, .mindmap-node polygon { + filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.4)) !important; +} + +.mindmap-node text, .mindmap-node tspan { + fill: #e0f7fa !important; +} + +/* Activation bars in sequence diagrams */ +.activation0, .activation1, .activation2 { + fill: #112240 !important; + stroke: #00e5ff !important; +} + +/* Loop/alt boxes */ +.loopLine { + stroke: #1a6a8a !important; + stroke-dasharray: 4 3 !important; +} + +.loopText tspan, .loopText text { + fill: #4dd0e1 !important; +} + +/* Label styling */ +.label text, .label tspan { + fill: #e0f7fa !important; +} + +/* Highlighted nodes with custom styles from mermaid */ +[style*="fill:#4A90D9"], [style*="fill:#E74C3C"], [style*="fill:#27AE60"], +[style*="fill:#F39C12"], [style*="fill:#8E44AD"], [style*="fill:#9B59B6"], +[style*="fill:#2ECC71"], [style*="fill:#3498DB"], [style*="fill:#E67E22"] { + filter: drop-shadow(0 0 10px rgba(0, 229, 255, 0.6)) !important; + stroke: #00e5ff !important; + stroke-width: 2px !important; +} diff --git a/articles/给龙虾做了个 CT:43 万行代码的 OpenClaw 架构全拆解.md b/articles/给龙虾做了个 CT:43 万行代码的 OpenClaw 架构全拆解.md new file mode 100644 index 0000000..a29c586 --- /dev/null +++ b/articles/给龙虾做了个 CT:43 万行代码的 OpenClaw 架构全拆解.md @@ -0,0 +1,456 @@ +# 给龙虾做了个 CT:43 万行代码的 OpenClaw 架构全拆解 + +> 分析日期:2026-03-11 +> 源码版本:基于 GitHub 最新主分支 +> 分析工具:Claude Opus 4.6 +> 代码规模:约 43 万行 TypeScript,monorepo 架构 + +--- + +## 一、项目总览 + +OpenClaw 是一个**多通道 AI 智能体网关系统**——它不是一个简单的聊天机器人框架,而是一个完整的「AI 员工」运行时平台。其核心能力是:让大语言模型通过多种消息渠道(Slack、Telegram、Discord、WhatsApp 等 30+ 平台)接收指令、自主执行任务、并将结果回传。 + +### 1.1 Monorepo 结构 + +``` +openclaw/ +├── src/ # 核心运行时源码(主体) +│ ├── gateway/ # WebSocket 网关服务器 +│ ├── acp/ # Agent Client Protocol 协议层 +│ ├── agents/ # Agent 运行时、模型集成、工具系统 +│ ├── memory/ # 向量检索 + 全文搜索记忆系统 +│ ├── plugins/ # 插件加载、注册、Hook 执行 +│ ├── channels/ # 通道管理器 +│ ├── routing/ # 消息路由与会话键解析 +│ ├── auto-reply/ # 心跳机制与自动回复 +│ ├── config/ # 配置加载与会话持久化 +│ ├── security/ # 安全审计与策略执行 +│ ├── infra/ # 基础设施(设备身份、TLS、投递) +│ ├── cli/ # CLI 命令行界面 +│ ├── web/ # Web/WhatsApp Webhook 处理 +│ ├── terminal/ # 终端 TUI 组件 +│ └── media-understanding/# 多媒体理解(图片/音频) +├── extensions/ # 34 个通道/功能插件 +├── packages/ # 兼容包(clawdbot、moltbot 旧名) +├── apps/ # 原生客户端(macOS/iOS/Android) +├── ui/ # React Web 管理界面 +├── skills/ # 内置技能 +├── docs/ # 文档 +├── test/ # 测试套件 +└── vendor/ # 第三方依赖(a2ui 等) +``` + +### 1.2 技术栈 + +| 层面 | 技术选型 | +|------|---------| +| 语言 | TypeScript (ESM),Node.js 22+ | +| 构建 | tsdown (基于 Rolldown) + Vite (UI) | +| 包管理 | pnpm workspace (monorepo) | +| 协议 | WebSocket + ACP (Agent Client Protocol) | +| 存储 | SQLite (sqlite-vec + FTS5) + JSON 文件 | +| 验证 | AJV Schema Validation | +| 测试 | Vitest + V8 Coverage | +| 容器 | Docker / Podman 多阶段构建 | +| 桌面 | SwiftUI (macOS) / React Native (移动端) | + +--- + +## 二、核心架构全景 + +![图表1](https://cdn.union.jxyunge.com/self-media/002/arch-01.png) + +--- + +## 三、六大核心子系统详解 + +### 3.1 Gateway(通信网关) + +Gateway 是整个系统的**中枢神经**,负责接收所有外部连接、认证设备、路由消息。 + +![图表2](https://cdn.union.jxyunge.com/self-media/002/arch-02.png) + +**核心文件:** + +| 文件 | 职责 | +|------|------| +| `src/gateway/server.impl.ts` | 网关启动入口 `startGatewayServer()` | +| `src/gateway/client.ts` | WebSocket 客户端,含重连与退避策略 | +| `src/gateway/server-chat.ts` | 聊天消息处理器 | +| `src/gateway/protocol/index.ts` | 协议帧定义与 AJV 校验 | +| `src/gateway/auth.ts` | 认证中间件 | +| `src/gateway/auth-rate-limit.ts` | 速率限制策略 | +| `src/gateway/server-channels.ts` | 通道生命周期管理 | + +**Gateway 启动序列:** + +![图表3](https://cdn.union.jxyunge.com/self-media/002/arch-03.png) + +**协议帧类型:** + +| 帧类型 | 方向 | 用途 | +|--------|------|------| +| `RequestFrame` | Client → Server | chat.send, config.get, sessions.list 等 | +| `ResponseFrame` | Server → Client | 请求响应数据 | +| `EventFrame` | Server → Client | agent_message, tool_call, usage_update 等 | +| `HelloOk` | Server → Client | 握手响应,含协议版本和能力声明 | + +**安全特性:** +- **CWE-319 防护**:`isSecureWebSocketUrl()` 禁止非回环地址使用明文 `ws://` +- **设备身份**:Ed25519 密钥对存储于 `~/.openclaw/credentials/` +- **TLS 指纹**:支持证书指纹验证(Certificate Pinning) +- **连接序列号**:检测消息间隙,防止重放攻击 + +--- + +### 3.2 ACP(Agent Client Protocol 协议层) + +ACP 是 OpenClaw 定义的**标准化 Agent 通信协议**,作为 Gateway 与 Agent 运行时之间的翻译层。 + +![图表4](https://cdn.union.jxyunge.com/self-media/002/arch-04.png) + +**AcpGatewayAgent 核心职责:** +- 翻译 ACP 协议 ↔ Gateway 协议 +- 管理会话生命周期(initialize → prompt → response) +- 速率限制(默认 120 请求 / 10 秒窗口) +- 追踪待处理的 prompt 和 tool call + +**AcpSessionManager 核心职责:** +- 所有 ACP 运行时会话的单例管理器 +- 运行时缓存(空闲 TTL 驱逐) +- 活跃回合与延迟统计 +- **Actor 队列**:防止对同一会话的并发写入 + +**会话键格式:** + +``` +@main → 默认主会话 +@jane → 名为 "jane" 的 Agent 会话 +$subagent-id:child-key → 子 Agent 会话 +thread:123 → 线程绑定会话 +acp:uuid → ACP/IDE 会话 +``` + +--- + +### 3.3 Agent Runtime(Agent 运行时) + +Agent 运行时是系统的**大脑**,负责 LLM 推理、工具调用、子 Agent 调度。 + +![图表5](https://cdn.union.jxyunge.com/self-media/002/arch-05.png) + +**支持的 LLM 提供商(10+):** + +| 提供商 | 说明 | +|--------|------| +| Anthropic | Claude Opus / Sonnet / Haiku | +| OpenAI | GPT 系列 | +| Google | Gemini 系列 | +| Qwen | 通义千问(阿里) | +| Minimax | 国产大模型 | +| Ollama | 本地部署任意开源模型 | +| Groq | 高速推理 | +| Grok | xAI | +| Azure | Azure OpenAI Service | +| Volc | 火山引擎(字节) | + +**模型降级链(Fallback Chains):** 当主模型不可用时,自动切换到备用模型,保证服务连续性。 + +**工具审批机制:** + +![图表6](https://cdn.union.jxyunge.com/self-media/002/arch-06.png) + +--- + +### 3.4 Plugin System(插件系统) + +插件系统是 OpenClaw 可扩展性的核心——所有通道集成、记忆后端、诊断工具都以插件形式存在。 + +![图表7](https://cdn.union.jxyunge.com/self-media/002/arch-07.png) + +**已包含的 34 个通道插件:** + +| 类别 | 插件 | +|------|------| +| 即时通讯 | Telegram, WhatsApp, Signal, iMessage, Line, Zalo | +| 团队协作 | Slack, Discord, MS Teams, Mattermost, Google Chat, 飞书 | +| 社交/社区 | Matrix, IRC, Nostr, Twitch, Tlon | +| 企业通讯 | Synology Chat, Nextcloud Talk, BlueBubbles | +| 语音 | voice-call | +| 开发/集成 | acpx, copilot-proxy, lobster | + +**Plugin Runtime API:** + +```typescript +PluginRuntime = { + subagent: { + run(), // 运行子 Agent + waitForRun(), // 等待运行完成 + getSessionMessages(), + deleteSession() + }, + channel: { + list(), // 列出通道 + inspect(), // 检查通道状态 + sendMessage() // 发送消息 + }, + core: { + config, // 全局配置 + workspaceDir, // 工作区目录 + agentId // 当前 Agent ID + } +} +``` + +**Hook 系统:** + +| Hook | 触发时机 | +|------|---------| +| `gateway.startup()` | 网关启动时 | +| `gateway.shutdown()` | 优雅关闭时 | +| `channel.ready()` | 通道连接就绪 | +| `channel.message()` | 收到消息时 | +| `session.start()` | 会话开始 | +| `session.prompt()` | LLM 推理前 | +| `session.response()` | LLM 响应后 | + +--- + +### 3.5 Memory System(记忆系统) + +记忆系统实现了**向量检索 + BM25 全文搜索**的混合搜索架构,是 Agent 长期记忆的基础。 + +![图表8](https://cdn.union.jxyunge.com/self-media/002/arch-08.png) + +**MemoryIndexManager 是单例模式**,每个 Agent + Workspace 一个实例: + +```typescript +// 获取或创建记忆管理器 +const memory = await MemoryIndexManager.get({ + cfg: config, + agentId: "main", + purpose: "chat" +}); + +// 混合搜索 +const results = await memory.search({ + query: "用户上周提到的跑步习惯", + limit: 10, + threshold: 0.7, + hybrid: { weight: 0.6 } // 向量权重 60%, BM25 权重 40% +}); +``` + +**关键特性:** +- **时间衰减**:近期记忆权重更高 +- **批量嵌入**:失败自动恢复 +- **查询缓存**:TTL 控制的 embedding 缓存层 +- **额外记忆路径**:可引入外部文档目录 + +--- + +### 3.6 Heartbeat(心跳机制) + +心跳是 OpenClaw 最具争议也最核心的设计——让 AI **主动醒来执行任务**,而不是被动等待指令。 + +![图表9](https://cdn.union.jxyunge.com/self-media/002/arch-09.png) + +**HEARTBEAT.md 示例:** + +```markdown +## 每日任务 + +- 每天早上 9:00 检查未读邮件,分类后发送摘要到 Slack #daily +- 监控竞品价格变动,降幅超过 10% 立即通知 +- 每周五下午生成本周工作总结 + +## 触发条件 + +- 仅在工作日执行 +- 静默模式:无变化时不发送消息 +``` + +**可见性控制:** + +| 配置项 | 说明 | 默认值 | +|--------|------|--------| +| `heartbeat.every` | 心跳间隔 | 30m | +| `heartbeat.enabled` | 是否启用 | true | +| `heartbeat.ackMaxChars` | ACK 最大字符数 | 300 | +| `SILENT_REPLY_TOKEN` | 静默回复标记 | #SILENT_ACK | +| `HEARTBEAT_OK` | 无需操作标记 | HEARTBEAT_OK | + +--- + +## 四、数据流全链路 + +一条消息从外部平台进入到最终响应,经过的完整链路: + +![图表10](https://cdn.union.jxyunge.com/self-media/002/arch-10.png) + +--- + +## 五、持久化与存储架构 + +![图表11](https://cdn.union.jxyunge.com/self-media/002/arch-11.png) + +**会话存储细节:** + +| 存储项 | 格式 | 说明 | +|--------|------|------| +| 会话元数据 | JSON | 模型、Token 用量、思考级别等 | +| 会话转录 | JSONL (追加写入) | 不可变的消息日志,按大小/数量自动轮转 | +| 记忆索引 | SQLite | 向量表 + FTS 表 + 缓存表 | +| 设备身份 | JSON | Ed25519 公私钥对 | +| 配置 | JSON | 全局配置,含 Secret 引用 | + +**Secrets 引用机制:** + +```json +{ + "providers": { + "anthropic": { + "apiKey": "${file://~/.openclaw/secrets/anthropic.key}" + } + } +} +``` + +支持 `${file://path}` 和 `${env://VAR_NAME}` 两种引用方式。 + +--- + +## 六、安全架构 + +![图表12](https://cdn.union.jxyunge.com/self-media/002/arch-12.png) + +**操作域(Scopes)细粒度控制:** + +| Scope | 权限 | +|-------|------| +| `gateway:full` | 网关完全控制 | +| `gateway:read` | 只读访问 | +| `channels:read` | 读取通道信息 | +| `messages:send` | 发送消息 | +| `config:write` | 修改配置 | +| `agents:manage` | Agent 管理 | + +**沙箱策略:** + +| 策略 | 说明 | +|------|------| +| `inherit` | 继承父会话沙箱模式 | +| `require` | 强制更严格的沙箱 | +| `forbidden` | 禁止子 Agent 派生 | + +**安全审计(`src/security/`):** +- `dangerous-tools.ts` — 危险工具扫描器 +- `dangerous-config-flags.ts` — 危险配置标记检测 +- `audit.ts` — 审计日志记录器 +- 临时路径防护、外部内容策略 + +--- + +## 七、通道集成架构 + +![图表13](https://cdn.union.jxyunge.com/self-media/002/arch-13.png) + +**通道插件必须实现的接口:** + +```typescript +interface ChannelPlugin { + // 配置 Schema 定义 + configSchema: JSONSchema; + + // 生命周期 + initialize(config: ChannelConfig): Promise; + shutdown(): Promise; + + // 消息处理 + onMessage(handler: MessageHandler): void; + sendMessage(target: Target, message: Message): Promise; + + // 状态 + getStatus(): ChannelStatus; + getAccounts(): AccountInfo[]; +} +``` + +**消息标准化:** 所有通道的消息都会被标准化为统一格式,包括: +- 文本内容 +- 附件(图片/文件/音频) +- 发送者身份 +- 线程/回复关系 +- 通道特定元数据 + +--- + +## 八、构建与部署 + +### 8.1 Docker 部署架构 + +![图表14](https://cdn.union.jxyunge.com/self-media/002/arch-14.png) + +### 8.2 发布通道 + +| 通道 | 版本格式 | npm dist-tag | +|------|---------|--------------| +| Stable | vYYYY.M.D | `latest` | +| Beta | vYYYY.M.D-beta.N | `beta` | +| Dev | main 分支 | 无 tag | + +### 8.3 客户端矩阵 + +| 平台 | 技术栈 | 位置 | +|------|--------|------| +| CLI | Commander.js + Clack | `src/cli/` | +| Web UI | React + Vite | `ui/` | +| macOS | SwiftUI + XPC Bridge | `apps/macos/` | +| iOS | React Native | `apps/ios/` | +| Android | React Native | `apps/android/` | + +--- + +## 九、关键设计模式总结 + +### 9.1 架构模式 + +![图表15](https://cdn.union.jxyunge.com/self-media/002/arch-15.png) + +### 9.2 核心设计决策 + +| 决策 | 选择 | 理由 | +|------|------|------| +| 协议 | WebSocket + 自定义协议 | 双向实时通信,低延迟 | +| 存储 | 文件系统 + SQLite | 零外部依赖,本地优先 | +| 插件加载 | jiti 动态导入 | 支持 TypeScript 直接加载,无需预编译 | +| 并发控制 | Actor 队列 | 避免锁竞争,每会话串行保证一致性 | +| 记忆检索 | 向量 + BM25 混合 | 语义理解 + 精确匹配,互补短板 | +| 安全模型 | 设备身份 + Scopes | 零信任架构,最小权限原则 | +| 心跳 | 文件驱动 (HEARTBEAT.md) | 无需额外调度基础设施,用户可直接编辑 | + +### 9.3 值得关注的工程亮点 + +1. **协议翻译器模式(ACP Translator)**:将外部标准协议与内部网关协议解耦,允许独立演进 +2. **运行时缓存 + 空闲驱逐**:会话不用时自动回收资源,用时自动恢复 +3. **Secret 引用而非内联**:配置文件中不存储明文密钥,而是引用外部文件或环境变量 +4. **会话转录追加写入**:不可变日志,天然支持故障恢复和审计 +5. **嵌入提供商可替换**:同一套记忆系统可无缝切换 OpenAI、Gemini、本地 Ollama 等后端 + +--- + +## 十、架构风险与局限 + +| 风险点 | 说明 | 影响 | +|--------|------|------| +| **明文 Markdown 存储** | HEARTBEAT.md、MEMORY.md 等以明文存储 | API Key 泄露风险 | +| **Root 级终端权限** | `system.run` 工具可执行任意命令 | 需依赖审批机制,但默认可绕过 | +| **心跳无人值守** | Agent 可在用户不知情时自主行动 | MoltMatch 等事件的根因 | +| **第三方 Skill 生态** | ClawHub 26% 插件含漏洞/恶意代码 | 供应链攻击面 | +| **单体网关** | Gateway 是单点,承载所有通道 | 高可用需额外架构 | +| **文件系统依赖** | 会话存储依赖本地文件系统 | 不原生支持分布式部署 | + +--- + +> **总结**:OpenClaw 的架构本质上是一个**面向消息的分布式系统**——Gateway 是交换机,Agent 是 LLM 驱动的任务执行器,Channel 是双向传输管道。记忆、安全和可扩展性是一等公民,而非事后补丁。但其"本地优先"的设计哲学也带来了明文存储和单点依赖的固有风险。