乐于分享
好东西不私藏

OpenClaw:整体架构与启动流程

OpenClaw:整体架构与启动流程
📢 导读 | 一个命令 openclaw gateway start 背后,到底发生了什么?我们从源码出发,拆解OpenClaw的启动流程与架构设计

前言

前面的文章我们学会了怎么装、怎么用、怎么写技能。但一个AI智能体框架为什么能7×24小时不挂?消息怎么被路由?技能怎么热加载?这些问题只有源码能给答案。

OpenClaw的代码库组织清晰、模块化程度高,是学习生产级Agent系统的绝佳范本。这一篇,我们从最顶层开始,看看一个守护进程(Gateway)是如何诞生的。

01 源码目录结构:一眼看懂模块职责

克隆代码后,先看 src/ 目录(核心 TypeScript 代码):
src/├── index.ts          # 入口:导出核心模块├── cli.ts            # CLI 命令行入口├── gateway/          # Gateway 守护进程核心│   ├── index.ts      # Gateway 启动/停止│   ├── server.ts     # HTTP/WebSocket 服务器│   ├── lifecycle.ts  # 进程生命周期管理│   └── ...├── agent/            # Agent 执行引擎│   ├── loop.ts       # 推理循环│   └── ...├── context/          # 上下文组装├── memory/           # 记忆系统├── skills/           # 技能加载与执行├── tools/            # 内置工具(文件、Shell、浏览器等)├── routing/          # 消息路由├── queue/            # 车道机制(并发控制)├── channels/         # 通道插件(钉钉、飞书等)├── plugins/          # 插件系统├── models/           # 模型路由与调用├── config/           # 配置管理├── logger.ts         # 日志└── utils/            # 通用工具

从命名就能看出职责边界。我们重点关注 启动流程,它会串联起这些模块。

02 入口文件:index.ts 与 cli.ts

2.1 包入口:index.ts

这个文件主要是导出模块,供其他代码或插件使用。不包含业务逻辑。

2.2 CLI 入口:cli.ts

当你在终端输入 openclaw gateway start,实际执行的是 CLI 逻辑。cli.ts 的核心是一个命令解析器(基于 commander):

import { program } from 'commander';program  .command('gateway')  .description('Manage the OpenClaw gateway')  .addCommand(    new Command('start')      .description('Start the gateway daemon')      .action(async () => {        // 1. 加载配置        // 2. 初始化日志        // 3. 启动 Gateway        await startGateway();      })  );

startGateway() 是 Gateway 启动的入口函数,位于 src/gateway/index.ts

03 配置加载:设计取舍

Gateway 启动前必须先读配置。配置来源有多个层级(优先级从高到低):

  • 命令行参数(如 --config

  • 环境变量(OPENCLAW_*

  • 项目目录 .openclaw/ 下的 config.yaml

  • 用户目录 ~/.openclaw/config.yaml

  • 内置默认配置

实现上,src/config/index.ts 中的 loadConfig() 函数会递归合并这些源。关键点:

  • 类型安全:使用 zod 校验配置结构,启动时立即报错。

  • 热加载:配置变更后触发 config:updated 事件,监听器(如 Gateway 路由)会重新初始化,无需重启。

// 简化逻辑exportasyncfunctionloadConfig(): Promise<Config> {const defaults = getDefaultConfig();const userConfig = awaitreadYaml(path.join(OPENCLAW_HOME'config.yaml'));const envConfig = loadEnvVars();const merged = merge(defaults, userConfig, envConfig);returnConfigSchema.parse(merged); // zod 校验}

04 Gateway 启动流程:一步一步

startGateway() 的执行顺序很关键。伪代码如下:

export async function startGateway() {  // 1. 加载配置  const config = await loadConfig();  // 2. 初始化日志系统(支持按级别、文件输出)  initLogger(config.logging);  // 3. 检查是否已有实例在运行(通过 PID 文件)  if (isGatewayRunning()) {    logger.info('Gateway is already running');    return;  }  // 4. 创建 HTTP/WebSocket 服务器  const server = createServer(config);  // 5. 加载所有通道插件  const channels = await loadChannels(config);  // 6. 加载所有技能(仅索引,不加载正文)  const skillIndex = await loadSkillIndex(config);  // 7. 启动内存存储(会话、记忆等)  await initStorage(config);  // 8. 启动消息队列(车道机制)  const queue = createMessageQueue(config);  // 9. 启动模型路由管理器  const modelRouter = createModelRouter(config);  // 10. 启动 HTTP 服务器,监听端口(默认 18789)  server.listen(config.gateway.port() => {    logger.info(`Gateway listening on port ${config.gateway.port}`);  });  // 11. 写 PID 文件,注册信号处理  writePidFile(process.pid);  setupSignalHandlers();}

每一步都可能抛出异常,所有错误都会被捕获并记录,确保进程不意外退出。

05 依赖注入与模块组织

OpenClaw 没有用复杂的 DI 框架,而是采用 工厂函数 + 显式传参 的方式。例如,创建 Agent 执行器时:

export function createAgentExecutor(options: {  modelRouter: ModelRouter;  toolRegistry: ToolRegistry;  skillLoader: SkillLoader;  memoryStore: MemoryStore;  logger: Logger;}) {  // 返回一个函数,该函数执行 Agent 循环  return async function executeAgent(context: MsgContext) {    // ...  };}

这种做法的好处是:

  • 依赖关系一目了然,方便测试(可 mock)

  • 不依赖全局状态,模块可独立复用

缺点是需要手动传递依赖链条,但随着模块划分细致,这并不可怕。

06 进程生命周期与守护

Gateway 设计为“常驻进程”。但如何保证它退出时能清理资源?如何防止僵尸进程?关键在信号处理:

function setupSignalHandlers() {  process.on('SIGINT'async () => {    logger.info('Received SIGINT, shutting down gracefully');    await shutdownGateway();    process.exit(0);  });  process.on('SIGTERM'async () => {    logger.info('Received SIGTERM, shutting down');    await shutdownGateway();    process.exit(0);  });}

shutdownGateway() 会:

  • 停止接收新请求(关闭 HTTP 服务器)

  • 等待正在处理的消息完成(设置超时)

  • 关闭数据库/文件句柄

  • 删除 PID 文件

生产环境常用 pm2 或 systemd 管理,Gateway 本身不实现自动重启。

最后

这一篇我们从顶层视角,搞懂了 OpenClaw 启动时都做了什么:加载配置、初始化日志、启动服务器、加载插件、构建依赖。这种“模块化+工厂函数+事件驱动”的设计,让系统既灵活又稳定。
~~如果觉得有帮助,欢迎关注点赞~~