OpenClaw 配置续集:网络修复、邮件插件与 Ollama 接入
#OpenClaw #Docker #Ollama #邮件插件 #网络排障
接上篇 《N100 自托管 OpenClaw + Hermes Agent 部署实录》,记录第二阶段的配置与踩坑。
一、修复 Docker 容器无法访问公网 WAN TCP
现象
容器内 ping 1.1.1.1 正常(ICMP 通),但 curl https://api.deepseek.com 超时。导致 DeepSeek / Anthropic API 全部失败,日志输出:
1 | Error: All models failed (2): deepseek/deepseek-chat: LLM idle timeout (120s) |
根本原因
N100 使用 WiFi(wlp2s0)上网。Docker bridge 模式下,NAT/MASQUERADE 规则在某些 WiFi 驱动上对 TCP 转发有问题——ICMP 能通(路由层),WAN TCP 超时(NAT 层)。
修复:改用 network_mode: host
1 | # ~/dockerfile/openclaw/docker-compose.yml |
注:
network_mode: host后不需要ports映射,容器直接绑定宿主机 IP。
同步修改 openclaw.json 的 bind
之前 bind: loopback 只在本机 127.0.0.1 监听。改为 lan 后绑定局域网 IP:
1 | { |
开放 UFW 防火墙
1 | sudo ufw allow 18789/tcp |
验证:从 Mac curl http://192.168.0.155:18789/ 返回 HTML,通。
二、配置邮件插件(163 + QQ)
最终有效的 openclaw.json 邮件段
1 | { |
注意:gmail 插件 manifest id 是 gmail(不是包名 @manuelfedele/openclaw-gmail-plugin),plugins.entries 的 key 必须用 gmail。
安装命令
1 | # 停容器 → 安装 → 重启(避免文件锁) |
三、踩坑:email 插件工具无法被 Agent 识别
现象
QQ(gmail 插件)工具全部正常,但 163(email 插件)工具 email_mailboxes_list 等完全不出现,Agent 报:
1 | ⚠️ 🛠️ `run openclaw email` failed |
openclaw plugins list 显示 email 状态为 loaded、toolNames: []。
排查过程
-
检查网络连通性:
node -e "tls.connect(993, 'imap.163.com', ...)"→CONNECTED,排除网络问题 -
检查插件 manifest:发现
email/openclaw.plugin.json只有skills字段,没有contracts.tools
1 | // email 插件(有问题的原始 manifest) |
1 | // gmail 插件(正常的 manifest) |
根本原因
OpenClaw 通过 contracts.tools 静态声明来决定向 Agent 暴露哪些工具。skills 字段用于 skill runner 机制,并不等价于工具暴露。email 插件 v0.1.0 缺少这个声明。
修复
在容器内手动补充 contracts.tools(文件在 volume 中持久化):
1 | docker exec openclaw python3 -c " |
然后刷新插件注册表:
1 | docker exec openclaw openclaw plugins registry --refresh |
验证:
1 | docker exec openclaw openclaw agent --agent main \ |
四、接入 Ollama(Win11, RTX 2070 Super)
设备信息
| 项目 | 值 |
|---|---|
| IP | 192.168.0.195 |
| GPU | NVIDIA RTX 2070 Super Max-Q(8GB VRAM) |
| Ollama 版本 | 0.30.11 |
| 已下载模型 | qwen2.5-coder:7b、llava:7b、nomic-embed-text、qwen3:8b、deepseek-r1:8b |
问题:Ollama 托盘 App 硬覆盖 OLLAMA_HOST
Ollama Windows 托盘 app(0.30.11)会在启动 server 时把 OLLAMA_HOST 强制设为 http://127.0.0.1:11434,忽略系统环境变量。server 日志可以确认:
1 | OLLAMA_HOST:http://127.0.0.1:11434 |
解决方案:Windows portproxy 内核级端口转发
不修改 Ollama 进程,直接在 Windows 网络栈层做端口映射:
1 | netsh interface portproxy add v4tov4 ` |
验证:
1 | netsh interface portproxy show all |
同时确保 Windows 防火墙开放入站规则:
1 | New-NetFirewallRule -DisplayName 'Ollama LAN' ` |
portproxy规则重启后自动保留,无需额外配置。
更新 openclaw.json
1 | { |
OpenClaw 检测到配置变更后热重载,无需重启容器:
1 | [reload] config change detected; evaluating reload (models.providers.ollama.baseUrl) |
五、当前完整 openclaw.json
1 | { |
六、经验总结
| 问题 | 根本原因 | 解决方案 |
|---|---|---|
| Docker 容器 WAN TCP 不通 | bridge 模式 + WiFi NAT 问题 | network_mode: host |
| email 插件工具 Agent 不可见 | manifest 缺少 contracts.tools |
手动补充 + plugins registry --refresh |
| Ollama 只监听 127.0.0.1 | 托盘 app 硬覆盖环境变量 | netsh portproxy 内核级转发 |
| 局域网无法访问 18789 端口 | UFW 默认拒绝 | sudo ufw allow 18789/tcp |
| 插件 config 注入 SecretRef 失败 | TypeBox Type.String() 校验拒绝对象 |
明文写入 openclaw.json |
七、迁移至 RTX 5080 新主机(2026-07-18)
背景
旧推理机 milin_win11(192.168.2.12,RTX 2070 Super 8GB)已就位,新主机 milin_desktop(192.168.2.10,RTX 5080 16GB)完成配置。将 OpenClaw 主力 LLM 切换至新主机的本地 Ollama。
新主机 Ollama 使用 NSSM 服务运行,直接绑定 0.0.0.0:11434,不再需要 portproxy。
坑:WiFi 网络为 Public,防火墙规则不生效
Windows 连接新 WiFi 时默认为 Public 网络配置文件。已有的防火墙规则设置了 Profile=Private,Domain,对 Public 网络无效,导致端口 11434 虽然在 netstat 中显示监听,但外部无法连接(连接超时)。
定位方法:
1 | # 从 N100 测试 TCP 连接 |
修复:
1 | # 方案 A:将 WiFi 改为 Private(推荐) |
迁移步骤
1 | # 1. 更新 N100 上的 OpenClaw .env |
当前 .env LLM 配置
1 | OPENCLAW_LLM_PROVIDER=ollama |
模型
qwen3.5-35b-a3b:latest为 Q4_K_M 量化(22 GB),从旧机局域网传输而非重新下载,首次加载约 9s。
八、从 Ollama 切换到 llama-server(2026-07-18)
背景
实测 Ollama 推理 qwen3.5-35b-a3b Q4_K_M 的 pp(prompt 处理)速度仅 39.9 tok/s,而 llama.cpp 直接运行可达 164 tok/s(提升 4x)。tg(token 生成)两者相当(~11 tok/s),瓶颈在 PCIe 带宽(模型 20.49 GiB > VRAM 16.3 GiB,约 4 GiB 在 CPU RAM)。由于我更侧重长上下文处理速度,切换到 llama-server 收益明显。
llama-server 配置(milin_desktop)
以计划任务运行,端口 11435(与 Ollama 11434 分开,方便回退):
1 | @echo off |
-rea off:禁用 Qwen3.5 默认开启的 thinking 模式。不禁用时content字段为空,全部输出在reasoning_content,OpenClaw 收到空响应。
验证 API 可达(从 N100):
1 | curl -s http://192.168.2.10:11435/v1/chat/completions \ |
添加 llamacpp provider 到 openclaw.json
在 /home/milin/dockerfile/openclaw/openclaw.json(容器直接挂载的顶层文件)的 models.providers 中添加:
1 | "llamacpp": { |
同时将 agents.defaults.model.primary 改为 "llamacpp/qwen3.5-35b-a3b"。
openclaw.json 内的 providers/agents 字段支持热重载,无需重启容器。
更新 .env
1 | OPENCLAW_LLM_PROVIDER=llamacpp |
.env变更不热重载,需要docker compose down && docker compose up -d。
坑:顶层 openclaw.json 优先级高于 data/ 目录
docker-compose.yml 挂载了两层:
1 | volumes: |
文件挂载会覆盖目录挂载中的同名文件。修改 ./data/openclaw.json 无效,必须修改顶层的 ./openclaw.json。
坑:Ollama 插件自动发现覆盖模型路由
OpenClaw 的 Ollama 插件会在启动时查询 Ollama 实例,自动注册发现的所有模型(如 ollama/qwen3.5-35b-a3b:latest)。当 llamacpp 配置的模型 ID 与 Ollama 自动发现的模型名匹配时,Ollama 插件注册的版本(含 :latest 标签)会在模型解析时优先于 llamacpp provider。
现象:启动日志显示 agent model: llamacpp/qwen3.5-35b-a3b,但实际请求走的是 provider=ollama model=qwen3.5-35b-a3b:latest。
修复:如果已切换到 llama-server,将 Ollama 插件禁用:
1 | # 修改 /home/milin/dockerfile/openclaw/openclaw.json |
此配置支持热重载,无需重启容器。
坑:会话模型选择存储在 sessions.json 和 trajectory.jsonl,不随 provider 配置热重载
OpenClaw 的 WebChat UI 每次连接都会广播当前已选中的模型(存储于浏览器 localStorage)。这个 model_change 事件会覆盖服务端的 agents.defaults.model.primary 配置,并写入 session 的 JSONL 文件中持久化。
现象:已将 agents.defaults.model.primary 设为 llamacpp/qwen3.5-35b-a3b,gateway 启动日志也显示正确,但 WebChat 发来的消息仍走 deepseek-chat(因为 UI 端 localStorage 里记的是 deepseek-chat)。
修复:在 WebChat UI 的模型选择器中手动切换到 llamacpp/qwen3.5-35b-a3b。切换后,UI 会持久化新选择,后续连接自动推送 llamacpp 模型。
llamacpp 首次调用性能说明
llama.cpp 的 KV cache 是 session 级的。第一次请求需要完整 prefill 系统提示(约 24,000 token / ~300 秒);一旦缓存预热,后续调用通过 LCP(最长公共前缀)匹配复用缓存,仅需处理新增 token:
| 指标 | 冷启动(首次) | 热缓存(后续) |
|---|---|---|
| prefill 时间 | ~300 s (24K token) | ~9 s (519 token) |
| generation 速度 | 3.8 tok/s | 4.6 tok/s |
| 总响应时间 | ~5 分钟 | ~30 秒 |
| KV graphs reused | 162 | 259 |
建议:服务启动后先发送一条消息"预热",之后正常使用响应时间约 30 秒。
当前最终配置
openclaw.json 关键变更:
-
models.providers.llamacpp:新增,baseUrl: http://192.168.2.10:11435/v1 -
agents.defaults.model.primary:llamacpp/qwen3.5-35b-a3b -
plugins.entries.ollama.enabled:false(避免模型路由冲突) -
agents.defaults.timeoutSeconds:600(首次冷启动需要 ~300 秒) -
models.providers.llamacpp.timeoutSeconds:600
当前 .env LLM 配置:
1 | OPENCLAW_LLM_PROVIDER=llamacpp |
九、待配置(计划中)
-
2026-07-24:
deepseek-chat→deepseek-v4-flash(模型重命名)
十、TODO:llama-server 自动启停与 OpenClaw 云端 API 降级
场景
打游戏或运行高 GPU 占用任务时,需要手动停止 llama-server 释放显存。目前已在 milin_desktop 桌面部署:
-
Stop_Llama.bat→ 双击停止 llama-server,释放 GPU -
Start_Llama.bat→ 双击启动 llama-server,等待就绪后弹出通知
待自动化的功能:
10.1 GPU 占用监测 → 自动停止
1 | # TODO: 用 NVML / nvidia-smi 监听 GPU 利用率 |
实现方案:Windows 计划任务 + PowerShell 脚本轮询 nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader
10.2 llama-server 不可用时自动切 OpenClaw 到云端 API
目前 OpenClaw 有 fallback 链配置(anthropic/claude-sonnet-4-6),但需要 llamacpp 调用失败后才触发,有延迟。
理想方案:llama-server 停止前,主动通知 OpenClaw 切换 provider。
1 | # 方案 A:修改 openclaw.json 的 primary model(需容器内执行) |
10.3 OpenClaw 调用时检测 llama-server 状态 → 自动唤醒
当 OpenClaw 有请求到来但 llama-server 未运行时:
-
检测
http://192.168.2.10:11435/health失败 -
触发
schtasks /Run /TN LlamaServer -
轮询等待就绪(约 15-30 秒,模型加载)
-
请求继续走 llamacpp
实现方案:llama-server 前置代理(nginx/caddy 或轻量 Python 脚本),拦截 /v1/* 请求并按需唤醒。
1 | OpenClaw → N100:11435_proxy → 检测 milin_desktop:11435 状态 |
