Skip to main content

An on-device hybrid search engine for Markdown documents

Project description

QMD-Py — Query Markup Documents

本地运行的混合文档搜索引擎。索引你的 Markdown 笔记、会议记录、文档和知识库,用关键词或自然语言搜索。Python 移植版,忠实复现 qmd 的核心算法。

QMD-Py 结合 BM25 全文检索、向量语义检索和 LLM 重排序,全部本地运行。支持 llama-cpp-python(GGUF 模型)、sentence-transformers、FlagEmbedding 三种后端。

快速开始

# 安装
pip install -e .

# 带 LLM 后端
pip install -e ".[mvp]"

# 带 MCP 支持
pip install -e ".[mcp]"

# 创建 collection
qmd-py add notes ~/notes
qmd-py add docs ~/Documents/docs --pattern "**/*.md"

# 添加上下文(关键特性——帮助 LLM 理解文档归属)
qmd-py context add notes "" "个人笔记和想法"
qmd-py context add docs "api" "API 文档"

# 生成 embedding
qmd-py embed

# 搜索
qmd-py search "项目进度"              # BM25 关键词检索
qmd-py query "如何部署服务"            # 混合检索 + 重排序(最佳质量)

# 获取文档
qmd-py get qmd://notes/meeting.md
qmd-py get "#abc123"                  # 用 docid

# 列出文件
qmd-py ls
qmd-py ls notes

架构

┌─────────────────────────────────────────────────────────────────┐
│                    QMD-Py Hybrid Search Pipeline                │
└─────────────────────────────────────────────────────────────────┘

                           ┌──────────────┐
                           │  User Query  │
                           └──────┬───────┘
                                  │
                   ┌──────────────┴──────────────┐
                   ▼                             ▼
          ┌────────────────┐            ┌────────────────┐
          │ Query Expansion│            │  Original Query│
          │  (fine-tuned)  │            │   (×2 weight)  │
          └───────┬────────┘            └───────┬────────┘
                  │                             │
                  │  lex / vec / hyde 变体       │
                  └──────────────┬──────────────┘
                                 │
           ┌─────────────────────┼─────────────────────┐
           ▼                     ▼                     ▼
     ┌───────────┐         ┌───────────┐         ┌───────────┐
     │ BM25+Vec  │         │ BM25+Vec  │         │ BM25+Vec  │
     │(原始 query)│         │(扩展 query1)│        │(扩展 query2)│
     └─────┬─────┘         └─────┬─────┘         └─────┬─────┘
           │                     │                     │
           └─────────────────────┼─────────────────────┘
                                 ▼
                    ┌─────────────────────────┐
                    │   RRF Fusion (k=60)     │
                    │   原始 query ×2 权重      │
                    │   Top-rank bonus: +0.05  │
                    │   取 Top 40 候选          │
                    └────────────┬────────────┘
                                 ▼
                    ┌─────────────────────────┐
                    │     LLM Re-ranking      │
                    │   (qwen3-reranker)      │
                    └────────────┬────────────┘
                                 ▼
                    ┌─────────────────────────┐
                    │  Position-Aware Blend   │
                    │  Rank 1-3:  75% RRF     │
                    │  Rank 4-10: 60% RRF     │
                    │  Rank 11+:  40% RRF     │
                    └─────────────────────────┘

检索算法

分数归一化

后端 原始分数 转换 范围
BM25 (FTS5) SQLite FTS5 BM25 abs(score) 0 ~ 25+
Vector 余弦距离 1 / (1 + distance) 0.0 ~ 1.0
Reranker LLM 0-10 评分 score / 10 0.0 ~ 1.0

融合策略

query 命令使用 Reciprocal Rank Fusion (RRF) + 位置感知混合:

  1. Query Expansion: 原始查询 (×2 权重) + LLM 生成的变体查询
  2. 并行检索: 每个查询同时搜索 FTS 和向量索引
  3. RRF 融合: score = Σ(1/(k+rank+1)),k=60
  4. Top-Rank Bonus: 任意列表中排名 #1 的文档 +0.05,#2-3 +0.02
  5. 强信号检测: BM25 top-1 分数 ≥0.85 且与 top-2 差距 ≥0.15 时跳过 expansion
  6. Top-K 筛选: 取 top 40 候选进入重排序
  7. LLM 重排序: 对每个 chunk(非全文)打分
  8. Position-Aware Blending:
    • RRF rank 1-3: 75% 检索 / 25% 重排序(保护精确匹配)
    • RRF rank 4-10: 60% 检索 / 40% 重排序
    • RRF rank 11+: 40% 检索 / 60% 重排序(信赖重排序)

