Qdrant合集


Qdrant合集

Qdrant介绍

  • 官网地址 https://github.com/qdrant/qdrant
  • web-ui地址 https://github.com/qdrant/qdrant-web-ui
  • 官方文档 https://qdrant.tech/documentation/
  • Qdrant 是一个开源的向量数据库,专门为高效的向量相似性搜索和存储而设计。
  • 集合(Collection):类似关系型数据库中的表,每个集合定义自己的向量维度和距离度量方式
  • 点/向量(Points/Vectors):基本存储单元,包含:ID + 向量 + Payload(元数据)
  • Payload:附加的 JSON 格式数据,用于存储向量相关的元信息,支持过滤和查询
  • 距离度量(Distance Metrics):Cosine(余弦相似度) - 最常用,Euclidean(欧几里得距离),Dot(点积),Manhattan(曼哈顿距离)
  • 分片(Shards):数据水平分割,提高并发和性能

Qdrant安装

windows

下载qdrant-x86_64-pc-windows-msvc.zip,解压,有一个文件qdrant.exe
创建配置文件config.yaml

log_level: INFO

service:
  host: 0.0.0.0
  http_port: 6333
  grpc_port: 6334
  api_key: "xxx"

storage:
  storage_path: "./storage"
  # 如果你想使用内存存储(重启后数据丢失),可以设置为true,但这里我们使用持久化存储
  # in_memory: false
  # 性能相关配置
  performance:
    max_search_threads: 4

optimizers:
  # 索引阈值,即达到多少点后才创建索引
  indexing_threshold: 10000

下载web-ui,解压,有一个打包后的dist文件夹
将dist文件夹中的所有文件放到static目录下

完整目录:

Qdrant\
-qdrant.exe
-storage\
-config\
--config.yaml
-static\

运行qdrant.exe启动Qdrant
web-ui地址 http://localhost:6333/dashboard

Qdrant测试使用

# python集成 http连接
from qdrant_client import QdrantClient
from qdrant_client.http.models import VectorParams, Distance, PointStruct, Filter, FieldCondition, MatchValue

# 初始化客户端
client = QdrantClient(
    host="localhost",
    https=False,
    api_key="passxxx",  # 如启用
)

# 创建 collection
client.create_collection(
    collection_name="gundams",
    vectors_config=VectorParams(size=4, distance=Distance.COSINE),
    # 向量索引HNSW配置,和payload index是两个东西
    hnsw_config={
        "m": 8,
        "ef_construct": 50,
        "full_scan_threshold": 1000  # 小数据集可全扫描
    },
    # 优化器配置也简化
    optimizers_config={
        "indexing_threshold": 1000,  # 早点建索引
        "memmap_threshold": 5000     # 内存映射阈值
    }
)

# 插入向量
points = [
    PointStruct(
        id=1,
        vector=[0.1, 0.9, 0.6, 0.4],  # 4 维
        payload={"ms": "高达F91", "role": "西布克"}
    ),
    PointStruct(
        id=2,
        vector=[0.3, 0.3, 0.1, 0.9],  # 4 维
        payload={"ms": "古兰森", "role": "白河愁"}
    )
]
client.upsert(collection_name="gundams", points=points)

# 搜索
search_result = client.query_points(
    collection_name="gundams",
    query=[0.2, 0.8, 0.5, 0.9],
    query_filter=Filter(must=[ FieldCondition(key="ms",match=MatchValue(value="高达F91")) ]), # http
    with_payload=True,
    limit=3
)

for point in search_result.points:
    print(point)
# python集成 grpc连接
from qdrant_client import QdrantClient
from qdrant_client.grpc import VectorParams, Distance, Filter, FieldCondition, Match, VectorsConfig, \
    PointId, PointStruct, Vectors, Vector, Value, Condition

# 初始化客户端
client = QdrantClient(
    host="localhost",
    api_key="passxxx",  # 如启用
    port=6333,
    grpc_port=6334,  # gRPC专用端口
    https=False,
    timeout=10.0,
    prefer_grpc=True  # 更高性能(需开放 6334)
)

# 创建 collection
client.create_collection(
    collection_name="mobilesuit",
    vectors_config=VectorsConfig(params=VectorParams(size=4, distance=Distance.Cosine)),
    # 向量索引HNSW配置,和payload index是两个东西
    hnsw_config={
        "m": 8,
        "ef_construct": 50,
        "full_scan_threshold": 1000  # 小数据集可全扫描
    },
    # 优化器配置也简化
    optimizers_config={
        "indexing_threshold": 1000,  # 早点建索引
        "memmap_threshold": 5000     # 内存映射阈值
    }
)

