OpenClaw智能体框架本地Docker部署全指南
1. 项目概述:这不是一个“装软件”的活,而是一次AI智能体基础设施的自主构建
OpenClaw 这个名字最近在开源AI智能体圈子里冒得很快,但很多人点开 GitHub 仓库第一眼看到的不是炫酷的演示视频,而是满屏的
docker-compose.yml
、
Dockerfile
、
.env.example
和一长串 Python 依赖。这恰恰说明了一件事:OpenClaw 的定位非常清晰——它不是一个开箱即用的 SaaS 产品,而是一个
可深度定制、可完全掌控的智能体运行时框架
。它的核心价值不在于“能做什么”,而在于“你能让它变成什么”。我第一次部署它的时候,本意是想快速搭个能查内部文档的客服机器人,结果三天后,它已经在我公司的 CI/CD 流水线里自动分析 PR 描述、调用 Jira API 创建子任务、再把生成的测试用例塞进 Confluence。这个转变不是靠点几下鼠标完成的,而是源于 OpenClaw 提供的那套“技能(Skill)”抽象和“工作流(Workflow)”编排能力。
所以,这篇指南的出发点就和市面上大多数“三步安装教程”不同。它不承诺“5分钟搞定”,但保证你部署完之后,心里有底:知道每个容器在干什么,清楚环境变量改了哪一项会触发什么连锁反应,明白为什么
OPENCLAW_SKILL_DIR
必须挂载为只读卷,也懂得如何在不碰核心代码的前提下,给你的智能体加上一个调用公司内部 HR 系统的全新能力。关键词里的“完整”二字,指的就是这种
全链路的掌控感
,而不是一个能跑起来的 demo。它适合三类人:一是技术负责人,需要评估这套框架能否融入现有 DevOps 体系;二是资深开发,打算基于它二次开发企业级智能体;三是对 AI 架构有好奇心的工程师,想亲手拆解一个现代智能体的“心脏”是如何跳动的。如果你只是想找一个现成的聊天机器人,那 Dify 或扣子可能更合适;但如果你的目标是让 AI 成为你业务流程里一个可编程、可审计、可运维的“数字员工”,那么 OpenClaw 就是你绕不开的起点。它不提供魔法,但它把施法的咒语和魔杖的锻造方法,都明明白白地写在了 README 里。
2. 整体架构与设计思路:为什么必须用 Docker?为什么不能只靠 pip install?
2.1 拆解 OpenClaw 的“四层金字塔”
OpenClaw 的设计不是平铺直叙的,它像一座精心堆叠的金字塔,每一层都承担着不可替代的职责。理解这座塔的结构,是避免后续部署中“改了A却崩了B”的关键。
-
最底层:运行时沙盒(Runtime Sandbox)
这一层由 Docker 容器构成,包括openclaw-api(主服务)、openclaw-worker(异步任务执行器)、redis(任务队列与缓存)、postgres(持久化存储)以及可选的minio(对象存储)。它们共同构成了一个隔离、稳定、可复现的执行环境。我见过太多人试图用pip install openclaw直接装在宿主机 Python 环境里,结果因为langchain版本冲突、pydantic主版本不兼容、甚至uvloop编译失败而卡死。Docker 的价值在这里体现得淋漓尽致:它把所有依赖的“时间戳”都锁死了。你拉取的openclaw/api:0.4.2镜像,里面 Python 是 3.11.9,langchain-core是 0.3.17,sqlalchemy是 2.0.31,这些组合经过了官方 CI 的千百次验证。你不需要去猜“哪个版本的llama-index能和openclaw-worker兼容”,因为镜像里已经给你配好了。 -
第二层:配置中枢(Configuration Hub)
所有上层逻辑的开关都集中在这个层面,核心就是.env文件。它不像传统 Web 应用那样只有DATABASE_URL和SECRET_KEY。OpenClaw 的.env是一个“策略声明文件”,比如OPENCLAW_DEFAULT_MODEL=ollama:qwen2:7b声明了默认大模型的地址,OPENCLAW_SKILL_AUTO_RELOAD=true则开启了技能热重载——这意味着你修改一个 Python 技能脚本后,无需重启整个服务,Worker 进程会在几秒内自动加载新代码。这个设计背后是深刻的工程权衡:热重载极大提升了开发效率,但它要求 Worker 进程必须能安全地卸载旧模块、导入新模块,这在 Python 里是个高风险操作。所以官方默认是false,而你在本地开发时可以放心设为true,但在生产环境,我强烈建议你关掉它,改用蓝绿部署的方式更新技能。 -
第三层:能力插槽(Capability Slot)
这就是“Skill”机制。OpenClaw 不预设任何具体功能,它只提供一个标准化的接口:一个继承自BaseSkill的 Python 类,必须实现execute方法,并通过@skill装饰器注册。你可以把它想象成一个 USB 接口标准。openclaw-skill-websearch是一个符合标准的“U盘”,openclaw-skill-jira是另一个,而你自己写的my-company-hr-skill只要也符合这个标准,就能即插即用。这种设计彻底解耦了框架核心与业务逻辑。我曾在一个客户项目里,用同一个 OpenClaw 集群同时运行着三个完全不同的智能体:一个面向销售的 CRM 数据查询助手,一个面向研发的代码库知识问答机器人,还有一个面向财务的发票识别与报销流程引导员。它们共享同一套 API、Worker 和数据库,但各自的技能集、提示词模板、权限控制规则,全部独立管理,互不干扰。 -
顶层:交互界面(Interaction Interface)
这一层是用户直接接触的部分,目前官方提供了两种:一个是基于 FastAPI 的 RESTful API,另一个是配套的 Web UI(openclaw-ui)。这里有个极易被忽略的细节:Web UI 本身 不包含任何业务逻辑 ,它只是一个纯粹的前端,所有的请求都转发给openclaw-api。这意味着,如果你想把 OpenClaw 集成到企业微信或飞书机器人里,你根本不需要动 UI 代码,只需要调用/v1/chat/completions这个 API 即可。它的设计哲学是“前端即胶水”,把所有复杂性都压在了后端 API 层,这为各种集成场景留出了巨大的灵活性。
2.2 为什么 Railway 部署是新手的“甜蜜陷阱”?
网络热词里频繁出现的 “railway部署”,确实是一个极低门槛的方案。你只需点击几下,选择 OpenClaw 的 GitHub 仓库,Railway 就会自动帮你创建服务、配置环境变量、启动容器。很多初学者因此误以为“部署完成了”。但我在帮客户做技术尽调时发现,超过 70% 的 Railway 部署案例,在两周内都会遇到一个致命问题: 技能无法持久化 。
原因很简单:Railway 的免费层使用的是临时磁盘(Ephemeral Disk)。你通过 Web UI 上传的技能文件、或者在
OPENCLAW_SKILL_DIR
目录下手动创建的 Python 脚本,一旦服务重启(Railway 会定期回收闲置资源),这些文件就全部消失了。你昨天刚写好的
jira_skill.py
,今天打开 UI 就找不到了。这不是 Bug,而是平台设计使然。Railway 的定位是“快速原型验证”,而非“生产环境托管”。它帮你省去了服务器采购和基础环境搭建的麻烦,但把“数据持久化”这个核心运维责任,悄悄转嫁给了使用者。要解决这个问题,你必须手动配置一个外部 MinIO 存储桶,并在
.env
中设置
OPENCLAW_STORAGE_BACKEND=minio
,这已经超出了 Railway 一键部署的能力范围。
所以,我的建议很明确: 把 Railway 当作你的“沙盒实验室”,而不是“生产机房” 。用它来快速验证 OpenClaw 的核心流程是否跑通,确认你的大模型 API 是否能被正确调用,测试一下基础的 Web UI 是否可用。一旦验证通过,立刻将重心转向本地 Docker Compose 部署或云服务器部署。后者虽然前期多花 2 小时,但换来的是对数据、配置、日志的完全掌控,这才是构建一个可靠智能体的基石。
2.3 本地部署 vs 云服务器:一次关于“可控性”的抉择
很多教程会告诉你:“推荐用 Docker Compose 在本地 Mac/Windows 上部署”。这话没错,但不够完整。我做过一个对比实验:在同一台 32GB 内存的 MacBook Pro 上,分别用 Docker Desktop 和一台 4C8G 的阿里云 ECS(Ubuntu 22.04)部署完全相同的 OpenClaw 配置。
| 维度 | Docker Desktop (Mac) | 云服务器 (ECS) |
|---|---|---|
| 首次启动时间 | 2分18秒 | 1分42秒 |
| 技能加载延迟(冷启动) | 平均 3.2 秒 | 平均 1.8 秒 |
| Redis 内存占用峰值 | 420MB | 310MB |
| Postgres 查询响应(10万条记录) | 86ms | 41ms |
| 日志排查便利性 |
需通过
docker logs
查看,多容器日志混杂
|
可直接
tail -f /var/log/openclaw/api.log
,日志按服务分离
|
差异的核心在于
资源调度的确定性
。Docker Desktop 在 macOS 上运行,本质上是通过一个轻量级 Linux VM(HyperKit)来承载容器。这个 VM 与宿主 macOS 共享内存和 CPU,当你的 Mac 同时开着 Chrome、IDEA、Zoom 时,VM 能分到的资源是动态且不可预测的。而云服务器的内核、CPU、内存、磁盘 I/O 都是独占的,调度器不会在关键时刻把你的
openclaw-worker
进程踢到后台去给其他进程让路。
但这并不意味着云服务器就是唯一答案。对于个人开发者或小团队,本地部署有其不可替代的优势:
调试链路极短
。当你在
my_skill.py
里加了一行
print("DEBUG: entering execute")
,保存后,如果启用了热重载,几秒钟内就能在终端看到输出。而在云服务器上,你需要
scp
上传文件、
ssh
进入容器、
docker exec
查看日志,这个过程会打断你的思考流。因此,我的实践是“双轨制”:日常开发和功能验证在本地 Docker Compose 环境进行,确保代码逻辑 100% 正确;当进入性能压测、高并发模拟、或与真实业务系统(如 SAP、Oracle)联调阶段,则无缝切换到云服务器环境。两个环境的
docker-compose.yml
文件几乎完全一致,唯一的区别是云服务器版本里多了一个
nginx
反向代理容器,用于处理 HTTPS 和负载均衡。
3. 核心部署实操:从零开始,一步一验
3.1 环境准备:那些被忽略的“前置检查”
在敲下第一个
docker-compose up
命令之前,有三项检查比安装 Docker 本身更重要。我见过太多人卡在这一步,然后在网上发帖问“为什么一直报错
connection refused
”,最后发现是防火墙没关。
第一项:检查 Docker 的存储驱动与磁盘空间
OpenClaw 的镜像体积不小,
openclaw-api
和
openclaw-worker
加起来轻松超过 2GB。Docker 默认的存储驱动在不同系统上不同:Linux 上通常是
overlay2
,而 macOS 的 Docker Desktop 使用的是
VFS
(一种较慢的驱动)。你可以在终端运行
docker info | grep "Storage Driver"
来确认。更重要的是磁盘空间。Docker Desktop 在 Mac 上默认只分配 60GB 磁盘给其内部 VM。当这个空间被
docker system prune -a
清理不干净的镜像、悬空卷、构建缓存占满时,
docker-compose up
会静默失败,没有任何错误提示,只是卡在
Pulling...
。解决方案是:打开 Docker Desktop 设置 -> Resources -> Disk image size,将其调大到 120GB,并定期执行
docker system prune -a --volumes
(注意:这会删除所有未使用的卷,请先备份重要数据)。
第二项:验证你的大模型后端是否真正“可达”
OpenClaw 本身不包含大模型,它只是一个“指挥官”,需要你提供一个“士兵”——即一个兼容 OpenAI API 标准的大模型服务。常见的有 Ollama(本地)、LM Studio(本地)、Together AI(云端)、Fireworks AI(云端)等。很多人部署失败,根源在于
curl
测试通过了,但 OpenClaw 却连不上。这是因为
curl
是从你的宿主机发起的请求,而 OpenClaw 的容器是在一个独立的 Docker 网络里运行的。容器内的
localhost
指向的是它自己,而不是你的宿主机。所以,如果你在本地用 Ollama,正确的地址不是
http://localhost:11434
,而是
http://host.docker.internal:11434
(Docker Desktop 自动提供的宿主机别名)。在 Linux 上,你需要在
docker-compose.yml
的
openclaw-api
服务下添加
extra_hosts: - "host.docker.internal:host-gateway"
。我建议你先在容器内测试连通性:
docker exec -it openclaw-api-1 sh
,然后在容器里执行
curl -v http://host.docker.internal:11434/api/tags
。只有这个命令返回了 Ollama 的模型列表,才算真正打通了“任督二脉”。
第三项:为 PostgreSQL 准备一个“干净”的初始化脚本
OpenClaw 的数据库迁移(Migration)是通过 Alembic 工具自动完成的,但它有一个前提:PostgreSQL 数据库必须是空的,或者至少没有
alembic_version
这张表。如果你之前部署过其他应用,或者用
docker-compose down -v
删除过卷,但
postgres
容器的卷(volume)没有被彻底清理,那么再次
up
时,PostgreSQL 会从旧数据启动,Alembic 就会报错
alembic_version table not found
。最稳妥的做法,是在第一次部署前,手动创建一个初始化 SQL 脚本
init.sql
:
-- init.sql
CREATE DATABASE openclaw;
\c openclaw;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
然后在
docker-compose.yml
的
postgres
服务下,添加
volumes
挂载:
postgres:
image: postgres:15
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
- pgdata:/var/lib/postgresql/data
这样,每次
postgres
容器首次启动时,都会自动执行这个脚本,确保数据库处于一个已知的、干净的状态。
3.2 docker-compose.yml 文件详解:不只是复制粘贴
官方 GitHub 仓库里的
docker-compose.yml
是一个很好的起点,但它更像是一个“最小可行配置”。要让它真正服务于你的生产需求,你需要理解并修改其中的每一个关键字段。下面是我经过 12 个项目验证后的“增强版”配置核心片段,并附上每项修改背后的实战理由。
version: '3.8'
services:
# API 服务:智能体的“大脑”
openclaw-api:
image: ghcr.io/openclaw/openclaw-api:0.4.2
restart: unless-stopped
environment:
# 【关键】模型地址:指向宿主机上的 Ollama
OPENCLAW_DEFAULT_MODEL: "ollama:qwen2:7b"
# 【关键】数据库连接:使用 service name,而非 localhost
DATABASE_URL: "postgresql+psycopg2://openclaw:openclaw@postgres:5432/openclaw"
# 【关键】Redis 地址:同上,使用 service name
REDIS_URL: "redis://redis:6379/0"
# 【关键】技能目录:挂载为只读,防止 Worker 进程意外修改代码
OPENCLAW_SKILL_DIR: "/app/skills"
# 【关键】启用 CORS,允许你的 Web UI 或其他前端调用
OPENCLAW_CORS_ORIGINS: "http://localhost:3000,https://your-company-ai.com"
# 【关键】日志级别:生产环境用 WARNING,开发用 DEBUG
LOG_LEVEL: "INFO"
volumes:
# 【关键】技能目录:映射到宿主机的 ./skills 目录,便于编辑
- ./skills:/app/skills:ro
# 【关键】日志目录:单独挂载,方便日志轮转和收集
- ./logs/api:/app/logs
# 【关键】健康检查:让 Docker 知道服务是否真的“活着”
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Worker 服务:智能体的“手脚”
openclaw-worker:
image: ghcr.io/openclaw/openclaw-worker:0.4.2
restart: unless-stopped
environment:
# 【关键】必须和 API 服务使用完全相同的数据库和 Redis 地址!
DATABASE_URL: "postgresql+psycopg2://openclaw:openclaw@postgres:5432/openclaw"
REDIS_URL: "redis://redis:6379/0"
OPENCLAW_SKILL_DIR: "/app/skills"
# 【关键】并发数:根据你的 CPU 核心数调整。4C 机器设为 4,8C 设为 8。
# 过高会导致模型推理排队,过低则浪费资源。
WORKER_CONCURRENCY: "4"
LOG_LEVEL: "INFO"
volumes:
- ./skills:/app/skills:ro
- ./logs/worker:/app/logs
# 【关键】依赖关系:必须等 API 和 Redis 都健康了才启动
depends_on:
openclaw-api:
condition: service_healthy
redis:
condition: service_healthy
# Redis:智能体的“短期记忆”
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- redisdata:/data
# 【关键】内存限制:防止 Redis 吃光所有内存
mem_limit: 512m
# PostgreSQL:智能体的“长期记忆”
postgres:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_DB: openclaw
POSTGRES_USER: openclaw
POSTGRES_PASSWORD: openclaw
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
- pgdata:/var/lib/postgresql/data
# 【关键】内存和 CPU 限制:保障数据库稳定性
mem_limit: 1g
cpus: 1.0
volumes:
redisdata:
pgdata:
这份配置里,
mem_limit
和
cpus
的设置是血泪教训换来的。有一次,我们没给
postgres
限内存,结果在一次大批量技能执行时,PostgreSQL 的 shared_buffers 占满了 4GB 内存,导致宿主机整体卡死,连 SSH 都连不上。
healthcheck
的配置同样重要。默认的
docker-compose.yml
里没有健康检查,这意味着即使
openclaw-api
因为数据库连接超时而卡在启动状态,Docker 也会认为它“running”,进而去启动
openclaw-worker
,而
worker
又会因为连不上健康的
api
而陷入无限重试循环,最终把 Redis 的连接池耗尽。有了健康检查,Docker 会严格按照依赖顺序启动,并在服务不健康时自动重启,大大提升了整个系统的鲁棒性。
3.3 技能(Skill)开发与加载:让你的智能体“学会新本领”
OpenClaw 的灵魂在于 Skill。一个 Skill 就是一个 Python 文件,它定义了智能体能做什么。官方示例里的
websearch
技能,是学习的最佳范本。但要真正掌握,你需要亲手写一个,并理解它的生命周期。
第一步:创建你的第一个 Skill
在项目根目录下,创建
skills
文件夹,然后新建
hello_world.py
:
# skills/hello_world.py
from openclaw.skill import BaseSkill, skill
@skill
class HelloWorldSkill(BaseSkill):
"""
一个最简单的技能,用于验证环境。
它会返回一条问候消息,并附带当前时间。
"""
name = "hello_world" # 技能的唯一标识符,必须全局唯一
description = "Say hello to the world and show current time."
def execute(self, input_data: dict) -> dict:
"""
技能的执行入口。
input_data 是来自 LLM 的结构化指令,例如 {"name": "hello_world", "args": {"user_name": "Alice"}}
"""
import datetime
user_name = input_data.get("args", {}).get("user_name", "World")
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 返回一个标准格式的字典,包含 result 和 metadata
return {
"result": f"Hello, {user_name}! The current time is {now}.",
"metadata": {
"skill_name": self.name,
"executed_at": now,
"status": "success"
}
}
第二步:理解 Skill 的加载机制
当你执行
docker-compose up
后,
openclaw-worker
容器启动时,会扫描
/app/skills
目录下的所有
.py
文件。它会导入每个文件,寻找所有被
@skill
装饰器标记的
BaseSkill
子类,并将它们注册到一个全局的技能仓库中。这个过程发生在 Worker 进程的主循环开始之前。所以,
技能文件的语法错误,会在 Worker 启动日志的第一行就暴露出来
。比如,如果你在
hello_world.py
里少写了一个冒号,你会在
docker logs openclaw-worker-1
的开头看到类似
SyntaxError: invalid syntax
的报错,而不是等到你调用技能时才出错。这是 OpenClaw 对开发者极其友好的一点:错误前置,反馈即时。
第三步:在 Web UI 中调用并验证
启动所有服务后,访问
http://localhost:3000
(假设你没改端口)。在 Chat 界面,输入以下指令:
请调用 hello_world 技能,参数是 user_name="张三"
如果一切顺利,你应该会看到智能体返回:
Hello, 张三! The current time is 2024-05-20 14:23:45.
这个看似简单的例子,其实包含了 Skill 开发的全部核心要素:
name
是技能的 ID,
description
是给 LLM 看的“说明书”,
execute
方法是真正的业务逻辑。
input_data
的结构是固定的,它由 OpenClaw 的 LLM 编排引擎根据你的提示词(Prompt)自动生成。你不需要关心 LLM 是怎么决定要调用
hello_world
的,你只需要确保,当它决定调用时,你的
execute
方法能给出一个符合预期的、结构化的
result
。
第四步:进阶技巧——技能的“上下文感知”
一个真正有用的技能,往往需要知道“当前对话的上下文”。比如,一个
get_weather
技能,不应该每次都问用户“你想查哪个城市?”,而应该能从之前的对话历史中提取出城市名。OpenClaw 通过
self.context
提供了这个能力。你可以在
execute
方法里这样写:
def execute(self, input_data: dict) -> dict:
# 获取当前对话的完整历史(最多 10 条)
conversation_history = self.context.get("conversation_history", [])
# 尝试从最后一条用户消息中提取城市名(简单正则)
import re
last_user_msg = ""
for msg in reversed(conversation_history):
if msg.get("role") == "user":
last_user_msg = msg.get("content", "")
break
city_match = re.search(r"([北京上海广州深圳杭州南京]+)", last_user_msg)
city = city_match.group(1) if city_match else "北京"
# ... 调用天气 API 获取数据 ...
return {"result": f"{city}今天的天气是晴天,25度。"}
这个
self.context
是 OpenClaw 在调用
execute
之前,自动注入的一个字典,里面包含了当前会话的所有元信息。这是 OpenClaw 区别于其他简单 Agent 框架的关键能力之一:它把“状态管理”这件事,从开发者手里接了过来,让你能更专注于业务逻辑本身。
3.4 Web UI 配置与反向代理:让智能体走出 localhost
OpenClaw 官方的
openclaw-ui
是一个静态 React 应用,它本身不处理任何业务逻辑,所有请求都转发给后端 API。这意味着,要让它在公网或公司内网里被访问,你必须配置一个反向代理(Reverse Proxy)。Nginx 是最成熟、最广泛的选择。
第一步:获取并构建 UI
官方 UI 代码在
https://github.com/openclaw/openclaw-ui
。克隆后,进入目录,运行
npm install && npm run build
。这会生成一个
build/
文件夹,里面就是所有静态文件。
第二步:编写 Nginx 配置
创建
nginx.conf
:
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 上游服务:指向你的 OpenClaw API
upstream openclaw_api {
server 127.0.0.1:8000; # 这是 docker-compose 中 openclaw-api 的端口
}
server {
listen 80;
server_name your-ai-domain.com;
# 处理静态文件
location / {
root /path/to/your/openclaw-ui/build;
try_files $uri $uri/ /index.html;
}
# 将所有 /api/ 开头的请求,代理到上游 API
location /api/ {
proxy_pass http://openclaw_api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 关键:WebSocket 支持,用于实时流式响应
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 重定向 HTTP 到 HTTPS(如果使用 SSL)
# if ($scheme != "https") {
# return 301 https://$server_name$request_uri;
# }
}
}
第三步:启动 Nginx 并测试
将
nginx.conf
放到
/etc/nginx/nginx.conf
,然后运行
nginx -t
测试配置语法,
nginx -s reload
重新加载。现在,访问
http://your-ai-domain.com
,你应该能看到和
localhost:3000
一模一样的 UI 界面。最关键的是,打开浏览器的开发者工具(F12),切换到 Network 标签页,你会发现所有的 API 请求,都变成了
http://your-ai-domain.com/api/v1/chat/completions
,而不是
http://localhost:8000/api/v1/chat/completions
。这证明反向代理已经成功地将前端的请求,透明地转发给了后端的 OpenClaw API 服务。
这个配置里,
proxy_http_version 1.1
和
Upgrade
头的设置是专门为 WebSocket 流式响应准备的。OpenClaw 的 Chat API 支持
stream=true
参数,当开启时,它会以
text/event-stream
的格式,逐块(chunk)返回 LLM 的生成结果,实现类似 ChatGPT 那样的“打字机”效果。如果没有正确配置 WebSocket,这个流式响应就会失败,前端会一直等待,直到超时。
4. 常见问题与排查技巧实录:那些只在深夜才出现的 Bug
4.1 “为什么会延迟?”——深入剖析 OpenClaw 的响应时间瓶颈
网络热词里反复出现的“openclaw 为什么会延迟”,是部署后最常被问及的问题。延迟不是单一原因造成的,而是一个典型的“木桶效应”:整个请求链路上最短的那块板,决定了最终的响应速度。我用一个真实的客户案例来拆解。
客户报告说,他们的 OpenClaw 智能体在回答一个简单问题时,平均需要 8-12 秒,而他们本地测试 Ollama 的
qwen2:7b
模型,单次推理只要 1.5 秒。我立刻让他们执行了三步诊断:
第一步:测量 API 层的延迟
在服务器上,直接
curl
调用 OpenClaw 的 API,绕过所有前端:
time curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "ollama:qwen2:7b",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'
结果:耗时 2.3 秒。这说明,从 API 接收到返回,本身只花了 2.3 秒,远低于 8 秒。问题不在 OpenClaw 框架本身。
第二步:测量模型层的延迟
在同一个服务器上,直接
curl
调用 Ollama 的原生 API:
time curl -X POST "http://localhost:11434/api/chat" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2:7b",
"messages": [{"role": "user", "content": "你好"}]
}'
结果:耗时 1.8 秒。这和客户本地测试的结果一致。模型层也没问题。
第三步:检查网络路径
这时,我注意到客户的
docker-compose.yml
里,
openclaw-api
服务的
OPENCLAW_DEFAULT_MODEL
配置是
http://host.docker.internal:11434
。而他们的 Ollama 服务,是运行在宿主机上,监听
0.0.0.0:11434
。问题就在这里:
host.docker.internal
是 Docker Desktop 的特性,在 Linux 服务器上并不存在!客户把本地 Mac 的配置,原封不动地搬到了云服务器上,导致
openclaw-api
容器根本无法解析
host.docker.internal
这个域名,DNS 查询失败后,会有一个长达 5 秒的超时,然后才尝试下一个 DNS 服务器,最终才连上
127.0.0.1
。这 5 秒,就是那 8 秒延迟里,最“冤枉”的部分。
解决方案很简单:在云服务器的
docker-compose.yml
中,将模型地址改为
http://172.18.0.1:11434
。
172.18.0.1
是 Docker 默认网桥
docker0
的 IP 地址,它在所有 Linux 发行版上都是稳定的,代表“宿主机在 Docker 网络中的网关”。修改后,
curl
测试时间立刻降到了 2.1 秒。
这个案例揭示了一个核心原则: 在排查 OpenClaw 延迟时,永远要从最底层(网络)开始,一层一层向上验证,而不是一上来就怀疑是框架或模型的问题 。一个完整的延迟排查清单如下:
| 层级 | 检查点 | 快速验证命令 | 正常耗时 | 异常表现 |
|---|---|---|---|---|
| 网络层 |
openclaw-api
容器能否 ping 通宿主机
|
docker exec openclaw-api-1 ping -c 3 172.18.0.1
| < 10ms |
Destination Host Unreachable
|
| 模型层 | 直接调用模型 API |
curl -X POST http://172.18.0.1:11434/api/chat ...
| ≈ 本地测试值 |
> 5 秒,或
Connection refused
|
| API 层 | 直接调用 OpenClaw API |
curl -X POST http://localhost:8000/v1/chat/completions ...
| 比模型层多 0.3-0.5 秒 |
> 2 秒,或
502 Bad Gateway
|
| 前端层 | 浏览器访问 UI |
打开
http://your-domain.com
,F12 看 Network
| < 100ms (HTML) |
Failed to load resource
|
4.2 技能加载失败:
ModuleNotFoundError
的终极解法
ModuleNotFoundError: No module named 'xxx'
是技能开发中最让人抓狂的错误之一。它通常出现在你为技能引入了新的第三方库之后,比如你想用
requests
调用一个 HTTP API,于是你在
requirements.txt
里加了
requests==2.31.0
,然后
docker-compose up
,结果
openclaw-worker
启动
更多推荐

所有评论(0)