
Agent 有个老问题:它知道怎么写代码、怎么查资料、怎么调用工具,但它不知道你自己的东西在哪里。
我说的不是联网搜索——Google 和 Bing 它已经会用了。我说的是你自己沉淀下来的那堆 Markdown:项目文档、SOP、会议纪要、故障排查记录、个人笔记。这些东西分散在十几个目录里,几百个 .md 文件,Agent 想找东西的时候,通常只能靠 rg 或 grep 翻一遍。
问题在于,grep 只做字面匹配。"扫地机器人激光雷达偏移" 和 "SLAM 建图漂移" 说的可能是同一件事,但 grep 认为它们毫无关系。
我最近开始用 QMD 来解决这个问题。它是 Query Markup Documents 的缩写——一个专门给 Markdown 目录建检索索引的本地工具。下面是我跑通之后的完整记录。
QMD 做了什么
给一个 Markdown 目录,建一个带 BM25 全文检索 + 向量语义搜索 + 混合检索的本地索引库。
Markdown 目录 → QMD 索引库(SQLite) → Agent 或人来检索
它不替代 LLM。它只负责"找到最相关的文档",最后整理成人话答案还是 LLM 的事。
核心卖点就两个:
- BM25 搜索:带相关度排序的关键词检索,比
LIKE强,比 grep 强 - 向量搜索:按语义找相似内容,能处理同义词、近义词和表述差异
两种模式还可以混合用(query 命令),自动做查询扩展 + BM25 + 向量 + rerank。
建索引
假设你的知识库目录是 /path/to/kb,先建一个独立的索引库(我给它起名叫 kb):
qmd --index kb collection add /path/to/kb --name kb --mask "**/*.md"
这条命令只是告诉 QMD"资料在哪里",它不会改你的 Markdown 文件。
然后刷新文本索引:
qmd --index kb update
这时候 BM25 搜索就能用了。如果你还想要语义搜索能力(推荐),再生成向量:
qmd --index kb embed
embed 会用本地 embedding 模型(默认是 embeddinggemma-300M Q8 量化版)把文档切块转成向量。它不是聊天模型,不回答问题,只是为语义检索准备数据。
查看当前状态:
qmd --index kb status
关于 --index 参数:它指定的是一个 SQLite 数据库,不是你的 Markdown 目录。QMD 的索引库里大概有这些东西:
kb.sqlite
├─ collections 注册过哪些目录
├─ documents 文件路径、标题、hash
├─ documents_fts BM25 全文检索索引
├─ content_vectors 文档切块后的文本片段
├─ vectors_vec 向量检索索引
└─ llm_cache 查询扩展和 rerank 的缓存
如果你还有其他服务(比如 OpenClaw)也在用 QMD,建议给自己的目录单独起 index 名字,互不干扰。
三种搜索模式
QMD 提供三种搜索方式,适用场景不同。搞清楚什么时候用哪个,能省很多时间。
search:BM25 关键词检索
qmd --index kb search "扫地机器人 激光雷达偏移" -c kb
BM25 会在意关键词出现了几次、关键词是否稀有、文档长度、多词是否都命中,最后算出一个相关度分数。
白话说:grep 回答"有没有这串字",BM25 回答"哪个文档最像你要找的"。
适用场景:
明确的故障关键词
告警码 / 错误码
产品名 / 型号
SOP 标题
文档名
任何你确切知道"文档里应该有这些词"的时候,用 search 就够了。
vsearch:向量语义检索
qmd --index kb vsearch "设备建图时位置一直飘怎么排查" -c kb
即使文档里没有"位置飘"这三个字,只要语义相近(比如写的是"SLAM 建图漂移""定位偏移""地图失真"),向量搜索也能找出来。
适用场景:
自然语言描述的问题
你不确定文档里具体怎么写的
需要找"意思接近但表述不同"的内容
query:混合检索
qmd --index kb query "设备建图时位置一直飘怎么排查" -c kb
混合检索会做四件事:查询扩展 → BM25 搜索 → 向量搜索 → rerank 重新排序。
听起来最强大,但有个坑:对于很短的中文故障词,查询扩展可能把你带到沟里去。比如你搜"偏移",它可能扩展成完全无关的方向。
这种时候退回 search 反而更好。
搜索结果怎么看
默认的搜索输出可能看起来很简略:
title: SLAM 建图漂移排查 SOP
tags: [product, sop, slam, drift]
这不是 bug。QMD 默认只展示一个很短的 snippet。如果命中了文件头部的 YAML tags,就只显示文件头。
这个结果仍然有用——它告诉你:最相关的文件是哪个、相关度多少、docid 是什么、命中位置在哪。
两个方案:
方案一:先搜后读。 用 search 找到文件,再用 get 读全文:
qmd --index kb search "激光雷达偏移" -c kb --files -n 10
qmd --index kb get "#docid"
方案二:让搜索直接吐更多内容。
qmd --index kb search "激光雷达偏移" -c kb --full --md -n 3
与 LLM 配合的正确姿势
QMD 自己不会给你整理出一段人话答案。它的定位是检索层,不是问答层。
完整的 Agent 知识检索流程应该是:
QMD search → QMD get → LLM 总结
具体操作:先用 QMD 搜索定位最相关的文档,把原文取出来,拼成 prompt 丢给 LLM:
基于下面这份文档,回答:"扫地机器人激光雷达偏移怎么处理?"
请按场景、原因、处理方式整理成简明 SOP。
[QMD 取回的文档内容]
这样做的好处是:LLM 的回答有了坚实的文档支撑,不容易编造。而且你清楚地知道答案来自哪篇文档,可以追溯验证。
判断规则速查
实际使用中,选搜索模式的决策很简单:
明确关键词 / 故障码 / 型号 → search
自然语言问题 → vsearch 或 query
短中文故障词(2-4字) → search(别用 query,扩展容易跑偏)
要最终答案 → QMD 找文档 + LLM 写答案
要看原文 → get
一个稳妥的默认命令组合:
# 找文件
qmd --index kb search "故障关键词" -c kb --files -n 10
# 读文档
qmd --index kb get "qmd://kb/docs/target-file.md"
常用命令速查
# 创建 collection
qmd --index kb collection add /path/to/kb --name kb --mask "**/*.md"
# 刷新关键词索引
qmd --index kb update
# 生成语义向量
qmd --index kb embed
# 查看状态
qmd --index kb status
# BM25 关键词搜索
qmd --index kb search "关键词" -c kb
# 向量语义搜索
qmd --index kb vsearch "自然语言问题" -c kb
# 混合检索
qmd --index kb query "自然语言问题" -c kb
# 读取文档
qmd --index kb get "#docid"
qmd --index kb get "qmd://kb/path/to/file.md"
# 搜索并输出更多内容
qmd --index kb search "关键词" -c kb --full --md -n 3
# 搜索只列文件
qmd --index kb search "关键词" -c kb --files -n 10
最后说一点
BM25 + 向量混合检索不新鲜,RAG 系统做了好几年了。QMD 的区别在于它是本地的、轻量的、专门给 Markdown 目录设计的——不需要 Elasticsearch 集群,不需要向量数据库服务,一个 SQLite 文件搞定。
对个人和小团队,够用了。