/ AIBLOG

技术笔记

先看懂向量检索再做 RAG:用 Qdrant 本地模式跑通最小实验

刚开始做知识库时,模型、分块、向量、数据库和提示词同时出现,很难判断错误发生在哪一步。可以先暂时拿掉模型,用三个手工向量验证“存入、排序、过滤”的行为,再逐步增加真实文档处理。

Qdrant Python 客户端提供本地模式,无需单独启动 Qdrant 服务;可以使用内存模式,也可以选择文件路径持久化。[1] 本文使用内存模式,不调用模型 API,不下载嵌入模型。资料核对日期为 2026 年 10 月 19 日。

先理解示例中的四个对象

官方示例使用 collection 保存 points,并为向量配置维度和距离计算方式;point 可以携带 payload 元数据,再通过条件限制搜索结果。[1][2]

在本文实验中,可以把它们理解为:

对象

本文的取值

Collection

rag_sandbox

向量

三个手工设计的三维向量

Point ID

1、2、3

Payload

标题和主题分类

这些向量由人直接赋值,并没有从标题计算。因此,本文实验只能验证数据操作与排序逻辑,不能证明系统理解中文标题。

安装客户端

在独立 Python 环境中执行:

1python -m pip install qdrant-client

记录实际安装版本,按项目现有方式保存依赖。首次安装需要访问允许的软件包源;本文不要求部署 Docker、建立云账号或填写 API Key。[1]

写入三条合成数据,再查询

保存为 vector_lab.py:

1from qdrant_client import QdrantClient, models
2
3client = QdrantClient(":memory:")
4collection = "rag_sandbox"
5
6client.create_collection(
7 collection_name=collection,
8 vectors_config=models.VectorParams(
9 size=3,
10 distance=models.Distance.COSINE,
11 ),
12)
13
14rows = [
15 (1, [1.0, 0.0, 0.0], "部署与运行", "ops"),
16 (2, [0.0, 1.0, 0.0], "界面与交互", "ui"),
17 (3, [0.0, 0.0, 1.0], "文档与检索", "knowledge"),
18]
19
20client.upsert(
21 collection_name=collection,
22 wait=True,
23 points=[
24 models.PointStruct(
25 id=point_id,
26 vector=vector,
27 payload={"title": title, "topic": topic},
28 )
29 for point_id, vector, title, topic in rows
30 ],
31)
32
33result = client.query_points(
34 collection_name=collection,
35 query=[0.9, 0.1, 0.0],
36 limit=2,
37 with_payload=True,
38)
39
40for point in result.points:
41 print(point.id, round(point.score, 4), point.payload)
42
43assert result.points[0].id == 1

执行:

1python vector_lab.py

按示例向量与余弦相似度的定义,第一条结果应是 ID 为 1 的点。这里使用断言检查排序,不要求终端输出逐字相同。代码对照官方客户端接口编写,尚未在读者环境实测。[1]

内存模式中的数据随客户端生命周期结束而消失。下一次运行会重新建立实验数据;需要持久保存时,才另行设计独立的数据目录和恢复方式。

加入一个分类过滤条件

在脚本末尾追加下面代码,仍然使用前面创建的客户端与 collection:

1filtered = client.query_points(
2 collection_name=collection,
3 query=[0.0, 1.0, 0.0],
4 query_filter=models.Filter(
5 must=[
6 models.FieldCondition(
7 key="topic",
8 match=models.MatchValue(value="ops"),
9 )
10 ]
11 ),
12 limit=2,
13 with_payload=True,
14)
15
16assert [point.id for point in filtered.points] == [1]
17print("过滤结果:", [point.id for point in filtered.points])

这次查询方向指向第二个向量,但过滤条件只允许 topic=ops,所以候选集合只剩 ID 为 1 的点。Qdrant 的条件过滤作用于 payload;它与向量相似度分别承担限制候选和排序的作用。[2]

一个结果排名第一,只表示它在当前候选中最靠前,不能据此断定它已经足够相关。本文建议后续加入“应当无答案”的样例,专门观察系统是否会把不相关内容交给生成模型。

把实验扩展到真实检索时,分步增加能力

本文建议按以下顺序推进:

  1. 用真实嵌入方法替换手工向量,并记录方法、版本和维度;
  2. 保存文档标识、版本与片段出处;
  3. 用查询与期望片段组成小型人工评测集;
  4. 先检查检索结果,再接入答案生成;
  5. 加入过滤后,专门验证允许与不允许出现的资料。

这些是实施建议,不是本文已经完成的功能。标题变成向量需要额外步骤,数据库不会因为 payload 中存有文字就自动获得语义向量。

如果后续需要用用户权限过滤资料,还应在应用层建立可信的授权校验,不能只依赖模型自觉传入一个过滤参数。

给 Agent 的最小任务

使用 qdrant-client 的内存模式和三条合成向量,验证写入、相似度排序和 topic 过滤。记录实际版本与断言结果,不连接现有数据库,不下载嵌入模型,不把实验描述为完整 RAG。

先建立这个可以解释每条结果的实验,再换成模型产生的向量,便更容易分清检索逻辑错误与嵌入质量问题。

参考来源

本文为原创整理;三条数据、实验代码、断言和推进顺序为作者设计,不包含检索效果或性能承诺。