目录

memory_search 反复 fallback?一次配置冲突引发的血案

qmd 数据库完好无损,为什么 memory_search 还是 fallback 到本地引擎?

memory_search 反复 fallback?一次配置冲突引发的血案

引言

升级 OpenClaw 到 2026.5.12 后,memory_search 突然"哑火"了——明明配了 qmd 向量搜索引擎,结果每次查询都 fallback 到本地小模型,搜索质量断崖式下跌。

更诡异的是:qmd 数据库完全正常,1934 个文档、14966 个向量,integrity_check 全通过。那 unable to open database file 到底是什么意思?

这是一次完整的踩坑记录,包含 5 个阶段的排查、3 个错误方向、和 1 个最终用 lsof 找到的意外根因。

症状

先看看问题长什么样。调用 memory_search 时,返回 JSON 里的关键字段:

{
  "results": [],
  "provider": "local",
  "model": "sentence-transformers/all-MiniLM-L6-v2",
  "fallback": {
    "from": "qmd",
    "reason": "unable to open database file"
  },
  "debug": {
    "backend": "builtin",
    "effectiveMode": "n/a",
    "searchMs": 2000,
    "hits": 0
  }
}

provider 应该是 "qmd" 却变成了 "local",还带一个让人误判的 "unable to open database file"

所有 agent 的 memory_search 都受影响,搜索质量大幅下降(本地小模型 vs qmd 向量搜索+reranking),搜索延迟也增加了(本地模型推理慢)。

/images/Code-Art-Studio-images/memory-search-qmd-fallback/illustrations/1.webp

第一阶段:怀疑数据库损坏

最直觉的反应——数据库文件坏了或权限不对。

# 检查 qmd 数据库完整性
sqlite3 ~/.openclaw/agents/main/qmd/xdg-cache/qmd/index.sqlite "PRAGMA integrity_check;"
# 返回:ok

# 检查文档数量
sqlite3 ~/.openclaw/agents/main/qmd/xdg-cache/qmd/index.sqlite "SELECT count(*) FROM documents;"
# 返回:1934

# 检查向量数量
sqlite3 ~/.openclaw/agents/main/qmd/xdg-cache/qmd/index.sqlite "SELECT count(*) FROM content_vectors;"
# 返回:14966

所有 10 个 sqlite 文件全部通过 integrity_check。sqlite3 能正常打开,权限也没问题。

结论:数据库没问题,unable to open database file 不是字面意思。

教训:SQLite 的 “unable to open database file” 错误码有时是逻辑层面的判断(如"数据库不可用"),不一定是文件系统级别的"打不开"。需要看 lsof 确认实际打开的文件。

第二阶段:追踪 qmd 调用链

既然数据库本身没问题,那问题出在调用链路上。

查看 qmd-gateway 脚本发现,它设置了独立的 XDG 路径:

#!/bin/bash
export XDG_CONFIG_HOME="/home/administrator/.openclaw/agents/main/qmd/xdg-config"
export XDG_CACHE_HOME="/home/administrator/.openclaw/agents/main/qmd/xdg-cache"
exec /home/administrator/.bun/bin/qmd "$@"

这意味着存在两套数据库并存:

  1. qmd 原生数据库~/.cache/qmd/index.sqlite(32MB,400 文件)
  2. OpenClaw 管理的 qmd 数据库~/.openclaw/agents/main/qmd/xdg-cache/qmd/index.sqlite(106MB,1934 文档)

qmd 本身能正常工作:

/usr/local/bin/qmd-gateway search "test" -c memory-dir-main
# 正常返回结果

但 OpenClaw 日志里藏着关键错误:

qmd session-start sync failed: Error: qmd update failed (code 1):
[Error: EIO: i/o error, scandir '/mnt/g/Documents/Obsidian-oklife/oklife/AI/05-智能体/AI Agents智能体']

原来是 qmd 同步文件时遇到了 EIO 目录,导致 process crash。当时判断这就是 unable to open database file 的直接原因。

第三阶段:EIO 目录的噩梦

问题目录:/mnt/g/Documents/Obsidian-oklife/oklife/AI/05-智能体/AI Agents智能体

