功能和问题

  • Chroma是一个开源的轻量向量数据库,适合快速开发和原型验证
  • Milvus是一个开源的分布式向量数据库,支持大规模的向量存储和高并发检索,适合企业级应用
  • FAISS是由Facebook开发的开源、轻量向量数据库检索速度快

chroma基础能力

要使用先安装chromadb库,以下简单封装了一个ChromaClient类,支持连接客户端、创建collection、切换collection、删除collection、查询collection等方法。

from typing import List
import chromadb
import logging

# chroma client 常用方法封装
class ChromaClient:
    def __init__(self, client = None):
        self.client = client or chromadb.Client() # 支持外部传入client
        self.collection = None

    # 支持自定义配置连接客户端
    def connet(
            self,
            settings=None,
            path: str = ".chroma_db1",
            tenant: str = "default_tenant",
            database: str = "default_database",
        ):
        self.client = chromadb.PersistentClient(
            path=path,
            settings=settings,
            tenant=tenant,
            database=database
        )

    # 创建collection
    def create_collection(self, collection_name):
        if not collection_name or not isinstance(collection_name, str):
            logging.error("Invalid collection name")
            return None
        try:
            self.collection = self.client.create_collection(collection_name)
            logging.info(f"Collection {collection_name} created")
            return self.collection
        except Exception as e:
            logging.error(f"Failed to create collection: {e}")
            return None

    # 切换到collection
    def switch_collection(self, collection_name):
        self.collection = self.client.get_or_create_collection(collection_name)
        return self.collection

    # 删除collection
    def delete_collection(self, collection_name):
        collection = self.get_collection(collection_name)
        if collection:
            try:
                self.client.delete_collection(collection_name)
                logging.info(f"Collection {collection_name} deleted")
                return True
            except Exception as e:
                logging.error(f"Failed to delete collection: {e}")
                return False
        logging.warning(f"Collection {collection_name} not found")
        return None

    # 查询collection
    def get_collection(self, collection_name):
        try:
            return self.client.get_collection(collection_name)
        except Exception as e:
            print(f"Failed to get collection")
            return None

    # 列出所有collection
    def get_list_collections(self):
        list_collections = self.client.list_collections()
        return list_collections

    # 相似度检索
    # n_results  默认是返回10个结果
    def query(self, query_embeddings, n_results=2):
        result = self.collection.query(
            query_embeddings=query_embeddings,
            n_results=n_results,
        )
        return result

    # 插入向量
    def add(
            self,
            documents: List[str],
            metadatas: List[dict],
            ids: List[str],
            embeddings:List[List[float]],
        ) -> bool:
        if not all([documents, metadatas, ids, embeddings]):
            raise ValueError("All arguments must be provided")
        self.collection.add(
            documents=documents,
            metadatas=metadatas,
            ids=ids,
            embeddings=embeddings
        )
        return True

    # 添加或更新向量
    def add_or_update(
            self,
            documents: List[str],
            metadatas: List[dict],
            ids: List[str],
            embeddings:List[List[float]],
        ) -> bool:
        self.collection.upsert(
            documents=documents,
            metadatas=metadatas,
            ids=ids,
            embeddings=embeddings
        )
        return True

使用示例代码

以下是一个简单的使用示例,展示了在rag中的基本步骤,包括文档加载、切分、向量化、存储到向量数据库、查询向量数据库等

from pathlib import Path
from langchain_text_splitters import RecursiveCharacterTextSplitter
from openai import OpenAI
from config.settings import settings
from langchain_community.document_loaders import TextLoader
from embedding.chroma_client import ChromaClient
import uuid

API_KEY = settings.api_key

def init():
    # 1、读取txt文件为 documents list
    cur_path = Path(__file__).parent
    doc_path = (cur_path.parent / "docs/deepseek百度百科.txt").resolve()
    documents = get_text(doc_path)
    # print(documents[0].page_content) // 读取出来是一个list 只有一个item
    # 2、切分文档
    chunks = splitter_document(documents[0].page_content)

    # 将chunks list向量化 转化为存储对象
    store_data = vector_documents(chunks)

    # 链接向量数据库 存储数据
    client = ChromaClient()
    collection = client.switch_collection("deepseek_vector")
    bool = client.add(
        documents=store_data["documents"],
        metadatas=store_data["metadatas"],
        ids=store_data["ids"],
        embeddings=store_data["embeddings"],
    )
    print(f"向量化结果存储成功-----------------------{bool}")

    # 查询collection里面的数据 include=["embeddings"]
    data = collection.get()
    print(f"查询存储结果-----------------------{data}")
    print(data)


# 读取文本
def get_text(file_path):
    loader = TextLoader(file_path, encoding="utf-8")
    documents = loader.load()
    return documents

#  切分文档
def splitter_document(document_text):
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=200,  # 分割长度
        chunk_overlap=40,  # 重叠长度 /重叠窗口大小
        separators=[
            "\n\n",
            "\n",
            "。",
            "!",
            "?",
            ";",
            " ",
            ""],  # 分割符 在分割长度内依次使用段落、换行符、中文句号、中文问号、中文分号、空格、无内容来切分
    )
    chunks = splitter.split_text(document_text)
    return chunks

# 文本向量化 并转为要存储的对象
def vector_documents(chunks):
    llm_client = OpenAI(
        api_key=API_KEY,
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    )
    chunks_pre_5 = chunks[:5]
    result = llm_client.embeddings.create(input=chunks_pre_5, model="text-embedding-v4")
    embeddings_list = [emb.embedding for emb in result.data]
    store_data = {
        "documents": chunks_pre_5,
        "embeddings": embeddings_list,
        "metadatas": [{"source": "deepseek 百科"} for i in range(len(chunks_pre_5))],
        "ids": [str(uuid.uuid4()) for i in range(len(chunks_pre_5))]
    }
    return store_data

if __name__ == "__main__":
    init()

MilVus

专为大规模向量检索设计的开源分布式向量数据库,向量数据库领域事实标准。 核心优势

  • 支持 百亿~千亿级向量,存算分离、水平扩展、高可用。
  • 索引丰富(HNSW、IVF_FLAT、IVF_SQ8、SCANN、GPU 加速),混合检索强。
  • 社区成熟、文档完善,国内云厂商(阿里云、腾讯云)均提供托管版。

部署

使用docker 部署

# 下载
curl -L https://github.com/milvus-io/milvus/releases/download/v2.4.4/milvus-standalone-docker-compose.yml -o docker-compose.yml

# 启动
docker compose up -d

# 查看运行状态
docker -ps
# 三个服务 healthy检查
# milvus-etcd
# milvus-minio
# milvus-standalone

可以使用milvus-cli来连接数据库,(跟mysql等一样),milvus-cli 是官方提供的命令行工具,

pip install milvus-cli

# 进入命令行
milvus_cli 

# 连接数据库 
connect

推荐使用 官方可视化工具attu

# 使用docker启动
docker run -d -p 8000:3000 -e MILVUS_URL=127.0.0.1:19530 zilliz/attu:latest

访问地址 http://127.0.0.1:8000/ 注意:访问Milvus 地址 不要写127.0.0.1 ,在docker里面没有指向Milvus,用ip访问

总结

向量数据库是一种用于存储和检索高维向量的数据库,它在文本检索、图像检索、视频检索等领域都有广泛的应用。