Skip to content
格物致知
返回

OpenClaw + Mem0 (OSS 模式) 部署踩坑实录

OpenClaw + Mem0 (OSS 模式) 部署踩坑实录

背景: 在 macOS (Apple Silicon) 上将 @mem0/openclaw-mem0 插件以 open-source 模式集成到 OpenClaw Gateway,使用自建 API 代理 (api.qadmlee.com) 替代 OpenAI 官方 API。

耗时: 约 10 小时(2026-02-20 全天)

最终状态: SDK 独立测试 add/search 通过 ✅,OpenClaw Gateway 集成仍有 baseURL 透传 bug 🔧


目录

  1. 阶段一:Mem0 API Server 容器化部署
  2. 阶段二:OpenClaw 插件安装与 SQLite Binding 地狱
  3. 阶段三:pgvector → Qdrant 迁移
  4. 阶段四:SQLITE_CANTOPEN 崩溃与 Shim 方案
  5. 阶段五:401 Unauthorized — baseURL 被吞掉
  6. 元坑:AI 辅助调试的系统性陷阱
  7. 替代方案:OpenMemory (CaviraOSS)
  8. 坑点速查表
  9. 最终架构

阶段一:Mem0 API Server 容器化部署

坑 1:官方 Docker 镜像缺 psycopg 依赖

现象mem0/mem0-api-server:latest 启动后连接 PostgreSQL 报 ModuleNotFoundError: No module named 'psycopg'

根因:官方镜像只预装了 SQLite 驱动,未包含 PostgreSQL 的 psycopg3 库和系统级 libpq-dev

解决:自定义 Dockerfile 补装依赖:

FROM mem0/mem0-api-server:latest
RUN apt-get update && apt-get install -y --no-install-recommends libpq-dev \
    && pip install --no-cache-dir "psycopg[binary,pool]" psycopg2-binary \
    && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /app/history

踩坑次数:3 轮 Dockerfile 重建(第一次漏 libpq-dev,第二次漏 psycopg2-binary,第三次漏 /app/history 目录)。


坑 2:SQLite history 路径不存在

现象sqlite3.OperationalError: unable to open database file(Python 端)

根因mem0Memory.from_config() 默认把 SQLite history 数据库写到 /app/history/,但容器内该目录不存在。

解决:Dockerfile 里 RUN mkdir -p /app/history,docker-compose 挂载 volume 持久化。


坑 3:Embedding 维度不匹配 (1536 vs 1024)

现象

psycopg.errors.DataException: expected 1536 dimensions, not 1024

根因:pgvector 表按 OpenAI 默认 1536 维创建,但实际使用的 text-embedding-qwen3-embedding-0.6b 模型输出 1024 维。

解决

  1. .envEMBEDDER_DIMS=1024
  2. docker volume rm mem0-server_pgdata 删掉旧 PG 数据
  3. 重启让 mem0 以 1024 维重建表

二次踩坑:第一次「以为改了」但远程 .env 实际没同步,日志暴露 1536 仍在。教训:改完配置后一定看启动日志确认参数生效。


坑 4:pgdata Volume 没有真正删除

现象:反复重启后维度错误依旧。

根因docker compose down 不删 volume,需要 docker compose down -v 或手动 docker volume rm。Volume 名可能带项目前缀(mem0-server_pgdata vs mem0_server_pgdata)。

解决docker volume ls | grep pg 确认实际名称后精准删除。


阶段二:OpenClaw 插件安装与 SQLite Binding 地狱

坑 5:jiti 的 __dirname 路径不到 sqlite3 native binary

现象

openclaw-mem0: recall failed: Error: Could not locate the bindings file.
→ /opt/homebrew/lib/node_modules/openclaw/node_modules/jiti/build/node_sqlite3.node

根因:OpenClaw 用 jiti(TypeScript 运行时)加载插件,jiti__dirname 解析到自己的目录而非 sqlite3 包的目录,导致 node-pre-gyp 的路径查找全部失败。这是 jiti 自身的 bug,与 Node.js 版本无关。

排查历程

  1. npm rebuild sqlite3 — sqlite3 被编译到了插件目录,但 jiti 仍然看不到
  2. ❌ 尝试 symlink hack — 在 OpenClaw 的 node_modules/ 中找不到原始 .node 文件
  3. ❌ 降级 Node.js v25 → v22 LTS — 编解决了 ABI 兼容但 jiti 路径问题依旧
  4. ✅ 最终发现:native binary 在插件目录 /Users/qadmlee/.openclaw/extensions/openclaw-mem0/node_modules/sqlite3/build/Release/node_sqlite3.node,但 jiti 的路径解析逻辑永远不会找到这里

