乐于分享
好东西不私藏

从程序员的角度看OpenClaw

从程序员的角度看OpenClaw

看下图OpenClaw在Github的加星速度,可以说史无前例,让我这个写了多年代码的老师傅羡慕不已。


为什么它如此出众?因为它把ai从聊天机器人(chatbots that respond)变成了可执行任务的智能体(agents that act)。之前的ai助手把焦点放在提示词工程上面,而OpenClaw则是建立了一个系统设施。在这个系统里,ai模型提供智能(intelligence),OpenClaw建立执行环境(会话管理、记忆系统、工具沙箱化、消息路由等),让大家看到了一种新的可能性。

程序员不相信神话,让我们看看它是如何做到的。


1 OpenClaw架构概览

见下图,OpenClaw是枢纽-辐射式架构(hub-and-spoke architecture),网关(Gateway)是单一中心枢纽,作为控制平面(control plane)连接用户输入(Desktop/CLI/Web UI/WhatsApp/Slack)和AI代理:

- 网关是一个Websocket 服务,连接消息平台(Messaging Channels)和用户控制接口(Control Interfaces),把消息路由到代理执行引擎(Agent Runtime)


- 代理执行引擎跑端对端的AI循环:从会话历史和记忆里构建上下文,调用AI模型,根据系统可用的能力执行工具调用(浏览器自动化/文件操作/画布/定时任务等),把更新后的状态持久化


得益于这种架构,你可以用现成的聊天应用接入智能体,得到一个连续而可控的会话体验。


2 通过插件实现可扩展性(Extensibility)

可以不修改核心代码通过插件扩展OpenClaw,插件有四种扩展方式:

- 频道(Channel)插件:额外的入站消息平台(Microsoft Teams/Matrix/Mattermost等)


- 记忆(Memory)插件:可选的存储后端(向量存储,知识图和缺省的SQLite)


- 工具(Tool)插件:系统内置了bash/browser/file等工具,用户可以通过插件进一步拓展


- 供应商(Provider)插件:定制LLM模型供应商或者使用自己的模型

插件代码在 extensions/ 目录下,由插件加载器加载。插件加载器的代码是  src/plugins/loader.ts ,它会扫描工作区(workspace)的包目录,从package.json里找openclaw.extensions字段,如果验证通过且配置存在就热加载,见下图


3 核心组件

3.1 频道适配器(Channel Adapters)

内置的频道适配器有src/telegram/、src/discord/、src/slack/、src/imessage/ 等,其他通过频道插件增加。频道适配器负责入站(inbound)消息和出站(outbound)消息规范化,让OpenClaw其他部分可以看到一致的消息接口,核心功能有四个:

- 认证
- 入站消息解析
- 访问控制
- 出站消息格式化

不同平台认证方式不一样,有些是扫二维码,有些是机器人token,有些需要第三方应用集成等。它们的配置不一样,有些存在 ~/.openclaw/credentials ,有些通过环境变量 TELEGRAM_BOT_TOKEN/DISCORD_BOT_TOKEN 提供。

对于入站消息,需要提取文本,处理媒体附件(图片、语音、视频、文档等),处理表情回应(点赞、点亮爱心等点击动作)和表情符号(发送的表情消息),维护主题或回应上下文。

访问控制是频道层面的安全控制。有两类消息,一类是别人直接发给聊天机器人(bot)的,称为直发消息(DM,direct message),需要控制哪些人可以发消息给机器人,这是通过许可列表(allowlist)指定的,比如 channels.whatsapp.allowFrom 指定对方的手机号码。对于没有指定在列表里的人,可以用DM控制策略处理:缺省pairing需要管理员许可,open允许任何人,disabled禁止任何人。另一类消息是组消息,组控制策略包括是否需要@机器人(如果对所有组消息都处理,机器人容易变成话唠机)以及特定于组的许可列表。

对于出站消息,频道适配器会处理平台差异,比如切割长消息、渲染Markdown文本、上传媒体文件等,甚至输入状态提示和在线状态信息的管理。


3.2 控制接口(Control Interfaces)

有四种方式可以控制OpenClaw网关:

- Web UI
- CLI
- macOS app
- 手机(Android/iOS)

Web UI集成在网关里,不需要单独服务,默认 http://127.0.0.1:18789/ ,可以用来聊天、配置管理、审视会话、节点管理和健康监控。

