Skip to main content

Qdrant数据写进去了却搜不到

· 4 min read

最近在做的系统中集成RAG,需要将商品知识库、物流知识库写入Qdrant,实现RAG检索。

技术栈:

Spring Boot
LangChain4j
Qdrant
OpenAI Embedding

整体流程非常简单:

文档

Chunk切分

Embedding

Qdrant存储

向量检索

LLM生成

结果测试时,出现了一个非常奇怪的问题。


问题现象

数据写入完全正常。

Qdrant后台也能看到数据:

{
"id": 1,
"vector": [...]
}

插入日志:

INFO Insert 5000 points success

但是查询始终为空:

List<ScoredPoint> result = client.search(...)

返回:

[]

完全搜不到数据。


第一轮排查

怀疑Embedding有问题。

打印向量长度:

Embedding embedding =
embeddingModel.embed(text).content();

System.out.println(
embedding.vector().length
);

结果:

1536

正常。

再查看Qdrant Collection:

curl localhost:6333/collections/docs

返回:

{
"vectors": {
"size": 1536
}
}

维度一致。

排除Embedding问题。


第二轮排查

怀疑相似度阈值过高。

代码:

SearchParams.newBuilder()
.setHnswEf(128)
.build();

搜索:

scoreThreshold=0.8

改成:

scoreThreshold=0.3

还是查不到。


第三轮排查

开始怀疑数据没写进去。

查看point数量:

curl localhost:6333/collections/docs

结果:

{
"points_count": 5000
}

确实存在。

甚至通过ID查询:

retrieve(1)

也能返回数据。

说明:

数据存在
向量存在
Collection存在

但就是搜不到。


真正原因

最后查看官方文档才发现问题。

创建Collection时:

VectorParams.newBuilder()
.setSize(1536)
.setDistance(Distance.Cosine)

后来项目切换Embedding模型:

bge-large-zh

向量维度变成:

1024

但是Collection没有重建。

此时出现:

Qdrant Collection: 1536维
Embedding结果: 1024维

写入时因为封装层没有显式报错。

查询时:

1536 vs 1024

向量根本不在同一个空间。

自然检索不到结果。


为什么会这样?

这里涉及Qdrant底层原理。

Qdrant本质上是:

向量ID

向量数组

HNSW索引

例如:

[0.12,0.33,0.55...]

HNSW建立的是固定维度空间。

比如:

1536维空间

所有向量必须:

长度完全一致

否则无法计算:

Cosine Similarity
Euclidean Distance
Dot Product

因为:

A=[1,2,3]

B=[1,2]

数学上根本无法求距离。

所以Qdrant要求:

Collection创建后
Vector Size不可变

深入源码

Qdrant创建Collection时:

message VectorParams {
uint64 size = 1;
Distance distance = 2;
}

对应:

CreateCollection.newBuilder()
.setCollectionName("docs")
.setVectorsConfig(...)

最终保存到:

Collection Config

后续所有Point都会校验:

vector.length == size

一旦不一致:

Wrong input vector dimension

或者某些SDK直接吞掉异常。


如何避免

方案一:启动时校验

项目启动时读取Collection信息:

CollectionInfo info =
client.getCollection("docs");

检查:

info.getConfig()
.getParams()
.getVectors()
.getSize();

与Embedding模型配置对比:

1536 == embeddingDimension

不一致直接启动失败。


方案二:Collection版本化

不要复用Collection。

例如:

knowledge_v1
knowledge_v2
knowledge_v3

Embedding模型升级:

text-embedding-3-small

bge-large-zh

直接创建:

knowledge_v4

重新构建索引。


方案三:记录Embedding模型

Payload中增加:

{
"model":"bge-large-zh"
}

检索异常时快速定位:

到底是哪批数据生成的Embedding

后续引出的Qdrant原理

这个问题解决后,又顺带理解了几个以前没注意的知识点:

Collection为什么不能随便改维度

因为HNSW索引是基于固定维度建立的。

修改维度意味着:

整个索引失效

必须重建。


为什么Embedding模型不能随便切换

因为不同模型生成的是不同向量空间:

OpenAI

1536维

BGE

1024维

Jina

768维

即使维度相同:

语义空间也不同

老数据和新数据混存会导致召回效果急剧下降。


为什么RAG要做全量重建

很多团队升级Embedding后发现:

检索命中率下降

根本原因往往不是Qdrant。

而是:

旧Embedding
+
新Embedding

混在同一个Collection里。


这个案例在真实项目里非常常见,而且很有迷惑性,因为:

数据能写进去
Collection也存在
没有明显报错
查询结果却永远为空

最后定位下来并不是Spring问题,也不是LangChain4j问题,而是Qdrant最核心的设计原则——一个Collection只能对应一个固定维度、固定语义空间的Embedding模型