精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖

锐英源精品原创文章,禁止转载和任何形式的非法内容使用,违者必究
有一定基础朋友,可以直接跳过本段,直接看下一段。本文是AI系列文章,欢迎在面包屑导航里点击人工智能进行系列学习。
锐英源软件内部平台稳定推进开发中,需要把数据持久化同时能CRUD创建读更新和删除,DeepSeek和TRAE都推荐了Qdrant,就配置了Qdrant环境,编写了类,实现了CRUD操作。
数据库就是高级的Excel,一直是信息化的核心重点,比如人的身份信息都是存在数据库里,高铁刷身份证能上,就是背后有数据库平台支撑。普通家长想要让子女对编程有兴趣,锐英源软件会有更通俗易懂的解释,欢迎关注联系。
锐英源软件也是第一次用,DeepSeek推荐在Assets页面下载,Assets的入口在Release页面,这是Exe发布页面,在github的Qdrant页面的右边,能看到Release页面,如下图:

下载解压后,不需要安装,将下载的压缩包解压到你选择的目录,例如 D:\Qdrant。
配置(可选):在 qdrant.exe 同级目录下创建一个 config 文件夹,并在其中新建 config.yaml 文件。你可以根据需要配置端口和存储路径:
yaml
service:
host: 0.0.0.0
http_port: 6333
grpc_port: 6334
storage:
storage_path: "./storage"
启动:直接双击 qdrant.exe 即可运行。
运行时,微软的安全工具Defender报警告,如下图:

Windows 已保护你的电脑
Microsoft Defender SmartScreen阻止了无法识别的应用启动。运行此应用可能会导致你的电脑存在风险。
应用: qdrant.exe
发行者:发布者未知
仍要运行 不运行
我机器上有防病毒软件,下载环境是可信的,机器是可信的,所以我点了仍要运行。运行了,TRAE里的代码就可以用HTTP方式来连接Qdrant了。
Qdrant运行时界面如下:

