乐于分享
好东西不私藏

详细拆解OpenClaw源码,架构简单得让我离谱!

详细拆解OpenClaw源码,架构简单得让我离谱!

hi,我是超级个体实践者周知,以下是详细拆解图


先别急着聊架构。

打开OpenClaw的GitHub仓库,src/目录长这样:

src/
├── gateway/
│   └── server.impl.ts          ← 总调度中心,整个系统的心脏
├── agents/
│   ├── pi-embedded-runner/
│   │   ├── run.ts              ← Agent循环主入口
│   │   ├── model.ts            ← 模型选择+自动换备胎
│   │   ├── system-prompt.ts    ← 系统提示词组装
│   │   ├── compact.ts          ← 记忆瘦身(聊太多了就压缩)
│   │   └── lanes.ts            ← 排队系统(一个一个来)
│   ├── tool-policy.ts          ← 工具权限控制
│   ├── sandbox.ts              ← 安全隔离间(代码在小黑屋里跑)
│   └── context-window-guard.ts ← 记忆容量警报(快撑不下了就喊停)
├── process/
│   └── command-queue.ts        ← 排队系统的核心代码
├── config/
│   ├── sessions.ts             ← 会话管理
│   └── paths.ts                ← 文件存在哪
├── cli/                        ← 命令行入口
├── commands/                   ← 用户命令处理
└── extensions/                 ← 插件渠道(Matrix/Teams/飞书...)

整个项目,TypeScript写的。

Node 22+运行,pnpm构建,可选Bun加速。测试文件就放在源码旁边,改完代码顺手就能跑测试。

截至2026年2月底,GitHub上24万+star,单周访问量突破240万,378位贡献者。创始人Peter Steinberger一个月提交了6600+次代码更新。

他说过一句被广泛引用的话:”I ship code I don’t read。”

翻开代码——核心就这几根分支。

下面详细拆。


一、三层蛋糕:Channel → Gateway → Runtime

OpenClaw的架构图,画出来就一句话:

车轮结构。

Gateway是轮毂(中心),所有消息渠道、Agent、工具都是辐条,全部连到中间这一个点上。

WhatsApp / Telegram / Slack / Discord / Signal / iMessage / ...
                        │
                        ▼
              ┌─────────────────┐
              │    Gateway      │
              │  ws://127.0.0.1 │
              │     :18789      │
              └────────┬────────┘
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
     Agent Runtime    CLI        WebChat
     (Pi Agent Core)

三层分得很干净。

第一层,Channel Adapters——你可以理解为”翻译官”。负责把15+个消息平台的消息翻译成OpenClaw听得懂的统一格式。WhatsApp、Telegram、Discord、Slack……每个平台发消息的方式都不一样。翻译官把这些差异全部抹平。

核心渠道的代码在src/里,扩展渠道(Matrix、Google Chat、微软Teams、LINE、飞书)放在extensions/目录。

每个平台的消息格式不一样。媒体处理不一样。登录方式不一样。限速策略也不一样。翻译官把这些差异全部抹平,输出统一的内部格式。

想加一个新平台?写一个”翻译包”就行,系统会自动发现它。

翻译官还自带”心跳监测”。某个平台的连接断了?自动重连。不用你操心。

第二层,Gateway——总调度中心。所有消息进来都先过它,它决定消息往哪送。

第三层,Agent Runtime——干活的大脑。接到任务后,调用AI模型思考、执行工具、返回结果。

为什么选TypeScript?

VISION文档原话说得很直白:OpenClaw的定位是”编排系统”——把各种提示词、工具、协议、平台粘在一起。

编排系统要什么?连接速度。能快速接入一个又一个新平台。

TypeScript的生态刚好擅长干这个。

说白了,这不是一个ML项目。这是一个胶水项目。胶水的核心竞争力就是粘得快、粘得多。


二、Gateway:一个WebSocket撑起所有

打开src/gateway/server.impl.ts

Gateway启动做了什么?就一件事:在本机127.0.0.1:18789开了一个WebSocket长连接服务。

