我对 Qdrant 和 Docling(以及 Ollama)的实战经验
发布: (2025年12月14日 GMT+8 03:25)
5 min read
原文: Dev.to
Source: Dev.to
第一次使用 Qdrant!
Qdrant 是一个开源的、高性能向量数据库和相似度搜索引擎。它存储、索引并搜索高维向量,这对于现代 AI 应用(如检索增强生成(RAG)系统、推荐引擎和图像识别)至关重要。Qdrant 使用 Rust 编写,具备速度快、内存效率高的特点,既可以部署在云端,也可以在本地部署。
在评估了各种 RAG 方案后,我想测试 Qdrant 的高级元数据过滤和混合搜索功能。我使用 Docling 文档中的实用 RAG 流水线示例来启动本地实例,并进行动手性能测试。
什么是 Docling?
Docling 是一个开源库,简化了跨多种格式的文档处理和解析,包括高级 PDF 理解。主要特性包括:
- 解析 PDF、DOCX、PPTX、XLSX、HTML、WAV、MP3、VTT、图像(PNG、TIFF、JPEG 等)以及更多
- 高级 PDF 理解:页面布局、阅读顺序、表格、代码、公式、图像分类等
- 统一的
DoclingDocument表示 - 导出为 Markdown、HTML、DocTags、无损 JSON
- 本地执行,适用于敏感数据和空气隔离环境
- 与 LangChain、LlamaIndex、Crew AI、Haystack 的即插即用集成
- 对扫描版 PDF 和图像提供广泛的 OCR 支持
- 支持多种 视觉语言模型(GraniteDocling)
- 音频 支持,配合自动语音识别(ASR)模型
- MCP 服务器,可连接任意代理
- 简单的 CLI
本地部署 Qdrant
快速入门页面是:
以下 Docker(或 Podman)命令可在几分钟内搭建 Qdrant:
docker pull qdrant/qdrant
mkdir -p qdrant_storage
docker run -p 6333:6333 -p 6334:6334 \
-v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
qdrant/qdrant
容器启动后你会看到类似如下信息:
_ _
__ _| |_ __ __ _ _ __ | |_
/ _` | __/ _` | '__/ _` | __|
| (_| | || (_| | | | (_| | |_
\__,_|\__\__,_|_| \__,_|\__|
Version: 1.16.2, build: d2834de0
Access web UI at http://localhost:6333/dashboard
REST API: http://localhost:6333
GRPC API: http://localhost:6334
示例 Python 应用
下面的代码演示了 Qdrant 的基本操作:创建集合、插入(upsert)点,以及执行普通和过滤的最近邻搜索。
# qdrant_app.py
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance,
VectorParams,
PointStruct,
Filter,
FieldCondition,
MatchValue,
UpdateStatus,
)
import time
# 1. Client initialization
print("1. Initializing Qdrant client...")
client = QdrantClient(url="http://localhost:6333")
# 2. Collection setup
COLLECTION_NAME = "city_vectors"
VECTOR_SIZE = 4
DISTANCE_METRIC = Distance.DOT
# Ensure a clean start
try:
client.delete_collection(collection_name=COLLECTION_NAME)
print(f" Collection '{COLLECTION_NAME}' deleted (if it existed).")
except Exception:
pass
print(f" Creating collection '{COLLECTION_NAME}'...")
client.create_collection(
collection_name=COLLECTION_NAME,
vectors_config=VectorParams(size=VECTOR_SIZE, distance=DISTANCE_METRIC),
)
print(" Collection created successfully.")
# 3. Upsert points
print("\n2. Upserting data points...")
points_to_insert = [
PointStruct(id=1, vector=[0.05, 0.61, 0.76, 0.74], payload={"city": "Berlin", "population": 3.7}),
PointStruct(id=2, vector=[0.19, 0.81, 0.75, 0.11], payload={"city": "London", "population": 8.9}),
PointStruct(id=3, vector=[0.36, 0.55, 0.47, 0.94], payload={"city": "Moscow", "population": 12.6}),
PointStruct(id=4, vector=[0.18, 0.01, 0.85, 0.80], payload={"city": "New York", "population": 8.4}),
PointStruct(id=5, vector=[0.24, 0.18, 0.22, 0.44], payload={"city": "Beijing", "population": 21.5}),
PointStruct(id=6, vector=[0.35, 0.08, 0.11, 0.44], payload={"city": "Mumbai", "population": 20.4}),
]
operation_info = client.upsert(
collection_name=COLLECTION_NAME,
wait=True,
points=points_to_insert,
)
print(f" Upsert operation status: {operation_info.status.name}")
if operation_info.status == UpdateStatus.COMPLETED:
time.sleep(1) # ensure index is ready
print(f" Successfully inserted {len(points_to_insert)} points.")
# 4. Plain nearest‑neighbors search
QUERY_VECTOR_1 = [0.2, 0.1, 0.9, 0.7]
print(f"\n3. Performing nearest neighbors search for query vector: {QUERY_VECTOR_1}")
search_result_1 = client.query_points(
collection_name=COLLECTION_NAME,
query=QUERY_VECTOR_1,
with_payload=True,
limit=3,
).points
print(" Top 3 closest points:")
for result in search_result_1:
print(f" - Score: {result.score:.4f}, City: {result.payload.get('city')}, Vector ID: {result.id}")
# 5. Filtered nearest‑neighbors search (only London)
QUERY_VECTOR_2 = [0.2, 0.1, 0.9, 0.7]
print(f"\n4. Performing filtered search (city='London') for query vector: {QUERY_VECTOR_2}")
search_result_2 = client.query_points(
collection_name=COLLECTION_NAME,
query=QUERY_VECTOR_2,
query_filter=Filter(
must=[FieldCondition(key="city", match=MatchValue(value="London"))]
),
with_payload=True,
limit=3,
).points
print(" Closest point filtered by city='London':")
for result in search_result_2:
print(f" - Score: {result.score:.4f}, City: {result.payload.get('city')}, Vector ID: {result.id}")
运行示例
python qdrant_app.py
典型输出:
1. Initializing Qdrant client...
Collection 'city_vectors' deleted (if it existed).
Creating collection 'city_vectors'...
Collection created successfully.
2. Upserting data points...
Upsert operation status: COMPLETED
Successfully inserted 6 points.
3. Performing nearest neighbors search for query vector: [0.2, 0.1, 0.9, 0.7]
Top 3 closest points:
- Score: 1.3620, City: New York, Vector ID: 4
- Score: 1.2730, City: Berlin, Vector ID: 1
- Score: 1.2080, City: Moscow, Vector ID: 3
4. Performing filtered search (city='London') for query vector: [0.2, 0.1, 0.9, 0.7]
Closest point filtered by city='London':
- Score: 0.8710, City: London, Vector ID: 2