OpenClaw_Technical_Analysis
OpenClaw 技术分析文档
项目版本: 2026.2.13
分析日期: 2026-02-14
分析范围: 完整源代码、架构设计、模块实现
目录
1. 项目整体与核心模块分析
1.1 项目概述
OpenClaw 是一个多渠道 AI 助手网关系统,采用插件化架构设计,支持多种聊天平台(WhatsApp、Telegram、Slack、Discord、Signal、iMessage 等)和 LLM 提供商(Anthropic Claude、OpenAI、Google Gemini 等)。
核心特性:
- 多渠道消息收发(20+ 渠道支持)
- 插件化扩展系统
- WebSocket Gateway 服务
- 跨平台支持(Node.js + macOS/iOS/Android 原生应用)
- 本地优先架构
- 多模型支持与故障转移
- 命令执行工具(Bash、Python、文件操作等)
- 内存管理和会话压缩
- 认证与权限控制
1.2 核心模块分析
1.2.1 用户认证与权限控制模块
职责说明:
处理用户身份验证、授权和访问控制,支持多种认证方式(OAuth、API Key、Token)。
实现逻辑:
位置: src/agents/auth-profiles/, src/gateway/auth-*.ts
关键组件:
-
AuthProfile 管理 (
src/agents/auth-profiles/)auth-profiles.ts: 认证配置文件管理session-override.ts: 会话级别的认证配置覆盖chutes.ts: Chutes OAuth 认证集成cooler.ts: 认证冷却期管理(防止频繁认证失败)ensureauthprofilestore.ts: 确保认证配置存储初始化markauthprofilefailure.ts: 标记认证失败记录
-
Gateway 认证 (
src/gateway/)auth-rate-limit.ts: 认证请求限流auth.ts: Gateway WebSocket 认证中间件exec-approval-manager.ts: 命令执行权限管理器exec-approval-forwarder.ts: 批准请求转发
-
Provider OAuth (
src/infra/oauth-*.ts,extensions/google-gemini-cli-auth/)- 支持 Anthropic OAuth、OpenAI OAuth、Google Gemini OAuth
createVpsAwareOAuthHandlers(): VPS 友好的 OAuth 处理器
关键类/函数:
// src/agents/auth-profiles/types.ts
export interface AuthProfileCredential {
provider: string;
apiKey?: string;
oauth?: OAuthCredential;
expiresAt?: number;
}
// src/agents/auth-profiles.ts
export function ensureAuthProfileStore(): void;
// src/gateway/auth-rate-limit.ts
export function createAuthRateLimiter(): AuthRateLimiter;
// src/commands/oauth-flow.ts
export function createVpsAwareOAuthHandlers(): {
start: (opts: OAuthStartOpts) => Promise<void>;
callback: (opts: OAuthCallbackOpts) => Promise<void>;
};
模块调用关系:
CLI Commands → AuthProfileStore → Provider Auth Methods → OAuth Handlers
Gateway WebSocket → AuthMiddleware → AuthRateLimiter → Token Validation
Agent Tools → ExecApprovalManager → Permission Check → Execution
问题识别:
- ✅ 认证逻辑高度模块化,支持多种认证方式
- ✅ 故障转移机制完善(
model-fallback.ts) - ⚠️ 部分认证流程代码较为复杂(
google-gemini-cli-auth/oauth.ts18KB) - ⚠️ OAuth 冷却期管理可能影响正常使用体验
1.2.2 数据处理 / 业务逻辑层
职责说明:
处理 AI 对话请求、工具调用、消息转换、会话管理等核心业务逻辑。
实现逻辑:
位置: src/agents/, src/auto-reply/
关键组件:
-
Agent Runner (
src/agents/cli-runner.ts,src/agents/pi-embedded.ts)runCliAgent(): CLI 模式 Agent 执行runEmbeddedPiAgent(): 嵌入式 PI Agent 执行(基于 @mariozechner/pi-agent-core)
-
Auto-Reply 系统 (
src/auto-reply/)reply/agent-runner-execution.ts: Agent 执行协调器(26.5KB)reply/dispatcher.ts: 消息分发器reply/triggers.ts: 触发条件处理reply/heartbeats.ts: 心跳机制(打字指示、状态同步)reply/directives.ts: 命令指令处理(/help,/new,/reset等)
-
模型选择与故障转移 (
src/agents/model-*.ts)model-selection.ts: 模型选择逻辑(12.3KB)model-fallback.ts: 故障转移机制(10.7KB)model-auth.ts: 模型认证管理model-catalog.ts: 模型目录
-
会话压缩 (
src/agents/compaction.ts)- 当会话消息超过模型上下文窗口时,自动压缩历史消息
- 支持智能摘要和截断策略
-
工具系统 (
src/agents/tools/,src/agents/bash-tools.*.ts)bash-tools.exec.ts: Bash 执行工具(33.4KB)bash-tools.process.ts: 进程管理工具(20.8KB)tools/gateway.ts: Gateway 工具(会话控制、子 Agent 等)tools/nodes.ts: 节点管理工具
关键类/函数:
// src/agents/cli-runner.ts
export async function runCliAgent(opts: CliAgentOpts): Promise<void>;
// src/agents/pi-embedded.ts
export async function runEmbeddedPiAgent(opts: PiAgentOpts): Promise<void>;
// src/agents/model-selection.ts
export function resolveThinkingDefault(config: OpenClawConfig): ThinkLevel;
export function resolveAgentModelPrimary(config: OpenClawConfig, agentId?: string): string;
// src/agents/model-fallback.ts
export async function runWithModelFallback(
fn: () => Promise<void>,
opts: FallbackOpts
): Promise<void>;
// src/agents/compaction.ts
export async function compactSession(
sessionKey: string,
messages: AgentMessage[]
): Promise<AgentMessage[]>;
模块调用关系:
Channel Message → Auto-Reply Dispatcher → Agent Runner → Model Selection → LLM Provider
↓
Tool Execution (Bash, File, etc.)
↓
Compaction (if context overflow)
↓
Message Response → Channel Send
问题识别:
- ✅ 业务逻辑与 LLM 核心分离,使用 @mariozechner/pi-agent-core
- ✅ 支持多种模型提供商和故障转移
- ⚠️
agent-runner-execution.ts文件较大(26.5KB),可进一步拆分 - ⚠️ Bash 工具代码复杂度高(
bash-tools.exec.ts33.4KB) - ⚠️ 多个模块之间存在隐式依赖(通过配置和会话状态)
1.2.3 API 接口层 / 控制器
职责说明:
提供 CLI 命令接口和 WebSocket Gateway API,处理外部请求。
实现逻辑:
位置: src/cli/, src/commands/, src/gateway/
关键组件:
-
CLI 系统 (
src/cli/)run-main.ts: CLI 入口路由program.ts: Commander.js 程序构建profile.ts: CLI 配置文件支持deps.ts: CLI 依赖注入
-
命令处理器 (
src/commands/)agent.ts: Agent 命令处理(17.8KB)agents.*.ts: Agents 子命令(add/delete/list/identity)gateway.ts: Gateway 启动命令onboard.ts: 引导向导doctor.ts: 诊断工具message.ts: 消息发送命令models.ts: 模型管理命令
-
Gateway Server (
src/gateway/server.impl.ts)- WebSocket + HTTP 混合服务器(23.5KB)
- 支持 OpenAI Chat Completions API (
POST /v1/chat/completions) - 支持 OpenResponses API (
POST /v1/responses) - 内置 WebSocket 协议(基于 @agentclientprotocol/sdk)
-
Gateway Methods (
src/gateway/server-methods/)server-methods.ts: 核心 Gateway 处理器config.ts: 配置管理接口send.ts: 消息发送接口skills.ts: Skills 管理接口talk.ts: 语音/对话接口nodes.ts: 节点管理接口exec-approval.ts: 批准请求处理
-
浏览器控制服务器 (
src/browser/)server.ts: 浏览器 WebSocket 服务器routes/agent.*.ts: Agent 相关路由routes/control.*.ts: 控制台路由
关键类/函数:
// src/cli/run-main.ts
export async function runCli(argv: string[]): Promise<void>;
// src/commands/agent.ts
export async function agentCommand(opts: AgentCommandOpts): Promise<void>;
// src/gateway/server.impl.ts
export async function startGatewayServer(
port: number,
opts: GatewayServerOptions
): Promise<GatewayServer>;
// src/gateway/server-methods/types.ts
export type GatewayRequestHandler = (
params: unknown,
ctx: GatewayRequestContext
) => Promise<GatewayResponse>;
模块调用关系:
CLI (openclaw agent/message) → Commands → Agent Runner → LLM
Browser/Gateway WebSocket → Gateway Methods → Core Logic → Channels
OpenAI Compatible API → Gateway Methods → Agent Runner → LLM
问题识别:
- ✅ CLI 使用 Commander.js,结构清晰
- ✅ Gateway 支持多种 API 协议(WebSocket、HTTP)
- ⚠️
server.impl.ts文件较大(23.5KB),职责较多 - ⚠️ 部分 Gateway 方法缺乏统一的错误处理
1.2.4 数据持久化 / ORM / DAO
职责说明:
管理配置文件、会话数据、插件状态等持久化存储。
实现逻辑:
位置: src/config/, src/config/sessions.ts
关键组件:
-
配置管理 (
src/config/)io.ts: 配置读写 I/Ovalidation.ts: 配置验证(Zod Schema)zod-schema.ts: 配置 JSON Schema 定义paths.ts: 路径解析runtime-overrides.ts: 运行时配置覆盖plugin-auto-enable.ts: 插件自动启用
-
会话存储 (
src/config/sessions.ts)- 会话元数据管理(
updateSessionStore(),resolveSessionFilePath()) - 会话 JSONL 文件存储
- 会话元数据管理(
-
插件配置 (
src/plugins/)config-state.ts: 插件配置状态管理install.ts: 插件安装与配置持久化
-
状态目录 (
src/config/paths.ts)- 默认:
~/.openclaw/ - 可通过环境变量
OPENCLAW_STATE_DIR覆盖
- 默认:
关键类/函数:
// src/config/io.ts
export function loadConfig(): OpenClawConfig;
export async function writeConfigFile(config: OpenClawConfig): Promise<void>;
// src/config/sessions.ts
export function updateSessionStore(
sessionKey: string,
entry: SessionEntry
): void;
export function resolveSessionFilePath(
sessionKey: string,
filename: string
): string;
// src/plugins/config-state.ts
export function getPluginConfigState(pluginId: string): PluginConfigState;
export function setPluginConfigState(
pluginId: string,
state: PluginConfigState
): void;
存储结构:
~/.openclaw/
├── openclaw.json # 主配置文件
├── .env # 环境变量(敏感信息)
├── plugins/
│ └── {plugin-id}.json # 插件配置
├── agents/
│ └── {agent-id}/
│ ├── sessions/ # 会话 JSONL 文件
│ ├── workspace/ # 工作区文件
│ └── skills/ # Agent Skills
├── channels/ # 渠道状态(可选)
└── diagnostics/ # 诊断日志
问题识别:
- ✅ 配置验证使用 Zod,类型安全
- ✅ 支持环境变量覆盖,安全性好
- ⚠️ 缺乏真正的 ORM,所有存储使用 JSON/JSONL
- ⚠️ 大量会话文件可能导致 I/O 性能问题
- ⚠️ 没有数据库层,难以实现复杂查询
1.2.5 配置管理 / 启动流程
职责说明:
管理系统配置、环境变量、启动参数和服务生命周期。
实现逻辑:
位置: src/config/, src/entry.ts, src/cli/run-main.ts
关键组件:
-
启动流程 (
src/entry.ts,src/cli/run-main.ts)- 实验性警告抑制(Node.js 22 兼容性)
- Windows 参数规范化
- CLI 配置文件支持
- 运行时守卫(
assertSupportedRuntime())
-
配置加载 (
src/config/)loadConfig(): 加载主配置readConfigFileSnapshot(): 读取配置快照migrateLegacyConfig(): 遗留配置迁移validateConfigObject(): 配置验证
-
环境变量处理 (
src/infra/env.ts,src/infra/dotenv.js)normalizeEnv(): 环境变量规范化loadDotEnv(): 加载 .env 文件- 支持从 Shell Profile 导入环境变量
-
Gateway 启动 (
src/gateway/server.impl.ts)- 配置快照读取与迁移
- 插件加载
- 通道管理器启动
- Cron 服务启动
- Node 注册表初始化
关键类/函数:
// src/config/io.ts
export function loadConfig(): OpenClawConfig;
// src/config/paths.ts
export function CONFIG_PATH(): string;
export function STATE_DIR(): string;
// src/gateway/server.impl.ts
export async function startGatewayServer(
port: number,
opts: GatewayServerOptions
): Promise<GatewayServer>;
// src/infra/runtime-guard.ts
export function assertSupportedRuntime(): void;
启动顺序:
1. entry.ts: Node.js 进程启动
2. run-main.ts: CLI 参数解析
3. loadConfig(): 配置加载与验证
4. Gateway Start: 插件加载 → 通道启动 → WebSocket 服务启动
5. Services Start: Cron, Node Registry, Health Monitor
问题识别:
- ✅ 启动流程清晰,分阶段初始化
- ✅ 支持配置迁移,向后兼容性好
- ⚠️ 启动时加载所有插件,可能影响启动速度
- ⚠️ 配置验证错误信息可能不够详细
1.2.6 中间件 / 插件机制 / 扩展点
职责说明:
实现插件系统,支持动态扩展渠道、工具、认证提供商等。
实现逻辑:
位置: src/plugins/, extensions/
关键组件:
-
插件注册表 (
src/plugins/registry.ts)- 插件记录存储(
PluginRecord) - 工具、通道、Provider、CLI 命令注册
- Hook 注册系统
- 插件记录存储(
-
插件加载器 (
src/plugins/loader.ts)- 从多个来源加载插件(bundled, global, workspace, config)
- 动态导入插件模块
- 插件依赖解析
-
插件 API (
src/plugins/types.ts)OpenClawPluginApi: 插件开发者接口- 注册方法:
registerTool(): 注册 Agent 工具registerChannel(): 注册消息通道registerProvider(): 注册 LLM 提供商registerHook(): 注册生命周期 HookregisterCli(): 注册 CLI 命令registerService(): 注册后台服务registerGatewayMethod(): 注册 Gateway 方法
-
Hook 系统 (
src/plugins/hooks.ts)- 14+ 个生命周期 Hook 点
- Hook 优先级支持
- 全局 Hook Runner
-
插件服务 (
src/plugins/services.ts)- 后台服务生命周期管理
- 支持启动/停止钩子
Hook 类型:
export type PluginHookName =
| "before_agent_start"
| "agent_end"
| "before_compaction"
| "after_compaction"
| "before_reset"
| "message_received"
| "message_sending"
| "message_sent"
| "before_tool_call"
| "after_tool_call"
| "tool_result_persist"
| "session_start"
| "session_end"
| "gateway_start"
| "gateway_stop";
插件示例:
// extensions/telegram/index.ts
export default function register(api: OpenClawPluginApi) {
api.registerChannel({
id: "telegram",
name: "Telegram",
// ...
});
api.registerCommand({
name: "telegram",
description: "Telegram-specific command",
handler: async (ctx) => { /* ... */ }
});
}
// extensions/voice-call/index.ts
export default function register(api: OpenClawPluginApi) {
api.registerHook("before_tool_call", async (event, ctx) => {
if (event.toolName === "call") {
// 自定义语音调用逻辑
}
});
api.registerService({
id: "voice-call-service",
start: async (ctx) => { /* 启动服务 */ },
stop: async (ctx) => { /* 停止服务 */ }
});
}
插件目录结构:
extensions/
├── telegram/
│ ├── index.ts # 插件入口
│ ├── openclaw.plugin.json # 插件清单
│ ├── package.json
│ └── src/ # 插件源码
│ ├── index.ts # 通道实现
│ ├── proxy.ts
│ └── webhook-set.ts
├── discord/
├── slack/
├── whatsapp/
├── memory-lancedb/ # 内存插件
├── voice-call/ # 语音通话插件
└── google-gemini-cli-auth/ # Google Gemini OAuth 插件
关键类/函数:
// src/plugins/loader.ts
export async function loadPlugins(opts: LoadPluginsOpts): Promise<void>;
// src/plugins/registry.ts
export type PluginRecord = {
id: string;
name: string;
version?: string;
source: string;
tools: PluginToolRegistration[];
channels: PluginChannelRegistration[];
providers: PluginProviderRegistration[];
// ...
};
// src/plugins/hooks.ts
export async function invokeHook(
hookName: PluginHookName,
event: unknown,
ctx: unknown
): Promise<void>;
模块调用关系:
Gateway Start → Load Plugins → Plugin Loader
↓
Plugin Registry
↓
┌─────────────┼─────────────┐
↓ ↓ ↓
Channel Tool Provider
Registration Registration Registration
↓ ↓ ↓
Channel Agent Model
Manager Runner Selection
问题识别:
- ✅ 插件系统设计完善,扩展性强
- ✅ Hook 机制支持 14+ 生命周期点
- ✅ 支持多种插件来源(bundled, global, workspace)
- ⚠️ 插件加载顺序可能影响功能(依赖关系不明确)
- ⚠️ 缺乏插件沙箱隔离(插件可访问全局环境)
- ⚠️ 插件错误可能导致整个系统崩溃
1.3 模块依赖关系图
2. 设计模式与架构风格分析
2.1 创建型模式
2.1.1 Factory Pattern(工厂模式)
使用位置: 多个模块
1. Channel Factory
- 位置:
src/channels/plugins/index.ts - 实现:
export function getChannelPlugin(id: ChannelId): ChannelPlugin | undefined { const resolvedId = String(id).trim(); return listChannelPlugins().find((plugin) => plugin.id === resolvedId); } - 解决的问题: 统一创建不同类型的通道实例
- 优点: 封装通道创建逻辑,支持动态扩展
- 缺陷: 返回
undefined可能导致运行时错误
2. Tool Factory
- 位置:
src/agents/tools/,src/plugins/ - 实现:
export type OpenClawPluginToolFactory = ( ctx: OpenClawPluginToolContext, ) => AnyAgentTool | AnyAgentTool[] | null | undefined; - 解决的问题: 根据上下文动态创建工具实例
- 优点: 支持工具的条件化注册
- 缺陷: 类型安全性较差(可能返回 null)
3. Agent Factory
- 位置:
src/agents/pi-embedded.ts - 实现: 使用 @mariozechner/pi-agent-core 的工厂方法创建 Agent
- 解决的问题: 根据配置创建不同类型的 Agent
- 优点: 支持嵌入式和远程 Agent
2.1.2 Builder Pattern(建造者模式)
使用位置: src/cli/program.ts, src/config/validation.ts
1. CLI Program Builder
- 位置:
src/cli/program.ts - 实现: 使用 Commander.js 构建命令行接口
export function buildProgram(): Command { const program = new Command(); program .name("openclaw") .version(VERSION) .command("agent", agentCommand) .command("gateway", gatewayCommand) // ... return program; } - 解决的问题: 复杂 CLI 命令结构的构建
- 优点: 支持子命令懒加载
- 缺陷: 命令注册逻辑分散
2. Config Builder
- 位置:
src/config/zod-schema.ts - 实现: 使用 Zod Schema 构建配置验证器
export const OpenClawSchema = z.object({ gateway: z.object({ port: z.number().optional(), bind: z.enum(["loopback", "lan", "tailnet", "auto"]).optional(), // ... }), agents: z.object({ defaults: AgentDefaultsSchema.optional(), }), // ... }); - 解决的问题: 复杂配置结构的验证与构建
- 优点: 类型安全,错误信息清晰
- 缺陷: Schema 定义冗长
2.1.3 Singleton Pattern(单例模式)
使用位置: 多个全局对象
1. Gateway Server Singleton
- 位置:
src/gateway/server.impl.ts - 实现: 全局变量存储 Gateway 实例
let gatewayServer: GatewayServer | null = null; export async function startGatewayServer(...) { if (gatewayServer) { throw new Error("Gateway server already running"); } // ... } - 解决的问题: 确保只有一个 Gateway 实例运行
- 优点: 防止端口冲突
- 缺陷: 全局变量,测试困难
2. Plugin Registry Singleton
- 位置:
src/plugins/runtime.ts - 实现: 模块级变量存储插件注册表
let activeRegistry: PluginRegistry | null = null; export function requireActivePluginRegistry(): PluginRegistry { if (!activeRegistry) { throw new Error("Plugin registry not initialized"); } return activeRegistry; } - 解决的问题: 全局插件状态管理
- 优点: 简化插件访问
- 缺陷: 违反依赖注入原则
3. Logger Singleton
- 位置:
src/logger.ts,src/logging/subsystem.ts - 实现: 使用 tslog 创建子系统日志器
const logger = createSubsystemLogger("gateway"); - 解决的问题: 统一日志管理
- 优点: 支持结构化日志
- 缺陷: 单例日志器难以测试
2.2 结构型模式
2.2.1 Adapter Pattern(适配器模式)
使用位置: 多个适配器模块
1. Channel Adapter
- 位置:
src/channels/plugins/ - 实现: 将不同聊天平台的 API 统一适配为
ChannelPlugin接口export type ChannelPlugin = { id: string; name: string; meta: ChannelMeta; normalizeIncoming: (raw: unknown) => IncomingMessage; send: (payload: SendPayload) => Promise<void>; // ... }; - 解决的问题: 统一不同平台的消息格式
- 优点: 支持快速添加新平台
- 缺陷: 平台差异导致适配器逻辑复杂
2. LLM Provider Adapter
- 位置:
src/agents/models-config.providers.ts(26.7KB) - 实现: 将 Anthropic、OpenAI、Google Gemini 等适配为统一接口
export type ModelProviderConfig = { id: string; label: string; baseUrl?: string; apiKey?: string; models?: ModelConfig[]; // ... }; - 解决的问题: 统一不同 LLM 提供商的 API
- 优点: 支持模型故障转移
- 缺陷: 适配器代码量大
3. Plugin Tool Adapter
- 位置:
src/agents/pi-tool-definition-adapter.ts - 实现: 将 OpenClaw 工具适配为 PI Agent 工具格式
- 解决的问题: 工具格式兼容性
- 优点: 集成第三方 Agent 框架
2.2.2 Facade Pattern(外观模式)
使用位置: src/config/config.ts, src/plugins/types.ts
1. Config Facade
- 位置:
src/config/config.ts - 实现:
export { loadConfig, writeConfigFile, validateConfigObject, // ... } from "./io.js"; export * from "./paths.js"; export * from "./types.js"; - 解决的问题: 简化配置模块的使用
- 优点: 统一导出,隐藏实现细节
- 缺陷: 导致导入路径不明确
2. Plugin API Facade
- 位置:
src/plugins/types.ts - 实现:
export type OpenClawPluginApi = { id: string; name: string; config: OpenClawConfig; registerTool: (tool: AnyAgentTool) => void; registerChannel: (plugin: ChannelPlugin) => void; // ... }; - 解决的问题: 为插件开发者提供简化接口
- 优点: 降低插件开发难度
- 缺陷: Facade 过于复杂(15 个方法)
2.2.3 Decorator Pattern(装饰器模式)
使用位置: src/agents/, src/plugins/hooks.ts
1. Hook Decorator
- 位置:
src/plugins/hooks.ts - 实现: Hook 系统装饰原始函数
export function invokeHook( hookName: PluginHookName, event: unknown, ctx: unknown ): Promise<void> { const handlers = getHookHandlers(hookName); for (const handler of handlers) { await handler(event, ctx); } } - 解决的问题: 在不修改原始代码的情况下增强功能
- 优点: 支持插件扩展核心功能
- 缺陷: Hook 顺序影响行为
2. Logging Decorator
- 位置:
src/infra/agent-events.ts - 实现: 装饰 Agent 事件,添加日志
export function emitAgentEvent(event: AgentEvent): void { log.debug(`[Agent Event] ${event.type}`, event); // ... } - 解决的问题: 统一事件日志记录
- 优点: 集中管理日志
2.2.4 Proxy Pattern(代理模式)
使用位置: 多个代理模块
1. Channel Proxy
- 位置:
src/telegram/proxy.ts,src/teleport/proxy.ts - 实现: 代理 WebSocket 连接和消息转发
- 解决的问题: 跨网络访问 Channel
- 优点: 支持远程节点
- 缺陷: 增加网络延迟
2. Exec Approval Proxy
- 位置:
src/gateway/exec-approval-manager.ts - 实现: 代理命令执行请求,添加批准逻辑
export class ExecApprovalManager { async requestApproval(params: ExecRequestParams): Promise<ExecApproval>; } - 解决的问题: 安全执行命令
- 优点: 防止恶意执行
- 缺陷: 增加用户交互成本
2.3 行为型模式
2.3.1 Strategy Pattern(策略模式)
使用位置: src/agents/model-selection.ts, src/agents/model-fallback.ts
1. Model Selection Strategy
- 位置:
src/agents/model-selection.ts(12.3KB) - 实现:
export function resolveAgentModelPrimary( config: OpenClawConfig, agentId?: string ): string { // 策略: 1. Agent-specific > 2. Default > 3. Env var } export function resolveThinkingDefault( config: OpenClawConfig ): ThinkLevel { // 策略: 1. Model support > 2. Config > 3. Default } - 解决的问题: 根据不同情况选择模型
- 优点: 灵活的模型选择策略
- 缺陷: 策略逻辑复杂
2. Auth Profile Rotation Strategy
- 位置:
src/agents/auth-profiles.ts - 实现: 支持轮询、最近使用、优先级等策略
export function resolveAuthProfileOrder( profiles: AuthProfile[] ): AuthProfile[] { // 策略: Round-robin / Last-used / Priority } - 解决的问题: 认证配置轮换
- 优点: 支持负载均衡
- 缺陷: 策略选择不透明
2.3.2 Observer Pattern(观察者模式)
使用位置: src/plugins/hooks.ts, src/infra/agent-events.ts
1. Hook Observer
- 位置:
src/plugins/hooks.ts(14KB) - 实现:
export type PluginHookRegistration = { pluginId: string; hookName: PluginHookName; handler: PluginHookHandlerMap[PluginHookName]; priority?: number; }; export function registerHook( events: string | string[], handler: InternalHookHandler, opts?: OpenClawPluginHookOptions ): void; - 解决的问题: 插件响应系统事件
- 优点: 松耦合,支持多观察者
- 缺陷: Hook 调用顺序影响结果
2. Agent Event Observer
- 位置:
src/infra/agent-events.ts - 实现:
export type AgentEvent = { type: string; agentId?: string; sessionKey?: string; data?: unknown; }; export function emitAgentEvent(event: AgentEvent): void; export function onAgentEvent( handler: (event: AgentEvent) => void ): () => void; - 解决的问题: 监听 Agent 生命周期事件
- 优点: 支持事件聚合
- 缺陷: 内存泄漏风险(未注销监听器)
2.3.3 Command Pattern(命令模式)
使用位置: src/commands/, src/process/
1. CLI Command
- 位置:
src/commands/agent.ts,src/commands/message.ts - 实现: 使用 Commander.js 封装命令
export async function agentCommand( opts: AgentCommandOpts ): Promise<void> { // 命令执行逻辑 } - 解决的问题: 命令参数解析和执行
- 优点: 支持命令组合
- 缺陷: 错误处理不统一
2. Exec Command
- 位置:
src/process/exec.ts,src/agents/bash-tools.exec.ts - 实现: 封装 Shell 命令执行
export async function runExecProcess( command: string, opts: ExecOptions ): Promise<ExecResult>; - 解决的问题: 统一命令执行接口
- 优点: 支持批准和限流
- 缺陷: 进程管理复杂
2.3.4 Template Method Pattern(模板方法模式)
使用位置: src/agents/pi-embedded.ts, src/auto-reply/reply/agent-runner-execution.ts
1. Agent Runner Template
- 位置:
src/auto-reply/reply/agent-runner-execution.ts(26.5KB) - 实现: 定义 Agent 执行的固定流程
export async function runReplyAgent( opts: ReplyAgentOpts ): Promise<void> { // 1. Prepare session // 2. Load skills // 3. Resolve model // 4. Run agent // 5. Handle compaction // 6. Deliver response } - 解决的问题: 统一 Agent 执行流程
- 优点: 保证执行顺序
- 缺陷: 流程复杂,难以修改
2. Channel Template
- 位置:
src/channels/plugins/types.ts - 实现: 定义通道必须实现的方法
export type ChannelPlugin = { id: string; name: string; normalizeIncoming: (raw: unknown) => IncomingMessage; send: (payload: SendPayload) => Promise<void>; // ... }; - 解决的问题: 统一通道接口
- 优点: 支持通道扩展
- 缺陷: 接口过于宽松
2.3.5 Chain of Responsibility Pattern(责任链模式)
使用位置: src/plugins/hooks.ts, src/infra/exec-approvals.ts
1. Hook Chain
- 位置:
src/plugins/hooks.ts - 实现: Hook 处理器链
export async function invokeHook( hookName: PluginHookName, event: unknown, ctx: unknown ): Promise<void> { const handlers = getHookHandlers(hookName); for (const handler of handlers) { const result = await handler(event, ctx); if (result?.block) break; // 提前终止 } } - 解决的问题: 多个插件处理同一事件
- 优点: 支持插件协作
- 缺陷: 链过长影响性能
2. Exec Approval Chain
- 位置:
src/infra/exec-approvals.ts - 实现: 批准请求处理链
export async function evaluateExecAllowlist( command: string, opts: ExecSecurity ): Promise<boolean>; - 解决的问题: 多级批准检查
- 优点: 安全性高
- 缺陷: 用户交互复杂
2.4 架构层级模式
2.4.1 MVC / MVP / MVVM 变体
OpenClaw 采用类似 MVC 的架构,但针对 CLI/Gateway 场景进行了调整:
Model (数据层)
├── 配置管理 (src/config/)
├── 会话存储 (src/config/sessions.ts)
└── 插件状态 (src/plugins/config-state.ts)
View (表现层)
├── CLI 接口 (src/cli/, src/commands/)
├── WebSocket Gateway (src/gateway/)
├── 浏览器 UI (src/browser/)
└── 原生应用 (apps/macos/, apps/ios/, apps/android/)
Controller (控制层)
├── Agent Runner (src/agents/cli-runner.ts, pi-embedded.ts)
├── Auto-Reply (src/auto-reply/)
├── Channel Manager (src/channels/)
└── Plugin Registry (src/plugins/)
特点:
- ✅ 分层清晰,职责分离
- ✅ Gateway 作为 Controller 统一协调
- ⚠️ View 和 Controller 耦合较紧密(Gateway 既处理 WebSocket 又调用 Agent)
2.4.2 Plugin Architecture(插件架构)
位置: 全局架构核心
实现:
Core System (OpenClaw)
├── Plugin Loader
├── Plugin Registry
├── Hook System
└── Plugin API
Plugins (Extensions)
├── Channel Plugins (telegram, discord, slack, ...)
├── Provider Plugins (google-gemini-cli-auth, minimax-portal-auth, ...)
├── Tool Plugins (memory-lancedb, voice-call, ...)
└── Service Plugins (diagnostics-otel, ...)
优点:
- ✅ 高度可扩展
- ✅ 插件独立开发
- ✅ 热加载支持(部分)
缺陷:
- ⚠️ 插件间依赖不明确
- ⚠️ 缺乏沙箱隔离
- ⚠️ 插件错误可能影响核心系统
2.4.3 Event-Driven Architecture(事件驱动架构)
位置: src/plugins/hooks.ts, src/infra/agent-events.ts
实现:
Event Sources
├── Agent Events (agent_start, agent_end, ...)
├── Gateway Events (gateway_start, gateway_stop, ...)
├── Message Events (message_received, message_sent, ...)
└── Tool Events (before_tool_call, after_tool_call, ...)
Event Bus (Hook System)
├── Hook Registry
├── Hook Dispatcher
└── Hook Prioritizer
Event Handlers (Plugins)
├── memory-lancedb (persist messages)
├── voice-call (handle tool calls)
└── diagnostics-otel (log events)
优点:
- ✅ 松耦合
- ✅ 支持异步处理
- ✅ 易于扩展
缺陷:
- ⚠️ 事件顺序不可控
- ⚠️ 错误处理复杂
- ⚠️ 调试困难
2.4.4 Microservices-like Architecture(微服务类架构)
位置: src/gateway/, src/process/, src/browser/
实现:
Gateway (主服务)
├── WebSocket Server
├── HTTP Server
└── Plugin Manager
Sidecars (辅助服务)
├── Browser Control Server (src/browser/server.ts)
├── Canvas Host (src/canvas-host/)
├── Exec Approval Server (src/gateway/exec-approval-*.ts)
└── Node Registry (src/gateway/node-registry.ts)
Nodes (远程节点)
├── macOS Node (apps/macos/)
├── iOS Node (apps/ios/)
├── Android Node (apps/android/)
└── Browser Node
优点:
- ✅ 服务独立部署
- ✅ 支持横向扩展
- ✅ 故障隔离
缺陷:
- ⚠️ 服务间通信复杂(WebSocket + IPC)
- ⚠️ 一致性难以保证
- ⚠️ 调试困难
2.5 可以引入但尚未使用的设计模式
2.5.1 Repository Pattern(仓储模式)
当前问题:
- 配置和会话数据分散在多个文件
- 缺乏统一的数据访问层
- 难以更换存储后端
建议:
// src/repositories/SessionRepository.ts
export interface ISessionRepository {
findByKey(key: string): Promise<Session | null>;
save(session: Session): Promise<void>;
delete(key: string): Promise<void>;
list(agentId?: string): Promise<Session[]>;
}
export class FileSessionRepository implements ISessionRepository {
async findByKey(key: string): Promise<Session | null> {
// 从文件系统加载
}
}
export class SqliteSessionRepository implements ISessionRepository {
async findByKey(key: string): Promise<Session | null> {
// 从 SQLite 加载
}
}
预期收益:
- ✅ 数据访问逻辑统一
- ✅ 支持更换存储后端
- ✅ 便于测试
2.5.2 Dependency Injection(依赖注入)
当前问题:
- 大量使用全局单例
- 模块间耦合度高
- 难以测试
建议:
// src/di/Container.ts
export class DIContainer {
private services = new Map<string, any>();
register<T>(token: string, factory: () => T): void {
this.services.set(token, factory);
}
resolve<T>(token: string): T {
const factory = this.services.get(token);
if (!factory) throw new Error(`Service ${token} not found`);
return factory();
}
}
// 使用
container.register('logger', () => new Logger());
container.register('sessionRepository', () => new FileSessionRepository());
const logger = container.resolve<Logger>('logger');
预期收益:
- ✅ 降低耦合
- ✅ 易于测试
- ✅ 支持服务替换
2.5.3 Circuit Breaker Pattern(熔断器模式)
当前问题:
- 模型故障转移依赖重试
- 缺乏熔断机制
- 可能导致雪崩
建议:
// src/circuit-breaker/CircuitBreaker.ts
export class CircuitBreaker {
private state: 'closed' | 'open' | 'half-open' = 'closed';
private failureCount = 0;
private nextAttemptTime = 0;
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'open' && Date.now() < this.nextAttemptTime) {
throw new Error('Circuit breaker is open');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
this.state = 'closed';
}
private onFailure(): void {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'open';
this.nextAttemptTime = Date.now() + this.timeout;
}
}
}
预期收益:
- ✅ 防止雪崩
- ✅ 自动恢复
- ✅ 提高可用性
3. 可视化图表
3.1 系统架构图
3.2 核心业务流程图 - 消息处理流程
3.3 插件加载流程图
3.4 模型故障转移流程图
4. 二次开发与架构优化建议
4.1 建议 #1: 引入 Repository 模式统一数据访问层
问题分析:
当前 OpenClaw 的数据访问逻辑分散在多个模块中:
src/config/io.ts处理配置读写src/config/sessions.ts处理会话存储src/plugins/config-state.ts处理插件配置- 各通道插件自行处理状态存储
存在的问题:
- 代码重复: JSON 序列化/反序列化逻辑重复
- 难以测试: 数据访问与业务逻辑耦合
- 难以扩展: 无法轻松更换存储后端(如从文件系统迁移到数据库)
- 缺乏事务: 无法保证数据一致性
实施方案:
步骤 1: 定义 Repository 接口
// src/repositories/interfaces/IConfigRepository.ts
export interface IConfigRepository {
load(): Promise<OpenClawConfig>;
save(config: OpenClawConfig): Promise<void>;
update(updater: (config: OpenClawConfig) => OpenClawConfig): Promise<void>;
}
export interface ISessionRepository {
findByKey(sessionKey: string): Promise<Session | null>;
save(session: Session): Promise<void>;
delete(sessionKey: string): Promise<void>;
list(agentId?: string): Promise<Session[]>;
appendMessage(sessionKey: string, message: AgentMessage): Promise<void>;
}
export interface IPluginConfigRepository {
get(pluginId: string): Promise<PluginConfig>;
set(pluginId: string, config: PluginConfig): Promise<void>;
delete(pluginId: string): Promise<void>;
list(): Promise<Map<string, PluginConfig>>;
}
步骤 2: 实现文件系统版本
// src/repositories/filesystem/ConfigRepository.ts
export class FileConfigRepository implements IConfigRepository {
constructor(private configPath: string) {}
async load(): Promise<OpenClawConfig> {
const content = await fs.readFile(this.configPath, "utf-8");
return parseConfigJson5(content);
}
async save(config: OpenClawConfig): Promise<void> {
const content = formatConfig(config);
await fs.writeFile(this.configPath, content, "utf-8");
}
async update(
updater: (config: OpenClawConfig) => OpenClawConfig
): Promise<void> {
const config = await this.load();
const updated = updater(config);
await this.save(updated);
}
}
// src/repositories/filesystem/SessionRepository.ts
export class FileSessionRepository implements ISessionRepository {
constructor(private sessionsDir: string) {}
async findByKey(sessionKey: string): Promise<Session | null> {
const sessionFile = this.getSessionFilePath(sessionKey);
if (!(await fs.exists(sessionFile))) return null;
const content = await fs.readFile(sessionFile, "utf-8");
return JSON.parse(content) as Session;
}
async appendMessage(
sessionKey: string,
message: AgentMessage
): Promise<void> {
const sessionFile = this.getSessionFilePath(sessionKey);
await fs.appendFile(sessionFile, JSON.stringify(message) + "\n", "utf-8");
}
private getSessionFilePath(sessionKey: string): string {
return path.join(this.sessionsDir, `${sessionKey}.jsonl`);
}
}
步骤 3: 实现 SQLite 版本(可选)
// src/repositories/sqlite/SessionRepository.ts
import Database from "better-sqlite3";
export class SqliteSessionRepository implements ISessionRepository {
private db: Database.Database;
constructor(dbPath: string) {
this.db = new Database(dbPath);
this.db.pragma("journal_mode = WAL");
this.initSchema();
}
private initSchema(): void {
this.db.exec(`
CREATE TABLE IF NOT EXISTS sessions (
session_key TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
message_count INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_key TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT,
created_at INTEGER NOT NULL,
FOREIGN KEY (session_key) REFERENCES sessions(session_key)
);
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_key);
`);
}
async findByKey(sessionKey: string): Promise<Session | null> {
const stmt = this.db.prepare(`
SELECT * FROM sessions WHERE session_key = ?
`);
const row = stmt.get(sessionKey) as any;
if (!row) return null;
const messagesStmt = this.db.prepare(`
SELECT role, content, created_at FROM messages
WHERE session_key = ? ORDER BY id
`);
const messages = messagesStmt.all(sessionKey) as AgentMessage[];
return {
sessionKey: row.session_key,
agentId: row.agent_id,
createdAt: row.created_at,
updatedAt: row.updated_at,
messages,
};
}
async appendMessage(
sessionKey: string,
message: AgentMessage
): Promise<void> {
const insertMsg = this.db.prepare(`
INSERT INTO messages (session_key, role, content, created_at)
VALUES (?, ?, ?, ?)
`);
insertMsg.run(
sessionKey,
message.role,
JSON.stringify(message.content),
Date.now()
);
const updateSession = this.db.prepare(`
UPDATE sessions SET
updated_at = ?,
message_count = message_count + 1
WHERE session_key = ?
`);
updateSession.run(Date.now(), sessionKey);
}
}
步骤 4: 创建 Repository Factory
// src/repositories/RepositoryFactory.ts
export type StorageBackend = "filesystem" | "sqlite" | "postgres";
export interface RepositoryConfig {
backend: StorageBackend;
paths: {
config: string;
sessions: string;
plugins: string;
};
database?: {
path?: string;
url?: string;
};
}
export class RepositoryFactory {
static createConfigRepository(
config: RepositoryConfig
): IConfigRepository {
if (config.backend === "filesystem") {
return new FileConfigRepository(config.paths.config);
}
// ... 其他实现
throw new Error(`Unsupported backend: ${config.backend}`);
}
static createSessionRepository(
config: RepositoryConfig
): ISessionRepository {
if (config.backend === "filesystem") {
return new FileSessionRepository(config.paths.sessions);
}
if (config.backend === "sqlite") {
return new SqliteSessionRepository(config.database?.path!);
}
throw new Error(`Unsupported backend: ${config.backend}`);
}
}
步骤 5: 迁移现有代码
// src/config/io.ts (迁移后)
import { RepositoryFactory } from "../repositories/RepositoryFactory.js";
let configRepository: IConfigRepository | null = null;
export function loadConfig(): OpenClawConfig {
if (!configRepository) {
const config = readConfigFileSnapshot();
const backend = config.parsed.storage?.backend || "filesystem";
const repoConfig = {
backend,
paths: {
config: CONFIG_PATH(),
sessions: SESSIONS_DIR(),
plugins: PLUGINS_DIR(),
},
};
configRepository = RepositoryFactory.createConfigRepository(repoConfig);
}
return configRepository.load();
}
export async function writeConfigFile(config: OpenClawConfig): Promise<void> {
if (!configRepository) {
await loadConfig(); // 初始化
}
return configRepository.save(config);
}
预期收益:
| 维度 | 收益 |
|---|---|
| 可维护性 | 数据访问逻辑集中,易于修改和调试 |
| 可测试性 | 可以使用 Mock Repository 进行单元测试 |
| 扩展性 | 支持轻松更换存储后端(文件 → SQLite → PostgreSQL) |
| 性能 | SQLite 版本支持索引和查询优化,提高大量会话场景下的性能 |
| 一致性 | Repository 可以实现事务支持,保证数据一致性 |
潜在风险与成本:
| 风险 | 缓解措施 |
|---|---|
| 迁移成本高 | 分阶段迁移,保持向后兼容 |
| 性能开销 | SQLite 版本需要基准测试,确保不降低性能 |
| 复杂性增加 | 提供清晰的文档和示例 |
| 测试覆盖 | 需要为新的 Repository 编写充分测试 |
实施优先级: 中等
预计工作量: 2-3 周
影响范围: src/config/, src/repositories/ (新增)
4.2 建议 #2: 引入依赖注入容器降低模块耦合
问题分析:
当前 OpenClaw 大量使用全局单例和直接导入,导致模块间耦合度高:
// 当前代码示例 (src/agents/cli-runner.ts)
import { loadConfig } from "../config/config.js"; // 直接导入
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
export async function runCliAgent(opts: CliAgentOpts) {
const config = loadConfig(); // 直接调用
ensureAuthProfileStore(); // 直接调用
// ...
}
存在的问题:
- 难以测试: 无法 Mock
loadConfig()和ensureAuthProfileStore() - 隐式依赖: 模块的依赖关系不明确
- 全局状态: 大量全局单例(Plugin Registry, Logger, 等)
- 循环依赖风险: 直接导入容易产生循环依赖
实施方案:
步骤 1: 选择轻量级 DI 库
推荐使用 inversify 或 awilix(无需修改 TypeScript 配置):
pnpm add awilix
步骤 2: 定义服务接口和实现
// src/di/interfaces/IConfigService.ts
export interface IConfigService {
load(): OpenClawConfig;
save(config: OpenClawConfig): Promise<void>;
}
// src/di/interfaces/IAuthService.ts
export interface IAuthService {
ensureStore(): void;
getProfile(provider: string): AuthProfile | null;
}
// src/di/interfaces/ILoggerService.ts
export interface ILoggerService {
debug(message: string, ...args: any[]): void;
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string, ...args: any[]): void;
}
步骤 3: 创建服务实现
// src/di/services/ConfigService.ts
import { injectable } from "tsyringe"; // 或使用 awilix
@injectable()
export class ConfigService implements IConfigService {
load(): OpenClawConfig {
return loadConfig();
}
async save(config: OpenClawConfig): Promise<void> {
return writeConfigFile(config);
}
}
// src/di/services/AuthService.ts
@injectable()
export class AuthService implements IAuthService {
ensureStore(): void {
return ensureAuthProfileStore();
}
getProfile(provider: string): AuthProfile | null {
// ...
}
}
// src/di/services/LoggerService.ts
@injectable()
export class LoggerService implements ILoggerService {
private logger: Logger;
constructor() {
this.logger = createSubsystemLogger("di");
}
debug(message: string, ...args: any[]): void {
this.logger.debug(message, ...args);
}
info(message: string, ...args: any[]): void {
this.logger.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
this.logger.warn(message, ...args);
}
error(message: string, ...args: any[]): void {
this.logger.error(message, ...args);
}
}
步骤 4: 创建 DI 容器
// src/di/container.ts
import { createContainer, asClass, asValue, asFunction } from "awilix";
import { ConfigService } from "./services/ConfigService.js";
import { AuthService } from "./services/AuthService.js";
import { LoggerService } from "./services/LoggerService.js";
import type { IConfigService, IAuthService, ILoggerService } from "./interfaces/index.js";
export interface IServiceContainer {
config: IConfigService;
auth: IAuthService;
logger: ILoggerService;
}
let container: awilix.AwilixContainer | null = null;
export function createDIContainer(): IServiceContainer {
if (container) {
return {
config: container.resolve<IConfigService>("configService"),
auth: container.resolve<IAuthService>("authService"),
logger: container.resolve<ILoggerService>("loggerService"),
};
}
container = createContainer({
injectionMode: awilix.InjectionMode.PROXY,
});
// 注册单例服务
container.register({
configService: asClass(ConfigService).singleton(),
authService: asClass(AuthService).singleton(),
loggerService: asClass(LoggerService).singleton(),
});
return {
config: container.resolve<IConfigService>("configService"),
auth: container.resolve<IAuthService>("authService"),
logger: container.resolve<ILoggerService>("loggerService"),
};
}
export function resetDIContainer(): void {
container = null;
}
步骤 5: 迁移现有模块
// src/agents/cli-runner.ts (迁移后)
import { inject, injectable } from "tsyringe";
import type { IConfigService, IAuthService, ILoggerService } from "../di/interfaces/index.js";
@injectable()
export class CliAgentRunner {
constructor(
@inject("IConfigService") private configService: IConfigService,
@inject("IAuthService") private authService: IAuthService,
@inject("ILoggerService") private logger: ILoggerService,
) {}
async run(opts: CliAgentOpts): Promise<void> {
this.logger.debug("Starting agent runner", { opts });
const config = this.configService.load();
this.authService.ensureStore();
// ...
}
}
// 调用方
import { createDIContainer } from "../di/container.js";
const container = createDIContainer();
const runner = new CliAgentRunner(
container.config,
container.auth,
container.logger
);
await runner.run(opts);
步骤 6: 支持函数式注入(更轻量)
如果不想引入 DI 库,可以使用简单的函数式注入:
// src/di/simple.ts
export type Deps<T> = {
[K in keyof T]: T[K];
};
export function injectDeps<T>(deps: Deps<T>): Deps<T> {
return deps;
}
// 使用
export async function runCliAgent(
opts: CliAgentOpts,
deps: Deps<{
config: () => OpenClawConfig;
ensureAuth: () => void;
logger: Logger;
}>
) {
const config = deps.config();
deps.ensureAuth();
deps.logger.info("Agent running...");
}
预期收益:
| 维度 | 收益 |
|---|---|
| 可测试性 | 可以轻松注入 Mock 服务,提高单元测试覆盖率 |
| 可维护性 | 依赖关系明确,便于重构 |
| 可扩展性 | 支持服务替换(如替换 Logger 实现) |
| 可复用性 | 服务可以在不同模块间共享 |
潜在风险与成本:
| 风险 | 缓解措施 |
|---|---|
| 学习成本 | 提供清晰的 DI 文档和示例 |
| 性能开销 | 使用轻量级 DI 库(如 awilix) |
| 过度设计 | 仅在核心模块使用 DI,保持简单 |
| 迁移成本 | 分阶段迁移,从新模块开始 |
实施优先级: 中等
预计工作量: 3-4 周
影响范围: 全局,但可渐进式迁移
4.3 建议 #3: 引入熔断器模式提高系统鲁棒性
问题分析:
当前 OpenClaw 的故障处理主要依赖重试和日志记录,缺乏熔断机制:
// src/agents/model-fallback.ts (当前实现)
export async function runWithModelFallback(
fn: () => Promise<void>,
opts: FallbackOpts
): Promise<void> {
let lastError: Error | null = null;
for (let i = 0; i < opts.maxAttempts; i++) {
try {
await fn();
return;
} catch (error) {
lastError = error as Error;
if (i < opts.maxAttempts - 1) {
await sleep(opts.backoffMs * (i + 1)); // 简单退避
}
}
}
throw lastError;
}
存在的问题:
- 雪崩风险: LLM 提供商故障时,大量请求同时重试,加剧负载
- 资源浪费: 持续重试失败的请求,浪费 CPU 和网络资源
- 用户体验差: 长时间等待失败
- 缺乏自动恢复: 即使服务恢复,也不会自动切换回正常状态
实施方案:
步骤 1: 定义熔断器接口
// src/circuit-breaker/ICircuitBreaker.ts
export enum CircuitBreakerState {
CLOSED = "closed",
OPEN = "open",
HALF_OPEN = "half_open",
}
export interface CircuitBreakerConfig {
failureThreshold: number; // 失败阈值(次数)
successThreshold: number; // 半开状态成功阈值(次数)
timeoutMs: number; // 熔断后等待时间
monitoringPeriodMs?: number; // 监控周期(可选)
}
export interface CircuitBreakerStats {
state: CircuitBreakerState;
failureCount: number;
successCount: number;
lastFailureTime?: number;
nextAttemptTime?: number;
}
export interface ICircuitBreaker {
execute<T>(fn: () => Promise<T>): Promise<T>;
getState(): CircuitBreakerState;
getStats(): CircuitBreakerStats;
reset(): void;
}
步骤 2: 实现熔断器
// src/circuit-breaker/CircuitBreaker.ts
export class CircuitBreaker implements ICircuitBreaker {
private state: CircuitBreakerState = CircuitBreakerState.CLOSED;
private failureCount = 0;
private successCount = 0;
private lastFailureTime?: number;
private nextAttemptTime?: number;
constructor(
private name: string,
private config: CircuitBreakerConfig,
private logger: Logger
) {}
async execute<T>(fn: () => Promise<T>): Promise<T> {
this.checkState();
if (this.state === CircuitBreakerState.OPEN) {
const waitTime = Math.max(0, (this.nextAttemptTime ?? 0) - Date.now());
throw new CircuitBreakerOpenError(
this.name,
`Circuit breaker "${this.name}" is open. Retry in ${waitTime}ms.`,
waitTime
);
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private checkState(): void {
if (
this.state === CircuitBreakerState.OPEN &&
this.nextAttemptTime &&
Date.now() >= this.nextAttemptTime
) {
this.state = CircuitBreakerState.HALF_OPEN;
this.successCount = 0;
this.logger.info(`Circuit breaker "${this.name}" transitioned to HALF_OPEN`);
}
}
private onSuccess(): void {
this.failureCount = 0;
if (this.state === CircuitBreakerState.HALF_OPEN) {
this.successCount++;
if (this.successCount >= this.config.successThreshold) {
this.state = CircuitBreakerState.CLOSED;
this.logger.info(
`Circuit breaker "${this.name}" recovered to CLOSED`
);
}
}
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (
this.state === CircuitBreakerState.HALF_OPEN ||
this.failureCount >= this.config.failureThreshold
) {
this.state = CircuitBreakerState.OPEN;
this.nextAttemptTime = Date.now() + this.config.timeoutMs;
this.logger.warn(
`Circuit breaker "${this.name}" opened. Next attempt in ${this.config.timeoutMs}ms`
);
}
}
getState(): CircuitBreakerState {
return this.state;
}
getStats(): CircuitBreakerStats {
return {
state: this.state,
failureCount: this.failureCount,
successCount: this.successCount,
lastFailureTime: this.lastFailureTime,
nextAttemptTime: this.nextAttemptTime,
};
}
reset(): void {
this.state = CircuitBreakerState.CLOSED;
this.failureCount = 0;
this.successCount = 0;
this.lastFailureTime = undefined;
this.nextAttemptTime = undefined;
this.logger.info(`Circuit breaker "${this.name}" reset`);
}
}
export class CircuitBreakerOpenError extends Error {
constructor(
public readonly name: string,
message: string,
public readonly waitTime: number
) {
super(message);
this.name = "CircuitBreakerOpenError";
}
}
步骤 3: 创建熔断器管理器
// src/circuit-breaker/CircuitBreakerManager.ts
export class CircuitBreakerManager {
private breakers = new Map<string, ICircuitBreaker>();
constructor(
private defaultConfig: CircuitBreakerConfig,
private logger: Logger
) {}
getOrCreate(name: string, config?: Partial<CircuitBreakerConfig>): ICircuitBreaker {
if (!this.breakers.has(name)) {
const breaker = new CircuitBreaker(
name,
{ ...this.defaultConfig, ...config },
this.logger
);
this.breakers.set(name, breaker);
}
return this.breakers.get(name)!;
}
get(name: string): ICircuitBreaker | undefined {
return this.breakers.get(name);
}
getAllStats(): Map<string, CircuitBreakerStats> {
const stats = new Map<string, CircuitBreakerStats>();
for (const [name, breaker] of this.breakers.entries()) {
stats.set(name, breaker.getStats());
}
return stats;
}
resetAll(): void {
for (const breaker of this.breakers.values()) {
breaker.reset();
}
}
}
步骤 4: 集成到 LLM 调用
// src/agents/llm-client.ts
import { CircuitBreakerManager, CircuitBreakerOpenError } from "../circuit-breaker/index.js";
export class LLMClient {
private circuitBreakers: CircuitBreakerManager;
constructor(
private modelCatalog: ModelCatalog,
private logger: Logger
) {
this.circuitBreakers = new CircuitBreakerManager(
{
failureThreshold: 3,
successThreshold: 2,
timeoutMs: 60000, // 1 分钟
},
this.logger
);
}
async chatCompletion(
provider: string,
model: string,
messages: Message[]
): Promise<ChatCompletionResponse> {
const breakerName = `${provider}:${model}`;
const breaker = this.circuitBreakers.getOrCreate(breakerName);
try {
return await breaker.execute(async () => {
const client = this.getClient(provider);
return await client.chat.completions.create({
model,
messages,
});
});
} catch (error) {
if (error instanceof CircuitBreakerOpenError) {
this.logger.warn(
`Circuit breaker open for ${breakerName}, skipping to next provider`,
{ waitTime: error.waitTime }
);
throw new CircuitBreakerSkippedError(breakerName, error.waitTime);
}
throw error;
}
}
// 获取熔断器统计(用于 Gateway 端点)
getCircuitBreakerStats(): Map<string, CircuitBreakerStats> {
return this.circuitBreakers.getAllStats();
}
}
步骤 5: 添加 Gateway 端点监控熔断器
// src/gateway/server-methods/diagnostics.ts
export function registerDiagnosticsHandlers(
gateway: GatewayServer,
llmClient: LLMClient
): void {
gateway.registerMethod("circuit-breakers.stats", async () => {
return {
breakers: Array.from(
llmClient.getCircuitBreakerStats().entries()
).map(([name, stats]) => ({ name, stats })),
};
});
gateway.registerMethod("circuit-breakers.reset", async ({ name }) => {
if (name) {
llmClient.resetCircuitBreaker(name);
} else {
llmClient.resetAllCircuitBreakers();
}
return { success: true };
});
}
步骤 6: 可视化熔断器状态(Control UI)
// src/browser/components/CircuitBreakerStatus.tsx
export function CircuitBreakerStatus() {
const [stats, setStats] = useState<Map<string, CircuitBreakerStats>>(new Map());
useEffect(() => {
const interval = setInterval(async () => {
const data = await gateway.call("circuit-breakers.stats");
setStats(new Map(data.breakers.map((b: any) => [b.name, b.stats])));
}, 5000);
return () => clearInterval(interval);
}, []);
return (
<div className="circuit-breaker-status">
<h3>Circuit Breakers</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>State</th>
<th>Failures</th>
<th>Next Attempt</th>
</tr>
</thead>
<tbody>
{Array.from(stats.entries()).map(([name, stat]) => (
<tr key={name}>
<td>{name}</td>
<td className={`state ${stat.state}`}>{stat.state}</td>
<td>{stat.failureCount}</td>
<td>
{stat.nextAttemptTime
? new Date(stat.nextAttemptTime).toLocaleTimeString()
: "-"}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
预期收益:
| 维度 | 收益 |
|---|---|
| 可用性 | 防止雪崩,自动隔离故障服务 |
| 性能 | 避免无效重试,节省资源 |
| 用户体验 | 快速失败,避免长时间等待 |
| 可观测性 | 熔断器状态可视化,便于监控 |
潜在风险与成本:
| 风险 | 缓解措施 |
|---|---|
| 误熔断 | 调整阈值,使用滑动窗口 |
| 恢复延迟 | 合理设置半开状态成功阈值 |
| 复杂性增加 | 提供简化配置,文档清晰 |
| 测试成本 | 编写单元测试和集成测试 |
实施优先级: 高
预计工作量: 1-2 周
影响范围: src/agents/, src/circuit-breaker/ (新增)
5. 项目目录结构解析
5.1 完整目录树
openclaw/
├── .agent/ # Agent 工作区(临时)
├── .agents/ # Agents 配置(临时)
├── .github/ # GitHub Actions 配置
│ ├── workflows/ # CI/CD 工作流
│ └── dependabot.yml # 依赖更新配置
├── .pi/ # PI Agent 缓存
├── apps/ # 原生应用
│ ├── android/ # Android 应用
│ │ ├── app/
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ ├── java/ # Kotlin/Java 代码
│ │ │ │ └── res/ # 资源文件
│ │ ├── build.gradle.kts
│ │ └── settings.gradle.kts
│ ├── ios/ # iOS 应用
│ │ ├── Sources/
│ │ │ ├── Calendar/ # 日历服务
│ │ │ ├── Camera/ # 相机服务
│ │ ├── Chat/
│ │ │ ├── Gateway/ # Gateway 连接
│ │ │ ├── Model/
│ │ │ │ └── NodeAppModel.swift # 主要数据模型
│ │ │ ├── Screen/ # 屏幕共享
│ │ │ ├── Voice/ # 语音功能
│ │ │ │ └── TalkModeManager.swift
│ │ └── fastlane/ # 自动化部署
│ ├── macos/ # macOS 应用
│ │ ├── Sources/OpenClaw/
│ │ │ ├── AppState.swift # 应用状态
│ │ │ ├── CanvasManager.swift # Canvas 管理
│ │ │ ├── ChannelsSettings/ # 渠道设置
│ │ │ ├── CronSettings/ # 定时任务
│ │ │ ├── DebugSettings/ # 调试设置
│ │ │ ├── ExecApprovals/ # 执行批准
│ │ │ └── Gateway/ # Gateway 连接
│ │ └── Package.swift
│ └── shared/ # 共享代码
│ └── OpenClawKit/ # Swift 共享库
│ └── Package.swift
├── assets/ # 静态资源
│ └── avatar-placeholder.svg
├── dist/ # 构建输出(Git 忽略)
├── docs/ # 文档
│ ├── index.md
│ ├── style.css
│ ├── brave-search.md
│ ├── ci.md
│ ├── logging.md
│ ├── network.md
│ └── ...
├── extensions/ # 插件目录
│ ├── bluebubbles/ # BlueBubbles 渠道插件
│ │ ├── index.ts
│ │ ├── openclaw.plugin.json
│ │ ├── src/
│ │ └── package.json
│ ├── discord/ # Discord 渠道插件
│ ├── slack/ # Slack 渠道插件
│ ├── telegram/ # Telegram 渠道插件
│ ├── whatsapp/ # WhatsApp 渠道插件
│ ├── memory-lancedb/ # LanceDB 内存插件
│ ├── voice-call/ # 语音通话插件
│ ├── google-gemini-cli-auth/ # Google Gemini OAuth 插件
│ └── ... (20+ plugins)
├── packages/ # NPM 包
│ ├── clawdbot/ # Clawdbot 别名包
│ └── moltbot/ # Moltbot 别名包
├── patches/ # NPM 补丁
├── scripts/ # 构建脚本
│ ├── build-and-run-mac.sh
│ ├── package-mac-app.sh
│ ├── check-ts-max-loc.ts
│ ├── protocol-gen.ts
│ ├── release-check.ts
│ └── ... (100+ scripts)
├── skills/ # Agent Skills
├── src/ # 核心源代码
│ ├── acp/ # Agent Client Protocol
│ │ ├── client.ts # ACP 客户端
│ │ ├── server.ts # ACP 服务器
│ │ └── translator.ts # 协议转换
│ ├── agents/ # Agent 系统
│ │ ├── agent-paths.ts # Agent 路径解析
│ │ ├── agent-scope.ts # Agent 作用域
│ │ ├── auth-profiles/ # 认证配置管理
│ │ ├── bash-tools.exec.ts # Bash 执行工具
│ │ ├── bash-tools.process.ts # 进程管理
│ │ ├── cli-runner.ts # CLI Agent 运行器
│ │ ├── cli-session.ts # CLI 会话
│ │ ├── compaction.ts # 会话压缩
│ │ ├── context.ts # 上下文管理
│ │ ├── model-catalog.ts # 模型目录
│ │ ├── model-fallback.ts # 模型故障转移
│ │ ├── model-selection.ts # 模型选择
│ │ ├── models-config.*.ts # 模型配置
│ │ ├── openclaw-tools.ts # OpenClaw 工具
│ │ ├── pi-embedded.ts # PI 嵌入式 Agent
│ │ ├── skills/ # Skills 管理
│ │ ├── subagent-registry.ts # 子 Agent 注册
│ │ └── tools/ # 工具定义
│ ├── auto-reply/ # 自动回复系统
│ │ ├── reply/
│ │ │ ├── agent-runner-execution.ts # Agent 执行协调
│ │ │ ├── agent-runner-helpers.ts # 辅助函数
│ │ │ ├── dispatcher.ts # 消息分发
│ │ │ ├── heartbeats.ts # 心跳机制
│ │ │ ├── memory-flush.ts # 内存刷新
│ │ │ └── triggers.ts # 触发器
│ │ └── directives.ts # 命令指令
│ ├── browser/ # 浏览器控制服务器
│ │ ├── server.ts
│ │ ├── routes/
│ │ │ ├── agent.*
│ │ │ ├── control.*
│ │ │ └── snapshot.*
│ │ └── agent-contract-*
│ ├── channels/ # 消息通道
│ │ ├── dock.ts # Channel Dock
│ │ ├── plugins/ # 通道插件注册
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── allowlist-match.ts
│ │ │ ├── channel-config.ts
│ │ │ └── directory-config.ts
│ │ └── registry.ts # 通道注册表
│ ├── cli/ # CLI 系统
│ │ ├── program.ts # Commander.js 程序
│ │ ├── run-main.ts # CLI 入口
│ │ ├── deps.ts # CLI 依赖
│ │ └── profile.ts # CLI 配置文件
│ ├── commands/ # CLI 命令
│ │ ├── agent.ts # Agent 命令
│ │ ├── agents.*.ts # Agents 子命令
│ │ ├── gateway.ts # Gateway 命令
│ │ ├── message.ts # 消息命令
│ │ ├── models.ts # 模型命令
│ │ ├── onboard.ts # 引导向导
│ │ ├── doctor.ts # 诊断工具
│ │ └── agent/ # Agent 子模块
│ ├── config/ # 配置管理
│ │ ├── io.ts # 配置 I/O
│ │ ├── paths.ts # 路径解析
│ │ ├── types.ts # 配置类型
│ │ ├── validation.ts # 配置验证
│ │ ├── zod-schema.ts # Zod Schema
│ │ ├── sessions.ts # 会话存储
│ │ ├── agent-dirs.ts # Agent 目录
│ │ ├── plugin-auto-enable.ts
│ │ └── runtime-overrides.ts
│ ├── gateway/ # Gateway 服务器
│ │ ├── server.impl.ts # Gateway 实现
│ │ ├── server-methods/ # Gateway 方法
│ │ ├── server-channels.ts # 通道管理
│ │ ├── server-cron.ts # Cron 服务
│ │ ├── server-discovery* # 服务发现
│ │ ├── server-model-catalog.ts
│ │ ├── server-plugins.ts # 插件加载
│ │ ├── server-reload.ts # 热重载
│ │ ├── exec-approval* # 执行批准
│ │ ├── node-registry.ts # 节点注册表
│ │ └── tls.ts # TLS 配置
│ ├── hooks/ # 内部 Hooks
│ ├── infra/ # 基础设施
│ │ ├── agent-events.ts # Agent 事件
│ │ ├── diagnostic-events.ts
│ │ ├── dotenv.ts # 环境变量加载
│ │ ├── env.ts # 环境变量规范化
│ │ ├── exec-approvals.ts # 执行批准逻辑
│ │ ├── heartbeat-events.ts
│ │ ├── node-shell.ts # Node.js Shell
│ │ ├── path-env.ts # PATH 环境变量
│ │ ├── restart.ts # 重启管理
│ │ ├── shell-env.ts # Shell 环境
│ │ └── update-startup.ts # 启动更新检查
│ ├── logging/ # 日志系统
│ │ ├── diagnostic.ts # 诊断日志
│ │ ├── subsystem.ts # 子系统日志
│ │ └── ...
│ ├── plugins/ # 插件系统
│ │ ├── loader.ts # 插件加载器
│ │ ├── registry.ts # 插件注册表
│ │ ├── hooks.ts # Hook 系统
│ │ ├── types.ts # 插件类型
│ │ ├── install.ts # 插件安装
│ │ ├── uninstall.ts # 插件卸载
│ │ ├── runtime/ # 插件运行时
│ │ ├── services.ts # 插件服务
│ │ └── commands.ts # 插件命令
│ ├── process/ # 进程管理
│ │ ├── child-process-bridge.ts
│ │ ├── exec.ts # 命令执行
│ │ ├── tau-rpc.ts # Tau RPC
│ │ └── command-queue.ts # 命令队列
│ ├── routing/ # 路由系统
│ │ └── session-key.ts # 会话密钥解析
│ ├── sessions/ # 会话管理
│ ├── tui/ # TUI 界面
│ ├── utils/ # 工具函数
│ ├── wizard/ # 引导向导
│ ├── agent-channel.ts
│ ├── channel-web.ts
│ ├── docker-setup.test.ts
│ ├── entry.ts # 应用入口
│ ├── extensionAPI.ts # 扩展 API
│ ├── globals.ts
│ ├── index.ts # 主导出
│ ├── logger.ts # 日志器
│ ├── polls.ts # 轮询
│ ├── runtime.ts # 运行时
│ ├── utils.ts # 工具函数
│ └── version.ts # 版本信息
├── test/ # 集成测试
│ ├── auto-reply.retry.test.ts
│ ├── gateway.multi.e2e.test.ts
│ ├── inbound-contract.providers.test.ts
│ ├── provider-timeout.e2e.test.ts
│ ├── global-setup.ts
│ ├── setup.ts
│ └── test-env.ts
├── ui/ # Web UI
│ ├── index.html
│ ├── package.json
│ ├── vite.config.ts
│ └── src/
├── vendor/ # 第三方库
├── .detect-secrets.cfg # 密钥检测配置
├── .dockerignore
├── .env.example # 环境变量示例
├── .gitignore
├── .npmrc
├── .oxfmtrc.jsonc # Oxfmt 格式化配置
├── .oxlintrc.json # Oxlint Lint 配置
├── .pre-commit-config.yaml # Pre-commit 钩子
├── .shellcheckrc # ShellCheck 配置
├── .swiftformat # Swift 格式化配置
├── .swiftlint.yml # Swift Lint 配置
├── AGENTS.md # Agent 文档
├── CHANGELOG.md # 变更日志
├── CONTRIBUTING.md # 贡献指南
├── Dockerfile # Docker 镜像
├── Dockerfile.sandbox # 沙箱 Docker 镜像
├── docker-compose.yml
├── fly.toml # Fly.io 部署配置
├── package.json # 主包配置
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md # 项目文档
├── tsconfig.json # TypeScript 配置
├── vitest.config.ts # Vitest 配置
└── openclaw.mjs # CLI 入口
5.2 关键目录说明
5.2.1 src/ - 核心业务代码
| 目录 | 职责 | 关键文件 |
|---|---|---|
src/agents/ |
Agent 系统、LLM 调用、工具执行 | cli-runner.ts, pi-embedded.ts, bash-tools.exec.ts |
src/auto-reply/ |
自动回复系统、消息分发 | reply/agent-runner-execution.ts, reply/dispatcher.ts |
src/channels/ |
消息通道管理、插件注册 | plugins/, dock.ts, registry.ts |
src/cli/ |
CLI 框架、命令行解析 | run-main.ts, program.ts |
src/commands/ |
CLI 命令实现 | agent.ts, gateway.ts, message.ts |
src/config/ |
配置管理、会话存储 | io.ts, sessions.ts, zod-schema.ts |
src/gateway/ |
Gateway WebSocket 服务器 | server.impl.ts, server-methods/ |
src/plugins/ |
插件系统、Hook 管理 | loader.ts, registry.ts, hooks.ts |
src/infra/ |
基础设施(日志、环境变量等) | agent-events.ts, dotenv.ts, exec-approvals.ts |
src/browser/ |
浏览器控制服务器 | server.ts, routes/ |
src/process/ |
进程管理、命令执行 | exec.ts, child-process-bridge.ts |
src/routing/ |
路由、会话密钥解析 | session-key.ts |
src/logging/ |
日志系统 | subsystem.ts, diagnostic.ts |
5.2.2 extensions/ - 插件目录
每个插件的标准结构:
extensions/{plugin-name}/
├── index.ts # 插件入口
├── openclaw.plugin.json # 插件清单
├── package.json # NPM 配置
├── README.md # 插件文档
├── src/ # 插件源码
│ └── index.ts # 插件实现
└── test/ # 插件测试(可选)
关键插件类型:
-
Channel Plugins - 消息通道插件
telegram/,discord/,slack/,whatsapp/,signal/,imessage/- 实现
ChannelPlugin接口
-
Provider Plugins - LLM 提供商插件
google-gemini-cli-auth/,minimax-portal-auth/,qwen-portal-auth/- 实现 OAuth 认证流程
-
Tool Plugins - 工具插件
memory-lancedb/(向量内存)voice-call/(语音通话)
-
Service Plugins - 服务插件
diagnostics-otel/(OpenTelemetry 诊断)
5.2.3 apps/ - 原生应用
| 应用 | 技术栈 | 职责 |
|---|---|---|
apps/macos/ |
Swift (SPM) | macOS 菜单栏应用、IPC 库 |
apps/ios/ |
Swift (XcodeGen) | iOS 应用、相机、屏幕共享、语音 |
apps/android/ |
Kotlin (Gradle) | Android 应用 |
apps/shared/ |
Swift (SPM) | 共享库(OpenClawKit) |
5.2.4 test/ - 集成测试
| 测试类型 | 文件模式 | 说明 |
|---|---|---|
| 单元测试 | src/**/*.test.ts |
模块级测试 |
| E2E 测试 | **/*.e2e.test.ts |
端到端测试 |
| Live 测试 | **/*.live.test.ts |
需要 API Key 的测试 |
| 集成测试 | test/*.test.ts |
跨模块测试 |
5.2.5 启动入口文件
| 文件 | 职责 |
|---|---|
openclaw.mjs |
NPM 包入口(CLI) |
src/entry.ts |
Node.js 进程入口 |
src/index.ts |
库主导出 |
src/cli/run-main.ts |
CLI 主逻辑 |
5.2.6 构建、部署相关文件
| 文件 | 用途 |
|---|---|
Dockerfile |
Docker 镜像构建 |
docker-compose.yml |
Docker Compose 配置 |
fly.toml |
Fly.io 部署配置 |
render.yaml |
Render 部署配置 |
scripts/package-mac-app.sh |
macOS 应用打包 |
scripts/make_appcast.sh |
Sparkle 更新清单 |
6. 新开发者实战入门指南
6.1 本地环境搭建
6.1.1 所需软件与版本
| 软件 | 版本要求 | 用途 |
|---|---|---|
| Node.js | ≥22.12.0 | 运行时环境 |
| pnpm | 10.23.0+ | 包管理器 |
| Git | 最新版本 | 版本控制 |
| TypeScript | 5.9.3+ | 类型检查 |
| Docker (可选) | 最新版本 | 容器化运行 |
可选工具:
| 软件 | 用途 |
|---|---|
| Bun | 快速运行 TypeScript(可选) |
| Swift | 开发 macOS/iOS 应用 |
| Xcode | iOS 应用开发(macOS) |
| Android Studio | Android 应用开发 |
6.1.2 依赖安装方式
方式 1: 全局安装(推荐)
# 使用 npm
npm install -g openclaw@latest
# 或使用 pnpm
pnpm add -g openclaw@latest
# 或使用 bun
bun add -g openclaw@latest
方式 2: 从源码安装
# 克隆仓库
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# 安装依赖
pnpm install
# 构建
pnpm build
# 全局链接
pnpm link --global
6.1.3 环境变量与配置
1. 复制环境变量模板:
cp .env.example .env
# 或复制到用户目录
cp .env.example ~/.openclaw/.env
2. 配置必要的环境变量:
编辑 .env 文件,设置以下变量:
# Gateway 认证(推荐)
OPENCLAW_GATEWAY_TOKEN=your-random-token-here
# LLM 提供商 API Key(至少设置一个)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
# 渠道 Bot Token(根据需要)
TELEGRAM_BOT_TOKEN=123456:ABCDEF...
DISCORD_BOT_TOKEN=...
SLACK_BOT_TOKEN=xoxb-...
# 可选:工具 API Key
BRAVE_API_KEY=...
PERPLEXITY_API_KEY=pplx-...
FIRECRAWL_API_KEY=...
3. 配置文件位置:
| 配置类型 | 默认路径 | 环境变量覆盖 |
|---|---|---|
| 配置文件 | ~/.openclaw/openclaw.json |
OPENCLAW_CONFIG_PATH |
| 状态目录 | ~/.openclaw/ |
OPENCLAW_STATE_DIR |
| 主目录 | ~ |
OPENCLAW_HOME |
6.2 从 0 到运行
6.2.1 克隆代码
# 克隆仓库
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# 切换到稳定分支(可选)
git checkout main
6.2.2 安装依赖
# 安装 Node.js 依赖
pnpm install
# 安装 UI 依赖(自动触发)
pnpm ui:build
# 验证安装
pnpm check
6.2.3 构建项目
# 完整构建
pnpm build
# 仅构建核心代码
pnpm tsdown
# 构建 UI
pnpm ui:build
# 构建 macOS 应用(需要 macOS)
pnpm mac:package
6.2.4 启动项目
方式 1: 运行引导向导(推荐新手)
# 启动向导(会自动安装 daemon)
openclaw onboard --install-daemon
方式 2: 手动启动 Gateway
# 启动 Gateway(默认端口 18789)
openclaw gateway --port 18789 --verbose
# 或使用开发模式
OPENCLAW_PROFILE=dev openclaw gateway --dev --port 18789
方式 3: 发送消息
# 通过 CLI 发送消息
openclaw message send --to +1234567890 --message "Hello from OpenClaw"
# 运行 Agent
openclaw agent --message "Ship checklist" --thinking high
方式 4: 使用 TUI
# 启动 TUI 界面
openclaw tui
# 开发模式 TUI
OPENCLAW_PROFILE=dev openclaw tui --dev
6.3 最小改动示例(Hello World)
6.3.1 新增一个简单的 CLI 命令
目标: 添加一个 openclaw hello 命令,输出 “Hello, OpenClaw!”
步骤 1: 创建命令文件
创建文件 src/commands/hello.ts:
// src/commands/hello.ts
import type { Command } from "commander";
export function helloCommand(): Command {
const cmd = new Command("hello");
cmd
.description("Say hello to OpenClaw")
.option("-n, --name <name>", "Your name", "World")
.action(async (opts) => {
console.log(`Hello, ${opts.name}!`);
console.log("Welcome to OpenClaw!");
});
return cmd;
}
步骤 2: 注册命令
编辑 src/cli/program.ts,找到 buildProgram() 函数,添加命令注册:
// src/cli/program.ts
import { helloCommand } from "../commands/hello.js"; // 新增导入
export function buildProgram(): Command {
const program = new Command();
program
.name("openclaw")
.version(VERSION)
.command("agent", agentCommand)
.command("gateway", gatewayCommand)
.command("hello", helloCommand()) // 新增命令
// ...
return program;
}
步骤 3: 测试命令
# 构建项目
pnpm build
# 测试命令
openclaw hello
# 输出: Hello, World!
openclaw hello --n Alice
# 输出: Hello, Alice!
6.3.2 新增一个简单的 Agent 工具
目标: 添加一个 get_current_time 工具,返回当前时间
步骤 1: 创建工具定义
创建文件 src/agents/tools/current-time.ts:
// src/agents/tools/current-time.ts
import type { AgentTool, AgentToolResult } from "@mariozechner/pi-agent-core";
export interface GetCurrentTimeParams {
timezone?: string;
format?: "iso" | "locale";
}
export const getCurrentTimeTool: AgentTool<GetCurrentTimeParams> = {
name: "get_current_time",
description: "Get the current time, optionally with timezone and format options",
parameters: {
type: "object",
properties: {
timezone: {
type: "string",
description: "Timezone (e.g., 'UTC', 'America/New_York')",
},
format: {
type: "string",
enum: ["iso", "locale"],
description: "Time format: 'iso' (ISO 8601) or 'locale' (localized)",
},
},
},
execute: async (params): Promise<AgentToolResult> => {
const { timezone, format = "locale" } = params || {};
let date = new Date();
// Apply timezone if provided
if (timezone) {
date = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
}
// Format time
let timeStr: string;
if (format === "iso") {
timeStr = date.toISOString();
} else {
timeStr = date.toLocaleString();
}
return {
content: [
{
type: "text",
text: `Current time: ${timeStr}${timezone ? ` (${timezone})` : ""}`,
},
],
};
},
};
步骤 2: 注册工具
编辑 src/agents/openclaw-tools.ts,添加工具注册:
// src/agents/openclaw-tools.ts
import { getCurrentTimeTool } from "./tools/current-time.js"; // 新增导入
export function registerOpenClawTools(tools: Map<string, AgentTool>): void {
// ... 现有工具注册
// 新增工具
tools.set(getCurrentTimeTool.name, getCurrentTimeTool);
}
步骤 3: 测试工具
# 构建项目
pnpm build
# 运行 Agent 并测试工具
openclaw agent --message "What time is it?"
# Agent 应该会调用 get_current_time 工具
6.3.3 创建一个简单的插件
目标: 创建一个 hello-world 插件,注册一个 /hello 命令
步骤 1: 创建插件目录
mkdir -p extensions/hello-world
cd extensions/hello-world
步骤 2: 创建插件清单
创建 extensions/hello-world/openclaw.plugin.json:
{
"id": "hello-world",
"name": "Hello World",
"version": "0.1.0",
"description": "A simple hello world plugin",
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}
步骤 3: 创建插件入口
创建 extensions/hello-world/index.ts:
// extensions/hello-world/index.ts
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
export default function register(api: OpenClawPluginApi) {
api.registerCommand({
name: "hello",
description: "Say hello from the plugin",
acceptsArgs: false,
requireAuth: false,
handler: async (ctx) => {
return {
text: `Hello, ${ctx.from || "there"}! This is from the hello-world plugin.`,
};
},
});
api.info("Hello World plugin registered");
}
步骤 4: 创建 package.json
创建 extensions/hello-world/package.json:
{
"name": "openclaw-plugin-hello-world",
"version": "0.1.0",
"description": "Hello World plugin for OpenClaw",
"type": "module",
"main": "index.ts",
"exports": {
".": "./index.ts"
},
"keywords": ["openclaw", "plugin"],
"license": "MIT"
}
步骤 5: 创建 README
创建 extensions/hello-world/README.md:
# Hello World Plugin
A simple plugin that adds a `/hello` command.
## Installation
The plugin is bundled with OpenClaw, so it's automatically loaded.
## Usage
Send `/hello` in any supported channel to see the greeting.
步骤 6: 测试插件
# 重新构建项目
pnpm build
# 启动 Gateway
openclaw gateway --dev
# 在任何支持的消息通道中发送 /hello
# 例如,使用 WebChat: http://localhost:18789
6.4 开发工作流
6.4.1 运行测试
# 运行所有单元测试
pnpm test:fast
# 运行所有测试(包括 E2E)
pnpm test
# 运行覆盖率测试
pnpm test:coverage
# 运行特定测试文件
pnpm vitest src/agents/cli-runner.test.ts
6.4.2 代码格式化与 Lint
# 格式化代码
pnpm format
# 检查格式
pnpm format:check
# Lint 代码
pnpm lint
# 自动修复
pnpm lint:fix
# 运行所有检查
pnpm check
6.4.3 调试技巧
1. 启用详细日志
openclaw gateway --verbose
2. 设置日志级别
export LOG_LEVEL=debug
openclaw gateway
3. 使用 Node.js 调试器
# 启动调试模式
node --inspect-brk openclaw.mjs gateway
# 在 VS Code 中连接调试器
# 使用 launch.json 配置:
{
"type": "node",
"request": "attach",
"name": "Attach to OpenClaw",
"port": 9229
}
4. 查看 Gateway 日志
# 实时查看日志
tail -f ~/.openclaw/gateway.log
# 或查看诊断日志
openclaw doctor
6.5 常见问题
Q1: 如何添加新的消息通道?
A: 参考现有通道插件实现:
- 在
extensions/下创建新目录 - 实现
ChannelPlugin接口 - 注册到
src/channels/plugins/ - 配置认证和参数
示例:参考 extensions/telegram/ 或 extensions/discord/
Q2: 如何添加新的 LLM 提供商?
A: 参考现有提供商配置:
- 在
src/agents/models-config.providers.ts添加提供商配置 - 实现认证流程(如需要)
- 添加模型映射
示例:参考 Google Gemini 配置
Q3: 如何调试插件?
A:
- 使用
api.logger.info()输出日志 - 设置
OPENCLAW_LOG_LEVEL=debug - 使用 VS Code 调试器断点
- 检查
~/.openclaw/plugins/{plugin-id}.json配置
Q4: 如何提交代码?
A:
# 1. 创建分支
git checkout -b feature/my-feature
# 2. 提交代码
git add .
git commit -m "feat: add my feature"
# 3. 推送到远程
git push origin feature/my-feature
# 4. 创建 Pull Request
# 参考 CONTRIBUTING.md
附录
A. 技术栈清单
| 类别 | 技术 |
|---|---|
| 运行时 | Node.js 22+, Bun (可选) |
| 语言 | TypeScript 5.9, Swift, Kotlin |
| 包管理 | pnpm 10.23.0+ |
| CLI 框架 | Commander.js 14.0+ |
| LLM 核心 | @mariozechner/pi-agent-core 0.52.10 |
| 协议 | @agentclientprotocol/sdk 0.14.1 |
| WebSocket | ws 8.19.0 |
| HTTP 服务器 | Express 5.2.1 |
| 验证 | Zod 4.3.6 |
| 日志 | tslog 4.10.2 |
| 测试 | Vitest 4.0.18 |
| 格式化 | Oxfmt 0.32.0 |
| Lint | Oxlint 1.47.0 |
| 构建 | tsdown 0.20.3, rolldown 1.0.0-rc.4 |
| UI 构建 | Vite, Lit 3.3.2 |
B. 关键依赖版本
{
"@agentclientprotocol/sdk": "0.14.1",
"@mariozechner/pi-agent-core": "0.52.10",
"@mariozechner/pi-ai": "0.52.10",
"@mariozechner/pi-coding-agent": "0.52.10",
"@mariozechner/pi-tui": "0.52.10",
"@whiskeysockets/baileys": "7.0.0-rc.9",
"@slack/bolt": "^4.6.0",
"@grammyjs/runner": "^2.0.3",
"grammy": "^1.40.0",
"sqlite-vec": "0.1.7-alpha.2",
"zod": "^4.3.6"
}
C. 参考资源
| 资源 | 链接 |
|---|---|
| 官方网站 | https://openclaw.ai |
| 文档 | https://docs.openclaw.ai |
| GitHub | https://github.com/openclaw/openclaw |
| Discord | https://discord.gg/clawd |
| DeepWiki | https://deepwiki.com/openclaw/openclaw |
结语
OpenClaw 是一个架构设计优秀、扩展性强的多渠道 AI 助手系统。本文档深入分析了项目的模块结构、设计模式、架构风格,并提出了可执行的优化建议。
项目亮点:
- ✅ 插件化架构,支持 20+ 消息通道
- ✅ 多 LLM 提供商支持与故障转移
- ✅ 跨平台支持(Node.js + macOS/iOS/Android)
- ✅ 完善的 Hook 系统
- ✅ 丰富的 Agent 工具
改进方向:
- ⚠️ 引入 Repository 模式统一数据访问
- ⚠️ 引入依赖注入降低模块耦合
- ⚠️ 引入熔断器提高系统鲁棒性
- ⚠️ 优化大文件拆分(减少单文件超过 20KB)
- ⚠️ 改进插件沙箱隔离
希望本文档能帮助新开发者快速上手,并为项目贡献者提供参考。
文档版本: 1.0.0
最后更新: 2026-02-14
作者: AI 技术分析师
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)