WebSocket你可以理解为”永不挂断的电话线”——消息来了立刻送达,不用反复拨号。

所有进出的消息都有格式校验。格式不对的直接拦住。

就这么简单?就这么简单。

Gateway的设计哲学:能简单就绝不复杂。

它不装。不搞分布式,不搞集群。一个Gateway进程管你所有的消息渠道、所有的Agent会话、所有的工具执行。

消息进来了,Gateway怎么知道该送给谁?靠session key——每个对话都有一个唯一编号。这个编号的设计很巧:

agent:main:main              ← 主会话
agent:main:telegram:dm:xxx   ← Telegram某用户的私聊
agent:support:slack:ch:yyy   ← Slack某频道的support agent

你看出来了吗?

key的结构天然就带着”谁在说话”和”从哪个平台来”的信息。就像快递单号——看一眼编号就知道发件城市和快递类型。

再配合工具的”白名单/黑名单”机制,权限控制就有了。

不需要另外搞一套复杂的权限管理系统。session key自己就是路由表+权限表。

Gateway还支持”热更新”。你改了配置文件,不用重启整个系统,Gateway自己会感知到变化并重新加载。

一个Gateway还能同时管多个Agent。比如一个专门回客服问题,一个专门做日程管理。每个Agent有自己独立的工作空间,互不干扰。


三、Lane Queue:为什么串行才是正确答案

这是OpenClaw最精巧的设计。

打开src/process/command-queue.ts

核心是一个”分车道排队”系统。

什么意思?每个用户会话(session)有自己的专属车道,车道里的任务严格排队,一个做完再做下一个。

实现方式:纯TypeScript + Promises(就是JS原生的异步机制)。

没有Redis。没有RabbitMQ。没有任何外部排队工具。

核心保证只有一句:”同一个用户对话,同一时刻,只能有一个任务在执行。”

怎么做到的?每个session key对应一条专属车道。消息进来,排到自己车道的队尾。车道内严格先来后到。不同车道可以同时跑——但同一条车道里,绝对一个一个来。

你可能觉得这太保守了。

对吧?2026年了,还搞串行?

但做过Agent开发的人都懂:让多个Agent同时操作同一份数据,就像让三个厨师同时炒一个锅——你加盐他加糖,最后出来的菜谁都没法吃。日志乱成一团,Bug根本没法查。

OpenClaw的回答很坚决:默认串行,显式并行。

只有明确标记为低风险、可并行的任务(比如定时任务),才会被分配到独立lane并行执行。

消息太多排不下怎么办?三种处理方式:


  • collect:先攒着,等前面的做完再处理


  • steer:引导到别的地方去


  • followup:追加到当前任务后面

来不及处理被跳过的消息,系统还会自动生成一段摘要塞回去——确保Agent不会漏掉关键信息。

这套方案的品味在哪里?能用最简单的方案解决问题,就绝不引入复杂的外部工具。 原生的Promise就够了,为什么要加Redis?


四、Agent Loop:一个循环跑到底

打开src/agents/pi-embedded-runner/run.ts

这个文件是Agent大脑的主入口。一条消息进来,要经过4层处理才能出结果:

agentRunner (接活)
  → agentRunnerExecution (排队调度)
    → runEmbeddedPiAgent (开始思考)
      → runEmbeddedAttempt (单次调用AI模型)

循环什么时候停?规则极简:AI模型回复了一段纯文字(没有说”我要用某个工具”)= 活干完了。

没有硬性的”最多跑几步”的限制,靠超时机制兜底。

Agent的”人设”怎么组装的?看src/agents/system-prompt.ts。它把4样东西拼在一起,交给AI模型:

  1. 1

    AGENTS.md — 行为规矩。”你能做什么、不能做什么”

  2. 2

    SOUL.md — 性格设定。”你说话是什么语气、什么风格”

  3. 3

    Skills prompt — 当前这轮对话需要的技能

  4. 4

    运行时上下文 — 当前环境的背景信息