解决:见阶段四的 SQLite Shim 方案。


坑 6:Node.js v25 缺少预编译 Binary

现象npm rebuild sqlite3 在 v25 上执行后仍无 .node 文件。

根因:Node.js v25 (ABI v141) 太新,sqlite3 / better-sqlite3 没有该版本的预编译 binary,需要手动 node-gyp rebuild

解决:降级到 Node.js v22 LTS (ABI v127),重新 npm install -g openclaw

二次踩坑:降级后旧 Gateway 进程(pid 78238)仍以 v25 运行,日志仍显示 runtimeVersion: 25.6.1。必须 kill -9 <pid> + launchctl bootout 彻底杀掉旧进程。


坑 7:openclaw gateway restart 并非原子操作

现象openclaw gateway restart 后日志里大量 Gateway already running (pid 78238); lock timeout,循环 10 秒一次持续数十次。

根因restart = stop + start,但 stop 通过 launchctl 异步执行,进程未完全退出时 start 就尝试绑定端口,被占用后 10s 重试。

解决

openclaw gateway stop
sleep 3
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
sleep 2
openclaw gateway install
openclaw gateway start

阶段三:pgvector → Qdrant 迁移

坑 8:Node.js SDK 不支持 pgvector

现象

openclaw-mem0: recall failed: Error: Unsupported vector store provider: pgvector

根因mem0 的 Node.js SDK(mem0ai/oss)只支持 Qdrant 作为向量存储,不支持 pgvector。 Python SDK 支持 pgvector,但 OpenClaw 的 @mem0/openclaw-mem0 插件用的是 Node.js SDK。

这是整个项目的关键转折点。 Python 端验证了 pgvector 全链路通过,但 OpenClaw 插件根本用不了。

解决

  1. docker-compose.yml 加 Qdrant 容器
  2. openclaw.jsonvectorStore.provider 改为 qdrant
  3. Python 端的 main.py 同步改回 Qdrant

坑 9:Qdrant Client/Server 版本不兼容

现象

Client version 1.13.0 is incompatible with server version 1.17.0

根因mem0ai 捆绑的 @qdrant/js-client-rest 是 1.13.0,默认拉取的 qdrant/qdrant:latest 是 1.17.0。客户端要求 minor 版本差不超过 1。

解决:固定 Docker 镜像版本为 qdrant/qdrant:v1.13.6(注意 必须带 v 前缀,不带会报 manifest unknown)。


坑 10:Qdrant Collection 维度又是 1536

现象:dimension → SDK 创建 collection 时使用默认 1536 维,searchPoints 返回 400。

根因:Node.js SDK 的 Qdrant provider 接受 dimension 字段(不是 embeddingModelDims),但代码 this.dimension = config.dimension || 1536 在某些路径下被忽略。

解决:预先手动创建正确维度的 collection:

curl -X PUT http://localhost:6333/collections/mem0_memories \
  -H "Content-Type: application/json" \
  -d '{"vectors":{"size":1024,"distance":"Cosine"}}'

阶段四:SQLITE_CANTOPEN 崩溃与 Shim 方案

坑 11:SQLITE_CANTOPEN 导致整个 Gateway 崩溃

现象

{"0":"[openclaw] Uncaught exception: Error: SQLITE_CANTOPEN: unable to open database file"}

每条 TG/Discord 消息都触发崩溃,Gateway 进程直接退出。

根因:jiti 加载 sqlite3 native module 时路径解析到错误位置。即使 binary 存在于插件目录,jiti 的 __dirname 始终指向自己。这不是配置问题,是 OpenClaw 使用 jiti 编译 TypeScript 插件时的固有兼容性缺陷。

解决:SQLite Shim(垫片)

创建一个纯 JavaScript 的假 sqlite3 包,让所有数据库操作静默返回成功:

// ~/.openclaw/extensions/openclaw-mem0/node_modules/sqlite3/lib/sqlite3.js (覆盖)
class Database {
  constructor(path, mode, cb) { if (typeof cb === 'function') cb(null); }
  run(sql, params, cb) { if (typeof cb === 'function') cb(null); }
  all(sql, params, cb) { if (typeof cb === 'function') cb(null, []); }
  get(sql, params, cb) { if (typeof cb === 'function') cb(null, undefined); }
  close(cb) { if (typeof cb === 'function') cb(null); }
}
module.exports = { Database, verbose: () => module.exports };

代价:History 记录(操作日志)不会持久化,但核心的 recall/capture 功能不受影响(记忆存在 Qdrant 中)。


