openclawmemoryqmdbackendtypescriptnode-llama-cppmcpperformance

深入分析 QMD (Quick Markdown Database) 后端的设计原理、优势及与 Builtin 后端的对比


QMD 是什么?

QMD = Quick Markdown Database,是 tobi/qmd 开发的 TypeScript 命令行工具,专门用于 Markdown 文档的高性能检索。

关键信息

  • 主要语言: TypeScript (79.1%) + Python (18.6%)
  • 运行环境: Node.js 22+ / Bun
  • 包名: @tobilu/qmd
  • 本地模型: 使用 node-llama-cpp 加载 GGUF 模型

定位

flowchart TB
    subgraph "OpenClaw Memory 双后端"
        A[Memory System] -->|默认| B[Builtin Backend]
        A -->|可选| C[QMD Backend]
        
        B -->|OpenClaw 内置| D[SQLite + 应用层算法]
        C -->|外部 CLI| E[TypeScript + node-llama-cpp]
        
        D -->|完全可控| F[零依赖]
        E -->|高级功能| G[需安装 npm 包]
    end

为什么需要 QMD?Builtin 的局限性

Builtin 的三大痛点

痛点具体问题影响
算法能力受限依赖外部 API 或简单算法无法使用本地 cross-encoder、query expansion 等高级算法
性能瓶颈Hybrid Search + MMR 都是应用层计算CPU 上很慢,特别是 query mode
查询理解弱简单的停用词过滤,无法语义扩展用户问 “thing we discussed” 找不到相关文档

具体场景对比

场景 1: 复杂查询

用户: "that thing we discussed yesterday about the API"

Builtin:
- 去掉停用词 -> "discussed API"
- 可能漏掉 "authentication" "OAuth" 等相关概念

QMD query mode:
- Query Expansion: "API authentication OAuth design"
- 召回率显著提升

场景 2: 重排序质量

Builtin MMR:
- 基于向量相似度的贪心选择
- 计算快但质量一般

QMD Re-rank:
- Cross-encoder 精排模型
- 理解更深,结果质量更高

QMD 的核心优势

1. 三种搜索模式

flowchart LR
    Q[Query] -->|默认| A[search mode]
    Q -->|纯向量| B[vsearch mode]
    Q -->|高质量| C[query mode]
    
    A -->|~50ms| D[BM25 + 简单向量]
    B -->|~30ms| E[纯向量]
    C -->|500ms-2s| F[查询扩展 + 重排序]
    
    D -->|推荐| G[日常使用]
    E -->|可选| H[语义优先]
    F -->|深度| I[研究场景]
模式命令特点延迟适用场景
searchqmd searchBM25 + 简单向量~50ms默认推荐,日常使用
vsearchqmd vsearch纯向量搜索~30ms语义匹配优先
queryqmd query查询扩展 + 重排序500ms-2s最高质量,深度研究

代码体现 (src/config/types.memory.ts):

export type MemoryQmdSearchMode = "query" | "search" | "vsearch";
 
// backend-config.ts 注释:
// Defaulting to `query` can be extremely slow on CPU-only systems
// (query expansion + rerank).
// Prefer a faster mode for interactive use.
const DEFAULT_QMD_SEARCH_MODE = "search";

2. 查询扩展 (Query Expansion)

问题: 用户查询和文档内容的不匹配

用户: "how to implement authentication"
文档: "API auth configuration guide"

问题: "authentication" ≠ "auth",BM25 无法匹配

QMD 解决方案:

原始查询: "authentication implementation"
扩展后: "authentication auth OAuth login security API"

实现: QMD 内置了高效的查询扩展算法,使用本地 LLM 模型生成查询变体。

3. 重排序 (Re-ranking)

Builtin MMR vs QMD Cross-encoder:

特性Builtin MMRQMD Re-rank
算法基于向量相似度的贪心选择Cross-encoder 精排模型
理解深度浅层(向量相似度)深层(BERT 语义理解)
计算成本O(n²) 向量计算更高,但本地模型优化
结果质量一般显著更好

4. 性能优势

延迟对比 (P95):

xychart-beta
    title "搜索延迟对比"
    x-axis [Builtin Hybrid, QMD search, QMD vsearch, QMD query]
    y-axis "延迟 (ms)" 0 --> 2000
    bar [200, 50, 30, 1000]

为什么更快?

  • 专用进程: QMD 作为独立进程运行,资源隔离
  • 本地模型: 使用 node-llama-cpp 加载本地 GGUF 模型,无需网络调用
  • 预建索引: 在后台维护优化过的索引结构
  • 专用算法: 针对搜索场景专门优化的查询扩展和重排序

MCP 协议集成

什么是 MCP?

MCP = Model Context Protocol,一种标准化的 LLM 工具调用协议。

两种调用方式对比