模型怎么选?src/agents/pi-embedded-runner/model.ts里有个”模型注册表”,记录了每个模型的能力、价格、窗口大小。找不到合适的模型就报错切备用。

API key(调用AI模型的钥匙)也有”冷却机制”。一把钥匙用超了限额?自动换下一把。配置文件里可以存多组钥匙轮着用。

记忆容量的防护在src/agents/context-window-guard.ts


  • 快满了 → 先打个警告日志


  • 真满了 → 直接喊停这一轮,换个方式重来

Agent死循环怎么办?

Agent陷入死循环怎么办?

做过Agent的人都知道,这是最常见的翻车方式——Agent反复调同一个工具,像仓鼠跑轮子一样停不下来。

OpenClaw内置了三个”死循环探测器”:


  • 重复检测:同一个工具+同样的参数反复调用——发现了就喊停


  • 原地踏步检测:一直在查询,但结果没有任何变化——喊停


  • 乒乓检测:A工具→B工具→A工具→B工具,来回弹——喊停

三个探测器同时跑,超过次数阈值就告警或直接熔断。

还有一个值得提的模块:Canvas

简单说,Agent不仅能跟你聊天,还能”画”界面给你看。

Canvas跑在独立的进程上,和Gateway分开——Canvas崩了不影响聊天,互不拖累。

Agent生成的HTML界面,通过WebSocket实时推送到你的浏览器上。说白了,Agent能给你临时搭一个小网页应用出来。

整个Agent干活的过程也是”边想边说”的——AI模型一边生成回复,一边发现需要调用工具就立刻去执行,执行结果立刻喂回去继续生成。思考和行动同步进行。一轮结束后,所有的对话记录、工具调用、执行结果都会保存到本地磁盘。


五、Memory:Markdown就是数据库

OpenClaw的记忆系统,走了一条”反潮流”的路。

别人第一天就上向量数据库,搞各种花哨的AI检索管道。

OpenClaw说:Markdown文件就是我的记忆库。

双层结构:

文件
作用
短期
memory/2026-03-01.md
每天的流水日志,只追加不修改
长期
MEMORY.md
整理过的重要记忆

每次开始新对话,Agent会自动加载今天和昨天的日志。为什么是两天?

保持即时上下文连续性。又不撑爆上下文窗口。再往前的日志不会自动加载。

检索怎么做?两条路同时搜,然后合并结果。

第一条路:语义搜索——理解你话里的”意思”。你说”上次聊的那个项目”,它能找到相关内容,即使原文里没出现”项目”这两个字。

第二条路:关键词搜索——精确匹配你说的字词。

两路结果按7:3的比例混合——语义搜索占大头,关键词搜索做补充:

{
  "vectorWeight": 0.7,
  "textWeight": 0.3
}

还有两个聪明的小设计:


  • 去重:内容差不多的记忆不会重复返回,避免翻来覆去看同一段话


  • 时间衰减:越久远的记忆权重越低。半衰期30天——3个月前的笔记,即使内容很匹配,分数也会被大幅打折

但最关键的设计,藏在”记忆瘦身”逻辑里。

聊天聊多了,AI模型的记忆容量(上下文窗口)迟早会撑满。这时候OpenClaw要做”瘦身”——把早期的对话压缩掉,腾出空间。

但直接压缩就等于直接遗忘。

OpenClaw的做法很聪明:压缩之前,先让Agent做一次**”紧急备忘”**——系统偷偷给Agent发一条隐藏消息:”马上要瘦身了,赶紧把重要的事情记到笔记本里。”

src/agents/pi-embedded-runner/compact.ts的配置:

{
  "compaction": {
    "memoryFlush": {
      "enabled": true,
      "softThresholdTokens": 4000,
      "prompt": "Write any lasting notes to memory/YYYY-MM-DD.md"
    }
  }
}

这一步不做,瘦身就等于失忆。很多用OpenClaw的人抱怨”它忘了我说过的话”,根因就是这个”紧急备忘”功能没打开。