CLI是命令行控制界面,用Commander.js实现,入口在openclaw.mjs,然后流程进入 src/cli/program.ts:


- openclaw gateway 控制网关启动/停止/重启


- openclaw agent 直接调用代理用


- openclaw channels login配对WhatsApp/Signal


- openclaw message send发送消息到网关


- openclaw doctor OpenClaw健康诊断


- openclaw onboard 打开命令行设置向导

macOS app代码在apps/macos/ ,运行后驻留在菜单条,可以控制网关的生命周期、语音唤醒、带有原生浏览器嵌入的WebChat,甚至能控制远程SSH网关。

手机(Android/iOS)作为WebSocket节点(node)连接网关,在连接握手里声明为 role: “node”。这不仅仅是聊天,还把设备能力(相机、截屏、位置服务、画布渲染)暴露给网关,网关可以用node.invoke协议方法调用这些能力,把手机变成代理工具箱的一个扩展。


3.3 网关控制平面(Gateway Control Plane)

网关的代码是 src/gateway/server.ts,用ws这个WebSocket库实现,缺省绑定到127.0.0.1:18789,是OpenClaw唯一可信数据源(single source of truth)。

当入站消息到达的时候,网关负责路由,包括访问控制检查、解析哪个会话处理这条消息、把它分发到合适的代理。它是如何保证多条并发消息(DM/心跳事件/WebHook等)在同样的会话或工具调用中不互相踩踏呢?

这是通过FIFO队列实现的:

- 一个会话只处理一条活跃消息
- 不同会话可并发运行,但会有并发数限制

一个会话相当于一条车行道,全局车道做并发数控制。如果多条消息到达同一个车行道,有三种队列模式:

- collect(缺省) 把多条消息合并成一条消息
- followup 等待,直到当前消息处理完成
- steer 在工具执行边界处插进当前消息

此外,消息排重(dedupe)和延迟防抖(debouncing)也是必要的。

网关还协调系统状态,包括会话、在线状态、健康监控、定时器任务。如果是非本地环路地址绑定,它还强制令牌或口令认证。此外,还有对DM配对的实现。

这种设计原则是精心规划的。首先,一台主机只有一个网关,满足WhatsApp这样严格的单设备协议;其次,整个协议是有类型的,所有WebSocket帧都会通过JSON数据格式验证,JSON数据格式是用TypeBox定义生成的。第三,系统是事件驱动的,而不是基于轮询。客户端只用订阅agent/presence/health/tick之类的事件,不用定期查询新消息。最后,任何产生副作用的操作都要求一个幂等key(idempotency key),便于安全重试和防重复动作。本地连接(环路地址或同样的tailnet)自动批准,远程连接要求用挑战-响应签名机制(challenge-response signing)明确批准。


3.4 代理执行引擎(Agent Runtime)

代理执行引擎的代码在 src/agents/piembeddedrunner.ts ,是和AI真正交互的地方。它使用了Pi Agent Core库(@mariozechner/pi-agent-core),遵循RPC风格的模型调用,支持流响应。它要实现四个目的:

- 解析会话(session resolution)
- 构建上下文(context assembly)
- 执行工具调用时流响应
- 把更新后的状态写回磁盘

当消息达到时,执行引擎需要判断哪个会话处理它。如果是你发送的直达消息,映射到main会话;如果是从频道发出的直达消息,映射到 dm::会话;如果是从频道发出的组消息,映射到group::会话。会话不仅仅是ID,还是安全边界。每种会话类型带着不同的权限和沙箱规则(比如main可以运行主机上的工具,dm/group会话会有更严格的allowlist许可列表和docker容器隔离)。

会话解析后,执行引擎开始为模型构造上下文:

- 从JSON持久化的会话文件加载会话历史(这样会话会从上次继续)


- 根据工作区(workspace)配置动态构建系统提示词


- 检索记忆区,找到跟本次会话相关的历史上下文

上下文构建完成后,执行引擎把它发送给AI模型,比如OpenAI GPT或者Anthropic Claude,然后一个token一个token地返回给客户端。

在模型响应的时候,执行引擎检查是否模型要求调用工具,如果需要就调用工具(比如运行bash命令、读写文件等),把工具执行结果发送回AI模型,参与后续的响应生成。在本轮会话结束后,执行引擎把更新后的会话状态(消息,工具调用/结果,以及其他状态)持久化到磁盘。