flowchart TB
    subgraph "方式 1: Direct Spawn"
        A[Node.js] -->|spawn| B[qmd process]
        B -->|执行| C[返回结果]
        C -->|exit| D[进程销毁]
        
        E[问题: 每次搜索都要启动新进程]
        F[延迟: 50-100ms 启动开销]
    end
    
    subgraph "方式 2: MCP via mcporter"
        G[Node.js] -->|mcporter call| H[mcporter Daemon]
        H -->|长连接| I[QMD MCP Server]
        I -->|keep-alive| J[进程常驻]
        J -->|复用| K[后续调用]
        
        L[优势: 进程常驻内存]
        M[延迟: 首次后接近纯内存查询]
    end

mcporter 集成代码

配置 (src/config/types.memory.ts):

export type MemoryQmdMcporterConfig = {
  enabled?: boolean;        // 是否启用 MCP
  serverName?: string;      // 默认 "qmd"
  startDaemon?: boolean;    // 自动启动守护进程
};

实现 (src/memory/qmd-manager.ts):

private async runQmdSearchViaMcporter(params: {
  tool: "search" | "vector_search" | "deep_search";
  query: string;
  limit: number;
}) {
  // 确保 mcporter 守护进程已启动
  await this.ensureMcporterDaemonStarted(params.mcporter);
  
  const selector = `${params.mcporter.serverName}.${params.tool}`;
  
  const result = await this.runMcporter([
    "call", selector,
    "--args", JSON.stringify({ query, limit }),
    "--output", "json"
  ]);
  
  return JSON.parse(result.stdout);
}

故障降级机制

设计哲学:优雅降级

flowchart TB
    A[Search Request] -->|默认| B{QMD Available?}
    
    B -->|Yes| C[QMD Backend]
    C -->|Success| D[Return Results]
    C -->|Fail| E[Log Error]
    E -->|Fallback| F[Builtin Backend]
    
    B -->|No| F
    
    F -->|Always Works| G[Return Results]
    
    H[核心原则: QMD 是增强,不是必需]

实现代码

Search Manager 层 (src/memory/search-manager.ts):

async search(query: string) {
  if (this.backend === "qmd") {
    try {
      return await this.qmdManager.search(query);
    } catch (err) {
      // QMD 失败,记录并降级
      log.warn("qmd failed; switching to builtin");
      
      // 从缓存中移除失败的 QMD 管理器
      QMD_MANAGER_CACHE.delete(this.cacheKey);
      
      // 切换到 Builtin
      this.fallbackToBuiltin();
    }
  }
  
  // 使用 Builtin 兜底
  return this.builtinManager.search(query);
}

关键决策:

  • ✅ QMD 失败不影响功能
  • ✅ 自动切换到 Builtin
  • ✅ 下次请求可重试 QMD
  • ✅ 用户无感知

架构决策:为什么共存而非替换?

渐进式采用策略

flowchart LR
    A[新用户] -->|开箱即用| B[Builtin]
    B -->|需要更高质量| C[尝试 QMD]
    C -->|满意| D[长期使用 QMD]
    C -->|不稳定| E[回退到 Builtin]
    
    F[原则: 基础版本可用,高级功能可选]

对比总结

维度BuiltinQMD建议
实现语言TypeScript (OpenClaw 内置)TypeScript (独立进程)-
依赖SQLite only需安装 qmd CLIBuiltin 零依赖
搜索模式Hybrid (固定)search/vsearch/queryQMD 更灵活
查询扩展QMD 胜
重排序MMRCross-encoderQMD 胜
性能~200ms~50ms (search)QMD 胜
适用场景默认/通用追求质量/可安装工具根据场景选择

给 dm_claw 的启示

Python 生态的优势

相比 OpenClaw (TypeScript),dm_claw (Python) 有更好的选择:

# Python 有丰富的 IR 库
from sentence_transformers import CrossEncoder  # 重排序
import faiss  # 高性能向量搜索
from rank_bm25 import BM25Okapi  # BM25
 
# 可以直接在 Python 层实现 QMD 的大部分功能
# 无需依赖外部 TypeScript 服务

建议架构

class MemoryBackend(ABC):
    @abstractmethod
    async def search(self, query: str) -> List[Result]:
        pass
 
class BuiltinBackend(MemoryBackend):
    """基础版本:SQLite + sentence-transformers"""
    pass
 
class EnhancedBackend(MemoryBackend):
    """增强版本:+ Cross-encoder + Query Expansion"""
    pass

总结

QMD 解决了什么问题?

  1. 算法能力: 本地查询扩展、Cross-encoder 重排序(无需网络调用)
  2. 性能: 本地模型推理,避免 API 延迟
  3. 灵活性: 三种搜索模式适应不同场景

核心设计思想

  • 渐进增强: Builtin 默认可用,QMD 是可选增强
  • 故障降级: QMD 失败自动回退,保证可用性
  • 协议标准化: MCP 协议支持,长连接降低延迟

关键洞察

QMD 不是替代 Builtin,而是可选的高级功能。这种双后端设计让 OpenClaw 既保持了开箱即用的简单性,又提供了追求高质量的可能。


相关文档: Memory 源码分析, Memory 设计思想, 项目概览