ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenClaw源码架构解析:跨平台AI Agent的模块化设计与部署实践

OpenClaw源码架构解析:跨平台AI Agent的模块化设计与部署实践 最近很多人在问 OpenClaw 的源码结构尤其是想把它部署到安卓、接入本地模型、甚至和 ROS2 仿真环境打通的那批朋友。我前后花了几个晚上把核心代码读了一遍也实际跑通了几个平台这里写一篇源码层面的架构梳理把我看到的模块划分、设计逻辑和几个容易卡住的地方讲清楚。如果你正打算读这份源码或者想基于它做二次开发这篇文章应该能帮你节省不少时间。1. 项目定位从热搜词反推 OpenClaw 的边界与野心先不急着打开代码我们做一件很有意思的事——看热搜词。OpenClaw 被频繁检索的场景其实暴露了使用者最真实的需求openclaw安卓部署、openclaw windows companion 怎么配置、如何用termux安装openclaw手机版下载步骤、ollama部署openclaw、openclaw skill、rosclaw openclaw ros2 humble gazebo。这些词串起来基本就是一张完整的架构地图。OpenClaw 不是一个单一功能的小工具它本质上是一个跨平台的个人 AI Agent 运行框架。注意我特意用了框架而不是应用是因为它的代码组织方式明显是为了承载多种形态的运行环境桌面端通过 Windows Companion 方式运行宿主程序负责屏幕捕获、输入模拟、系统命令执行移动端通过 Termux 在安卓上搭建 Python 环境跑一个裁剪版的 Agent 核心用手机算力或远端 API 驱动仿真环境通过 rosclaw 桥接层接入 ROS2 Humble 和 Gazebo让 Agent 能感知仿真世界并做出动作模型层既支持 OpenAI 兼容的 HTTP API也支持通过 Ollama 跑本地模型热搜词里专门有人问openclaw只能用接入api的方式使用算力吗这说明很多人关心能否完全离线跑。从源码角度来说这种多目标部署的需求直接决定了它的顶层代码被拆成了几个相对独立的子包而不是一个单体外壳。你打开仓库根目录的src/或者openclaw/目录如果第一眼没看到一堆互相 import 的单文件那是正常的——这个项目的架构是围绕运行时Runtime和适配器Adapter展开的。我建议任何人动手改代码前先花半小时把热搜词里出现的这些功能点在源码里定位一遍。这样你后面看每个模块的时候脑子里会有个三维地图这个文件是给哪个平台用的由谁加载依赖什么配置项。1.1 源码目录里的命名暗含架构意图我读过的绝大多数 Python 项目目录命名都比较随意。但 OpenClaw 的顶层命名相当克制它透露出作者一开始就规划好了边界core/Agent 主循环、状态机、上下文管理不依赖任何具体平台能力skills/技能定义与注册机制每个技能是一个可独立调用的模块models/模型网关统一封装不同后端API、Ollama的请求协议platform/平台适配层Windows、Linux、Android 的差异在这里抹平ros_bridge/ROS2 桥接负责话题订阅、动作调用、仿真环境交互。这五块就是 OpenClaw 源码架构的中轴线。后面我讲的每个小节本质上都是在展开这条中轴线上的具体实现。那些杂七杂八的配置文件YAML、TOML只是用来串联这些模块的胶水不是架构的核心。2. 顶层入口与配置加载Agent 启动时到底发生了什么很多人读源码习惯直接从main()看起这个思路没错但 OpenClaw 的启动路径有一点点绕——因为它的入口不是唯一的。Windows 下有companion启动器安卓 Termux 下是纯命令行入口桌面 Linux 可能直接跑一个 TUI 面板。所以源码里你会见到至少三个启动脚本它们最后都会汇聚到同一个AgentRuntime类上。2.1 入口分流companion、CLI、TUI 三种姿势platform/windows/下的 companion 不是一个简单的托盘程序。我在读源码时注意到它做了两件关键的事捕获宿主机的交互界面数据把屏幕图像、当前焦点窗口等环境信息序列化塞进 Agent 的上下文。提供安全的命令执行通道所有的系统调用都经过一条带审批回调的通道避免 Agent 失控执行危险命令。而 Termux 下的入口就轻量多了。你可以把它类比成一个无头模式没有屏幕捕获只有 stdin/stdout 交互Agent 通过读取终端内容来做决策输出直接打印回终端。解决方案是直接打字让 Agent 执行命令而桌面版是要让 Agent 自己去看屏、自己点按钮。这两种模式对core/的复用程度完全不同核心循环几乎不用改但感知信号的来源长不一样。这种多入口、单核心的设计我觉得是这份源码里最值得借鉴的地方。它把交互形式和决策逻辑彻底解耦了。你要做二次开发时只需要接一个新的感知适配器比如麦克风输入不需要碰 Agent 循环本身。2.2 配置加载的两层分离全局默认值 平台覆盖配置文件没有放在一个巨型 JSON 里而是拆成了层叠式结构。全局配置里定义了模型参数、温度、最大步数这类通用项平台配置则覆盖了各自独有路径Windows 下的屏幕尺寸、Termux 下的TERMUX_PREFIX路径之类。加载顺序大致是defaults load_yaml(config/default.yaml) platform_conf load_yaml(fconfig/{runtime_name}.yaml) config deep_merge(defaults, platform_conf) runtime AgentRuntime(configconfig, backendcreate_model_gateway(config))这种设计的直接收益是你换平台时不会因为某个字段缺失导致启动崩溃而且可以很轻松地加一个config/ros2.yaml来跑仿真环境。我实操时经常改的是model.provider这个字段在openai和ollama之间切换改配置文件比改代码方便得多。3. Agent 核心循环的实现逻辑它的状态机和上下文怎么管理这一节讲core/里面的东西。Agent 的核心循环不管在哪个平台都是一样的这套循环你可以把流程拆成五个阶段感知、规划、执行、观察、反思。[感知] - 环境快照图像/文本/状态 [规划] - 给 LLM 补上下文生成下一步计划 [执行] - 调用技能或工具 [观察] - 获取执行结果追加到上下文 [反思] - 判断任务完成与否未完成则跳回规划这个五阶段循环看起来简单但源码里的实现细节决定了实际使用体验的天壤之别。3.1 上下文窗口的滑动管理由于模型上下文是有限的OpenClaw 并没有把整个对话历史无限堆进去。源码里有一个ContextWindow类它会维护一个可配置的消息预算——比如从最近的 20 条消息里保留全部系统指令和最近两轮工具结果更早的内容则被摘要压缩。压缩动作本身也调用一次模型生成一个小总结塞回上下文。我一开始以为这种摘要逻辑会写得很随意结果发现它有一个保护机制摘要永远放在系统消息层级的最末尾并且带上时间戳。这样模型看到一个摘要消息能清楚地对齐这只是一段过去史当前状态我才是主角。3.2 状态机不只是简单的循环源码里并没有一个while True无限循环那么粗暴。核心是一个FSMState枚举和对应处理器映射。比如WAITING_INPUT、COMPUTING_PLAN、EXECUTING_SKILL、OBSERVING_RESULT这几种状态之间的跳转是有严格约束的。技能调用超时会从EXECUTING_SKILL强制拽回COMPUTING_PLAN并且在上下文里追加一条错误观察而不是让整个 Agent 卡死。这个超时设计是值得你抄作业的它本质上给 Agent 加了一个弹性机制防止某个技能无限期挂起。本地的 Ollama 推理时如果遇到算力瓶颈模型响应慢但不至于让整个 Agent 挂死原因就在这里。4. Skill 机制的解构扩展点到底在哪一层热搜词里有openclaw skill这说明普通用户也在关心怎么给 Agent 加自定义能力。源码里技能系统走的是注册表 装饰器模式这和很多 Python 框架比如 Flask 的 Blueprint是同一个路子。4.1 技能装饰器的定义方式定一个新技能大概长这个样子from openclaw.skills import skill skill(file_reader, description读取本地文本文件内容, require_confirmationTrue) class FileReader: def execute(self, params: dict, context: dict) - str: path params[path] with open(path, r, encodingutf-8) as f: return f.read()这里的require_confirmationTrue是一个安全门。源码里对所有涉及文件系统写操作、网络请求、系统安装的命令都会默认开启人工确认。而读取类操作可以免确认因为风险低。我建议你扩展技能的时候一定要按这套装饰器来而不是自己去core/里 import 一个类去改主循环。因为技能机制还做了动态加载skills 目录下新增一个.py文件只要里面有被skill修饰的类运行时就能被发现并纳入工具列表不需要重新编译或改配置。4.2 技能如何暴露给模型模型调用技能不是靠记忆这个技能叫什么名字的。源码里有一个技能列表收集和工具 schema 转换的过程每次请求模型前Agent 会把当前可用的技能转换成模型能识别的 JSON Schema描述函数名、参数类型和具体说明。然后模型在响应里返回一个结构化调用请求再由执行器分发到对应技能类。这个设计和 OpenAI Function Calling 的方式完全兼容。所以你在代码里能看到一个通用FunctionCallingAdapter它负责和模型网关交互把模型返回的{name: file_reader, arguments: {path: /tmp/a.txt}}这样的内容转换成 Python 类的方法调用。实际用下来技能越多模型上下文占用的 schema 空间就越大。OpenClaw 做了一个小优化只会把可能用得上的技能转发给模型按上下文相关度做个粗筛。这个筛选逻辑在skills/matcher.py里我建议对性能有要求的人细读一下它会根据当前工作空间的目录结构来猜测哪些技能更相关。5. 模型接入层API 算力与 Ollama 本地推理的融合设计热搜词里那个问题——openclaw只能用接入api的方式使用算力吗——答案肯定不是。源码里models/目录下的 Model Gateway 是一个抽象层它把所有后端统一成一个chat()接口。OpenAI 兼容 API 和 Ollama 都只是这个接口的不同实现。5.1 统一的模型接口设计看一段简化版的接口定义你会秒懂class BaseModelGateway(ABC): abstractmethod def chat(self, messages, toolsNone, **kwargs): pass abstractmethod def stream_chat(self, messages, toolsNone, **kwargs): pass abstractmethod def embed(self, text): passOpenAIGateway直接调httpx请求远端 APIOllamaGateway则往本地http://localhost:11434/api/chat发请求。两者的返回结构都会被统一转成ModelResponse数据类这样上层完全不用关心模型跑在哪。5.2 流式输出的处理差异这块我不说你可能注意不到。远程 API 的流式输出通常是 SSE 格式而 Ollama 的流式是 JSON Lines。因为上层统一了接口所以这个差异被隔离在网关层里。但二次开发时如果你直接读取stream_chat的返回值会发现字段名已经被统一成delta_content不会暴露底层协议细节。这种设计让切换后端非常顺滑我在实际部署时就靠改配置来回切。5.3 算力路由与环境变量优先级特别值得注意的是OpenClaw 允许你在同一个会话里混合使用算力。比如规划阶段和反思阶段用本地 Ollama 小模型执行阶段的工具参数提取用高能力 API 模型。源码里确实有ModelRoute的配置项planning: ollama/qwen2.5:7b、execution: openai/gpt-4o这种写法是支持的。但我在实测中踩过坑如果 API 后端突然断连网关不会自动降级到本地模型而是直接抛BackendUnavailableError由 Agent 循环捕获后追加一条系统错误。这算是一个设计取舍——它不掩饰故障而是把故障本身变成模型可感知的信息。对需要稳妥离线运行的场景我建议你在配置里把fallback.enabled打开让网关在超时后自动切 Ollama。源码里这个参数默认是 false原因可能是避免无感知的模型切换导致对话上下文出现格式不兼容。6. ROS2 与 Gazebo 集成rosclaw 桥接层剖析既然热搜词里有rosclaw openclaw ros2 humble gazebo专门说一嘴这块。OpenClaw 对机器人仿真的支持不是简单地把 Gazebo 当作一个普通工具调用而是在架构上设计了一个独立的桥接层。6.1 桥接层与 Agent 核心的通信协议ros_bridge/文件夹里的核心是一个RosBridgeNode它本质上是一个 ROS2 节点负责持续订阅仿真环境里的话题消息把它们转成 Agent 的观察事件。同时 Agent 的规划结果也可以转成 ROS2 的 action 请求通过 action server 发送给 Gazebo 里的仿真机器人。我把它理解成一个翻译器把 ROS2 的sensor_msgs/msg/LaserScan、nav_msgs/msg/Odometry这些消息翻译成 Agent 能理解的文本描述再把 Agent 决定的目标坐标翻译回 ROS2 的MoveBaseActionGoal。这个翻译过程不是在 Agent 循环里硬编码的而是作为一组内置技能注册进去的。6.2 为什么在源码里单独建一个 bridge 目录这里的架构决策值得学习作者没有在core/里直接 importrclpyROS2 的 Python 客户端库因为这会强制所有使用 OpenClaw 的人都安装 ROS2 依赖。把 ROS2 相关代码全部隔离在ros_bridge/里通过进程间通信和 core 交互这样平时跑普通任务的用户完全不受影响而做机器人仿真的用户单独装ros2依赖就能启动全部功能。我对接 Gazebo 时的实际体验是打开一个终端跑ros2 launch gazebo_ros gazebo.launch.py再跑ros2 run openclaw_ros_bridge bridge_nodeAgent 就能通过话题读到激光雷达的数据。如果你要做强化学习闭环可以在这个桥接层的基础上再加一个环境奖励模块让 Agent 的反思阶段能直接收到数值化奖励。桥接层本身接口足够干净这个扩展完全不突兀。7. 跨平台细节安卓 Termux 部署、Windows Companion 背后的工程取舍最后把热搜词里最热门的两个部署场景串起来看——openclaw安卓部署和openclaw windows companion。这两个场景在源码里代表了两种不同的适配哲学裁剪适配和原生适配。7.1 Termux 安卓部署Python 依赖裁剪与存储路径适配在源码层面OpenClaw 是一个纯 Python 项目理论上有 Python 环境就能跑。但在 Termux 里会遇到的是系统依赖缺失问题而不是 Python 层面问题。比如pillow库需要 zlib、libjpeg 的系统库编译Termux 默认不自带完整工具链。源码里专门有一条针对 Termux 的安装脚本scripts/setup_termux.sh我读了一下它做的事很实在pkg install python rust binutils装基础编译链设置PILLOW_VERSION环境变量规避特定构建问题把配置目录链接到$PREFIX/var/openclaw避免在手机上出现奇怪的路径冲突强制使用--no-cache-dir安装依赖减小存储占用。实际在手机上跑之后你还是要有预期管理——手机端推理速度确实慢。我用中端安卓机跑 Ollama 7B 模型单次推理大约要十几秒Agent 跑完一个多步任务能感觉到明显延迟。但作为个人助理做简单命令交互完全够用。7.2 Windows Companion感知与操作的边界控制Windows 版源码里有一个模块我特意看了很久platform/windows/vision_controller.py。它管理着屏幕捕获频率和鼠标键盘动作反馈。这套设计解决了一个真问题如果 Agent 要操作 GUI它需要在每个决策点重新截屏但如果截屏频率太高Windows 桌面会卡顿而且上下文里塞满相似图片会浪费 token。源码里给了一个默认策略——只在动作完成并等待 500ms 稳定后再截屏并且用像素变化率来检测屏幕是否已经稳定变化率低于阈值才把快照交给模型。这实际上是一种非常节省算力的感知策略。我在 Windows 上实测配合本地 Ollama 使用时每轮 Agent 决策的耗时主要集中在模型推理上屏幕捕获本身的开销几乎可以忽略。7.3 配置联动的平行对照表我把几个平台的关键配置项做个对照方便你横向理解配置项WindowsTermux/安卓ROS2Gazebo感知输入屏幕快照终端文本传感器话题动作输出鼠标键盘模拟命令行执行ROS2 Action模型网关可配任意后端推荐 Ollama 本地可配任意后端关键依赖PyAutoGUI、pywin32pillow、termux-apirclpy、gazebo_msgs配置目录%APPDATA%/openclaw$PREFIX/var/openclaw工作空间config/这张表基本就是 OpenClaw 源码里 platform 适配层的完整映射。你看清楚这张表再回源码里去找对应目录思路会清晰很多。8. 源码阅读顺序建议从哪些文件开始最省力如果你和我一样是带着改造 OpenClaw的目的来读源码的我建议按这个顺序读而不是从入口顺着跑一遍先看models/gateway.py搞懂模型接口长什么样这是你接国产模型、接本地模型、接多模态模型都要过的关卡。再看skills/registry.py搞懂技能注册机制这样你可以用最少的代码量给 Agent 加本领。然后看core/agent.py搞懂主循环的状态机这时候你才知道技能和模型是如何被串起来的。最后按你需要部署的目标平台去读对应的platform/适配层。我最不建议先读的是各种工具函数文件utils.py这类。它们确实很实用但就算你读完了也不知道为什么需要它们容易读完就忘。还有一个小技巧源码里大量使用了 Pydantic 模型做数据校验你在读的时候如果看到一个构造函数的参数和文档对不上很可能是数据类校验逻辑拦住了。这时候别急着改代码优先去看对应的schema.py把字段要求对齐。9. 二次开发中最容易踩的三个坑这个项目我已经完整跑通并在上面做了两处定制下面这几个坑是真实撞出来的写给准备动手的人看。第一个坑技能 schema 和模型能力不匹配。如果你给某个模型配了 20 个技能但这个模型本身不支持那么复杂的 function calling响应里经常出现 JSON 解析失败。我的解法是给技能做分组在配置里给不同模型指定不同的技能子集别把全部技能塞给一个弱模型。源码里支持skills.enabled配置项直接用。第二个坑Termux 环境下使用根文件系统的路径。安卓的 Termux 默认根目录是/data/data/com.termux/files不是常规的 Linux 根目录。OpenClaw 源码里明明有路径适配但如果你通过 Python 的os.path.expanduser(~)去定位配置目录仍然可能拿到一个并不存在的路径。建议把配置目录显式通过环境变量OPENCLAW_HOME指定到 Termux 的$PREFIX/var下别依赖自动检测。第三个坑Windows 下截屏与 DPI 缩放。如果你在 Windows 显示缩放比例是 125% 或 150%PyAutoGUI 的坐标计算会偏移。OpenClaw 源码里做了 DPI 感知声明但如果你自己写额外的屏幕操作技能需要主动调用SetProcessDpiAwarenessContext否则你传的坐标会在不同屏幕上有系统性偏差。这个问题极其隐蔽不缩放屏幕根本发现不了。10. 衍生扩展如何把 OpenClaw 变成你的个人任务自动化中心源码读过之后我个人的感受是它最强的能力不是某个单一技能而是给你留了一整套技能配线。目前我基于它搭了自己的本地自动化流程用 Termux 部署在旧手机上做备忘录定时提醒通过本地 Ollama 模型做意图识别同时在工作电脑上跑 Windows 伴侣模式做文件整理和网页信息收集。两条链路共用同一套 skill 定义改一份配置就行。对我下一步感兴趣的方向是做 ROS2 仿真和实体硬件打通——OpenClaw 的桥接层已经掩盖了大部分通信细节把sensor_msgs的扫描数据丢给本地小模型让仿真机器人做简单的避障与目标导航并不是什么天方夜谭。这也是为什么我在前文反复强调最核心的资产是core/和skills/这两层平台适配只是外层壳。最后提醒一句如果你只是想快速上手先把config/下的 YAML 逐个看一遍、搞懂每个字段的含义这比读任何一篇文章都更能帮你建立体感。等你把配置玩熟了再回来读本文提到的核心模块会另有收获。
返回列表