下图是会话上下文的构建细节


工作区配置文件:


- AGENTS.md 核心代理指令(缺省自带),是基本要求:允许做什么,全局约束,非协商性的规则,所有会话适用


- SOUL.md 个性化和格调指引(可选):代理该如何和人对话和构造回答,但无关工具行为和安全边界


- TOOLS.md 用户特定的工具约束(可选),只是工具使用的个性化说明,非工具注册和工具定义

动态上下文(每回合会话Turn都会执行):


- 会话历史 当前会话的最近消息


- Skills (skills//SKILL.md) 定义和使用指令


- Memory 语义相近的历史会话检索

工具定义(自动生成):


- 内建工具(src/agents/pi-tools.ts, src/agents/openclaw-tools.ts),含bash/浏览器/文件操作/画布/核心能力


- 插件工具 通过api.registerTool(toolName, toolDefinition)注册

基础系统:


- Pi Agent Core 来自代理执行引擎的基础指令

你可以修改工作区文件改变上下文内容,从而定制代理行为,注意Skill发现和注入有个细节:OpenClaw不是把所有Skills注入上下文,而是有选择性地注入跟当前会话回合(turn)相关的Skills,防止上下文膨胀,降低模型效果。


4 交互和协作

4.1 画布(Canvas)和A2UI(Agent-to-UI)

画布和A2UI在mac app和ios/android app上可用,让AI代理驱动图形渲染。

用户和AI主流的交互方式是用户输入消息,AI响应,和命令行交互差不多。

画布和A2UI提供了图形交互的可能。AI驱动图形界面,用户点击其中的按钮或输入文本,AI执行操作并更新图形界面。这种GUI交互有很大的应用空间,比如人机对战打游戏。

这块代码还没成熟(之前canvasHost是18793端口,最近我checkout的代码和网关合在一起了),刚兴趣的同学可以研究一下。


4.2 语音唤醒和对讲模式

在mac app和ios/android上可用,语音识别和文本转语音用的是ElevenLabs。语音的优势是可以解放双手,如果语音上下文充分(不是简单的哼哈),识别率还是挺准的。

4.3 多代理路由

多代理路由支持把不同频道或者组消息路由到不同的代理,每个代理有独立的工作区、AI模型和行为规则,如下图


多代理(Multi-Agent)和子代理(Sub-Agent)不同,多代理用于代理之间相互隔离没有交集的情况,或者虽有交集但功能上处于同一层级。子代理是有层级关系的,下层代理服从上层代理的调度。

我试过用多代理做模型区分,要求不苛刻的场合跑ollama本地模型,比如qwen3.5:4b,否则用商业模型,不细致测还体验不出差别来。


4.4 会话工具

会话工具让代理之间可以跨会话合作,用以完成复杂的任务或者信息共享,免去在不同聊天上下文之间拷贝粘贴,这些工具有:

- session_list 发现活跃的会话


- sessions_send 可以把消息发送给其他会话,如果设置了 announceStep: "ANNOUNCE_SKIP", 用户感知不到会话之间的消息来往


- session_history 从其他会话取聊天记录,便于做出有依据的决定


- session_spawn 用程序方式创建新会话,分摊工作


4.5 定时器任务和Webhooks触发

这两种方式可通过配置设置,处理重复执行的任务或者与外部系统集成。


5 数据存储和状态管理

OpenClaw的数据和配置存储在用户主目录的很多地方。


5.1 核心配置

主要的配置文件是 ~/.openclaw/openclaw.json ,使用JSON5格式,不像严格的JSON,可以加备注和尾部逗号,手工编辑更友好。配置是分层的,环境变量覆盖配置文件里的值,配置文件覆盖缺省值,这样可以把敏感的token放到环境变量。


5.2 会话状态和压缩

OpenClaw把会话记录以JSONL格式保存在磁盘里,同时还有一个存储文件把会话Key映射到会话Id和相关元数据。

- 存储文件:~/.openclaw/agents//sessions/sessions.json


- 记录文件:~/.openclaw/agents//sessions/.jsonl

这样重启时可以恢复会话。另外这些文件可能包含敏感信息,专设目录可提高安全性。

当会话消息超过上下文限制时,会进行会话压缩。在压缩前,会从以前的会话消息提取关键信息,持久化到记忆区域(Memory)。然后把以前的会话消息生成摘要,丢弃摘要过的消息。

会话ID是所有权和信任边界,比如主会话ID是 agent::main,具有所有能力;DM会话是agent::dm,组会话是agent::group,缺省沙箱化,用以隔离不信任的输入和多人聊天。


5.3 记忆检索和获取

当用户输入消息时,记忆(Memory)系统会自动搜索过往消息,找出相关记录,然后把它们和当前输入一起装进当前上下文,发送给AI模型。这个检索过程如下图



相似度(vector_score)常用的计算公式有:余弦相似度(cosine similarity)、点积(dot product)和欧几里德距离(euclidean distance)

bm25_score ≈ 关键词出现次数 × 关键词稀有程度 ÷ 文档长度影响

最终分数

final_score = α * vector_score + β * bm25_score

α 和 β 是缩放系数。

为啥要根据两个指标打分呢?实践证明这是很有必要的。向量相似度是找语义相关的记录,关键词匹配是找ID、符号、错误信息、人名地名等匹配的记录,缺少任一种都会导致召回率下降。

实际情况更复杂,因为如果单靠分数定输赢,无法反应真实的信息流动。所以还要根据记录时间做些调整(temporal decay,让新记录分数高点),以及增加一些多样性(MMR re-ranking,防止出现类似的记录)。

以上过程由两个步骤组成:


- memory_search 从索引中检索相关记忆


- memory_get 根据检索出的内容读出原始记忆内容


既然是检索就会有个建立索引的过程。首先,要有一个唯一真实数据源,这就是记忆文件,有两类:

- MEMORY.md 长期记忆文件,包含提炼出的比较靠谱的事实或经验数据,只会被主会话或者私有会话加载,不在组聊天中出现


- memory/YYYY-MM-DD.md 日常会话记录,以追加方式写入(append-only)

记忆系统会对这些文件建立向量索引:

- 如果配置了本地向量模型(local.modelPath),就用它


- 否则检查OpenAI的API key,存在就用OpenAI向量化


- 否则检查Gemini API key


- 否则禁用记忆检索

OpenClaw会对记忆数据源文件进行监控,一旦文件变化累计到一定时间就会重建索引,如果向量化方式变了就全部重建。此外,建索引时还需要考虑排重。

以上是龙虾的整体结构,这个项目代码量很大,经常把我的ai编程环境搞崩,不过代码的可读性还是很高的。篇幅有限,我只说一下遇到的坑:

- 如果只是研究代码,从release下源码,不要git clone

- 上船过程onboard尽量skip掉各种配置,后续可慢慢补全

- macOS app值得编译出来玩玩,iOS的不好用,安卓的我没编译过

- 远程node连接用tailscale serve解决TLS证书问题,没有证书非回路地址不让连

- 如果只是远程使用WebUI可用ssh forward

- 命令行直接修改openclaw.json比WebUI配置方便

- 从源码编译没有openclaw命令,需要pnpm openclaw运行。如果嫌麻烦,可以 pnpm link --global在项目根目录运行,生成openclaw命令

- 如果用ollama跑本地模型,注意在配置里控制上下文窗口大小,默认256K上下文容易导致内存不足,我的jetson nano 4B模型是4K上下文窗口,要正经养龙虾还得靠商业模型

- 设备许可和节点许可 是两个概念,分别用openclaw的devices和nodes子命令分别管理

- 多读官方文档,不要轻信ai给你的歪点子

在老师傅看来,龙虾的意义在未来而不是现在,小伙伴们如果没条件养龙虾不要着急。在打孔编程时代,人机交互很差,很难想象Ada Lovelace(第一位女码神)是怎么熬过来的。后来有了操作系统,程序员可以调用底层api开发应用,产生了大量软件工程实施方案。未来软件产业可能不是调用操作系统api,而是调用ai代理,为各行各业构建Skills和工具,解决实际应用问题。普适性的ai操作系统(AOS, Agent OS)还有很长的路要走,特定领域的代理(DSA,Domain Specific Agent)会在近期繁荣起来,小伙伴们准备好了吗?

Ada Lovelace
参考

https://ppaolo.substack.com/p/openclaw-system-architecture-overview

https://theagentstack.substack.com/p/openclaw-architecture-part-1-control

https://theagentstack.substack.com/p/openclaw-architecture-part-3-memory

https://docs.openclaw.ai