这是一个 WSL 2 环境下 NTFS 通过 9p 协议挂载的目录(/mnt/g/ 指向 Windows G 盘)。

排查时间线

  1. 在 Windows 侧删除重建该子目录 → 没用,WSL 9p 层仍然报 EIO
  2. 执行 chkdsk G: /f → G 盘被卸除,/mnt/g 挂载点失效
  3. 尝试手动 mount -t 9p 重新挂载 → 失败,WSL 内核没有编译 9p 模块
  4. 用户重启 WSL(wsl --shutdown)→ 挂载恢复,但目录 EIO 依然
  5. 再次在 Windows 侧剪切到桌面再粘贴回来 → 改名后 EIO 仍然

关键发现:即使目录重建,WSL 内核 9p 层缓存了旧的 inode 状态。stat 显示 inode 号完全一样(844424930139347),scandir 持续报 EIO。

WSL 9p 的限制

  • 9p 文件系统模块没有 modprobe,不能手动加载
  • 只能依赖 WSL 自动挂载机制
  • 某些 NTFS 特殊目录状态通过 9p 暴露给 Linux 时,会导致元数据操作失败

教训

  • chkdsk 会卸除卷,导致 /mnt/g 失效,需要重启 WSL
  • WSL 9p 目录缓存无法通过常规手段清除
  • 不要在 WSL 排查上过度发散,先回到核心问题

第四阶段:ignorePatterns 的尝试

既然修不了 EIO 目录,就想在配置里排除它。这是一系列失败的尝试:

尝试 1:在 openclaw.json 中添加 ignorePatterns

{
  "path": "/mnt/g/Documents/Obsidian-oklife/oklife/AI",
  "name": "ai",
  "pattern": "**/*.md",
  "ignorePatterns": ["**/05-agent智能体/**", "**/05-智能体/**"]
}

结果:openclaw config validate 报错:Unrecognized key: "ignorePatterns"。qmd 不支持此字段。

教训:不要向配置文件添加未经文档确认的字段,会导致 config validate 报错,污染配置。

尝试 2:在 qmd index.yml 中添加 exclude_patterns

ai:
  path: /mnt/g/Documents/Obsidian-oklife/oklife/AI
  pattern: "**/*.md"
  exclude_patterns:
    - "05-agent智能体/**"
    - "05-智能体/**"

结果:memory_search 仍然 fallback。qmd 也不支持 exclude_patterns 字段。

尝试 3:通过 config.patch / config.apply 修改 memory.qmd.paths → 失败:memory.qmd.paths 是受保护路径,不允许通过 patch 修改

最终用户手动清理了 ignorePatterns 配置,config validate 恢复通过。

第五阶段:lsof 一句话破案

排查已经陷入僵局——EIO 修不了、配置改不了、排除字段不支持。

转折点是用 lsof 看 Gateway 进程到底打开了什么文件:

lsof -p $(pgrep -f "openclaw.*gateway") | grep sqlite

结果让人大跌眼镜——Gateway 打开的是:

/home/administrator/.openclaw/memory/oc-engineer.sqlite

不是 qmd 的 index.sqlite!

检查这个 builtin 数据库:

sqlite3 ~/.openclaw/memory/oc-engineer.sqlite ".tables"
# chunks, chunks_fts, files, embedding_cache, meta

sqlite3 ~/.openclaw/memory/oc-engineer.sqlite "SELECT count(*) FROM chunks;"
# 0

sqlite3 ~/.openclaw/memory/oc-engineer.sqlite "SELECT count(*) FROM files;"
# 0

数据库结构在,但数据为空。再对照配置:

{
  "memory": {
    "backend": "qmd"
  },
  "agents": {
    "defaults": {
      "memorySearch": {
        "provider": "local",
        "model": "all-MiniLM-L6-v2",
        "fallback": "none"
      }
    }
  }
}

根因找到了

  1. memory.backend = "qmd"memorySearch.provider = "local" 配置冲突
  2. OpenClaw 执行 memory_search 时,先检查 ~/.openclaw/memory/<agent>.sqlite
  3. 该数据库 chunks=0, files=0 → 判定"数据库不可用"
  4. unable to open database file,然后 fallback 到 local 内置引擎