阶段五:401 Unauthorized — baseURL 被吞掉

坑 12:SDK OpenAIEmbedder 硬编码 apiKey,丢弃 baseURL

现象:SDK 独立测试 Memory.search() 始终报 401 You didn't provide an API key,即使配置了正确的 baseURL 和 apiKey。

根因(SDK 源码级 Bug)

Bug #1:OpenAIEmbedder 构造器硬编码

// mem0ai/dist/oss/index.js 第 206 行
this.openai = new import_openai.default({ apiKey: config.apiKey });
// ❌ 完全没有传入 baseURL!

所有请求默认打到 api.openai.com,而用户的 key 只在自建代理有效。

Bug #2:ConfigManager.mergeConfig() 白名单过滤丢弃 baseURL

配置合并逻辑中,embedder 配置只提取了 apiKey / url / model,直接把用户写的 baseURL 丢弃了:

// 即使 openclaw.json 里写了 baseURL: "https://api.qadmlee.com/v1"
// ConfigManager 在合并时只保留了白名单字段
embedder: { model: ..., apiKey: ..., url: ... }  // baseURL 消失了

Bug #3:OpenAIStructuredLLM 同样漏传

LLM 端的结构化提取类也存在相同问题。

解决(热补丁脚本)

// hot_patch.js — 修改 SDK dist 文件
const fs = require('fs');
const SDK = '~/.openclaw/extensions/openclaw-mem0/node_modules/mem0ai/dist/oss/index.js';
let code = fs.readFileSync(SDK, 'utf8');

// 1. 修复 ConfigManager 吞掉 embedder baseURL
code = code.replace(
  /embedder:\s*\{[^}]*\}/,
  match => match.replace('}', ', baseURL: userConf?.baseURL }')
);

// 2. 修复所有 OpenAI 客户端构造器
code = code.replace(
  /new (import_openai\d*\.default)\(\s*\{\s*apiKey:\s*config\.apiKey\s*\}\s*\)/g,
  'new $1({ apiKey: config.apiKey, baseURL: config.baseURL || config.url || process.env.OPENAI_BASE_URL })'
);

fs.writeFileSync(SDK, code);

当前状态:补丁已修复 embedder 端的 401,LLM 端(gpt-5.2 结构化调用)仍在调试中。


元坑:AI 辅助调试的系统性陷阱

元坑 1:AI 幻觉 OpenClaw CLI 命令

AI 给出的命令实际结果
openclaw chaterror: unknown command 'chat'
openclaw gateway logserror: too many arguments for 'gateway'
openclaw mem0 stats❌ 不存在 mem0 子命令
openclaw mem0 search❌ 不存在

正确命令openclaw memory searchopenclaw logsopenclaw tui

教训:AI 对非主流/闭源工具的 CLI 命令是猜测的。先跑 --help 确认命令存在再操作。


元坑 2:AI 猜测 config 字段名导致多轮返工

AI 猜测的字段SDK 实际期望
embeddingModelDimsdimension
historyStore.type不存在,历史由 SDK 内部管理
oss.vectorStore.config.embedding_model_dimsdimension
Node.js SDK 的 baseURL 在 config 中ConfigManager 白名单过滤

教训:遇到配置不生效时,直接 grep SDK 源码确认字段名,不要相信 AI 的推测。


元坑 3:Python SDK ≠ Node.js SDK 的功能差异

功能Python SDKNode.js SDK
pgvector✅ 支持❌ 不支持
Qdrant
Redis
Supabase
embedding_model_dims 字段dimension

教训:不要假设两种语言的 SDK 功能对等。这个差异导致了 pgvector 全部白做、不得不迁移到 Qdrant。


替代方案:OpenMemory (CaviraOSS)

在调试过程中研究了 CaviraOSS/OpenMemory 作为替代方案:

维度mem0 (openclaw-mem0)OpenMemory (MCP)
集成方式OpenClaw 原生插件,自动 autoRecall/CaptureMCP 工具,AI 手动调用
部署复杂度坑极多(本文档内容)简单,SQLite 开箱即用
自动提取上下文✅ 框架级 Hook❌ 依赖 AI 主动调用
跨工具通用只对 OpenClawClaude/Antigravity/Cursor 通用
存储Qdrant (需外部)SQLite (本地)

结论:OpenMemory 部署更简单但缺少自动化的 autoCapture 机制,需要在 System Prompt 中指示 AI 主动存取记忆。


坑点速查表