# 插入向量
points = [
    PointStruct(
        id=PointId(num=1),
        vectors=Vectors(vector=Vector(data=[0.1, 0.9, 0.6, 0.4])),  # 4 维
        payload={"ms": Value(string_value="高达F91"), "role": Value(string_value="西布克")}
    ),
    PointStruct(
        id=PointId(num=2),
        vectors=Vectors(vector=Vector(data=[0.3, 0.3, 0.1, 0.9])),  # 4 维
        payload={"ms": Value(string_value="古兰森"), "role": Value(string_value="白河愁")}
    )
]
client.upsert(collection_name="mobilesuit", points=points)

# 搜索
search_result = client.query_points(
    collection_name="mobilesuit",
    query=[0.3, 0.3, 0.1, 0.9],
    # query_filter=Filter(must=[ Condition(field=FieldCondition(key="ms",match=Match(keyword="高达F91"))) ]), # grpc
    with_payload=True,
    limit=3
)
for point in search_result.points:
    print(point)

嵌入模型

  • 嵌入模型:将文字转换成机器能理解的连续的数值向量
  • BGE-large-zh:对中文进行了深度优化的开源文本嵌入模型;基于Transformer架构,输出一个1024维的浮点数向量。通常需要L2归一化
    # bge-large-zh向量化脚本demo
    from transformers import AutoModel, AutoTokenizer
    import torch
    
    model_name = "../bge-large-zh"
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    device = "cuda:0"
    
    # 加载量化模型
    model = AutoModel.from_pretrained(
        model_name,
        dtype=torch.float16,  # 使用半精度
        low_cpu_mem_usage=True,
        device_map=device
    )
    
    
    # 处理文本
    def handle_text(text):
        # 生成张量,张量指将文本编码,告诉程序存在哪些字符,最终生成的向量指文本的语义转化,告诉程序文本所表达的意思
        inputs = tokenizer(text, return_tensors="pt", max_length=512, truncation=True).to(device)
        # 获取向量
        with torch.no_grad():  # 禁用梯度
            outputs = model(**inputs)
            embeddings = outputs.last_hidden_state[:, 0]  # 取CLS token 提取每个序列的CLS token的隐藏状态作为句子表示
            embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)  # 使用L2范数(欧几里得范数) 在第1维度(隐藏维度)上进行归一化
            return embeddings
    
    
    # 处理文本
    def handle_texts(texts):
        result = []
        for text in texts:
            # 生成张量,张量指将文本编码,告诉程序存在哪些字符,最终生成的向量指文本的语义转化,告诉程序文本所表达的意思
            inputs = tokenizer(text, return_tensors="pt", max_length=512, truncation=True).to(device)
            # 获取向量
            with torch.no_grad():  # 禁用梯度
                outputs = model(**inputs)
                embeddings = outputs.last_hidden_state[:, 0]  # 取CLS token 提取每个序列的CLS token的隐藏状态作为句子表示
                embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)  # 使用L2范数(欧几里得范数) 在第1维度(隐藏维度)上进行归一化
                result.append(embeddings)
                print(embeddings)
        return result

Qdrant配合嵌入模型使用

from qdrant_client import QdrantClient
from qdrant_client.http.models import VectorParams, Distance, PointStruct

import bge_handle as bge

# 初始化客户端
client = QdrantClient(
    host="localhost",
    api_key="passxxx",  # 如启用
    port=6333,
    https=False,
    timeout=10.0
)


def create_collection():
    # 创建 collection
    client.create_collection(
        collection_name="gundam_event",
        vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
        # 向量索引HNSW配置,和payload index是两个东西
        hnsw_config={
            "m": 16,
            "ef_construct": 100,
            "full_scan_threshold": 1000,  # 小数据集可全扫描
            "max_indexing_threads": 0  # 0表示自动选择
        },
        # 优化器配置也简化
        optimizers_config={
            "indexing_threshold": 10000,  # 早点建索引
            "memmap_threshold": 10000  # 内存映射阈值
        }
    )


def insert(orders):
    points = []
    for order in orders:
        order_vector = bge.handle_text(order['历史事件'])
        print(order)
        point = PointStruct(
            id=order['编号'],
            vector=order_vector[0].tolist(),
            payload=order
        )
        points.append(point)
    client.upsert(collection_name="gundam_event", points=points)


