Appearance
5.2.1 向量检索是怎么找到相似内容的?
先给结论:向量检索本质上是在一个向量空间里做“最近邻搜索”。系统会把用户问题编码成向量,再去索引里找距离更近、相似度更高的那些知识对象。
所以它的核心不是“理解答案”,而是“找语义上更接近的候选”。
它的基本流程是什么
如果先把一轮最简化的向量检索拆开来看,通常是这样:
- 用户输入问题
- 系统把问题转成查询向量
- 索引里已经提前存好了知识块向量
- 系统计算查询向量和这些知识向量之间的相似度
- 取最接近的一批结果返回
你可以先把它理解成:
- 问题和文档块都变成了空间里的点
- 系统现在要找“离当前问题这个点更近”的那些点
为什么叫“最近邻搜索”
因为向量检索真正做的,就是在很多候选向量里寻找:
- 最近的若干个邻居
这也是为什么很多资料里会出现:
kNNANN- 最近邻搜索
这些词。
这里的“最近”,并不一定指物理距离,而更像是:
- 在当前相似度度量下更接近
常见的比较方式包括:
- 余弦相似度
- 点积
- 欧氏距离
在文本类 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 边界
- 混合检索
后面仍然很重要。
一个常见误区
很多人会把向量检索理解成:
- 系统已经知道哪条内容是对的
这通常过头了。
更稳的理解是:
- 系统只是按语义接近程度,先找出一批更可能相关的候选
后面的“是不是当前真正该用的内容”,还要结合版本、权限、业务边界和后续排序一起判断。
一句话总结
向量检索本质上是在向量空间里做最近邻搜索。系统把问题转成查询向量,再去索引里找更接近的知识对象。它擅长做语义召回,但返回的是候选,而不是最终答案。