RAG 里的 HNSW:向量数据库为什么能很快找到相似内容

最近在看 RAG 相关的东西,发现很多向量数据库、知识库系统都会提到一个词:HNSW。
一开始我还把它记成了“分层最小世界导航”,后来查了一下,准确点说应该是:
Hierarchical Navigable Small World,中文一般叫 分层可导航小世界图。
名字听起来很算法,很论文,很像那种看一眼就想关掉网页的东西。但它其实是在解决一个非常朴素的问题:
当我的知识库里有很多很多文本片段时,RAG 怎么快速找到和用户问题最相关的那几段?
这篇就不写得太学术了,我想用自己的理解,把 HNSW 在 RAG 里到底干了什么讲清楚。
先从 RAG 的检索说起
一个常见的 RAG 流程大概是这样:
用户提问 -> 把问题转成向量 -> 去向量数据库里找相似内容 -> 拿到相关文档片段 -> 拼进 prompt -> 让大模型回答这里最关键的一步是:去向量数据库里找相似内容。
比如我问:
HNSW 在向量检索里有什么用?
系统会先把这句话变成一个向量。知识库里的每个 chunk,也就是每个文本片段,也提前被转成了向量。
接下来要做的事情就是:
找出哪些 chunk 的向量,和这个问题的向量最接近。
最直接的方法当然是挨个比。
如果知识库里只有 100 个 chunk,那没什么问题。
如果有 100 万个 chunk 呢?
每次提问都把 100 万个向量拿出来算一遍相似度,当然也不是不行,但会越来越慢。
所以实际系统一般不会老老实实挨个比,而是会用一种叫 ANN 的方法。
ANN 是 Approximate Nearest Neighbor,意思是 近似最近邻搜索。
它的思路很现实:
我不一定非要每次都找到数学意义上 100% 精确的最近邻。
我只要能非常快地找到一批足够接近、足够有用的结果,就可以了。
RAG 本来也是一个工程系统。很多时候,比起“理论上绝对最相似”,我们更关心的是:
- 查得够不够快
- 召回的内容够不够相关
- 用户最终得到的回答靠不靠谱
HNSW 就是 ANN 里非常常用的一种索引方法。
HNSW 可以理解成一张“分层地图”
我觉得理解 HNSW 最好的方式,不是先看图论和复杂度,而是把它想象成一张地图。
假设你要去一个陌生城市找一家小店。
最笨的方法是:从城市最左边开始,一条街一条街扫过去,看到每一家店都进去问问。
这当然能找到,但太慢了。
现实中我们不会这样走。我们一般会:
- 先走高速或者主干道,快速接近目标区域。
- 到了附近之后,再走普通道路。
- 最后进入小巷子,慢慢找那家具体的店。
HNSW 大概也是这个意思。
它不是把所有向量平铺成一大堆点,然后每次从头到尾扫一遍。它会把这些点组织成一个分层的图结构:
高层: A -------- H \ /中层: A --- C --- F --- H \ \ / /底层: a-b-c-d-e-f-g-h-i-j-k-l越高层,节点越少,连接跨度越大,像高速路。
越底层,节点越多,连接越细,像城市里的普通街道。
搜索的时候,HNSW 会先在高层快速移动,尽量靠近目标区域。然后一层一层往下走,越往下找得越细。
这就是“分层”和“可导航”的含义。
“小世界”是什么意思?
HNSW 里的 Small World,也就是“小世界”,可以简单理解成:
图里的节点虽然很多,但你通常不需要走太多步,就能从一个地方接近另一个地方。
有点像社交网络里的“六度分隔”。
你和一个陌生人看起来隔得很远,但通过朋友的朋友的朋友,可能几步就能联系上。
放到向量检索里,每个向量点都会和一些邻居建立连接。这样搜索时就不需要把所有点都看一遍,而是可以沿着这些连接一步步往更接近目标的方向走。
它的核心味道有点像:
我先找一个大概方向,然后不断问:附近有没有更像 query 的点?
有就走过去。
没有就说明这一层差不多到头了,往下一层继续细找。
HNSW 搜索时大概怎么走?
假设我们有一个问题向量 q,要在知识库里找最相似的 chunk。
HNSW 的搜索过程可以粗略理解成:
- 从最上层的入口点开始。
- 看看当前点的邻居里,有没有谁离
q更近。 - 如果有,就移动到那个更近的点。
- 如果没有,就下到下一层。
- 重复这个过程。
- 到最底层后,用更大的候选池认真搜一圈。
- 返回最接近的 top-k 个结果。
注意,它不是“随机乱逛”。
它每一步都是在尝试往更接近 query 的方向走。
这有点像你在山上找最低点:
我站在当前位置,看看周围有没有更低的地方,有就走过去,没有就换一个更细的地图继续看。当然,真实 HNSW 比这个更复杂,它会维护候选集合,也会控制每层搜索范围。但作为理解 RAG 里的检索原理,这个直觉已经够用了。
建图的时候发生了什么?
搜索快不快,关键取决于索引怎么建。
HNSW 建索引时,会把每个向量当成一个节点插入图里。
插入一个新节点时,它会做几件事:
- 随机决定这个节点能出现到第几层。
- 从高层开始,找和它比较接近的已有节点。
- 在每一层给它连上一些邻居。
- 控制每个节点最多能连多少条边,避免图变得太密。
为什么要随机决定层数?
因为 HNSW 需要一种自然的“分层稀疏结构”。大部分节点只在底层,少部分节点能出现在高层,极少数节点能出现在很高的层。
这就像城市道路:
- 小路很多
- 主路少一些
- 高速入口更少
高层节点少,所以搜索可以快速跨越大范围。底层节点多,所以最后能细致地找到相似内容。
这几个参数挺重要
如果你用过 Milvus、Qdrant、Weaviate、Chroma 或者一些本地向量库,可能会见过这些参数。
M
M 可以简单理解成:每个节点最多连多少个邻居。
M 越大,图越密,搜索时路更多,召回率通常更好。
但代价是内存更高,索引也更大。
如果把 HNSW 当成城市地图,M 就像每个路口最多连出去多少条路。
路多,绕路的概率小。
但路太多,地图也更复杂。
efConstruction
efConstruction 是建索引时用的候选池大小。
它越大,建图时就越认真,索引质量通常越好。
但构建速度会变慢。
可以理解成:
修路的时候,多观察一些附近区域,再决定这条路该连到哪里。
看得越多,路可能修得越合理。
但修路时间也更长。
efSearch
efSearch 是查询时用的候选池大小。
它越大,搜索时看过的候选点越多,召回率通常越好。
但查询会变慢。
这个参数在 RAG 里很有存在感。
如果 efSearch 太小,系统可能查得很快,但漏掉真正重要的 chunk。最后大模型拿不到关键上下文,就只能开始一本正经地胡说。
如果 efSearch 调大一些,检索会慢一点,但更可能把关键内容召回来。
所以它本质上是在做一个取舍:
更快的速度 <-> 更高的召回HNSW 在 RAG 里到底负责什么?
这一点我觉得很重要:
HNSW 只负责帮你更快地找到相似向量。
它不负责判断文章有没有写好。
它不负责判断 chunk 切得合不合理。
它不负责理解用户真正想问什么。
它也不负责保证最终回答一定正确。
在 RAG 系统里,它更像是一个底层加速器。
如果说 RAG 是让大模型带着资料回答问题,那么 HNSW 做的事情就是:
从一堆资料碎片里,尽快把“看起来可能相关”的那几块找出来。
但 RAG 的效果不只取决于 HNSW。
还会受这些东西影响:
- embedding 模型好不好
- chunk 切分是否合理
- 文档本身质量怎么样
- top-k 取多少
- 有没有 rerank
- 有没有关键词检索补充
- 有没有元数据过滤
- prompt 怎么拼
所以不要把 HNSW 神化。
它很重要,但它只是检索链路里的一环。
一个容易误会的地方
很多人第一次接触向量检索,会有一种感觉:
既然用了向量数据库,那它应该能精准理解我的问题吧?
其实不是。
向量检索找的是“语义相似”。
语义相似不等于一定有答案。
相似内容也不一定就是正确上下文。
比如你问:
如何配置 HNSW 的 efSearch?
向量检索可能找出一堆关于 HNSW 原理的文章,但里面未必真的讲参数调优。
这时候就算 HNSW 搜得很快,也不代表 RAG 答得一定好。
所以在真实项目里,经常会把向量检索和其他方法组合起来,比如:
- 向量检索找语义相关
- 关键词检索找精确命中
- 元数据过滤限制范围
- rerank 重新排序
- 最后再交给大模型组织答案
HNSW 解决的是“快点找到候选内容”,不是“一步到位找到完美答案”。
我自己的理解
如果用一句话总结 HNSW,我会这么说:
HNSW 就是在向量空间里修了一套分层道路系统,让检索不用挨个敲门,而是先走高速接近目标,再下到街区慢慢找。
暴力搜索像是:
每个 chunk 我都看一遍。HNSW 像是:
我知道大概该往哪个方向走,先走远路,再走近路,最后在目标附近认真找。这就是为什么它适合 RAG。
RAG 的知识库一旦变大,检索就不能只靠“全部算一遍”。HNSW 用一种近似但高效的方式,把搜索范围快速缩小,让系统能在可接受的时间里找到相关内容。
当然,近似就意味着它不是绝对完美。
它可能漏掉一些结果,也需要调参数。
但在工程上,这种取舍通常是值得的。
最后
以前我看到 HNSW 这种词,会下意识觉得它离应用很远,好像是向量数据库内部的黑盒。
但把它放回 RAG 流程里看,它其实非常具体:
用户问了一个问题,系统要从一堆文本片段里快速找资料。
HNSW 就是那个帮系统快速“认路”的索引结构。
理解到这里,再看向量数据库里的 M、efConstruction、efSearch,就不会觉得它们只是几个神秘参数了。
它们本质上都在控制一件事:
这张向量地图修得多密?建图时有多认真?搜索时愿意多找一会儿吗?RAG 的很多问题,最后都会回到这些朴素的工程取舍上:
快一点,还是准一点?
省内存,还是高召回?
简单点,还是多加一层 rerank?
HNSW 不会替我们做完所有决定,但它确实是理解 RAG 检索时很值得搞懂的一块拼图。
参考
- Malkov, Yashunin: Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