/images/Code-Art-Studio-images/memory-search-qmd-fallback/illustrations/2.webp

根因总结

层级原因
直接原因builtin 数据库(oc-engineer.sqlite)是空的,OpenClaw 判定其不可用
根本原因memory.backendmemorySearch.provider 配置不一致
加剧因素qmd 同步时遇到 EIO 目录 crash,让排查方向跑偏

排查时间线

阶段耗时方向结果
数据库检查8minsqlite 损坏/权限❌ 数据库正常
qmd 调用链27minqmd 配置问题⚠️ 发现 EIO 错误
EIO 目录修复4h+9p/chkdsk/重建❌ 无法修复
ignorePatterns44min配置排除❌ qmd 不支持
lsof 破案5min实际文件操作✅ 找到根因

讽刺的是:耗时最短的 lsof 一步破案,而 EIO 目录花了 4 小时却发现是无关因素。

完整教训清单

技术层面

  1. 配置一致性第一memory.backendmemorySearch.provider 必须对齐,否则会导致不可预测的 fallback
  2. “unable to open database file” 不一定是字面意义:可能是逻辑判断(如"数据不可用"),需要 lsof 确认实际打开的文件
  3. WSL 9p 目录缓存:某些 NTFS 特殊状态会导致 scandir EIO,且 WSL 内核会缓存目录状态
  4. SQLite 错误码的多义性:同一个错误码可能代表不同的逻辑含义

排查方法层面

  1. 先看日志,再猜原因:日志里有完整的错误链,不应该瞎猜
  2. 用 lsof/strace 确认实际行为:不要假设代码按你想象的方式执行
  3. 从最简单的原因开始排查:配置冲突 > 文件权限 > 文件系统 bug
  4. 不要过度发散:本次排查在 EIO 目录上浪费了大量时间,根因却是配置问题

OpenClaw 运维层面

  1. 升级后要检查所有核心功能:从 2026.5.6 升级到 2026.5.12 后,memory_search 一直有问题但没发现
  2. 配置验证要全面openclaw config validate 应该成为每次配置变更后的标准步骤
  3. 备份配置:修改前应该备份,今天多次通过 gateway 工具修改配置,没有手动备份

待解决事项

  1. 配置对齐:需要将 memory.backendmemorySearch.provider 对齐(都改为 qmd 或都改为 builtin)
  2. builtin 数据库填充:如果继续使用 builtin 后端,需要让 OpenClaw 填充 ~/.openclaw/memory/<agent>.sqlite
  3. EIO 目录/mnt/g/Documents/Obsidian-oklife/oklife/AI/05-agent智能体 仍然存在 EIO,但不影响 qmd 工作(已排除在扫描范围外)

关键命令备忘

# 检查 gateway 打开的 sqlite 文件
lsof -p $(pgrep -f "openclaw.*gateway") | grep sqlite

# 检查 builtin memory 数据库内容
sqlite3 ~/.openclaw/memory/oc-engineer.sqlite "SELECT COUNT(*) FROM chunks; SELECT COUNT(*) FROM files;"

# 检查所有 sqlite 数据库完整性
find ~/.openclaw -name "*.sqlite" -exec sqlite3 {} "PRAGMA integrity_check;" \;

# 查看 qmd 相关日志
rg "qmd|memory.*backend|memory.*fallback|unable.*open" /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log

# 验证配置
openclaw config validate

关联阅读

  • [[2026-07-04-1118-wsl-9p-protocol-bug-memory-search|memory_search 突然变弱?一次 WSL 9p 协议 Bug 引发的血案]]

参考来源


–全文完–

感谢阅读
若你有故事想讲、有困惑想聊、或是想找个人说说心里话,甚至只是吐槽发泄一下情绪,都欢迎来找我聊聊:   《内容已折叠,点击展开》

希望我写的每一个字,成为我自己和某个人活下去、拼下去的力量。                     《内容已折叠,点击展开》

“技术终归是工具,而我们一次次认真把问题理顺,守住的其实不只是页面样式和代码输出,还有那一点不愿被混乱打败的心气,是每一个深夜仍愿点灯前行的人。”

转载请注明来自https://oklife.me。

文尾配图水墨画图片