分数解读

分数 含义
0.8 - 1.0 高度相关
0.5 - 0.8 中等相关
0.2 - 0.5 有一定相关
0.0 - 0.2 低相关

智能分块

文档按 ~900 token 分块,15% 重叠,使用断点检测算法寻找自然切割点:

模式 分数 说明
# Heading 100 H1 标题
## Heading 90 H2 标题
### Heading 80 H3 标题
#### ~ ###### 70~50 H4-H6
``` 80 代码块边界
--- / *** 60 分隔线
空行 20 段落边界
- item / 1. item 5 列表项
换行 1 最小断点

算法: 接近 900 token 目标时,在前 200 token 窗口内搜索最佳断点。分数衰减公式:finalScore = baseScore × (1 - (distance/window)² × 0.7)。代码块内的断点被忽略——代码保持完整。

Context 系统

Context 是 QMD 的核心特性——为路径添加描述性元数据,帮助 LLM 理解文档归属。

# Collection 级别
qmd-py context add notes "" "个人笔记和想法"

# 子路径级别
qmd-py context add notes "work" "工作相关笔记"
qmd-py context add notes "work/meetings" "会议记录"

# 层级继承:搜索 notes/work/meetings/2024.md 会返回所有匹配的 context 拼接
# → "个人笔记和想法\n工作相关笔记\n会议记录"

# 列出所有 context
qmd-py context list

# 删除
qmd-py context remove notes "work/meetings"

CLI 命令

Collection 管理

qmd-py add <name> <path> [--pattern "**/*.md"]   # 添加 collection
qmd-py remove <name>                              # 删除 collection
qmd-py collection rename <old> <new>              # 重命名
qmd-py list                                       # 列出所有 collection
qmd-py ls [collection]                            # 列出文件
qmd-py update [name]                              # 重新索引
qmd-py status                                     # 索引状态

搜索

qmd-py search <query> [-c collection] [-n 10]     # BM25 检索
qmd-py query <query> [-c collection] [-n 10]      # 混合检索 + 重排序

输出格式

--format cli     # 默认终端格式
--format json    # JSON(适合 agent 消费)
--format csv     # CSV
--format xml     # XML
--format md      # Markdown
--format files   # 简单文件列表:docid,score,filepath,context
--full           # 显示完整内容
--line-numbers   # 显示行号

文档操作

qmd-py get <file> [-c collection]                 # 获取文档
qmd-py get qmd://notes/file.md                    # 虚拟路径
qmd-py get "#abc123"                              # docid
qmd-py get file.md:42 --max-lines 20             # 指定行范围
qmd-py embed [--force]                            # 生成 embedding
qmd-py cleanup                                    # 清理孤立数据 + VACUUM

MCP Server

QMD-Py 提供 MCP (Model Context Protocol) 服务器,通过 stdio transport 与 Claude Desktop 等客户端通信。

工具列表:

  • qmd_search — BM25 关键词检索
  • qmd_deep_search — 混合检索 + query expansion + 重排序
  • qmd_vector_search — 向量语义检索
  • qmd_get — 获取文档(路径或 docid,支持模糊匹配建议)
  • qmd_index — 索引/更新 collection
  • qmd_status — 索引健康状态
  • qmd_collections — 列出 collection

Claude Desktop 配置 (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "qmd": {
      "command": "qmd-py",
      "args": ["serve"]
    }
  }
}

LLM 后端

QMD-Py 支持三种后端,按优先级自动选择:

llama-cpp-python(推荐)

使用 GGUF 模型,与原版 qmd 相同的模型:

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

模型从 HuggingFace 下载,缓存在 ~/.cache/qmd-py/models/

sentence-transformers(fallback)

纯 Python embedding,不需要编译 llama-cpp。适合快速测试。

FlagEmbedding

专用 reranker 后端(FlagReranker),可与其他后端组合使用。

数据存储

数据库: ~/.config/qmd/qmd.db (SQLite)

collections     -- 集合目录配置
path_contexts   -- 路径 context 描述
documents       -- 文档元数据(path, title, hash, active)
documents_fts   -- FTS5 全文索引
content         -- 文档内容(content-addressable,按 SHA256 去重)
content_vectors -- embedding 分块(hash, seq, pos)
vectors_vec     -- sqlite-vec 向量索引
llm_cache       -- LLM 响应缓存(query expansion, rerank)

配置文件: ~/.config/qmd/qmd.yaml

环境变量

变量 默认值 说明
QMD_CONFIG_DIR ~/.config/qmd 配置目录
QMD_DATA_DIR ~/.cache/qmd 数据/缓存目录
XDG_CONFIG_HOME ~/.config XDG 配置根目录
XDG_CACHE_HOME ~/.cache XDG 缓存根目录

系统要求

  • Python >= 3.11
  • SQLite >= 3.35(FTS5 支持)
  • GPU(可选): CUDA 或 Apple MPS 加速 embedding/reranking

安装

# 基础安装
pip install -e .

# 完整安装(所有 LLM 后端 + MCP)
pip install -e ".[mvp,mcp]"

# 开发环境
pip install -e ".[mvp,mcp,dev]"
pytest tests/ -v

项目结构

qmd/
├── core/
│   ├── db.py           # SQLite 数据库层(schema、CRUD、FTS5、sqlite-vec)
│   ├── config.py       # YAML 配置管理、collection/context 操作
│   ├── store.py        # 文档索引层(content-addressable 存储、增量更新)
│   ├── retrieval.py    # 混合检索引擎(BM25 + Vector + RRF + Rerank)
│   ├── chunking.py     # 智能分块(断点检测、代码围栏保护)
│   ├── document.py     # 文档查找辅助(docid、模糊匹配、glob、cleanup)
│   └── watcher.py      # 文件监听(watchdog,自动索引)
├── cli/
│   ├── main.py         # CLI 入口(argparse,所有命令)
│   └── formatter.py    # 输出格式化(JSON/CSV/XML/MD/Files)
├── llm/
│   ├── base.py         # LLM 抽象接口
│   ├── llama_cpp.py    # llama-cpp-python 后端
│   ├── sentence_tf.py  # sentence-transformers 后端
│   ├── flagembed.py    # FlagEmbedding reranker 后端
│   └── models.py       # 模型配置、GPU 检测
├── mcp/
│   └── server.py       # MCP Server(stdio transport)
├── utils/
│   ├── paths.py        # 路径工具、VirtualPath (qmd://)
│   ├── snippet.py      # 摘要提取、标题提取
│   └── hashing.py      # SHA256 content hash
└── __init__.py         # create_store() / create_llm_backend() 入口

致谢

Python 移植自 qmd(Tobias Lütke),核心检索算法、分块策略和融合逻辑忠实复现原版设计。

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qmd-0.1.0.tar.gz (107.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qmd-0.1.0-py3-none-any.whl (74.4 kB view details)

Uploaded Python 3

File details

Details for the file qmd-0.1.0.tar.gz.

File metadata

  • Download URL: qmd-0.1.0.tar.gz
  • Upload date:
  • Size: 107.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for qmd-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5388bd4da98625186f39f043675b2f3e4ba668efbdfc6e84e196a0ed66480047
MD5 7a55d075dfb7fe4654759f5bdf65442c
BLAKE2b-256 f2ca7ae4405af2e12f6a16075ed2148e65aa7a636b0b161133af2b860f898047

See more details on using hashes here.

File details

Details for the file qmd-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: qmd-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 74.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for qmd-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7bcac46cfaf6b1aa81a32251a3d06e683489bc207ed335f56b983333a2b728b9
MD5 f5b10716ae8e060bd949f28863f19df8
BLAKE2b-256 b0f223c393ff8e52680280f03aaf3943f6532427aebede302127302015f01d74

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page