1. OpenClaw Memory 本地模式为什么必须自己搭推理链路
OpenClaw 的 Memory 模块本质上是一套「语义检索」系统,它要做的事情是把你和 Agent 的历史对话、工作区笔记、会话摘要切成片段,再用一个 embedding 模型把每段文本转成向量,存进本地向量库,之后每次对话前做一次相似度召回。这套流程里最关键的组件就是 embedding provider,也就是「谁来把文字变成向量」。默认情况下 OpenClaw 只认三种 provider:openai、gemini、local。前两个都要走远端 API,意味着你的记忆内容会被发到外部服务,而且每次召回都在烧 token;local 模式则是把 embedding 模型跑在你自己机器上,数据不出本地,长期用下来成本几乎为零。
问题在于,local 模式并不是装个包就能跑。它底层依赖 node-llama-cpp,而 node-llama-cpp 又需要原生编译 llama.cpp,llama.cpp 想用 GPU 加速就得有 CUDA 和 cuDNN。这条链路任何一环断了,Memory 都会静默降级或者直接报错。我一开始跑openclaw doctor的时候看到的就是那句经典的提示:Memory search is enabled but no embedding provider is configured,语义召回直接不工作。更坑的是,网上关于 OpenClaw Memory 本地模式的资料非常零散,很多只讲了改配置,没讲清楚 node-llama-cpp 的原生编译前置条件,导致我在这上面绕了很久。
这篇内容适合两类人:一是已经在 Ubuntu 上跑 OpenClaw、想让 Memory 真正生效但不想用 OpenAI/Gemini 的;二是想搞清楚 llama.cpp + CUDA + node-llama-cpp 这条本地推理链路到底怎么串起来的。我会按「环境校验 → 编译 → 配置 → 验证 → 排障」的顺序走一遍,命令和配置片段都可以直接复制。另外,如果你暂时不想在本地折腾 GPU,或者想让 Agent 的对话模型走统一通道,也可以把远端 endpoint 指到 TaoToken 的 API 通道,用一套 Key 管理多个模型,后面我会给出切换方法。
先说清楚整体架构,避免你装到一半不知道自己在装什么。OpenClaw 的 Memory 检索流程是这样的:用户消息进来 → 切分成 chunk → 调用 embedding provider 生成向量 → 写入 sqlite-vec 向量表 → 下次对话时对 query 做 embedding → 向量相似度检索 → 把命中的记忆片段拼进上下文。local 模式下,embedding provider 就是 node-llama-cpp 加载的 GGUF 模型,默认是embeddinggemma-300M-Q8_0.gguf,大概 0.6GB。node-llama-cpp 本身是个 Node 绑定层,它需要调用 llama.cpp 的 native 库,而这个 native 库要针对你的 GPU 编译。所以顺序不能乱:CUDA → cuDNN → llama.cpp → node-llama-cpp → OpenClaw 配置。我见过有人直接npm install node-llama-cpp然后发现跑的是 CPU 版,速度慢到 Memory 索引一个工作区要几分钟,就是因为跳过了前面的编译步骤。
还有一点要提前说:OpenClaw 的 Memory 配置里有个fallback字段。如果你设成openai,本地失败时它会自动切到远端 embedding 并记录原因,看起来「能用」但其实一直在走远端。我建议调试阶段先设成none,这样任何本地链路问题都会直接暴露出来,不会被你忽略。等本地完全跑通再决定要不要保留 fallback。
2. Ubuntu 24.04 下 CUDA 与 cuDNN 的校验式安装
这一节的目标不是「装上就行」,而是装完之后你能用命令证明它真的可用。很多人装完 CUDA 就跑nvcc --version,看到版本号就以为成了,结果编译 llama.cpp 时GGML_CUDA=ON没生效,最后还是 CPU 版。所以我会在每一步都加验证命令。
先确认你的显卡和驱动状态。Ubuntu 24.04 上执行:
nvidia-smi如果这条命令报 command not found,说明 NVIDIA 驱动还没装,先通过ubuntu-drivers devices看推荐驱动版本,再用sudo apt install nvidia-driver-<版本>安装,重启后再验证。nvidia-smi右上角会显示 CUDA Version,那是驱动支持的最高 CUDA 版本,不是你已安装的版本,别混淆。
接下来装 CUDA Toolkit。我用的方式是 NVIDIA 官方 apt 源,比 runfile 干净,卸载也方便:
mkdir -p /opt/nvidia && cd /opt/nvidia wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-ubuntu2404.pin sudo mv cuda-ubuntu2404.pin /etc/apt/preferences.d/cuda-repository-pin-600 wget https://developer.download.nvidia.com/compute/cuda/13.1.1/local_installers/cuda-repo-ubuntu2404-13-1-local_13.1.1-590.48.01-1_amd64.deb sudo dpkg -i cuda-repo-ubuntu2404-13-1-local_13.1.1-590.48.01-1_amd64.deb sudo cp /var/cuda-repo-ubuntu2404-13-1-local/cuda-*-keyring.gpg /usr/share/keyrings/ sudo apt-get update sudo apt-get -y install cuda-toolkit-13-1装完必须配环境变量,否则nvcc找不到:
echo 'export PATH=/usr/local/cuda-13.1/bin${PATH:+:${PATH}}' | sudo tee -a /etc/profile.d/cuda.sh echo 'export LD_LIBRARY_PATH=/usr/local/cuda-13.1/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}' | sudo tee -a /etc/profile.d/cuda.sh source /etc/profile.d/cuda.sh验证:
nvcc --version输出里能看到Cuda compilation tools, release 13.1才算成功。注意LD_LIBRARY_PATH这一条很关键,llama.cpp 编译时链接libcudart.so靠的就是它,漏了会在 cmake 阶段报找不到 CUDA 库。
然后是 cuDNN。cuDNN 是 CUDA 的深度神经网络加速库,llama.cpp 的 CUDA 后端会用到它做部分算子加速:
wget https://developer.download.nvidia.com/compute/cudnn/9.19.0/local_installers/cudnn-local-repo-ubuntu2404-9.19.0_1.0-1_amd64.deb sudo dpkg -i cudnn-local-repo-ubuntu2404-9.19.0_1.0-1_amd64.deb sudo cp /var/cudnn-local-repo-ubuntu2404-9.19.0/cudnn-*-keyring.gpg /usr/share/keyrings/ sudo apt-get update sudo apt-get -y install cudnn验证安装位置和版本:
dpkg -L libcudnn9-cuda-13 | head -20 dpkg -l | grep cudnn这里有个容易踩的坑:包名大小写敏感,libcudnn9-cuda-13里的cuda-13要和你实际装的 CUDA 大版本对应,装的是 13.1 就写 13。如果你dpkg -l | grep cudnn什么都没输出,说明包名不对或者源没更新成功。
想更彻底地验证 CUDA + cuDNN 能跑深度学习算子,可以装个 PyTorch 做一次张量运算。这一步不是必须的,但能帮你排除「驱动装了但运行时不可用」的情况:
python -c "import torch; print(torch.cuda.is_available()); print(torch.version.cuda)"输出True和对应的 CUDA 版本就说明整条 GPU 运行时链路是通的。到这一步,CUDA 和 cuDNN 才算真正校验完成,可以进入 llama.cpp 编译。
3. 编译 llama.cpp 并让 node-llama-cpp 走 CUDA
llama.cpp 是整个本地推理链路的核心,node-llama-cpp 只是它的 Node 封装。所以先把它编译好,并且确保 CUDA 后端被启用。
克隆源码并编译:
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build -DGGML_CUDA=ON -DLLAMA_OPENSSL=ON cmake --build build --config Release -j$(nproc)两个参数解释一下:GGML_CUDA=ON是启用 CUDA 后端,这是 GPU 加速的关键;LLAMA_OPENSSL=ON是为了支持从 HuggingFace 直接拉模型时的 HTTPS 请求,不加的话llama-cli -hf会报 SSL 相关错误。我第一次编译时漏了 OpenSSL,后面下载模型一直失败,回头重新编译才解决。
编译完成后验证二进制:
./build/bin/llama-cli --version如果输出里带CUDA字样,说明 GPU 后端编译进去了。再用一个小模型做推理验证:
./build/bin/llama-cli -hf Qwen/Qwen3-0.6B-GGUF:Q8_0 -p "hello" -n 16这条命令会从 HuggingFace 拉取 Qwen3-0.6B 的 GGUF 量化模型并跑一次推理。如果能看到生成的 token 输出,说明 llama.cpp 本身没问题。如果报 SSL 错误,回到上一步确认LLAMA_OPENSSL=ON;如果报 CUDA 相关错误,检查LD_LIBRARY_PATH是否包含/usr/local/cuda-13.1/lib64。
接下来是 node-llama-cpp。这里有个认知误区:很多人直接npm install -g node-llama-cpp就以为装好了,但这样装出来的往往是预编译的 CPU 版,或者干脆没有 native 绑定。正确做法是让它基于你已经编译好的 llama.cpp 做原生构建:
sudo apt-get update sudo apt-get install -y build-essential cmake git libstdc++6 libgomp1 npx node-llama-cpp source download npx node-llama-cpp source buildsource download会拉取 node-llama-cpp 对应的 llama.cpp 源码,source build会针对你的环境编译。这一步如果卡住或者报错,先执行npx --no node-llama-cpp inspect gpu看 GPU 检测结果。我实测下来,如果 llama.cpp 已经编译成功,inspect gpu能正确识别到 CUDA,但有时会提示 Vulkan 检测到却使用失败,这个提示不影响 CUDA 路径,可以忽略。
验证 node-llama-cpp 版本和 GPU 状态:
node-llama-cpp --version npx --no node-llama-cpp inspect gpu版本号能正常输出(我这边是 3.16.1),并且 GPU 检测里 CUDA 显示可用,就说明 node-llama-cpp 已经能调用 GPU 了。这一步是整个链路里最容易出问题的环节,如果source build一直失败,可以退一步用npm install -g node-llama-cpp装预编译版,先保证功能可用,但性能上会是 CPU 推理,Memory 索引速度会明显变慢。
到这里,本地推理链路的三层(CUDA/cuDNN → llama.cpp → node-llama-cpp)就都通了。接下来是把它接进 OpenClaw 的配置。
4. OpenClaw Memory 配置片段与 sqlite-vec 修复
OpenClaw 的配置文件在~/.openclaw/openclaw.json。Memory 相关的配置挂在agents.defaults.memorySearch下面。下面是我实际在用的片段,你可以直接对照修改:
{ "agents": { "defaults": { "model": { "primary": "zai/glm-5" }, "models": { "zai/glm-5": { "alias": "GLM" } }, "workspace": "/home/band/.openclaw/workspace", "compaction": { "mode": "safeguard", "reserveTokensFloor": 20000, "memoryFlush": { "enabled": true, "softThresholdTokens": 4000, "systemPrompt": "Session nearing compaction. Store durable memories now.", "prompt": "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store." } }, "maxConcurrent": 4, "subagents": { "maxConcurrent": 8 }, "memorySearch": { "provider": "local", "local": { "modelPath": "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf" }, "fallback": "none", "sources": ["memory", "sessions"], "experimental": { "sessionMemory": true }, "store": { "vector": { "enabled": true, "extensionPath": "~/.npm-global/lib/node_modules/sqlite-vec/node_modules/sqlite-vec-linux-x64/vec0.so" } } } } } }几个关键点必须说清楚。第一,modelPath写的是 HuggingFace 上的模型标识,不是本地文件路径。我一开始以为要先把 GGUF 下载到本地再填绝对路径,结果卡了很久。OpenClaw 在provider: "local"时会通过 node-llama-cpp 解析这个标识,如果本地缓存没有就自动下载到~/.node-llama-cpp/models,而且支持断点续传。第二,fallback设成none,这样本地失败不会偷偷切远端,问题会直接暴露。第三,extensionPath指向 sqlite-vec 的动态库,这个路径因安装方式而异,下面单独讲。
sqlite-vec 是向量存储的扩展。OpenClaw 默认会去找一个sqlite-vec.so,如果找不到就会报:
[memory] sqlite-vec unavailable: /home/band/sqlite-vec.so: cannot open shared object file: No such file or directory我试过按官方编译教程从源码编译 sqlite-vec,./scripts/vendor.sh && make loadable走下来还是有报错,没继续折腾。后来用了一个更省事的办法,直接通过 npm 装:
npm install -g sqlite-vec装完之后动态库在~/.npm-global/lib/node_modules/sqlite-vec/node_modules/sqlite-vec-linux-x64/vec0.so。如果你不确定 npm 全局目录在哪,用whereis node-llama-cpp或whereis pnpm找到bin目录,lib就在同级。把这个路径填进extensionPath,重启 gateway 后 sqlite-vec 就能加载了。
配置改完执行:
openclaw gateway restart openclaw memory status --deep如果看到模型开始下载到~/.node-llama-cpp/models,说明 local provider 已经生效。等下载完成,memory status --deep会显示向量库状态和索引的片段数量。
顺便说下远端切换。如果你某些场景想让对话模型走统一通道,而不是本地模型,可以把 endpoint 指到 TaoToken 的 API 通道。TaoToken 提供统一的 Key 管理,一个 Key 可以调多个模型,适合在本地 GPU 忙不过来或者想用更大模型时做补充。配置方式是在 OpenClaw 的 provider 配置里把 base URL 设成https://taotoken.net/api,Key 用你在控制台生成的。这样本地 embedding 走 GPU,对话模型走远端,两边互不干扰。具体 Key 的获取和模型列表可以在 TaoToken 控制台 里看,接入细节参考 接入文档。
5. 常见报错对照:401、local proxy failed、reading choices、OAuth
这一节把我实际遇到和社区里高频出现的报错整理成对照表,方便你按图索骥。
| 报错关键词 | 出现位置 | 根因 | 处理方式 |
|---|---|---|---|
| 401 Unauthorized | 远端 provider 调用 | Key 无效或未配置 | 检查remote.apiKey或环境变量,重新生成 Key |
| local proxy failed | gateway 启动 | 本地 endpoint 不可达或端口占用 | 确认本地服务监听端口,检查防火墙 |
| reading choices | 对话请求返回 | 响应体不是预期 JSON 结构 | 确认 base URL 指向正确的 API 路径 |
| OAuth 相关 | 认证流程 | 认证方式与 provider 不匹配 | 改用 API Key 方式,或重新走认证 |
| sqlite-vec unavailable | memory status | 动态库路径错误 | 修正extensionPath指向 vec0.so |
| no local model file found | doctor | node-llama-cpp 未装或模型未下载 | 装 node-llama-cpp,确认 modelPath 格式 |
重点说几个。401通常出现在你把远端 provider 配好但 Key 没填对的时候。OpenClaw 的远端配置里 Key 可以放环境变量也可以放配置文件,如果你同时配了两处,以配置文件为准。local proxy failed一般是你把 base URL 指向了本地某个代理端口,但那个服务没起来。如果你用的是 TaoToken 的 API 通道,base URL 应该是https://taotoken.net/api,不要带多余的路径后缀,否则会走到错误的 endpoint 上。
reading choices这个报错比较隐蔽,它通常意味着你请求的 API 返回了非 OpenAI 兼容格式的响应。OpenClaw 的远端调用默认按 OpenAI 的choices结构解析,如果你的 endpoint 返回的是别的结构,就会在解析阶段报这个错。解决办法是确认你用的通道是 OpenAI 兼容的。TaoToken 的 API 通道是兼容格式,配好 Base URL、Key、Model ID 三件套就能直接用。
关于三件套,不管你用的是 Claude Code、Cline MCP 还是 Codex 的 auth.json,核心都是这三个字段:
{ "baseURL": "https://taotoken.net/api", "apiKey": "sk-你的Key", "model": "claude-sonnet-4-5" }Model ID 要写通道支持的完整标识,不要写别名。如果你在 OpenClaw 里同时配了本地 embedding 和远端对话模型,注意两者的配置块是分开的,别把 embedding 的 modelPath 和对话模型的 model 搞混。
还有一个坑是openclaw doctor和openclaw memory status --deep的提示不完全一致。doctor 更偏向配置检查,memory status 会实际去加载模型和向量库。所以修完配置后以memory status --deep的结果为准。如果 doctor 还报旧错误,先openclaw gateway restart再跑一次。
6. 一次对话请求验证本地与远端切换
配置改完、服务重启后,最后一步是用一次真实对话把两条链路都验证一遍。
先验证本地 Memory 是否工作。执行:
openclaw memory status --deep正常输出会包含向量库路径、已索引片段数、embedding 模型名称。如果模型还在下载,会显示下载进度。等下载完成后,往工作区写一条测试记忆,比如在~/.openclaw/workspace/memory/下建一个 markdown 文件写点内容,然后触发一次索引:
openclaw memory index --force openclaw memory search "测试关键词"如果 search 能返回你刚写的内容片段,说明本地 embedding + 向量检索整条链路是通的。
再验证远端对话通道。如果你配了 TaoToken 的 API 通道,可以用一次简单的模型对话来确认 Key 和 endpoint 都正确。在 OpenClaw 里发一条消息,或者直接用 curl 测:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"ping"}]}'返回里有choices字段和正常内容,就说明远端通道没问题。想快速对比不同模型的表现,可以在 模型对话 里直接试,不用改本地配置。
两条链路都验证通过后,你的 OpenClaw 就是一个「本地 embedding + 可选远端对话」的混合架构:Memory 检索完全在本地 GPU 上跑,数据不出机器;对话模型按需走远端统一通道,一个 Key 管多个模型。长期编码或者跑 Agent 任务的话,可以考虑 Coding Plan,额度更划算。
最后留一个实用技巧:如果你发现 Memory 索引速度慢,先确认npx --no node-llama-cpp inspect gpu里 CUDA 是可用状态。如果显示的是 CPU,说明 node-llama-cpp 没走 GPU,回到第 3 节重新做 source build。另外~/.node-llama-cpp/models目录会缓存下载的 GGUF,换模型时记得清理旧文件,不然磁盘会悄悄涨上去。