#坑点阶段状态
1Docker 镜像缺 psycopg 依赖容器化✅ 已解决
2SQLite history 目录不存在容器化✅ 已解决
3Embedding 维度 1536 vs 1024 (pgvector)容器化✅ 已解决
4pgdata volume 未删除导致旧表残留容器化✅ 已解决
5jiti 路径解析找不到 sqlite3 binary插件✅ Shim 绕过
6Node.js v25 缺預编译 binary插件✅ 降级 v22
7Gateway restart 非原子操作插件✅ 已解决
8Node.js SDK 不支持 pgvector迁移✅ 切换 Qdrant
9Qdrant Client/Server 版本不兼容迁移✅ 固定 v1.13.6
10Qdrant Collection 维度 1536 默认值迁移✅ 手动建表
11SQLITE_CANTOPEN Gateway 崩溃运行时✅ SQLite Shim
12OpenAIEmbedder 硬编码丢 baseURLSDK Bug🔧 热补丁
13ConfigManager 白名单过滤 baseURLSDK Bug🔧 热补丁
14AI 幻觉 CLI 命令元坑⚠️ 需要人工验证

总结教训

一、AI 犯了什么错?

1. 不验证就给命令,造成大量无效操作

2. 猜测配置字段名,反复猜错

3. 没有提前确认 Node.js SDK 和 Python SDK 的功能差异

4. Dockerfile 分三次才写对

5. 给错 Qdrant 镜像版本号

6. 误判问题根因,导致弯路

7. 声称「不影响核心功能」但实际导致 Gateway 崩溃


二、用户犯了什么错?

1. 远程文件同步后没验证实际内容

2. 过度信任 AI 给出的方案,没有及时 challenge

3. 没有在每步操作后检查日志

4. 改 Node.js 版本后没重启终端 session


三、以后怎么避免?

对 AI 的约束规则

规则说明
先查后说对不确定的 CLI 命令、config 字段、Docker tag,必须先用工具验证(--help / grep 源码 / 查 Docker Hub),禁止凭记忆猜测
SDK 差异检查前置涉及跨语言 SDK(Python vs Node.js)时,第一步确认目标 SDK 的功能支持范围,不可用 Python 文档推导 Node.js 行为
一次性写对 Dockerfile先读基础镜像的 ENTRYPOINT/CMD 和依赖树,列出所有缺失依赖后一次补全,禁止「跑一次 → 报错 → 加一行 → 再跑」的循环
禁止无根据的「不影响」没有实际测试验证过的判断,不能说「不影响核心功能」。必须写「未验证,建议测试确认」
错误计数器连续 2 轮给出无效方案后,主动暂停并声明:「我可能判断错了根因,建议换个角度排查」

对用户的建议

建议说明
改配置后立刻 diff 验证远程 catdiff 确认文件内容已更新,检查启动日志确认参数生效
AI 说「不影响」时追问「证据?」让 AI 给出具体的代码路径或日志依据,而不是接受模糊保证
新工具前先跑 --help不管 AI 给什么命令,先 openclaw --help 看真实命令列表
大方案前先做最小验证在 AI 给出「pgvector 全套部署方案」时,先问「OpenClaw 插件到底用哪个 SDK?这个 SDK 支持 pgvector 吗?」——5 分钟的追问可以省 2 小时
版本切换后验证全链路切 Node.js 版本后:检查 node -v → 重启 Gateway → 看日志 runtimeVersion → 发消息测试。不只是看终端输出

最终架构

┌────────────┐     TG/Discord/Web      ┌──────────────────┐
│  用户终端   │ ─────────────────────── │  OpenClaw Gateway │
└────────────┘                         │  (Node.js v22)    │
                                       │                   │
                                       │ @mem0/openclaw-mem0│
                                       │  ├ autoRecall     │
                                       │  ├ autoCapture    │
                                       │  └ SQLite Shim    │
                                       └──────┬────────────┘
                                              │ mem0ai/oss (Node.js SDK)
                              ┌───────────────┼───────────────┐
                              │               │               │
                        ┌─────▼─────┐   ┌─────▼─────┐   ┌────▼────┐
                        │  Qdrant   │   │ LLM Proxy │   │Embedder │
                        │ v1.13.6   │   │gpt-5.2    │   │qwen3-0.6b│
                        │ :6333     │   │api.qadmlee│   │api.qadmlee│
                        └───────────┘   └───────────┘   └─────────┘

部署文件清单 (viking/mem0-server/):


写给后来人:如果你在 2026 年看到这份文档,mem0 的 Node.js SDK 很可能已经修了 baseURL 透传和 pgvector 支持。在开始部署前,先检查 mem0ai 的 changelog,或许这些坑已不再存在。


分享文章:

上一篇
你好世界