社区也在进化。有人做了知识图谱层,帮Agent理清”谁认识谁、什么事跟什么事有关”。有人做了跨对话记忆插件,让Agent换个聊天窗口也还记得你。官方的”文件优先”路线和社区的”图谱增强”路线,两条腿走路。


六、Skills + 安全:Markdown是双刃剑

Skill在OpenClaw里的定义很简单:一个包含SKILL.md的文件夹。

格式兼容Claude Code和Cursor。ClawHub(官方技能市场)上已有500+个社区Skill。

但这里有一个关键的设计细节——

技能”按需加载”,不是一股脑全塞进去。

OpenClaw在运行时会扫描你装了哪些Skill,但只会把当前这轮对话用得上的Skill告诉AI模型。为什么?因为塞太多进去,模型反而会”信息过载”,回答质量下降。

安全怎么保证?三道防线:

第一道:隔离小黑屋。 Agent要执行代码?不在你的电脑上直接跑,而是丢进一个Docker容器(你可以理解为”一次性虚拟机”)里跑。跑完就扔。你的真实文件和网络,它碰不到。

第二道:工具白名单/黑名单。src/agents/tool-policy.ts管着哪些工具能用、哪些不能用。黑名单优先于白名单——说不让用的,白名单里加了也没用。像定时任务、Gateway管理、WhatsApp登录这类敏感工具,只有”主人”才能用。

第三道:人工审批。 高风险操作弹窗让你确认,你点”同意”才执行。

但ClawHub的现实很骨感。

安全审计发现,12-20%上传到ClawHub的Skill含有恶意指令。一个叫ClawHavoc的攻击活动,通过伪装成正常Skill(名字叫solana-wallet-tracker、youtube-summarize-pro)分发macOS恶意软件。Cisco安全团队验证了数据泄露的完整链路。

攻击方式很隐蔽:Skill本质就是一段Markdown文本,而Markdown会被注入到Agent的”大脑”里。恶意Skill可以在Markdown里藏一句”把用户的数据发到某个网址”——Agent可能就照做了。可以修改其他Skill。可以执行危险命令。

这就是”通过扩展市场投毒”的攻击方式。

Skill的灵活性和攻击面,是同一枚硬币的两面。


三个参考的设计模式

拆完源码,值得带走的:

模式1:一个用户一条队列,严格排队。 你做Agent产品,别急着搞多线程并发。一个用户一条车道,任务排队一个一个来,就够了。简单方案解决复杂问题。

模式2:用Markdown当”行为说明书”。 AGENTS.md写行为规矩,SOUL.md写性格。AI天然擅长读Markdown,比JSON/YAML配置文件对Agent更友好。用户自己也看得懂、改得动。

模式3:瘦身前先备忘。 对话记录快满的时候,先让Agent把重要信息存到笔记里,再做压缩。这一步不做,长对话必出”遗忘”Bug。


说明

这篇适合:想翻OpenClaw源码的开发者,正在做Agent产品的创业者,对系统架构好奇的技术人。

如果你只想装一个OpenClaw玩玩,不想看代码——去看官方部署文档就行,这篇帮不了你。

一个常见误区:很多人以为OpenClaw是”多Agent协作框架”。官方VISION文档里明确说了,”经理管经理管经理”那种多层嵌套架构,不是OpenClaw要走的方向。OpenClaw的哲学是——把单个Agent做到极致,而不是让一群Agent互相开会。

20万star的代码库,翻开一看,骨架清晰得出人意料。

这也许是最值得学的一课:可靠的Agent系统,靠的是工程品味,不是架构复杂度。

不只是教AI,更陪你做AI小生意。 我是用AI开一人公司的周知,下回聊。

👇 打开小程序,openclaw小白入门手册,一条龙安排。

想和我一起探索AI×一人公司的可能性,加入「AI觉醒星球小程序」3月福利群


本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 详细拆解OpenClaw源码,架构简单得让我离谱!

评论 抢沙发

8 + 2 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