锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 | 人工智能 / Qdrant向量数据库Python封装类、Qdrant向量数据库安装配置环境详解
联系方式
微信公众号
微信扫一扫,新经验快学到
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft
服务方向
技术文章

锐英源精品原创文章,禁止转载和任何形式的非法内容使用,违者必究


Qdrant向量数据库Python封装类、Qdrant向量数据库安装配置环境详解


背景和通俗引导

有一定基础朋友,可以直接跳过本段,直接看下一段。本文是AI系列文章,欢迎在面包屑导航里点击人工智能进行系列学习。

锐英源软件内部平台稳定推进开发中,需要把数据持久化同时能CRUD创建读更新和删除,DeepSeek和TRAE都推荐了Qdrant,就配置了Qdrant环境,编写了类,实现了CRUD操作。

数据库就是高级的Excel,一直是信息化的核心重点,比如人的身份信息都是存在数据库里,高铁刷身份证能上,就是背后有数据库平台支撑。普通家长想要让子女对编程有兴趣,锐英源软件会有更通俗易懂的解释,欢迎关注联系。

Qdrant向量数据库安装配置环境详解

锐英源软件也是第一次用,DeepSeek推荐在Assets页面下载,Assets的入口在Release页面,这是Exe发布页面,在github的Qdrant页面的右边,能看到Release页面,如下图:

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报警告,如下图:

Qdrant被Defender警告

 

Windows 已保护你的电脑
Microsoft Defender SmartScreen阻止了无法识别的应用启动。运行此应用可能会导致你的电脑存在风险。
应用: qdrant.exe
发行者:发布者未知
仍要运行 不运行

 

我机器上有防病毒软件,下载环境是可信的,机器是可信的,所以我点了仍要运行。运行了,TRAE里的代码就可以用HTTP方式来连接Qdrant了。

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 在本地运行时的数据存储结构。


Qdrant向量数据库Python封装类

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操作,不用管底层是什么数据库了。具体细节不清楚,欢迎关注锐英源软件进行交流。

友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州市金水区文化路97号郑州大学北校区院内原校办工厂楼101室