Access web UI at http://localhost:6333/dashboard
Agentic Skills:https://skills.qdrant.tech/
Config file not found: config/development
There is a potential issue with the filesystem for storage path ./storage. Details: Filesystem type check is not supported on this platform
storage::content_manager::consensus::persistent Loading raft state from ./storage\raft_state.json
Distributed mode disabled
Telemetry reporting enabled, id: 198304c7-d821-4c7b-b1bf-bacf2aa29ce7
qdrant::actix::web_ui Static content folder for Web UI './static' does not exist
qdrant::actix REST transport settings: keep_alive=5s, client_request_timeout=5s, client_disconnect_timeout=5s
qdrant::actix TLS disabled for REST API
qdrant::tonic Qdrant gRPC listening on 6334
qdrant::actix Qdrant HTTP listening on 6333
qdrant::tonic TLS disabled for gRPC API
actix_server::builder starting 19 workers
actix_server::server Actix runtime found; starting in Actix runtime
actix_server::server starting service: "actix-web-service-0.0.0.0:6333", workers: 19, listening on: 0.0.0.0:6333
从启动提示里可以看到,qdrant官网上是有Skill技能文档,http://localhost:6333/dashboard用于观察状态,提示里还有一些错误,但是实际上没有影响数据的存取。
在代码运行后,会在代码目录下生成qdrant_data目录,我特意看了下,里面有配置文件meta.json和sqlite数据库文件,看到的 SQLite 文件很可能就是 Qdrant 在本地模式下用于持久化数据的组件。它和 meta.json 一起,共同构成了 Qdrant 在本地运行时的数据存储结构。
class QdrantVectorStore(VectorStore):
"""
Qdrant 向量存储 - P1 阶段实现
支持两种部署模式:
1. 嵌入式本地模式(无 Docker):path="./qdrant_data"
数据持久化到本地磁盘,程序重启不丢失,适合单机开发和小规模生产。
2. 服务端模式(需 Docker/独立部署):url="http://localhost:6333"
Qdrant 作为独立服务运行,支持多客户端并发、集群、快照备份。
url 指向 Qdrant 服务的 REST API 地址:
- localhost:6333 是本机 Qdrant 的 REST API 端口
- 生产环境改为远程服务器地址即可
"""
def __init__(self, collection: str, dim: int = 1024,
path: str = None, url: str = None,
force_recreate: bool = False,
client=None):
"""
Args:
collection: 集合名
dim: 向量维度
path: 本地路径,设置则用嵌入式模式(无需 Docker)
嵌入式模式下同一 path 只能有一个 QdrantClient 实例,
多个 collection 应共享同一个 client(传入 client 参数)
url: 服务端地址,设置则用服务端模式(需 Docker 部署 Qdrant)
force_recreate: 是否强制重建集合(清空旧数据)
client: 已创建的 QdrantClient 实例(多 collection 共享时使用)
"""
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
self._collection = collection
self._dim = dim
self._id_map: Dict[str, str] = {} #
self._owns_client = False # 是否拥有 client 的生命周期(用于 close 决策)
# 复用外部传入的 client(嵌入式多 collection 共享场景)
if client is not None:
self._client = client
elif path:
self._client = QdrantClient(path=path)
self._owns_client = True
elif url:
self._client = QdrantClient(url=url)
self._owns_client = True
else:
raise ValueError("必须指定 client / path(嵌入式)或 url(服务端)")
# 创建或重建集合
if force_recreate and self._client.collection_exists(collection):
self._client.delete_collection(collection)
if not self._client.collection_exists(collection):
self._client.create_collection(
collection_name=collection,
vectors_config=VectorParams(size=dim, distance=Distance.COSINE),
)
def _to_uuid(self, id_: str) -> str:
"""将业务 ID 确定性映射为 UUID 字符串(Qdrant point id 要求 UUID 格式)"""
import uuid as _uuid
if id_ not in self._id_map:
self._id_map[id_] = str(_uuid.uuid5(_uuid.NAMESPACE_DNS, id_))
return self._id_map[id_]
def _to_filter(self, filter: Optional[dict]):
"""将 {key: value} dict 转为 Qdrant Filter 对象"""
if not filter:
return None
from qdrant_client.models import Filter, FieldCondition, MatchValue
conditions = [
FieldCondition(key=k, match=MatchValue(value=v))
for k, v in filter.items()
]
return Filter(must=conditions)
def upsert(self, ...
metadatas: Optional[List[dict]] = None) -> None:
from qdrant_client.models import PointStruct
if metadatas is None:
metadatas = [{} for _ in ids]
points = []
...
self._client.upsert(self._collection, points=points, wait=True)
def delete(self, ids: List[str]) -> None:
uuid_ids = [self._to_uuid(id_) for id_ in ids]
self._client.delete(self._collection, points_selector=uuid_ids, wait=True)
def search(self, ...
filter: Optional[dict] = None) -> List[SearchHit]:
qfilter = self._to_filter(filter)
results = self._client.query_points(
...
)
hits = []
for point in results.points:
payload = point.payload or {}
hits.append(SearchHit(
id=payload.get("_id", str(point.id)),
...
metadata={k: v for k, v in payload.items()
if k not in ("_id", "content")},
))
return hits
def count(self) -> int:
result = self._client.count(self._collection, exact=True)
return result.count
def get(self, id_: str) -> Optional[dict]:
"""按原始 ID 获取单条记录"""
uuid_id = self._to_uuid(id_)
try:
records = self._client.retrieve(
collection_name=self._collection,
ids=[uuid_id],
with_payload=True,
with_vectors=True,
)
if not records:
return None
r = records[0]
payload = r.payload or {}
return {
"id": payload.get("_id", str(r.id)),
"vector": np.array(r.vector, dtype=np.float32) if r.vector else None,
...
"metadata": {k: v for k, v in payload.items()
if k not in ("_id", "content")},
}
except Exception:
return None
def close(self):
"""关闭客户端(仅对 self 拥有的 client 生效,共享 client 由外部管理)"""
if self._owns_client:
self._client.close()
为了业务保密,有些代码用...省略了。里面的vector数据转换成Qdrant支持的PointStruct类是代码的核心。而from qdrant_client.models import PointStruct里说明PointStruct是qdrant_client.models里的类。
讲到这里,就明白了什么是向量数据库了!在前面文章里讲过Transform大模型把文本转换成向量值,向量值在这里的体现就是vector,vector和以前的结构化数据库约束不一样,所以要用新型的向量数据库。
经过这样的封装,上层就可以直接执行CRUD操作,不用管底层是什么数据库了。具体细节不清楚,欢迎关注锐英源软件进行交流。