QMD:本地智能文档搜索引擎完全指南

引言:你的知识库需要一把钥匙

作为程序员、写作者或知识工作者,我们每天都在产生大量的 Markdown 文档——技术笔记、会议记录、项目文档、博客草稿……这些文档散落在不同的文件夹中,随着时间推移,它们变成了"数字废墟":你知道某篇笔记一定存在,却怎么也找不到。

传统的文件搜索工具(如 Spotlight、grep)只能基于文件名或关键词匹配,无法理解语义。而云端笔记工具(如 Notion、Obsidian)虽然提供了搜索功能,却存在数据隐私和访问限制的问题。

QMD(Query Markup Documents) 正是为了解决这个痛点而生的——一个完全本地运行的智能文档搜索引擎,它结合了 BM25 全文检索、向量语义搜索和 LLM 重排序,让你能够用自然语言快速找到任何文档中的任何内容。


一、QMD 是什么

QMD 是一个开源的 CLI 工具 + 库,由 @tobi 开发,专为 Markdown 文档设计。它的核心特性包括:

特性 说明
完全本地 所有数据和模型都在本地运行,无需联网
混合搜索 BM25 关键词 + 向量语义 + LLM 重排序
智能分块 自动将长文档切分为语义完整的片段
上下文感知 支持为集合添加描述性上下文,提升搜索质量
MCP 集成 提供 Model Context Protocol 服务器,可与 Claude 等 AI 工具集成
SDK 支持 可作为 Node.js/Bun 库嵌入你的应用

二、QMD 解决什么问题

2.1 传统搜索的困境

flowchart LR
    A[用户搜索<br/>"认证流程"] --> B{传统搜索}
    B -->|关键词匹配| C[找到"认证"出现<br/>的文档]
    B -->|无法理解| D[遗漏"登录"<br/>"鉴权"相关内容]
    
    E[用户搜索<br/>"怎么部署"] --> F{语义搜索}
    F -->|理解意图| G[找到"发布指南"<br/>"上线步骤"等]
    
    style B fill:#ffcccc
    style F fill:#ccffcc

传统搜索的问题:

  • 关键词依赖:必须精确匹配关键词,同义词、近义词无法召回
  • 无语义理解:无法理解查询意图,只能机械匹配
  • 结果排序差:无法判断文档与查询的相关性

2.2 QMD 的解决方案

flowchart TB
    subgraph 索引阶段
        A[Markdown文档] --> B[智能分块<br/>~900 tokens/块]
        B --> C[BM25索引<br/>SQLite FTS5]
        B --> D[向量嵌入<br/>本地Embedding模型]
        C --> E[SQLite数据库<br/>~/.cache/qmd/index.sqlite]
        D --> E
    end
    
    subgraph 查询阶段
        F[用户查询] --> G[查询扩展<br/>LLM生成变体]
        G --> H[并行搜索<br/>BM25 + Vector]
        H --> I[RRF融合<br/>Reciprocal Rank Fusion]
        I --> J[LLM重排序<br/>qwen3-reranker]
        J --> K[位置感知融合<br/>Top结果加权]
        K --> L[最终结果]
    end
    
    style E fill:#e6f3ff
    style L fill:#e6ffe6

QMD 通过三层架构解决搜索难题:

  1. BM25 全文检索:快速定位包含关键词的文档
  2. 向量语义搜索:理解语义相似性,召回同义词、近义词
  3. LLM 重排序:精确判断文档与查询的相关性

三、QMD 的核心架构

3.1 混合搜索管道

