
拿到一份OpenClaw的源码仓库我一般不会先刷README而是直接敲tree。目录结构就是一篇文章的目录透过它你才能真正看懂这个项目想干什么、能干什么、扩展点在哪里。很多朋友私信问我OpenClaw怎么部署、怎么接Ollama、怎么写skill我通常都会先让他们把目录结构过一遍因为八成的问题其实都出在对目录的理解上。这篇我就把OpenClaw的目录结构从头到尾拆一遍每个顶层目录的职责、关键文件的作用、哪些能改哪些不能乱动以及在不同部署场景下目录会发生什么变化。1. OpenClaw目录结构的设计哲学分层与可插拔1.1 从单体脚本到模块化框架早期很多类似的个人智能体项目都是一个main.py从头写到尾里面塞满了模型调用、Prompt模板、各种工具函数。功能少了还能跑一旦要加第二个技能、换一个底模、接一个机器人仿真环境代码就开始互相纠缠改一行能蹦出一串问题。OpenClaw从一开始就把自己定位成一个可扩展的智能体运行框架所以它的目录结构也是围绕扩展性来设计的。你可以把这套结构想象成一家餐厅core是后厨的灶台和出餐流程skills是厨师手里的菜谱providers是给后厨送食材的供应商storage则是冰箱和冷藏室。菜谱可以随时换食材供应商也可以换只要灶台本身稳定餐厅就能正常运转。这种分层的好处在于模块之间只通过约定好的接口通信。比如providers目录里每一种模型服务都要实现同一个chat接口上层业务根本不需要知道当前用的到底是本地Ollama还是某个云服务商的API。目录在这里不只是为了好看它本质上就是把架构设计直接落在文件系统里了。1.2 一句话记住核心目录OpenClaw的根目录并不复杂顶层就是五个openclaw/ ├── app/ # 主程序引擎、Agent、技能、模型适配、存储 ├── config/ # 所有配置文件 ├── data/ # 运行时数据数据库文件、日志、临时文件 ├── scripts/ # 安装、部署、维护脚本 └── tests/ # 自动化测试先记住这五个就不会走偏。app是你日常开发的主战场config是你调参的地方data这个目录建议永远不要手动改scripts里提供的是日常操作的入口而tests属于保命用的每次改动之后跑一遍就知道有没有把已有功能搞坏。后面提到的所有子目录几乎都落在app下面。先有顶层鸟瞰图再钻进具体细节读代码的效率会高很多排查问题也不再像无头苍蝇一样到处翻。2. 根目录入口、依赖与配置的解耦2.1 main.py 与 pyproject.toml在OpenClaw根目录里两个最重要的文件是main.py和pyproject.toml。main.py是整个进程的唯一入口它做的事情很少加载config目录下的配置初始化core引擎然后启动事件循环。很多新手喜欢在main.py里写自己的业务逻辑这是第一个要避开的坑。main.py必须保持“薄”一旦它变厚后面调试、跑测试、切换部署环境都会非常难受。如果你是从FastAPI项目转过来的会发现这个思路很熟悉入口只负责创建application对象并暴露给启动命令真正的逻辑都放在app包里。pyproject.toml负责管理项目依赖和打包元数据。建议无论多小的项目都不要用requirements.txt硬顶着因为OpenClaw的安装和部署脚本会优先读取pyproject.toml只有一些历史兼容场景才会看requirements.txt。另外pyproject里的依赖分组也值得关注比如src、dev、ros、mobile这些extra按需安装比全量安装省心得多。安装时用pip install -e .[ros]只装ROS2扩展目录会干净很多。2.2 config/ 下那些yaml文件config目录是OpenClaw整个项目里改动最频繁的地方。主配置在config/config.yaml里面定义系统级参数比如日志级别、HTTP端口、Agent默认角色、技能调用超时时间。其他文件按领域拆分我通常见到的布局是这样config/ ├── config.yaml # 系统主配置 ├── skills.yaml # 技能开关与默认参数 ├── providers.yaml # 模型供应商配置 └── logging.yaml # 日志格式与输出目标拆文件不是随意的。主配置只管那些跟业务无关的稳定性参数比如监听端口、工作线程数providers.yaml只存放模型服务地址和密钥skills.yaml控制哪些技能启用、哪些默认禁用。这样拆的核心原因和12-Factor应用的理念一样配置与代码分离。你换一个部署环境时不需要改动app/下的任何Python文件只需要调整yaml里的参数即可。2.3 .env 与环境变量注入根目录下的.env.example不是摆设它列出了所有适合在部署时手动注入的环境变量比如数据库连接串、Ollama服务地址、云服务API Key的变量名。OpenClaw加载配置的优先级是真实环境变量优先于.env文件.env文件优先于config下的yaml。这种设计在服务器部署时特别有用你不需要把密钥写在会被提交到Git仓库的yaml文件里。我在实际部署中踩过坑改了config.yaml里的模型名但进程没重启OpenClaw启动时才会一次性读取配置所以一直走的是旧配置。部分新版本支持openclaw config reload热加载但大多数场景下最稳妥的流程还是“改配置、保存、重启”。另外.env文件不要提交进Git.env.example才应该提交。很多开源项目把这项写进.gitignoreOpenClaw也是这样一旦你把真实.env推上去了密钥泄露只是时间问题。3. app/core智能体引擎的心脏3.1 引擎、事件总线与任务队列进入app目录之后第一眼要看的不是skills而是core。core里保存着整个框架最核心的机制目录大致如下app/core/ ├── engine.py # 引擎生命周期管理 ├── events.py # 事件类型定义 ├── bus.py # 事件总线 ├── task_queue.py # 异步任务队列 ├── scheduler.py # 定时任务调度 └── plugin_loader.py # 技能扫描与注册engine.py负责启动、停止、崩溃恢复这些生命周期动作。bus.py是事件总线它是OpenClaw能够“让所有模块都有机会响应消息”的关键。你给智能体发一句话总线把消息广播出去所有订阅了这个事件的Agent都会收到Agent再决定要不要调用某个skill。task_queue是异步任务池耗时的技能调用会被丢进队列里去跑不阻塞主线程。如果你要做一个“每天定时抓取电商价格并推送”的功能大概率就要同时研究scheduler和task_queue。3.2 插件加载器如何扫描skillsplugin_loader是整套可插拔架构的功臣。它启动时会扫描app/skills目录下的每一个子目录读取技能描述文件然后把可用的技能登记到内存注册表里。这个扫描动作并不是简单遍历一下目录它还会做依赖检查和冲突检测如果某个技能声明了需要requests而当前环境没安装这个技能会被标记为禁用并在启动日志里给出一行警告。我见过不少人把自定义技能放错位置放在app根目录或者其他临时目录结果加载器永远扫不到。技能目录必须直接放在app/skills/下面每个技能一个子目录目录名就是技能ID。这一步看着简单但搭错了整个技能体系都起不来属于“目录结构直接决定功能是否可用”的典型例子。另外技能目录内部不要嵌套太深plugin_loader默认只扫描一层如果你在技能目录下又套了一层加载器会认为那是一个后续需要手动加载的子模块。3.3 核心引擎的修改边界core是改动成本最高的地方不建议常规业务去碰它。OpenClaw社区早期有人为了加一个“特殊前缀触发某技能”的功能直接在bus.py里写了硬编码结果后续升级时跟官方补丁冲突整个分支都废掉了。正确做法是把这类触发逻辑放到Agent层或者写成一个新的skill去监听事件。如果确实觉得某个引擎行为不合理优先去官方仓库的issue里确认一下新版本是否已经支持而不是自己动手魔改。我自己的习惯是core目录只读除非在做二次开发级别的定制。日常加功能、调行为都在skills、agents、providers这三个目录里完成。这样升级OpenClaw版本时只需要处理config和skills的兼容问题几乎不用担心core冲突。把这个边界守住你手头的分支就能长期跟上游同步不会越改越累。4. app/skills技能插件的标准姿势4.1 一个skill的标准目录结构skill是OpenClaw可扩展性的灵魂也是大家最关心的目录。每个技能独立成子目录推荐结构如下app/skills/web_search/ ├── manifest.yaml # 技能元信息名称、入口、权限 ├── __init__.py # Python包标识 ├── handler.py # 核心处理函数 ├── requirements.txt # 技能独立依赖 └── assets/ # 静态资源模板、词典、小工具脚本manifest.yaml是加载器判断技能是否合法的关键。里面至少要有name、version、author、description、entry这五个字段entry指向处理函数的完整模块路径。很多新手在entry里写成相对路径“handler.run”加载器是不认的正确写法是“skills.web_search.handler.run”。这个细节在官方文档里有但特别容易被忽略而且一旦写错日志里只会出现一句“skill ignored”不会明确告诉你是entry写错了。4.2 技能注册与依赖隔离技能的启用状态在config/skills.yaml里管理。你可以把配置文件理解成技能总开关manifest只是把技能接入了系统到底通没通电还得看当前环境的enable列表。这样设计的好处是仓库里可以保留大量默认不启用的技能部署时按需打开不浪费一点资源。依赖隔离同样重要。每个skill的requirements.txt不会在OpenClaw主安装时自动安装你需要运行openclaw skill install 名字或者直接执行scripts/install_skill.py。安装脚本会把该技能声明的依赖合并进虚拟环境并检查版本冲突。如果不走这个流程直接全局pip install很容易把系统环境搞乱多个skill互相覆盖包版本。我在维护一个多技能实例时就踩过A技能升级了requests、把B技能搞挂的坑。从那时起我坚持每个skill尽量少引第三方库能用标准库解决就别偷懒。4.3 电商类skill的实战拆解热搜词里出现“openclaw电商”可见不少人拿它做比价、订单跟踪、店铺数据这类自动化场景。电商类skill的目录通常会比通用技能多两个模块一个是适配电商平台API的client一个是把不同平台返回结构统一成内部标准的normalizer。目录长这样app/skills/ecommerce/ ├── manifest.yaml ├── handler.py # 对外入口 ├── platforms/ │ ├── _base.py # 平台适配基类 │ ├── shop_a.py │ └── shop_b.py ├── normalizer.py # 结果统一结构 └── requirements.txt为什么要单列platforms目录因为各电商平台的API参数、签名方式、返回字段差异很大但OpenClaw内部只需要一种“商品信息对象”。把适配和标准化分开后新增一个平台就只需要在platforms下新增一个文件handler完全不用动。这也是目录结构引导你写出可维护代码的典型例子。如果你做的电商技能涉及登录状态不要把Cookie写进代码里放进config或环境变量这是另一个层面的安全问题但目录结构可以帮你天然隔离掉这种隐患。5. app/providers模型接入层绕不开的Ollama5.1 providers目录要解决的问题OpenClaw本身不内置大模型它把模型服务统一收敛到providers目录。这样设计是为了回答一个核心问题你的Agent底层到底用本地模型还是远程API这件事不能影响上层业务。providers目录通常包含这些文件app/providers/ ├── base.py # 统一接口定义 ├── ollama.py # Ollama本地推理接入 ├── openai_compatible.py # 兼容OpenAI的API ├── web_api.py # 其他HTTP API封装 └── registry.py # 供应商注册与回退逻辑base.py里会定义chat、embed、tools_list这类基础方法所有具体provider都要继承并实现。registry.py负责根据config里设置的active_provider值返回对应的实例。这样你在agents和skills里拿到的是一个统一的provider对象根本不用关心当前跑的是Ollama还是云端API切换底模对业务代码完全无感。如果你要新增一个本地推理引擎比如vLLM直接在providers目录下新增一个适配文件并注册进来就行。5.2 Ollama本地模型与配置位置用Ollama部署OpenClaw是当前很主流的一种玩法。配置上先在providers.yaml里指定默认供应商providers: active: ollama ollama: base_url: http://127.0.0.1:11434 model: qwen2.5:7b temperature: 0.7 num_ctx: 8192base_url是Ollama服务地址默认端口11434。如果你在本机同时跑OpenClaw和Ollama用127.0.0.1就够了如果Ollama跑在另一台机器或Docker容器里这里要改成实际可达的IP或域名。num_ctx是上下文窗口长度直接影响长对话下的显存占用新手很容易忽略这个参数导致Ollama在高并发时OOM。这些参数都在providers目录之外通过配置文件控制正好体现目录分层的价值改模型参数不用改代码改代码不用碰模型参数。5.3 “只能用API算力吗”的答案很多人在搜索“OpenClaw只能用接入API的方式使用算力吗”答案是否定的。OpenClaw的providers目录同时支持本地推理和远程API两类接入。本地推理的代表就是ollama.py它通过Ollama推理引擎直接调用本机的CPU或GPU算力不需要任何云端服务远程API则走openai_compatible.py或web_api.py。这个双轨制恰恰是OpenClaw目录结构里最值得琢磨的地方它把“算力来源”彻底抽象成了可替换的组件。从实践看我的建议是追求隐私、离线可用、不想按量付费就用Ollama追求当前最强大模型能力、不在乎按量付费就接云端API。甚至可以在同一个实例里配置多个provider通过config切换或根据任务类型调用不同供应商。只要providers目录不坏切换模型的成本几乎为零。6. agents与storage角色状态和记忆6.1 agents目录怎么组织agents目录存放Agent角色与行为策略。Agent可以理解为“带着人设和决策逻辑的大脑”它决定在什么时机调用哪个skill。典型结构如下app/agents/ ├── base.py # Agent公共基类 ├── state_machine.py # 对话状态机 ├── registry.py # 角色注册表 └── builtin/ ├── assistant/ # 通用助手 ├── translator/ # 翻译角色 └── robot_operator/ # 机器人控制角色每个Agent目录里通常有一个描述文件比如agent.yaml定义角色预置、系统提示词和默认策略另有一个策略文件决定它是直接触发skill还是带条件判断。state_machine是复杂对话场景的核心比如“先收集参数再调用电商查询技能”这一步骤就是一个状态流转。如果只需要普通聊天助手用builtin里的assistant就够了要自定义人设千万别改builtin直接在app/agents/下新建角色目录升级时不用担心冲突。6.2 memory和db存储分层记忆系统决定了OpenClaw能不能记住上下文。在目录层面记忆相关代码集中放在app/storage/app/storage/ ├── memory/ │ ├── session.py # 短期会话记忆 │ └── vector.py # 向量存储接口 ├── db.py # 数据库读写封装 └── cache.py # 缓存接口session.py管理一次对话里的历史记录vector.py负责把长期记忆向量化比如存入本地向量数据库。db.py是关系型数据的统一入口OpenClaw默认使用SQLite配置成PostgreSQL也不会太难。任务状态、技能调用日志这类业务数据都会走db.py所以data目录里会出现类似openclaw.db的文件。记忆策略要在配置里统一指定而不是每个skill各搞一套存储。统一接口的好处是以后从SQLite换到PostgreSQLskills层完全不感知。6.3 data/目录的坑data/是运行时生成的数据目录里面除了数据库文件还有日志、临时上传文件、向量索引。这个目录最需要注意两点一是别提交进Git二是别手动往里乱放东西。正确做法是在.gitignore里加上data/让每个部署环境自己初始化数据。我见过有人把模型权重下载到data目录里导致仓库体积迅速膨胀每次克隆都要拉下来一堆根本不该进版本库的二进制文件。还有一个很容易被忽略的点data/目录的读写权限。在Linux服务器上跑OpenClaw时如果进程用户对data没有写权限启动时会报数据库初始化的错误。这个错误和代码没关系纯粹是目录权限问题但排查起来很费时间。所以我把chmod和data目录检查直接写进部署脚本确认可写了再启动主程序。目录权限这类小事往往比逻辑bug更能消耗人的耐心。7. 跨平台部署ROS2与Termux目录差异7.1 ros/机器人集成目录OpenClaw不只是一个跑在服务器上的聊天程序在ROS2场景里它还能担任机器人的决策节点。为了不把机器人桥接代码污染进主程序仓库里专门保留了ros/目录ros/ ├── bridge/ # Topic/Service 桥接层 ├── actions/ # ROS2 Action 定义与客户端 ├── launch/ # 启动文件 └── models/ # 自定义消息类型bridge的作用是把OpenClaw的事件转换成ROS2消息再发布到/cmd_vel这类Topic上。在Gazebo仿真环境中先用roslaunch启动仿真世界再启动OpenClaw的桥接节点Agent就能读仿真里的传感器消息并输出控制指令。这套流程听起来复杂但目录把职责切得很清楚app里不直接import rclpy而是通过ros/bridge做适配。以后升级ROS2版本只需要动ros目录核心智能体逻辑可以原样保留。搜索里的“rosclaw openclaw ros2 humble gazebo”指的就是这套组合玩法。7.2 mobile/与Termux部署Termux能在安卓上提供Linux环境于是很多人在手机上头安装OpenClaw这就是mobile/目录存在的意义。它在仓库里的位置大致是mobile/ ├── termux/ │ ├── install.sh # 一键安装脚本 │ ├── run.sh # 启动脚本 │ └── storage_path.env # 安卓存储路径映射安卓上的文件目录和服务器完全不一样不能把data/直接放在App私有目录里否则系统清理缓存时数据可能就没了。install.sh通常会在Termux的存储权限目录下建立openclaw_data然后通过storage_path.env把data目录映射过去。这个流程不是改Python代码而是改环境变量和软链接。所以你在手机安装OpenClaw时不要按PC路径死记硬背先跑一下install.sh确认它把data放到了哪里再做后续配置。7.3 不同部署形态对目录结构的影响服务器部署时完整的app、config、data、scripts是最佳形态Termux部署时tests和ros目录完全可以裁剪掉以节省空间mobile/termux/install.sh会自动处理机器人部署时需要额外保留ros目录并安装对应的ROS2依赖电商应用部署时重点配置skills/ecommerce和providers.yaml。目录的选择性组合能力是OpenClaw比较舒服的地方它不是“一套代码跑天下”而是“一套框架按场景裁剪目录”。从传统项目迁移过来的朋友可能会问这跟SpringBoot或FastAPI的目录结构有什么本质区别我的理解是FastAPI项目通常按“接口层-服务层-存储层”纵向切OpenClaw按“引擎-技能-模型-记忆”横向切核心不是请求处理而是能力编排。你不需要把OpenClaw硬套Web项目的MVC结构它的目录就是为Agent场景设计的。下表可以快速看出不同场景下的目录取舍部署场景必须保留可裁剪服务器通用app、config、data、scriptsros、mobile安卓Termuxapp核心、config、mobiletests、rosROS2机器人app、config、rostests、mobile电商自动化app/skills/ecommerce、providersros、mobile8. 常见问题与排查技巧实录8.1 skill不加载先查这三处我遇到最多的问题是自定义skill明明写了但OpenClaw就像没看见一样。这时先依次排查三处第一skill目录是否直接放在app/skills/下面目录名是否和manifest里的name保持一致第二manifest.yaml里的entry字段是否写成了带完整包路径的方式第三config/skills.yaml的enable列表是否包含这个技能。这三处只要错一个插件加载器就会跳过。用openclaw skill list命令能看到当前被识别的技能列表比靠猜快很多。下面这张小表是我常用的排查定位表遇到症状直接对号入座症状优先排查位置常见原因技能不出现app/skills/目录目录层级不对或目录名与name不一致manifest报错技能目录/manifest.yamlentry缺少完整路径或YAML缩进错误技能被禁用config/skills.yamlenable列表未包含该技能模型不响应config/providers.yamlbase_url错误或模型名不存在手机部署报权限错mobile/termux/storage_path.envdata目录映射权限不足8.2 配置改了没生效缓存与权限配置没生效多半不是缓存诡异而是改错了文件或者权限不对。前面提过加载优先级环境变量会覆盖yaml所以先检查系统里是否设置了同名环境变量。另一个常见问题是YAML缩进。YAML对缩进极其敏感特别是providers.yaml里嵌套多层配置时少两个空格就会解析失败。OpenClaw启动时会打印配置解析错误这类日志很容易被忽略看到“ConfigParseError”别慌去检查缩进即可。还要注意config目录的权限。如果配置目录被改成只读OpenClaw启动时也许成功但写缓存文件会失败表现为配置看起来没有被加载。Linux下用ls -l config/看权限必要时用chmod修正。这些都是五分钟能解决的排查路径但我见过有人花了一下午去改Python代码最后发现只是yaml文件里多了一个Tab。YAML规范里禁止用Tab缩进这也是一个老生常谈但要反复强调的坑。8.3 目录速查命令与维护技巧最后分享一套实用的目录速查命令。在OpenClaw根目录执行tree -L 3 -I __pycache__|*.pyc|.git|data这条命令能快速看到三层以内的完整目录结构同时过滤掉缓存和运行时目录。维护上我建议每次大版本升级之后用tree导出一份结构和官方文档里的参考结构做diff能及时发现目录里多出来的模块或丢失的文件。尤其在多分支并行开发时目录结构diff比代码diff更能暴露架构漂移。目录整洁不是洁癖它是降低长期维护成本最实惠的手段。我个人实际操作的体会是目录结构这个东西第一眼感觉只是文件夹排列用久了才发现它一直在默默约束着你的设计。我在OpenClaw上踩过几次坑之后现在每接手一个新环境会先把config和providers两个目录读完再看skills开关最后才去跑功能。如果你也是刚接触OpenClaw建议不要想着“等需求来了再研究目录”先花半小时把顶层结构过一遍然后在data下建一个sandbox目录去试第一个skill。目录顺了后面的部署和调优大概率也顺希望这篇拆解能帮你少走一段弯路。