第一章:Qdrant 概述与核心概念
1.1 什么是向量数据库
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 向量数据库 | 专为高效存储、索引和检索高维向量(embedding)设计的数据库系统。 | 不适用于传统结构化数据的复杂事务处理;主要用于相似性搜索场景。 |
| Embedding(嵌入) | 将文本、图像、音频等非结构化数据通过模型(如 BERT、CLIP)转换为数值向量。 | 向量维度通常为几十到几千维;质量直接影响检索效果。 |
| 相似性搜索 | 根据向量之间的距离(如余弦相似度)查找最相近的条目。 | 需选择合适的距离度量方式;高维空间存在”维度灾难”问题。 |
| 近似最近邻(ANN) | 在可接受误差范围内快速找到近似最近邻,而非精确最近邻,以提升性能。 | Qdrant 使用 HNSW 等算法实现 ANN;牺牲少量精度换取大幅速度提升。 |
1.2 Qdrant 简介与特性
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Qdrant | 开源、高性能、支持过滤的向量搜索引擎,用 Rust 编写,提供 gRPC 和 REST API。 | 开源版本功能已非常完整;企业版提供额外安全与管理功能。 |
| 高性能检索 | 基于 HNSW 图索引,支持毫秒级响应,适用于实时应用。 | 性能受向量维度、索引参数、硬件影响;需合理配置。 |
| Payload 过滤 | 支持在向量搜索时附加结构化元数据(如标签、类别、时间)进行条件过滤。 | Payload 字段需提前建立索引以加速过滤;否则会全扫描。 |
| 多语言客户端 | 官方提供 Python、TypeScript、Rust 等 SDK,社区支持 Go、Java 等。 | 推荐使用官方维护的客户端以确保兼容性。 |
| 云原生支持 | 支持 Docker、Kubernetes 部署,提供 Qdrant Cloud 托管服务。 | 本地开发推荐 Docker 快速启动;生产环境建议配置持久化卷。 |
1.3 Qdrant 与其他向量数据库对比(如 Pinecone、Weaviate、Milvus)
| 框架名称 | 所属类别 | 用途 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| Qdrant | 开源向量数据库 | 高效向量存储与带过滤的相似性搜索 | 需要自托管、强过滤能力、低延迟的语义搜索或 RAG 应用 | 社区活跃,文档完善;企业功能需付费 |
| Pinecone | 托管向量数据库 | 全托管向量检索服务 | 快速上线、无需运维、适合初创团队或原型验证 | 闭源;成本随数据量/请求量线性增长 |
| Weaviate | 向量+图数据库 | 结合语义搜索与知识图谱 | 需要将向量与实体关系联合建模的场景(如智能问答、知识库) | 架构较重;学习曲线陡峭 |
| Milvus | 开源向量数据库 | 超大规模向量检索(十亿级) | 工业级 AI 平台、需要分布式集群与高吞吐的场景 | 部署复杂;依赖较多组件(如 etcd, MinIO) |
1.4 核心术语解释(Point、Vector、Payload、Collection、Distance Metric 等)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Collection | Qdrant 中的顶层容器,类似关系数据库中的”表”,用于组织一组向量数据。 | 创建时需指定向量维度和距离度量;不可修改(需重建)。 |
| Point | Collection 中的基本数据单元,包含一个向量和可选的 Payload 元数据。 | 每个 Point 必须有唯一 ID(整数或字符串)。 |
| Vector | 浮点数组,表示数据的嵌入向量,如 [0.23, -0.45, ..., 0.89]。 | 所有 Point 的向量维度必须一致;不支持稀疏向量(截至 v1.10)。 |
| Payload | 附加在 Point 上的 JSON 兼容元数据,如 {"category": "tech", "score": 9.5}。 | 可用于过滤、排序;建议对高频查询字段建立索引。 |
| Distance Metric | 衡量两个向量相似性的函数,Qdrant 支持 Cosine、Dot、Euclidean。 | Cosine 最常用(忽略向量长度);Dot 需归一化才等价于 Cosine。 |
| HNSW | Hierarchical Navigable Small World,Qdrant 默认使用的 ANN 索引结构。 | 构建索引需额外时间和内存;可通过 m、ef_construct 参数调优。 |
| Shard | Collection 的分片单元,用于水平扩展和并行查询。 | 单机模式下通常只有一个分片;集群模式可配置多个。 |
第二章:环境搭建与快速入门
2.1 安装 Qdrant(Docker / Binary / Cloud)
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 使用 Docker 安装 | 执行命令:docker pull qdrant/qdrant,然后运行:docker run -p 6333:6333 qdrant/qdrant | 需提前安装 Docker;默认数据存储在内存中,重启丢失(如需持久化见 2.2) |
| 使用二进制文件安装 | 从 GitHub Releases 下载对应平台的压缩包(如 qdrant-x86_64-unknown-linux-gnu.tar.gz),解压后运行 ./qdrant | 适用于无 Docker 环境;需手动管理进程和配置文件 |
| 使用 Qdrant Cloud | 访问 https://cloud.qdrant.io/ 注册账号,创建项目并获取 API URL 和密钥 | 免运维;免费额度有限;适合快速验证或生产托管 |
2.2 启动本地 Qdrant 实例
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 启动带持久化的 Docker 实例 | 创建目录 mkdir ./qdrant_storage,运行:docker run -p 6333:6333 -v $(pwd)/qdrant_storage:/qdrant/storage qdrant/qdrant | 数据将保存在本地 ./qdrant_storage 目录,重启容器后保留 |
| 自定义配置启动 | 下载默认配置 config.yaml,修改参数(如 storage_path、cors),挂载配置:docker run -p 6333:6333 -v $(pwd)/config.yaml:/qdrant/config/config.yaml qdrant/qdrant | 配置文件路径必须为 /qdrant/config/config.yaml |
| 验证服务是否启动成功 | 访问 http://localhost:6333 或执行 curl http://localhost:6333 | 成功返回 JSON 包含 "title": "qdrant - vector search engine" |
2.3 使用 REST API 与 Qdrant 交互
| 方法名称 | 语法(HTTP) | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 获取服务信息 | GET / | 检查 Qdrant 是否运行 | curl http://localhost:6333 | 无需认证;返回版本和状态信息 |
| 列出所有 Collection | GET /collections | 查看已有集合 | curl http://localhost:6333/collections | 返回 JSON 结构包含 collections 列表 |
| 创建 Collection | PUT /collections/{collection_name} | 创建新集合 | 见下方代码块 | vectors.size 必须为正整数;distance 可选 Cosine/Dot/Euclidean |
| 插入 Point | PUT /collections/{name}/points | 批量插入向量数据 | 见下方代码块 | id 可为整数或字符串;vector 长度必须匹配集合定义 |
| 搜索 Point | POST /collections/{name}/points/search | 执行相似性搜索 | 见下方代码块 | limit 控制返回结果数量;可添加 filter 字段进行条件过滤 |
创建 Collection:
curl -X PUT http://localhost:6333/collections/test_collection \
-H "Content-Type: application/json" \
-d '{"vectors": {"size": 4, "distance": "Cosine"}}'
插入 Point:
curl -X PUT http://localhost:6333/collections/test_collection/points \
-H "Content-Type: application/json" \
-d '{"points": [{"id": 1, "vector": [0.1,0.2,0.3,0.4], "payload": {"city": "Berlin"}}]}'
搜索 Point:
curl -X POST http://localhost:6333/collections/test_collection/points/search \
-H "Content-Type: application/json" \
-d '{"vector": [0.15,0.25,0.35,0.45], "limit": 3}'
2.4 使用 Python 客户端连接 Qdrant
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 安装客户端 | pip install qdrant-client | 安装官方 Python SDK | pip install qdrant-client | 需 Python ≥3.7 |
| 初始化客户端 | from qdrant_client import QdrantClient | 连接本地 Qdrant 实例 | client = QdrantClient("localhost", port=6333) | 默认端口 6333;支持 https 和 API key(云服务) |
| 连接 Qdrant Cloud | client = QdrantClient(url="https://xxx.us-east-1.aws.cloud.qdrant.io", api_key="your_key") | 连接托管服务 | 见下方代码块 | api_key 在 Cloud 控制台获取;必须使用 HTTPS |
| 检查连接状态 | client.get_collections() | 验证连接是否成功 | collections = client.get_collections(); print(collections) | 返回 CollectionInfo 列表;若连接失败抛出异常 |
连接 Qdrant Cloud 示例:
client = QdrantClient(
url="https://abcd.us-east-1.aws.cloud.qdrant.io",
api_key="sk-xxxx"
)
2.5 创建第一个 Collection 并插入数据
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建 Collection | client.create_collection(collection_name, vectors_config=VectorParams(size=dim, distance=Distance.COSINE)) | 定义向量集合结构 | 见下方代码块 | size 必须与后续插入向量长度一致;Distance 枚举值:COSINE, DOT, EUCLID |
| 插入单个 Point | client.upsert(collection_name, points=[PointStruct(id=1, vector=[...], payload={})]) | 插入一条带元数据的向量 | 见下方代码块 | id 必须唯一;payload 为 dict |
| 批量插入 Points | client.upsert(collection_name, points=point_list) | 高效插入多条数据 | 见下方代码块 | 建议批量大小 100~1000;过大可能超时 |
| 验证插入结果 | client.scroll(collection_name, limit=5) | 查看已插入的数据 | retrieved = client.scroll("demo", limit=5); print(retrieved[0]) | scroll 用于遍历;search 用于相似性查询 |
创建 Collection:
from qdrant_client.models import VectorParams, Distance
client.create_collection(
collection_name="demo",
vectors_config=VectorParams(size=4, distance=Distance.COSINE)
)
插入单个 Point:
from qdrant_client.models import PointStruct
client.upsert(
collection_name="demo",
points=[PointStruct(id=1, vector=[0.1,0.2,0.3,0.4], payload={"name": "Alice"})]
)
批量插入 Points:
points = [
PointStruct(id=i, vector=[i*0.1]*4, payload={"idx": i})
for i in range(1, 6)
]
client.upsert("demo", points=points)
第三章:Collection 管理
3.1 Collection 的创建与配置参数详解
| 方法名称 | 语法(Python 客户端) | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
create_collection | client.create_collection(collection_name, vectors_config, shard_number=None, replication_factor=None, write_consistency_factor=None, on_disk_payload=False, hnsw_config=None, optimizers_config=None, wal_config=None) | 创建新 Collection,支持完整配置 | 见下方代码块 | vectors_config 必填;其他参数为可选,用于性能调优;创建后无法修改 size 和 distance |
VectorParams | VectorParams(size=int, distance=Distance) | 定义向量基本结构 | VectorParams(size=384, distance=Distance.DOT) | size 必须 > 0;distance 取值:COSINE / DOT / EUCLID |
HnswConfigDiff | HnswConfigDiff(m=None, ef_construct=None, full_scan_threshold=None, max_indexing_threads=None, on_disk=None) | 配置 HNSW 索引参数 | HnswConfigDiff(m=16, ef_construct=100, on_disk=True) | m 控制图连接数(默认 16);ef_construct 影响索引质量(默认 100);on_disk=True 可降低内存占用 |
OptimizersConfigDiff | OptimizersConfigDiff(deleted_threshold=None, vacuum_min_vector_number=None, default_segment_number=None, max_segment_size=None, memmap_threshold=None, indexing_threshold=None) | 控制段合并与内存策略 | OptimizersConfigDiff(memmap_threshold=10000) | memmap_threshold:超过此向量数使用磁盘映射;调整可平衡内存与性能 |
创建 Collection 完整示例:
from qdrant_client.models import VectorParams, Distance
client.create_collection(
collection_name="articles",
vectors_config=VectorParams(size=768, distance=Distance.COSINE),
shard_number=2,
on_disk_payload=True
)
3.2 Collection 的更新与删除
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
update_collection | client.update_collection(collection_name, optimizer_config=None, collection_params=None) | 更新 Collection 的优化器或参数(有限支持) | 见下方代码块 | 不能修改向量维度或距离度量;主要用于调整索引触发阈值等运行时参数 |
delete_collection | client.delete_collection(collection_name) | 删除整个 Collection 及其所有数据 | client.delete_collection("temp_data") | 操作不可逆;删除后释放磁盘和内存资源;若 Collection 不存在,抛出异常(可捕获) |
collection_exists | client.collection_exists(collection_name) | 检查 Collection 是否存在(v1.9+) | exists = client.collection_exists("articles") | 推荐在创建前检查,避免重复创建错误 |
更新 Collection 示例:
from qdrant_client.models import OptimizersConfigDiff
client.update_collection(
"articles",
optimizer_config=OptimizersConfigDiff(indexing_threshold=50000)
)
3.3 Collection 的元信息查询
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
get_collection | client.get_collection(collection_name) | 获取 Collection 的完整元信息 | info = client.get_collection("articles"); print(info.config.params.vectors.size) | 返回包含 vectors、hnsw_config、optimizers 等配置的对象 |
get_collections | client.get_collections() | 列出所有 Collection 名称及简要信息 | 见下方代码块 | 返回 CollectionInfo 列表;适用于监控或管理界面 |
retrieve_collection_stats | client.http.client.points_count(collection_name)(间接方式) | 获取 Point 总数等统计信息 | 官方暂未提供直接 stats API | Qdrant 暂无内置”统计信息”接口;可通过 count 方法获取精确数量(见第四章) |
注:截至 Qdrant v1.10,
get_collection是获取元信息的主要方法,返回结构包含:
config.params.vectors:向量配置config.hnsw_config:HNSW 索引参数config.optimizer_config:优化器设置points_count:当前 Point 数量(部分版本支持)
列出所有 Collection 示例:
collections = client.get_collections()
for c in collections.collections:
print(c.name)
3.4 向量维度与距离度量选择(Cosine、Dot、Euclidean)
| 概念名称 | 说明 | 适用场景 | 注意事项 |
|---|---|---|---|
| 向量维度(size) | 向量中浮点数的个数,如 BERT 输出通常为 768 维 | 文本嵌入:384/768/1024;图像特征:512/2048 | 所有 Point 必须严格匹配该维度;维度越高,内存和计算开销越大;不支持动态变更 |
| Cosine 距离 | 衡量向量方向相似性,忽略长度:cos(θ) = (A·B) / (‖A‖‖B‖) | 文本语义搜索;已归一化的向量 | 最常用;对向量缩放不敏感;Qdrant 中值越小表示越相似(实际存储 1 - cos(θ)) |
| Dot Product(点积) | A·B = Σ(Ai × Bi) | 未归一化的稠密检索;需保留向量幅度信息 | 若向量已 L2 归一化,则 Dot ≈ Cosine;否则可能受向量长度主导,导致偏差 |
| Euclidean(欧氏距离) | √Σ(Ai - Bi)² | 图像特征、坐标数据等几何空间 | 对异常值敏感;高维下区分度下降(“维度灾难”);值越小越相似 |
距离类型常量(Python):
| 距离类型常量 | 对应值 | 说明 |
|---|---|---|
Distance.COSINE | "Cosine" | 默认推荐 |
Distance.DOT | "Dot" | 用于未归一化向量 |
Distance.EUCLID | "Euclid" | 用于几何距离场景 |
重要提示:
- 一旦 Collection 创建,无法更改 distance 或 size,需重建。
- 使用 Dot 时,若希望等效于 Cosine,应在插入前对向量做 L2 归一化:
import numpy as np
vector = np.array([0.3, 0.4, 0.5])
norm_vec = vector / np.linalg.norm(vector)
第四章:数据操作(CRUD)
4.1 插入向量数据(Points)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
upsert | client.upsert(collection_name, points=[PointStruct(id, vector, payload)]) | 插入或覆盖单个/多个 Point | 见下方代码块 | id 可为 int 或 str;若 id 已存在,则覆盖原 Point(全量替换);payload 可为 None |
PointStruct | PointStruct(id=id, vector=vec, payload=payload) | 构造单个 Point 对象 | PointStruct(id="doc_1", vector=[0.5]*768, payload={"author": "Alice"}) | vector 必须是 float 列表;长度必须与 Collection 定义一致 |
插入 Point 示例:
from qdrant_client.models import PointStruct
client.upsert(
collection_name="demo",
points=[PointStruct(id=101, vector=[0.1,0.2,0.3,0.4], payload={"title": "Intro"})]
)
4.2 批量插入与更新
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
upsert(批量) | client.upsert(collection_name, points=point_list) | 一次性插入/更新多个 Point | 见下方代码块 | 推荐批量大小:100~1000;过大可能导致超时或内存溢出;原子性:部分失败不影响已成功写入的 Point |
| 使用 ID + 向量列表(简化写法) | client.upsert(collection_name, points=Batch(ids=[...], vectors=[...], payloads=[...])) | 更高效的批量写入方式 | 见下方代码块 | Batch 是官方推荐的大批量写入方式;payloads 元素可为 dict 或 None;向量、ID、Payload 长度必须一致 |
批量 upsert 示例:
points = [
PointStruct(id=i, vector=[i*0.1]*4, payload={"idx": i})
for i in range(1000, 1010)
]
client.upsert("demo", points=points)
使用 Batch 简化写法:
from qdrant_client.models import Batch
client.upsert(
"demo",
points=Batch(
ids=[1, 2],
vectors=[[0.1,0.2,0.3,0.4], [0.5,0.6,0.7,0.8]],
payloads=[{"tag": "A"}, {"tag": "B"}]
)
)
4.3 按 ID 查询与删除 Point
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
retrieve | client.retrieve(collection_name, ids=[id1, id2]) | 按 ID 获取 Point 的向量和 Payload | 见下方代码块 | 返回顺序不一定与请求 ID 顺序一致;不存在的 ID 被忽略(不报错) |
delete | client.delete(collection_name, points_selector=PointIdsList(points=[id1, id2])) | 按 ID 删除 Point | 见下方代码块 | 删除后不可恢复;支持整数和字符串 ID 混合;返回操作状态(如 deleted_count) |
PointIdsList | PointIdsList(points=[id1, id2, ...]) | 构造 ID 删除选择器 | PointIdsList(points=[1, 2, "abc"]) | 用于 delete 的 points_selector 参数 |
按 ID 查询示例:
result = client.retrieve("demo", ids=[101, "doc_1"])
for r in result:
print(r.id, r.payload)
按 ID 删除示例:
from qdrant_client.models import PointIdsList
client.delete("demo", points_selector=PointIdsList(points=[101, "doc_1"]))
4.4 Payload 字段的增删改查
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
set_payload | client.set_payload(collection_name, payload={...}, points=[id1, id2]) | 为指定 Point 设置/覆盖 Payload 字段 | client.set_payload("demo", payload={"status": "published"}, points=[101]) | 会覆盖同名字段;新增字段不会影响其他字段 |
overwrite_payload | client.overwrite_payload(collection_name, payload={...}, points=[id1]) | 完全替换 Point 的整个 Payload | client.overwrite_payload("demo", payload={"title": "New"}, points=[101]) | 原有 Payload 被清空;相当于先删除再设置 |
delete_payload | client.delete_payload(collection_name, keys=["key1", "key2"], points=[id1]) | 删除指定 Payload 字段 | client.delete_payload("demo", keys=["temp_flag"], points=[101]) | 仅删除指定 key;若 key 不存在,静默忽略 |
clear_payload | client.clear_payload(collection_name, points=[id1]) | 清空 Point 的所有 Payload | client.clear_payload("demo", points=[101]) | Payload 变为空 dict {};向量仍保留 |
4.5 向量与 Payload 的联合存储策略
| 概念名称 | 说明 | 最佳实践 | 注意事项 |
|---|---|---|---|
| 向量存储 | Qdrant 将向量存储在内存或磁盘(取决于 HNSW on_disk 配置) | 高频检索向量建议保留在内存;超大规模可启用 on_disk_vectors(v1.9+) | 向量无法单独更新;需通过 upsert 替换整个 Point |
| Payload 存储 | Payload 默认存储在内存;可通过 on_disk_payload=True 存于磁盘 | 大 Payload(如长文本)建议启用 on_disk_payload;小而频繁查询的字段应保留在内存 | on_disk_payload 会降低过滤性能;创建 Collection 时指定,不可更改 |
| Payload 索引 | 对高频过滤字段建立索引可加速查询 | 使用 client.create_payload_index(collection, field_name, field_type) | 未索引字段在 filter 时会全扫描;支持 keyword、integer、float、geo 等类型;索引占用额外磁盘空间 |
| 联合更新策略 | 向量和 Payload 必须一起 upsert | 若仅需更新 Payload,仍需提供原向量;可缓存向量避免重复计算 | Qdrant 不支持”仅更新 Payload”或”仅更新向量”;设计应用层时应保留原始向量副本 |
Payload 索引创建示例:
from qdrant_client.models import PayloadSchemaType
client.create_payload_index(
collection_name="articles",
field_name="category",
field_schema=PayloadSchemaType.KEYWORD
)
第五章:向量检索与搜索
5.1 基础相似性搜索(search)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
search | client.search(collection_name, query_vector=vector, limit=k) | 执行基础向量相似性搜索 | 见下方代码块 | query_vector 长度必须匹配 Collection 定义;返回按相似度降序排列的结果;score 含义取决于 distance:Cosine 越大越相似,Euclid 越小越相似 |
SearchRequest(底层) | client.http.points_api.search_points(...) | 直接调用 REST API(不推荐常规使用) | 一般通过 client.search 封装即可 | 仅用于调试或特殊需求 |
search 示例:
result = client.search(
collection_name="demo",
query_vector=[0.15, 0.25, 0.35, 0.45],
limit=3
)
for hit in result:
print(hit.id, hit.score)
5.2 带 Payload 过滤的条件搜索(filter)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
search + filter | client.search(collection_name, query_vector=vec, query_filter=Filter(...), limit=k) | 在向量搜索基础上添加结构化条件过滤 | 见下方代码块 | must 表示 AND 条件;should 表示 OR;过滤字段建议提前建立 Payload 索引;未索引字段会触发全扫描,性能差 |
| 常用过滤条件构造 | FieldCondition(key="field", match=MatchValue(value="x")) 等 | 构建不同类型的过滤条件 | 见下方代码块 | 支持 keyword、integer、float、geo、text;MatchAny 用于多值匹配(v1.8+) |
带过滤搜索示例:
from qdrant_client.models import Filter, FieldCondition, MatchValue
result = client.search(
"articles",
query_vector=[0.1]*768,
query_filter=Filter(
must=[FieldCondition(key="category", match=MatchValue(value="tech"))]
),
limit=5
)
多种过滤条件构造:
# 数值范围过滤
FieldCondition(key="year", range=Range(gte=2020, lte=2025))
# 数值阈值过滤
FieldCondition(key="score", range=Range(gte=5.0))
# 多值匹配
FieldCondition(key="tags", match=MatchAny(any=["a", "b"]))
5.3 多向量融合搜索(multi-vector / rerank)
| 概念/方法名称 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 多向量融合(非原生支持) | Qdrant 原生不支持单 Point 存储多个向量;需应用层实现融合 | 方案1:对多个 query_vector 分别搜索,再加权合并结果;方案2:在插入前将多向量拼接或加权平均为单向量 | Qdrant v1.10 不支持 multi-vector 字段;建议在 embedding 阶段完成融合(如 CLIP 图文联合嵌入) |
| Rerank(重排序) | 先用 Qdrant 快速召回 top-k,再用精排模型(如 Cross-Encoder)重打分 | 见下方代码块 | Qdrant 仅负责粗排(ANN);精排需外部模型;适用于 RAG 场景;可结合 LangChain 的 ReRanker 组件 |
注:截至 Qdrant v1.10,官方未提供内置多向量或 rerank API,需在客户端实现。
Rerank 示例:
hits = client.search("docs", query_vec, limit=50)
reranked = cross_encoder.rerank(query, [h.payload["text"] for h in hits])
5.4 搜索参数详解(top_k、score_threshold、with_payload、with_vector)
| 参数名称 | 语法位置 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
limit(即 top_k) | client.search(..., limit=10) | 控制返回结果数量 | client.search("demo", vec, limit=10) | 默认值通常为 10;最大值受配置限制(默认 10000) |
score_threshold | client.search(..., score_threshold=0.5) | 过滤低于阈值的结果 | client.search("demo", vec, score_threshold=0.8) | 仅保留 score ≥ threshold 的结果;Cosine 范围 [0,1];Dot/Euclid 需根据数据分布设定 |
with_payload | client.search(..., with_payload=True/False/["field1"]) | 控制是否返回 Payload | client.search("demo", vec, with_payload=["title"]) | True:返回全部 Payload;False:不返回;列表:仅返回指定字段(节省带宽) |
with_vector | client.search(..., with_vector=True/False) | 控制是否返回原始向量 | client.search("demo", vec, with_vector=False) | 默认 False(节省传输开销);调试或重计算时可设为 True |
5.5 分页与游标支持(offset / scroll)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
search + offset | client.search(..., offset=10, limit=10) | 实现简单分页(跳过前 N 条) | page2 = client.search("demo", vec, offset=10, limit=10) | 仅适用于小偏移量(< 1000);大 offset 性能急剧下降(需遍历前 N 条) |
scroll | client.scroll(collection_name, scroll_filter=Filter(...), limit=page_size, offset=last_id) | 基于游标的高效遍历(推荐) | 见下方代码块 | offset 为上一批最后一个 Point ID;支持按 Payload 过滤;适合导出或全量遍历 |
ScrollResponse | 返回 (List[Record], Optional[PointId]) | scroll 方法的返回结构 | records, next_id = client.scroll("demo", limit=5) | 当 next_id 为 None 时表示已到末尾;Record 包含 id、vector、payload |
分页建议:
- 用户翻页(如第1/2/3页):可用 offset(数据量小时)
- 数据导出、批处理:必须用 scroll
- 搜索结果分页:不推荐深度分页,应限制总页数
scroll 示例:
points, next_offset = client.scroll("demo", limit=10)
# 下一页
points2, _ = client.scroll("demo", limit=10, offset=next_offset)
第六章:高级功能
6.1 Payload 索引优化(关键字索引、范围索引)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
create_payload_index | client.create_payload_index(collection_name, field_name, field_schema) | 为指定 Payload 字段创建索引 | 见下方代码块 | 支持 KEYWORD、INTEGER、FLOAT、GEO、TEXT;创建后可加速 filter 查询;索引构建异步进行,不影响写入 |
delete_payload_index | client.delete_payload_index(collection_name, field_name) | 删除指定字段的索引 | client.delete_payload_index("articles", "temp_tag") | 删除后该字段过滤将全扫描;释放索引占用的磁盘空间 |
PayloadSchemaType 枚举 | KEYWORD / INTEGER / FLOAT / GEO / TEXT | 指定索引类型 | PayloadSchemaType.INTEGER # 用于 score 字段 | TEXT 索引需在配置文件中启用 tokenizer(如 “whitespace”);GEO 字段必须是标准 GeoJSON 格式 |
创建 Payload 索引示例:
from qdrant_client.models import PayloadSchemaType
client.create_payload_index(
"articles",
"category",
field_schema=PayloadSchemaType.KEYWORD
)
6.2 向量量化与内存优化(HNSW、Product Quantization)
| 概念/方法名称 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| HNSW 索引参数调优 | 通过 m 和 ef_construct 控制图密度与构建质量 | 在 create_collection 中设置:HnswConfigDiff(m=24, ef_construct=200) | m 越大,内存越高,召回率越高;ef_construct 影响索引构建时的搜索深度;默认 m=16, ef_construct=100 |
on_disk_vectors(v1.9+) | 将向量存储在磁盘而非内存,降低 RAM 占用 | create_collection(..., on_disk_vectors=True) | 仅支持单向量 Collection;检索延迟略增(需磁盘 I/O);适用于超大规模(>1亿点) |
| Product Quantization(PQ) | Qdrant 暂未原生支持 PQ;依赖 HNSW 实现 ANN | 无直接 API | 官方路线图中提及未来可能支持;当前内存优化主要靠 on_disk_vectors + HNSW |
| 内存映射阈值(memmap_threshold) | 超过指定向量数后使用磁盘映射 | OptimizersConfigDiff(memmap_threshold=20000) | 避免小集合占用过多内存;与 on_disk_vectors 不冲突 |
建议:
- 小于 100 万点:全内存 + 默认 HNSW
- 100 万~1 亿点:
on_disk_vectors=True+ 调高 m/ef- 超过 1 亿点:考虑分片集群 + SSD 存储
6.3 分片与集群部署(Sharding & Replication)
| 概念名称 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 分片(Sharding) | 将 Collection 数据水平拆分到多个分片,提升并行查询能力 | create_collection(..., shard_number=4) | 单机模式下分片仍存在,但由同一节点管理;集群模式下分片分布到不同节点;创建后不可修改 shard_number |
| 副本(Replication) | 每个分片可配置多个副本,实现高可用 | create_collection(..., replication_factor=2) | 仅在集群模式生效;replication_factor ≥ 2 才具备容错能力;写入需多数副本确认(write_consistency_factor) |
write_consistency_factor | 控制写入成功所需的最小副本数 | create_collection(..., write_consistency_factor=2) | 必须 ≤ replication_factor;默认为 1(性能优先);设为 2 可避免脑裂写入 |
| 集群部署方式 | 使用多个 Qdrant 节点组成 Raft 共识集群 | 启动时指定 --uri 和 --bootstrap | 需配置共享存储或云磁盘;官方提供 Kubernetes Helm Chart;节点间通过 gRPC 通信 |
集群启动示例:
# 节点1
./qdrant --uri http://node1:6335 --bootstrap
# 节点2
./qdrant --uri http://node2:6335 --bootstrap http://node1:6335
6.4 快照备份与恢复
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
create_snapshot | client.create_snapshot(collection_name) | 创建 Collection 的快照(异步) | info = client.create_snapshot("articles"); snapshot_name = info.name | 快照保存在 storage/snapshots 目录;可跨版本恢复(需兼容);创建期间 Collection 只读 |
list_snapshots | client.list_snapshots(collection_name) | 列出所有快照 | snapshots = client.list_snapshots("articles") | 返回快照名、创建时间、大小等信息 |
delete_snapshot | client.delete_snapshot(collection_name, snapshot_name) | 删除指定快照 | client.delete_snapshot("articles", "articles-2026-02-03-09-30.snapshot") | 释放磁盘空间 |
restore_from_snapshot | 需通过 REST API 或文件系统操作 | 从快照恢复数据 | 停止 Qdrant → 复制 snapshot 文件到 storage/snapshots → 启动时添加 --restore-snapshot 参数 | 官方 Python 客户端不支持 restore API;恢复会覆盖同名 Collection;建议在独立实例测试后再生产恢复 |
快照路径示例:
./qdrant_storage/snapshots/articles/articles-2026-02-03-09-30.snapshot
6.5 权限控制与 API Key 管理(企业版)
| 功能名称 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| API Key 认证 | 限制客户端访问权限(仅企业版) | 在 config.yaml 中启用:service.api_key: "your-secret-key" | 开源版不支持 API Key;所有请求需带 header: api-key: your-secret-key |
| 基于角色的访问控制(RBAC) | 为不同用户分配只读/读写权限(企业版) | 通过 Qdrant Cloud 控制台或企业 API 配置 | 开源版无此功能;适用于多租户 SaaS 场景 |
| CORS 与网络隔离 | 限制前端或外部服务访问 | config.yaml 中配置:cors_origin: ["https://your-app.com"] | 开源版支持 CORS 配置;生产环境应禁用 * |
| 审计日志 | 记录关键操作(如删除、快照) | 企业版内置;开源版需自行监控 API 调用 | 企业版提供日志导出与告警 |
重要提示:
- 开源版 Qdrant 默认无任何认证机制,暴露公网极危险
- 生产部署必须:
- 通过 Nginx/API 网关加鉴权
- 或使用 Qdrant Cloud
- 或自行封装代理层
第七章:客户端开发实践
7.1 Python SDK 使用详解
| 方法/类名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
QdrantClient 初始化 | QdrantClient(host, port) 或 QdrantClient(url, api_key) | 连接本地或云服务实例 | client = QdrantClient("localhost", port=6333) | 本地开发用 host/port;Qdrant Cloud 必须用 url + api_key |
models 模块导入 | from qdrant_client.models import * | 使用结构化参数对象 | from qdrant_client.models import VectorParams, Distance, PointStruct | 官方推荐使用 models 中的类型,避免字典拼写错误 |
| 异步客户端 | from qdrant_client.async_qdrant_client import AsyncQdrantClient | 支持 async/await | 见下方代码块 | 需在 async 函数中使用;性能优于同步客户端(高并发场景) |
| 批量插入(Batch) | Batch(ids, vectors, payloads) | 高效批量写入 | 见下方代码块 | 比列表 of PointStruct 更省内存;payloads 元素可为 None |
异步客户端示例:
async_client = AsyncQdrantClient("localhost")
await async_client.search(...)
Batch 批量写入示例:
points = Batch(
ids=[1, 2],
vectors=[[0.1]*4, [0.2]*4],
payloads=[{"a": 1}, {"b": 2}]
)
client.upsert("demo", points=points)
7.2 JavaScript / TypeScript 客户端使用
| 方法/类名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
QdrantClient 初始化 | new QdrantClient({ host, port }) | 连接本地实例 | const client = new QdrantClient({ host: "localhost", port: 6333 }); | 安装:npm install @qdrant/js-client-rest |
| 连接 Cloud | new QdrantClient({ url, apiKey }) | 连接托管服务 | 见下方代码块 | 必须使用 HTTPS;apiKey 在 Cloud 控制台获取 |
| 创建 Collection | await client.createCollection(name, config) | 定义集合结构 | await client.createCollection("posts", { vectors: { size: 384, distance: "Cosine" } }); | vectors 配置必须指定 size 和 distance |
| 插入 Point | await client.upsert(name, { points }) | 写入数据 | 见下方代码块 | id 可为 number 或 string;payload 为普通对象 |
| 搜索 | await client.search(name, { vector, limit }) | 执行查询 | const results = await client.search("posts", { vector: [0.15, 0.25], limit: 3 }); | 返回数组,含 score、id、payload 等字段 |
JavaScript 连接 Cloud 示例:
const client = new QdrantClient({
url: "https://xxx.cloud.qdrant.io",
apiKey: "sk-xxxx"
});
JavaScript 插入 Point 示例:
await client.upsert("posts", {
points: [{ id: 1, vector: [0.1, 0.2], payload: { title: "JS" } }]
});
7.3 Rust 原生客户端调用
| 方法/结构体 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
QdrantClient::from_url | QdrantClient::from_url("http://localhost:6333") | 创建客户端 | let client = QdrantClient::from_url("http://localhost:6333"); | 安装依赖:qdrant-client = "1" in Cargo.toml;默认使用 reqwest 异步运行时 |
| 异步操作(async) | client.create_collection(...).await | 所有操作均为 async | 见下方代码块 | 必须在 tokio 或 async-std 环境中运行;错误类型为 qdrant::QdrantError |
PointStruct 构造 | PointStruct { id, vector, payload } | 构建数据点 | 见下方代码块 | id 类型为 Option;payload 为 HashMap<String, Value> |
| 搜索请求 | client.search_points(...).await | 执行向量搜索 | 见下方代码块 | SearchPoints 结构体支持 filter、with_payload 等字段 |
Rust 示例:
// 创建 Collection
client.create_collection(
CreateCollection {
collection_name: "rust_demo".to_string(),
vectors_config: ...
}
).await?;
// 构造 Point
PointStruct {
id: Some(PointId::from(1)),
vector: vec![0.1, 0.2, 0.3, 0.4],
payload: HashMap::new()
}
// 搜索
let res = client.search_points(
SearchPoints {
collection_name: "rust_demo".into(),
vector: vec![0.15; 4],
limit: 3,
..Default::default()
}
).await?;
7.4 与 LangChain / LlamaIndex 集成
| 集成方式 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| LangChain Qdrant | Qdrant.from_documents(documents, embeddings, url, api_key) | 将文档自动嵌入并存入 Qdrant | 见下方代码块 | 自动创建 Collection;需安装 langchain[qdrant];支持 metadata 作为 Payload |
| LangChain 检索 | vector_store.similarity_search(query, k=4) | 执行语义搜索 | results = vector_store.similarity_search("What is AI?", k=3) | 底层调用 Qdrant search API;可传入 filter 参数 |
| LlamaIndex Qdrant | QdrantVectorStore(qdrant_client=client, collection_name=name) | 作为 LlamaIndex 的向量存储后端 | 见下方代码块 | 需先初始化 QdrantClient;与 LlamaIndex 的 IndexBuilder 集成 |
| 混合检索(LangChain + Filter) | vector_store.similarity_search(query, filter={"category": "tech"}) | 带条件的 RAG 检索 | results = vector_store.similarity_search("AI trends", filter={"year": {"$gte": 2023}}) | filter 语法需符合 Qdrant 规范;字段需建立 Payload 索引 |
LangChain 集成示例:
from langchain.vectorstores import Qdrant
vector_store = Qdrant.from_documents(
docs, embeddings,
url="http://localhost:6333",
collection_name="lc_demo"
)
LlamaIndex 集成示例:
from llama_index.vector_stores import QdrantVectorStore
vector_store = QdrantVectorStore(client, "llama_demo")
storage_context = StorageContext.from_defaults(vector_store=vector_store)
7.5 异步操作与批量处理最佳实践
| 实践名称 | 操作细节 | 代码示例(Python) | 注意事项 |
|---|---|---|---|
| 异步批量插入 | 使用 asyncio.gather 并发 upsert | 见下方代码块 | 避免单次过大 batch;控制并发数(如用 asyncio.Semaphore) |
| 流式插入(生成器) | 按需生成 Point 并分批写入 | 见下方代码块 | 内存恒定,适合大数据集;最后一批别忘记 flush |
| 异步搜索并发 | 并行执行多个 query | 见下方代码块 | 适用于多路召回场景;注意 Qdrant 服务器 CPU/内存瓶颈 |
| 错误重试机制 | 对网络错误自动重试 | 见下方代码块 | 推荐使用 tenacity 库;避免因瞬时故障中断流程 |
批量大小建议:
- 小内存机器:100~200
- 高配服务器:500~1000
- 过大会导致 timeout 或 OOM;可通过监控 Qdrant 的 RAM 和 CPU 调整
异步批量插入示例:
async def insert_batch(client, batch):
return await client.upsert("demo", points=batch)
tasks = [insert_batch(client, b) for b in batches]
await asyncio.gather(*tasks)
流式插入示例:
def point_generator():
for i in range(100000):
yield PointStruct(id=i, vector=[...], payload={})
batch = []
for p in point_generator():
batch.append(p)
if len(batch) == 500:
client.upsert("demo", points=batch)
batch = []
异步搜索并发示例:
queries = [[0.1]*768, [0.2]*768]
tasks = [client.search("demo", q, limit=5) for q in queries]
results = await asyncio.gather(*tasks)
错误重试示例:
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
async def safe_search(client, vec):
return await client.search("demo", vec)
第八章:生产部署与运维
8.1 单机部署 vs 分布式集群
| 部署模式 | 说明 | 适用场景 | 注意事项 |
|---|---|---|---|
| 单机部署(Standalone) | 单个 Qdrant 实例运行,所有数据存储在本地磁盘或内存 | 开发/测试环境;小型应用(< 1000 万向量);低可用性要求场景 | 无高可用能力;单点故障风险;资源受限时可启用 on_disk_vectors |
| 分布式集群(Cluster) | 多节点组成 Raft 共识集群,支持分片与副本 | 生产环境;数据量 > 1 亿向量;要求 99.9%+ 可用性 | 需至少 3 节点保证容错;网络延迟影响写入性能;配置复杂,需共享存储或云磁盘 |
| 对比维度 | 单机部署 | 分布式集群 |
|---|---|---|
| 部署复杂度 | 低(单容器) | 高(需协调多个节点) |
| 扩展性 | 垂直扩展(加 CPU/RAM) | 水平扩展(加节点) |
| 容灾能力 | 无 | 支持节点故障自动恢复 |
| 运维成本 | 低 | 高(需监控、日志聚合等) |
| 推荐数据规模 | < 50M Points | > 50M Points |
8.2 Docker Compose 与 Kubernetes 部署方案
| 部署方式 | 操作细节 | 配置示例(关键部分) | 注意事项 |
|---|---|---|---|
| Docker Compose(单机) | 编写 docker-compose.yml 启动服务 | 见下方代码块 | 必须挂载 storage 目录实现持久化;默认配置适用于开发 |
| Docker Compose(集群模拟) | 多服务定义 + 自定义网络 | 见下方代码块 | 仅用于测试集群行为;生产不推荐用 Compose 部署集群 |
| Kubernetes(Helm) | 使用官方 Helm Chart 部署 | 见下方代码块 | 支持 StatefulSet + PVC;自动配置 headless service 和 Raft URI;需提前配置 StorageClass |
| Kubernetes(自定义 YAML) | 手动编写 StatefulSet | 见下方代码块 | POD_NAME 需通过 downward API 注入;需配置 headless service 用于节点发现 |
Docker Compose(单机)示例:
version: '3'
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
volumes:
- ./qdrant_storage:/qdrant/storage
Docker Compose(集群模拟)示例:
services:
qdrant1:
command: ["--uri", "http://qdrant1:6335", "--bootstrap"]
qdrant2:
command: ["--uri", "http://qdrant2:6335", "--bootstrap", "http://qdrant1:6335"]
Kubernetes(Helm)示例:
helm repo add qdrant https://qdrant.github.io/qdrant-helm
helm install qdrant qdrant/qdrant \
--set replicaCount=3 \
--set persistence.enabled=true
Kubernetes(自定义 YAML)示例:
apiVersion: apps/v1
kind: StatefulSet
spec:
replicas: 3
template:
spec:
containers:
- name: qdrant
args: ["--uri", "http://$(POD_NAME).qdrant-headless:6335", ...]
8.3 监控指标与日志分析(Prometheus / Grafana)
| 组件 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| Prometheus 指标暴露 | Qdrant 内置 /metrics 端点(需启用) | 在 config.yaml 中添加:telemetry.prometheus: true | 默认端口 6333 的 /metrics;指标前缀:qdrant_ |
| 关键监控指标 | qdrant_collection_vector_index_size(索引大小)、qdrant_request_duration_seconds(请求延迟)、qdrant_memory_usage_bytes(内存占用) | 在 Prometheus 中查询:rate(qdrant_request_duration_seconds_count[5m]) | 建议设置告警:延迟 > 500ms 或错误率 > 1% |
| Grafana 仪表盘 | 可视化 Qdrant 性能 | 导入官方 Dashboard ID(如 19473)或自定义 | 官方提供 JSON 模板;需关联 Prometheus 数据源 |
| 日志收集 | Qdrant 输出结构化日志到 stdout | 使用 Fluentd / Loki / Filebeat 采集容器日志 | 日志级别可通过 RUST_LOG 环境变量控制(如 RUST_LOG=info);错误日志包含 trace_id 便于追踪 |
最小 Prometheus 配置示例:
scrape_configs:
- job_name: 'qdrant'
static_configs:
- targets: ['qdrant:6333']
8.4 性能调优指南(内存、CPU、磁盘 I/O)
| 资源类型 | 调优参数 | 推荐值 | 说明 |
|---|---|---|---|
| 内存 | memmap_threshold(优化器) | 10000~50000 | 超过该向量数使用磁盘映射,降低 RAM 占用 |
| 内存 | on_disk_vectors | true(大数据集) | 向量存磁盘,牺牲少量延迟换内存 |
| CPU | max_indexing_threads(HNSW) | CPU 核数 - 1 | 控制索引构建并发线程数 |
| 磁盘 I/O | 使用 NVMe SSD | — | Qdrant 对磁盘随机读写敏感,HDD 性能差 |
| 索引质量 | ef_construct(HNSW) | 100~200 | 越高召回率越好,但构建越慢 |
| 索引密度 | m(HNSW) | 16~64 | 越大图连接越多,内存和查询时间增加 |
| 批量写入 | 单次 batch size | 200~1000 | 平衡吞吐与内存;过大易超时 |
| 场景 | 推荐配置 |
|---|---|
| 高召回率搜索 | m=32, ef_construct=200, ef=128(搜索时) |
| 低延迟写入 | memmap_threshold=5000, indexing_threshold=50000 |
| 超大规模(>1亿) | on_disk_vectors=true + SSD + shard_number=8 |
8.5 版本升级与兼容性说明
| 操作步骤 | 操作细节 | 注意事项 |
|---|---|---|
| 备份数据 | 创建快照或复制 storage 目录 | 升级前必须备份;快照可通过 client.create_snapshot() 触发 |
| 检查兼容性 | 查阅官方 Release Notes | 主版本升级(如 v1.x → v2.x)可能破坏兼容性;补丁版本(v1.9.1 → v1.9.2)通常安全 |
| 停机升级(单机) | 1. 停止旧容器;2. 启动新版本容器(挂载相同 storage) | Qdrant 支持向前兼容 storage 格式;启动时自动迁移元数据(如有) |
| 滚动升级(集群) | 逐个替换节点(需 >=3 节点) | 确保集群健康(quorum 可用);新旧版本需兼容(通常限 minor 版本内) |
| 回滚方案 | 启动旧版本镜像 + 原 storage | 若新版本启动失败,可回退;快照可用于跨版本恢复(需测试) |
兼容性规则(截至 v1.10):
- 存储格式向后兼容:v1.0+ 的数据可被 v1.10 读取
- REST/gRPC API 保持稳定,废弃接口会提前标记
- Python SDK 主版本与 Server 主版本对齐(如 SDK v1.x 对应 Server v1.x)
升级命令示例(Docker):
# 停止旧实例
docker stop qdrant
# 拉取新镜像
docker pull qdrant/qdrant:v1.10.0
# 启动新实例(挂载原数据卷)
docker run -d -p 6333:6333 -v ./qdrant_storage:/qdrant/storage qdrant/qdrant:v1.10.0
第九章:典型应用场景案例
9.1 语义搜索系统构建
| 步骤名称 | 操作细节 | 代码示例(Python) | 注意事项 |
|---|---|---|---|
| 文本嵌入生成 | 使用 Sentence-BERT 等模型将文档转为向量 | 见下方代码块 | 嵌入维度需固定(如 384);批量 encode 提升效率 |
| 创建 Collection | 定义向量结构与 Payload 字段 | 见下方代码块 | 推荐 Cosine 距离;Payload 存原文、URL、时间等元数据 |
| 批量插入文档 | 将文档 ID、向量、元数据写入 Qdrant | 见下方代码块 | ID 可用 UUID 或业务主键;Payload 中保留原文用于展示 |
| 语义搜索 | 用户输入查询,返回相似文档 | 见下方代码块 | 可加 filter 限定来源(如 site:example.com);结果按 score 降序排列 |
文本嵌入生成:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
vectors = model.encode(documents)
创建 Collection:
client.create_collection(
"semantic_search",
vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)
批量插入文档:
points = [
PointStruct(id=i, vector=vec, payload={"text": doc, "url": url})
for i, (vec, doc, url) in enumerate(zip(vectors, docs, urls))
]
client.upsert("semantic_search", points=points)
语义搜索:
query_vec = model.encode("How to use Qdrant?")
results = client.search(
"semantic_search",
query_vector=query_vec,
limit=5,
with_payload=True
)
9.2 推荐系统中的向量召回
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 用户/物品向量化 | 通过协同过滤或深度模型生成 user/item embedding | item_vectors = load_item_embeddings() | 用户向量可实时生成(如行为序列聚合);物品向量通常离线批量生成 |
| 构建物品库 | 将物品向量存入 Qdrant,Payload 包含 ID、类目、价格等 | 见下方代码块 | 若使用 Dot Product,向量应归一化;类目字段建议建 KEYWORD 索引 |
| 实时召回 | 根据用户向量检索 Top-K 相似物品 | 见下方代码块 | 必须加类目/价格等业务过滤;召回结果供精排模型二次排序 |
| 冷启动处理 | 新用户用热门物品向量代替 | if not user_has_history: user_vec = get_popular_item_avg_vector() | 可维护一个”热门池” Collection 供 fallback |
构建物品库:
client.create_collection("items", VectorParams(size=128, distance=Distance.DOT))
client.upsert("items", points=[
PointStruct(id=item_id, vector=vec, payload=item_meta)
])
实时召回:
user_vec = get_user_embedding(user_id)
recs = client.search(
"items",
query_vector=user_vec,
query_filter=Filter(must=[FieldCondition(key="category", match=MatchValue("electronics"))]),
limit=20
)
9.3 RAG(检索增强生成)架构集成
| 组件 | 说明 | 集成方式 | 注意事项 |
|---|---|---|---|
| 文档分块 | 将长文档切分为 200~500 字 chunks | 使用 LangChain TextSplitter | 避免跨语义切分;保留 chunk 元信息(页码、标题) |
| 向量存储 | Qdrant 作为向量数据库 | LangChain Qdrant.from_documents(...) | Collection 名按知识库隔离(如 “kb_finance”);Payload 存 chunk_text + source |
| 检索阶段 | 用户问题 → 嵌入 → Qdrant 搜索 | retriever = vector_store.as_retriever(search_kwargs={"k": 4}) | 可加 metadata 过滤(如 time > 2023);支持多路召回(混合关键词+向量) |
| 生成阶段 | 将检索结果拼入 Prompt 送入 LLM | 见下方代码块 | 需控制上下文长度(避免超 token);可对检索结果做 rerank 提升相关性 |
端到端示例(LangChain):
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vector_store.as_retriever(),
chain_type="stuff"
)
response = qa_chain.run("Qdrant 支持哪些距离度量?")
生成阶段 Prompt 拼接示例:
context = "\n".join([doc.page_content for doc in docs])
prompt = f"基于以下信息回答:{context}\n\n问题:{query}"
9.4 图像/音频特征向量检索
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 特征提取 | 使用 CNN(图像)或 VGGish(音频)生成 embedding | 见下方代码块 | 输出向量需 flatten;建议 L2 归一化(便于用 Cosine) |
| 存储多模态数据 | 向量 + Payload(含文件路径、标签、类型) | 见下方代码块 | Payload 中存原始文件路径或 URL;tags 字段建索引支持过滤 |
| 跨模态搜索 | 用图像查相似图像,或用文本查图像(需统一嵌入空间) | 见下方代码块 | 跨模态需同一嵌入模型(如 CLIP);不同模型生成的向量不可混用 |
| 相似图推荐 | ”以图搜图”功能 | similar_images = client.search("images", query_vector=target_feat, limit=20) | 可加 geo 过滤(如”附近景点图片”);返回结果前端展示缩略图 |
特征提取示例:
import torchvision.models as models
resnet = models.resnet50(pretrained=True)
features = resnet(img_tensor)
存储多模态数据:
client.upsert("media", points=[
PointStruct(
id=img_id, vector=feat,
payload={"type": "image", "path": "/data/img123.jpg", "tags": ["cat", "outdoor"]}
)
])
跨模态搜索:
# 图像→图像
query_feat = extract_image_feature(query_img)
results = client.search("media", query_vector=query_feat, limit=10)
# 文本→图像(需 CLIP 等多模态模型)
9.5 实时个性化广告投放
| 步骤名称 | 操作细节 | 代码示例 | 注意事项 |
|---|---|---|---|
| 广告素材向量化 | 对广告标题、描述、图片联合嵌入 | ad_vector = clip_model.encode_text(ad_text) + clip_model.encode_image(ad_image) | 多特征融合(加权平均或拼接);向量代表广告语义 |
| 用户兴趣建模 | 实时聚合用户点击/浏览行为生成兴趣向量 | user_interest = weighted_avg([ad_vectors for ad in recent_clicks]) | 可滑动窗口更新(最近 1 小时行为);新用户用人群画像向量初始化 |
| 实时匹配 | QPS 高的在线服务调用 Qdrant 搜索 | 见下方代码块 | 必须过滤预算耗尽或暂停的广告;设置 score_threshold 避免低质曝光 |
| 频次控制 | 在 Payload 中记录曝光次数(应用层实现) | 见下方代码块 | Qdrant 不提供频控原语;可结合 Redis 实现全局频控 |
实时匹配示例:
ads = client.search(
"ads",
query_vector=user_interest,
query_filter=Filter(must=[
FieldCondition(key="campaign_status", match=MatchValue("active")),
FieldCondition(key="budget_left", range=Range(gte=0.01))
]),
limit=10,
score_threshold=0.6
)
频次控制示例:
if ad_id in user_exposure and user_exposure[ad_id] > 3:
skip ad
性能要求:
- P99 延迟 < 50ms
- 单实例支持 > 1000 QPS
- 建议:
on_disk_vectors=false+ SSD + HNSW m=32