Skip to content

5.2.1 向量检索是怎么找到相似内容的? ​

先给结论:向量检索本质上是在一个向量空间里做“最近邻搜索”。系统会把用户问题编码成向量,再去索引里找距离更近、相似度更高的那些知识对象。

所以它的核心不是“理解答案”,而是“找语义上更接近的候选”。

它的基本流程是什么 ​

如果先把一轮最简化的向量检索拆开来看,通常是这样:

  1. 用户输入问题
  2. 系统把问题转成查询向量
  3. 索引里已经提前存好了知识块向量
  4. 系统计算查询向量和这些知识向量之间的相似度
  5. 取最接近的一批结果返回

你可以先把它理解成:

  • 问题和文档块都变成了空间里的点
  • 系统现在要找“离当前问题这个点更近”的那些点

为什么叫“最近邻搜索” ​

因为向量检索真正做的,就是在很多候选向量里寻找:

  • 最近的若干个邻居

这也是为什么很多资料里会出现:

  • kNN
  • ANN
  • 最近邻搜索

这些词。

这里的“最近”,并不一定指物理距离,而更像是:

  • 在当前相似度度量下更接近

常见的比较方式包括:

  • 余弦相似度
  • 点积
  • 欧氏距离

在文本类 RAG 场景里,最常见的通常还是余弦相似度一类的度量。

一个最小示意 ​

python
query = "定制商品支持七天无理由退货吗?"
query_vector = embed(query)

chunks = [
    {"id": "c1", "text": "定制类商品不支持无理由退货。"},
    {"id": "c2", "text": "普通商品支持七天无理由退货。"},
    {"id": "c3", "text": "如何修改收货地址?"}
]

results = []
for chunk in chunks:
    score = cosine_similarity(query_vector, embed(chunk["text"]))
    results.append({"id": chunk["id"], "score": score, "text": chunk["text"]})

results = sorted(results, key=lambda x: x["score"], reverse=True)

这段代码当然只是示意,但它说明了向量检索最核心的几件事:

  • 先把问题转成向量
  • 再和候选向量比较
  • 最后按相似度排序

为什么真实系统不会每次都这样暴力比较 ​

如果知识库很小,这样逐条比较当然也能跑。

但在真实系统里,知识对象可能有几十万、几百万甚至更多。每次查询都逐条暴力比一遍,成本会非常高。

所以真实系统里通常会引入专门的向量索引结构,用来更快地找到近邻。

你可以先粗略理解成两类思路:

  • 精确搜索:更接近全量比较,结果更准,但更慢
  • 近似搜索:用更高效的索引结构找“足够近”的邻居,通常更快

这也是为什么很多向量库会同时提到:

  • 精确 kNN
  • ANN
  • HNSW

这些概念。

向量检索真正返回的是什么 ​

这里也要先看清。

向量检索通常返回的,不是“答案”,而是:

  • 一批相似候选
  • 每条候选的相似度分数
  • 对应的文本和 metadata

后面系统往往还要继续做:

  • metadata 过滤
  • 重排
  • 聚合
  • 上下文构造
  • 最终回答生成

所以向量检索在整条链路里更像:

  • 首轮语义召回器

而不是:

  • 最终判断器

为什么有时会返回“看起来也不太像”的结果 ​

这是向量检索非常容易被误解的一点。

向量检索通常会返回“最接近的几个结果”,但“最接近”不等于“足够相关”。

也就是说,如果当前查询本来就和库里大多数内容都不太相关,系统仍然可能返回:

  • 相对最近
  • 但其实并不真的有用

所以很多向量搜索系统在纯向量查询下,即使没有真正合适的答案,也仍然会返回 k 个近邻候选。

这也是为什么:

  • 相似度阈值
  • metadata 边界
  • 混合检索

后面仍然很重要。

一个常见误区 ​

很多人会把向量检索理解成:

  • 系统已经知道哪条内容是对的

这通常过头了。

更稳的理解是:

  • 系统只是按语义接近程度,先找出一批更可能相关的候选

后面的“是不是当前真正该用的内容”,还要结合版本、权限、业务边界和后续排序一起判断。

一句话总结 ​

向量检索本质上是在向量空间里做最近邻搜索。系统把问题转成查询向量,再去索引里找更接近的知识对象。它擅长做语义召回,但返回的是候选,而不是最终答案。