flowchart TB
    subgraph 输入
        Q[用户查询<br/>"project timeline"]
    end
    
    subgraph 查询扩展
        Q --> E1[原始查询 ×2权重]
        Q --> E2[LLM生成变体1]
        Q --> E3[LLM生成变体2]
    end
    
    subgraph 并行检索
        E1 --> R1[BM25搜索]
        E1 --> R2[向量搜索]
        E2 --> R3[BM25搜索]
        E2 --> R4[向量搜索]
        E3 --> R5[BM25搜索]
        E3 --> R6[向量搜索]
    end
    
    subgraph 结果融合
        R1 & R2 & R3 & R4 & R5 & R6 --> F1[RRF融合<br/>k=60]
        F1 --> F2[Top排名奖励<br/>#1:+0.05 #2-3:+0.02]
        F2 --> F3[取Top 30]
    end
    
    subgraph 重排序
        F3 --> R[LLM重排序<br/>qwen3-reranker<br/>Yes/No + logprobs]
    end
    
    subgraph 输出
        R --> O1[位置感知融合<br/>Rank 1-3: 75%RRF+25%Rerank]
        O1 --> O2[Rank 4-10: 60%RRF+40%Rerank]
        O2 --> O3[Rank 11+: 40%RRF+60%Rerank]
        O3 --> Result[最终结果]
    end
    
    style Result fill:#90EE90

3.2 智能分块策略

flowchart LR
    A[长文档] --> B[扫描断点]
    B --> C{评分}
    
    C -->|100分| D[# Heading 1]
    C -->|90分| E[## Heading 2]
    C -->|80分| F[### Heading 3]
    C -->|80分| G[```代码块]
    C -->|60分| H[--- 分隔线]
    C -->|20分| I[空行]
    C -->|5分| J[- 列表项]
    
    D & E & F & G & H & I & J --> K[选择最高分断点<br/>距离衰减: score×(1-(d/w)²×0.7)]
    K --> L[生成Chunk<br/>~900 tokens]
    L --> M[15%重叠<br/>保持上下文]
    
    style K fill:#FFE4B5
    style L fill:#E6E6FA

智能分块的优势

  • 保持语义单元完整(章节、段落、代码块不割裂)
  • 代码块特殊保护(不破坏代码完整性)
  • 15% 重叠保持上下文连贯

四、安装与配置

4.1 系统要求

  • Node.js: >= 22
  • Bun: >= 1.0.0(可选,但推荐)
  • macOS: 需要 Homebrew SQLite(brew install sqlite

4.2 安装

1
2
3
4
5
6
7
8
9
# 使用 npm
npm install -g @tobilu/qmd

# 或使用 bun(推荐)
bun install -g @tobilu/qmd

# 临时运行(不安装)
npx @tobilu/qmd ...
bunx @tobilu/qmd ...

4.3 自动下载的模型

首次运行时会自动下载以下 GGUF 模型(总计约 2GB):

模型 用途 大小
embeddinggemma-300M-Q8_0 向量嵌入 ~300MB
qwen3-reranker-0.6b-q8_0 结果重排序 ~640MB
qmd-query-expansion-1.7B-q4_k_m 查询扩展 ~1.1GB

模型缓存位置:~/.cache/qmd/models/


五、快速开始

5.1 创建集合

集合(Collection)是 QMD 管理文档的基本单位:

1
2
3
4
5
6
7
8
9
10
11
# 添加笔记集合
qmd collection add ~/notes --name notes

# 添加会议记录
qmd collection add ~/Documents/meetings --name meetings

# 添加工作文档(自定义匹配模式)
qmd collection add ~/work/docs --name docs --mask "**/*.md"

# 查看所有集合
qmd collection list

5.2 添加上下文(关键!)

上下文是 QMD 的杀手锏功能,它帮助搜索系统理解文档的用途:

1
2
3
4
5
6
7
8
9
10
# 为集合添加描述
qmd context add qmd://notes "个人笔记和想法,包含技术学习、读书笔记"
qmd context add qmd://meetings "会议记录和纪要,包含周会、评审会"
qmd context add qmd://docs "工作文档,包含API文档、设计文档"

# 添加全局上下文(适用于所有集合)
qmd context add / "我的知识库,包含工作和个人笔记"

# 查看所有上下文
qmd context list

5.3 生成向量嵌入

1
2
3
4
5
# 为所有文档生成嵌入(900 tokens/块,15%重叠)
qmd embed

# 强制重新嵌入所有文档
qmd embed -f

5.4 搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 快速关键词搜索
qmd search "项目时间线"

# 语义搜索
qmd vsearch "如何部署"

# 混合搜索(推荐,质量最好)
qmd query "季度规划流程"

# 限定集合搜索
qmd query "API设计" -c docs

# 获取更多结果
qmd query "认证流程" -n 10

# 设置最小分数阈值
qmd query "错误处理" --min-score 0.3

# 显示完整文档
qmd query "架构设计" --full

# JSON 输出(用于脚本)
qmd query "性能优化" --json

# 显示评分详情
qmd query "缓存策略" --json --explain

5.5 获取文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 通过路径获取
qmd get "docs/api-reference.md"

# 通过 docid 获取(搜索结果显示的 #abc123)
qmd get "#abc123"

# 从指定行开始,最多100行
qmd get "meetings/2024-01-15.md:50" -l 100

# 批量获取(支持 glob 模式)
qmd multi-get "journals/2025-05*.md"

# 限制文件大小
qmd multi-get "docs/*.md" --max-bytes 20480

六、与 Claude 集成

QMD 提供了 MCP(Model Context Protocol)服务器,可以与 Claude Desktop/Code 无缝集成:

6.1 Claude Desktop 配置

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json

1
2
3
4
5
6
7
8
{
"mcpServers": {
"qmd": {
"command": "qmd",
"args": ["mcp"]
}
}
}

6.2 Claude Code 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
# 从插件市场安装(推荐)
claude plugin marketplace add tobi/qmd
claude plugin install qmd@qmd

# 或手动配置 ~/.claude/settings.json
{
"mcpServers": {
"qmd": {
"command": "qmd",
"args": ["mcp"]
}
}
}

6.3 HTTP 模式(共享服务)

默认使用 stdio 模式(每个客户端启动独立进程),如需共享服务:

1
2
3
4
5
6
7
8
9
# 前台启动
qmd mcp --http # localhost:8181

# 后台守护进程
qmd mcp --http --daemon
qmd mcp stop # 停止服务

# 查看状态
qmd status # 显示 "MCP: running (PID ...)"

七、SDK 使用

QMD 也可以作为 Node.js/Bun 库使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { createStore } from '@tobilu/qmd'

// 创建存储
const store = await createStore({
dbPath: './my-index.sqlite',
config: {
collections: {
docs: {
path: '/path/to/docs',
pattern: '**/*.md'
},
notes: {
path: '/path/to/notes'
}
}
}
})

// 搜索
const results = await store.search({
query: "authentication flow",
limit: 5,
minScore: 0.3
})

// 输出结果
console.log(results.map(r =>
`${r.title} (${Math.round(r.score * 100)}%)`
))

// 关闭
await store.close()

7.1 高级搜索选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 简单查询(自动扩展)
const results = await store.search({
query: "rate limiting",
intent: "API throttling and abuse prevention",
collection: "docs",
limit: 5,
minScore: 0.3,
explain: true // 包含评分详情
})

// 预扩展查询(手动控制)
const results = await store.search({
queries: [
{ type: 'lex', query: '"connection pool" timeout -redis' },
{ type: 'vec', query: 'why do database connections time out under load' }
],
collections: ["docs", "notes"]
})

// 跳过重排序(更快)
const fast = await store.search({
query: "auth",
rerank: false
})

7.2 直接访问底层方法

1
2
3
4
5
6
7
8
9
// BM25 关键词搜索(快,无LLM)
const lexResults = await store.searchLex("auth middleware", { limit: 10 })

// 向量搜索(有嵌入,无重排序)
const vecResults = await store.searchVector("how users log in", { limit: 10 })

// 手动查询扩展
const expanded = await store.expandQuery("auth flow", { intent: "user login" })
const results = await store.search({ queries: expanded })

八、数据存储结构

erDiagram
    COLLECTIONS {
        string name PK "集合名称"
        string path "绝对路径"
        string glob_pattern "匹配模式"
        int doc_count "文档数"
        int active_count "活跃文档数"
        datetime last_modified "最后修改"
        boolean includeByDefault "默认包含"
    }
    
    DOCUMENTS {
        string docid PK "6字符哈希"
        string collection FK "所属集合"
        string path "相对路径"
        string title "文档标题"
        int content_hash "内容哈希"
        datetime last_modified "最后修改"
    }
    
    PATH_CONTEXTS {
        string collection FK
        string path "虚拟路径 qmd://..."
        string context "描述文本"
    }
    
    CONTENT_VECTORS {
        string hash FK "文档哈希"
        int seq "块序号"
        int pos "字符位置"
        blob vector "768维向量"
    }
    
    DOCUMENTS_FTS {
        string docid FK "FTS5虚拟表"
        string content "索引内容"
    }
    
    VECTORS_VEC {
        string hash_seq PK "hash_seq组合"
        blob vector "sqlite-vec格式"
    }
    
    LLM_CACHE {
        string query_hash PK
        string response "缓存的LLM响应"
        datetime created "创建时间"
    }
    
    COLLECTIONS ||--o{ DOCUMENTS : contains
    COLLECTIONS ||--o{ PATH_CONTEXTS : describes
    DOCUMENTS ||--o{ CONTENT_VECTORS : embeds
    DOCUMENTS ||--|| DOCUMENTS_FTS : indexes
    CONTENT_VECTORS ||--|| VECTORS_VEC : stores

存储位置:~/.cache/qmd/index.sqlite


九、最佳实践

9.1 上下文策略

上下文是提升搜索质量的关键:

1
2
3
4
5
6
7
# ✅ 好的上下文:描述文档的用途和内容类型
qmd context add qmd://notes/tech "技术学习笔记,包含算法、系统设计、编程语言"
qmd context add qmd://meetings/q4 "Q4季度会议,包含规划、复盘、资源协调"

# ❌ 差的上下文:过于笼统或重复路径名
qmd context add qmd://notes "笔记"
qmd context add qmd://meetings "会议"

9.2 集合组织建议

mindmap
  root((知识库))
    工作
      项目文档
      技术规范
      会议记录
      复盘总结
    个人
      技术笔记
      读书笔记
      想法随记
      学习资料
    归档
      已完成项目
      过期文档

9.3 定期维护

1
2
3
4
5
6
7
8
9
10
11
# 更新索引(检测新增、修改、删除的文件)
qmd update

# 更新并拉取远程仓库(如果是git管理的)
qmd update --pull

# 清理缓存和孤立数据
qmd cleanup

# 查看索引状态
qmd status

十、多语言支持

默认的 embeddinggemma-300M 对中文支持有限,可以切换到 Qwen3-Embedding:

1
2
3
4
5
# 使用 Qwen3-Embedding-0.6B(更好的中文支持)
export QMD_EMBED_MODEL="hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf"

# 重新嵌入所有文档
qmd embed -f

结语

QMD 代表了个人知识管理的下一代范式——本地优先、AI 增强、隐私安全。它不仅仅是一个搜索工具,更是一个能够理解你文档语义的知识助手。

在这个信息爆炸的时代,我们需要的不是更多的存储空间,而是更智能的检索能力。QMD 让每一篇笔记都能被找到,让每一个想法都不会丢失。

"The best search is the one you don’t have to think about."


参考链接


本文创建于 2026-03-16,基于 QMD 最新版本