def search(text):
    vector = bge.handle_text(text)
    search_result = client.query_points(
        collection_name="gundam_event",
        query=vector[0].tolist(),
        limit=2
    )
    return search_result


order_list = [{
    '编号': 1,
    '类型': 'UC',
    '历史事件': '格里普斯战役(UC 0087-0088),反提坦斯组织“奥古”(AEUG)与吉恩残党“阿克西斯”联合对抗提坦斯。最后提坦斯瓦解,奥古损失惨重,阿克西斯成为新威胁。',
    '人物': ['卡缪','西洛克', '科瓦特罗', '哈曼']
}, {
    '编号': 2,
    '类型': 'SEED',
    '历史事件': '血染情人节CE 70,地球联合军秘密部队使用核弹袭击PLANT的农业卫星“尤尼乌斯7号”,造成大量调整者平民死亡。此事件成为全面战争的导火索。扎夫特对地球联合宣战。因《禁止核武条约》及N干扰器存在,战争进入以机动战士和光束武器为主的“MS战时代”。',
    '人物': ['基拉','阿斯兰·萨拉','卡嘉莉','乌兹米·纳拉·阿斯哈']
},{
    '编号': 3,
    '类型': 'UC',
    '历史事件': '拉普拉斯之乱(UC 0096),新吉恩残党“带袖的”、联邦激进派、毕斯特财团,围绕“拉普拉斯之盒”(揭露联邦政府腐败的宪章原案)的争夺战。最终宪章真相公开,动摇联邦统治合法性,宇宙住民独立运动获道义支持。',
    '人物': ['奥黛丽','巴纳吉·林克斯', '弗尔·弗朗托', '利迪·马瑟纳斯']
}]


create_collection()
insert(order_list)
print(search("“拉普拉斯之盒” 的内容被公开——它并非武器,而是宇宙世纪宪章的原版第15条,承诺将来给予宇宙移民平等的自治权。这一真相动摇了联邦统治的合法性,为宇宙居民争取权利提供了法理依据"))
# excel_util
from openpyxl import load_workbook
from typing import List

def read_excel_rows(file_path, start_row, end_row, sheet_name, has_header: bool = True, header_names: List[str] = None):
    # 加载工作簿
    wb = load_workbook(filename=file_path, data_only=True)
    # 获取工作表
    if sheet_name:
        ws = wb[sheet_name]
    else:
        ws = wb.active
    # 读取表头
    headers = header_names
    if has_header:
        header_row = ws[1]
        for cell in header_row:
            headers.append(cell.value if cell.value else f"Column_{cell.row}")
    # 确保行号在有效范围内
    if start_row < 1:
        start_row = 1
    if end_row > ws.max_row:
        end_row = ws.max_row
    dict_list = []
    # 读取指定行范围
    for row_idx in range(start_row, end_row + 1):
        row_dict = {}
        row = ws[row_idx]
        for i, cell in enumerate(row):
            if i < len(headers):
                row_dict[headers[i]] = cell.value
        dict_list.append(row_dict)
    return dict_list


def demo():
    # 使用示例
    header_names = ['编号','类型','历史事件','人物']
    data_list = read_excel_rows('../file/qdrant_demo.xlsx', start_row=3, end_row=4, sheet_name='Sheet1', has_header=False, header_names=header_names)
    for data in data_list:
        print(data)
# excel导入qdrant
from qdrant_client import QdrantClient
from qdrant_client.http.models import VectorParams, Distance, PointStruct

import excel_util
import bge_handle as bge

# 初始化客户端
client = QdrantClient(
    host="localhost",
    api_key="passxxx",  # 如启用
    port=6333,
    https=False,
    timeout=10.0,
)


def insert(orders):
    points = []
    for order in orders:
        order_vector = bge.handle_text(order['历史事件'])
        print(order)
        point = PointStruct(
            id=order['编号'],
            vector=order_vector[0].tolist(),
            payload=order
        )
        points.append(point)
    client.upsert(collection_name="gundam_event", points=points)


def insert_by_excel():
    header_names = ['编号','类型','历史事件','人物']
    data_list = excel_util.read_excel_rows('../file/qdrant_demo.xlsx', start_row=3, end_row=12, sheet_name='Sheet1', has_header=False, header_names=header_names)
    insert(data_list)


# create_collection()
# insert(order_list)
insert_by_excel()

  目录