【AI大模型】不止语义检索,Milvus+LangChain全文检索RAG教程来了

前言

最近,OpenAI和LangChain杠上了。一个是大模型扛把子,一个是最受欢迎的大模型框架

而抬杠的原因很简单LangChain觉得OpenAI 根本不懂agent:

但总结一句话来说,就是对于agent开发,OpenAI更倾向于高级抽象能加速开发;而LangChain则觉得大模型要能真正发挥作用,需要精确控制能确保可靠性 ,因此workflow和agent都必不可少

实际上,两者的争论没有完全的对错之分。OpenAI指出的的是长期趋势,而LangChain给出的,则是立足当下,针对具体问题的更优解决方案。

以企业内部知识库问答系统构建来说,长期看,通过大模型智能调度各种工具然后基于milvus在内的向量数据库进行检索并给出答案,会是大势所趋;

但是具体到各种现实场景,仅仅是对接milvus,向量数据库中的索引算法有几十种,根据用户的需求不同,以及成本的不同考量,要如何对其中进行抉择,往往还需要开发者们的实际经验参与,将其写成workflow。

一个最简单的例子是,全文检索和语义搜索究竟哪个效果更佳?

答案是,看你在搜什么。

比如,同样是检索鲁迅先生的文章,在我们需要查找“藤野先生”“祥林嫂”的时候,全文检索就是最优解;

但是当我们检索诸如“少年在瓜田刺猹”这类描述性内容,那么语义检索就是帮你精准找到相关内容的最优解。

那么,该如何通过LangChain与Milvus构建一个RAG系统?本文将对此做出解读。

01

背景科普:如何在向量数据库中实现全文检索

(本章节主要做一些基础技术内容的科普,如果已经了解相关背景,可直接跳过)

全文检索是一种信息检索技术,能够从大量文本数据中快速找出包含特定词语或短语的文档,非常适合精确术语的查找。

例如,当你需要查找包含"Python 3.9新特性"的文档时,全文检索能够精确定位包含这些关键词的内容。

不过,与简单的关键词匹配不同,全文检索考虑了词语在文档中的重要性、出现频率以及上下文关系,从而提供更加精准和相关的搜索结果。

图片

与之相对应的是语义搜索,主要关注内容的含义而非具体词语,适合理解查询意图。例如,当你搜索"编程语言最新版本功能"时,语义搜索可能会返回关于Python、Java等多种语言最新版本的信息,即使文档中没有出现你使用的确切词语。

长久以来,很多人可能会有一个误解,那就是ES适合全文检索,而向量数据库只适合语义检索。

其实Milvus等现代****向量数据库的一个重要创新是实现了稀疏向量(用于全文检索)和密集向量(用于语义搜索)的协同工作。这种融合使系统能够同时支持基于关键词的精确匹配和基于语义的相似性搜索,为用户提供更全面的搜索体验。

稀疏向量:想象一个超长的列表,对应词汇表中的每个词。如果文档包含某个词,对应位置标记为1(或词频),其余位置为0。例如,在一个包含10万词的词汇表中,一篇文档可能只包含100个不同的词,所以向量中99.9%的元素都是0。

密集向量:想象一个相对较短的列表(如几百个数字),每个数字都包含文档的某些语义特征。与稀疏向量不同,密集向量中几乎所有元素都有非零值,它们共同表示文档的"意义",而不是具体的词。

稀疏向量和密集向量在表示方式和应用场景上有显著差异:

图片

一句话总结来说,就是稀疏向量可以通过更多的维度,以及更少的非零元素,来实现关键词匹配等效果。

在Milvus中,通过内部优化机制,这两种向量表示能够高效协同工作,为用户提供更准确、更全面的搜索结果。

02

实战:LangChain与Milvus构建RAG系统

第一步,环境准备

  • Python 3.8+
  • Docker(用于运行Milvus)
  • OpenAI API密钥
  • 基本的Python编程知识
  • 了解向量数据库的基本概念

第二步, 启动Milvus服务

使用Docker Compose启动Milvus服务:

wget https://github.com/milvus-io/milvus/releases/download/v2.2.8/milvus-standalone-docker-compose.yml -O docker-compose.ymldocker-compose up -d

验证Milvus服务是否正常运行:

docker ps | grep milvus

安装必要的依赖

pip install --upgrade langchain langchain-core langchain-community langchain-text-splitters langchain-milvus langchain-openai bs4

设置环境变量

# import os  设置OpenAI API密钥os.environ["OPENAI_API_KEY"] = "your-api-key-here"# 设置Milvus连接信息URI = "http://localhost:19530"TOKEN = "your-token-here"  # 如果需要

第三步, 文档预处理与向量化

文档预处理是构建高效RAG系统的关键第一步。以下是使用LangChain和Milvus进行文档预处理和向量化的详细步骤:

首先,我们需要从各种来源加载文档。LangChain提供了多种文档加载器,可以处理不同格式的文件。

from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader# 加载文本文件def load_text_documents(directory):    loader = DirectoryLoader(directory, glob="**/*.txt", loader_cls=TextLoader)    return loader.load()# 加载PDF文件def load_pdf_documents(directory):    loader = DirectoryLoader(directory, glob="**/*.pdf", loader_cls=PyPDFLoader)    return loader.load()# 组合加载多种格式def load_all_documents(directory):    text_docs = load_text_documents(directory)    pdf_docs = load_pdf_documents(directory)    return text_docs + pdf_docs

接下来,我们需要把长文档分割成较小的块,以便更好地进行向量化和检索。

from langchain.text_splitter import RecursiveCharacterTextSplitterdef split_documents(documents, chunk_size=1000, chunk_overlap=200):    text_splitter = RecursiveCharacterTextSplitter(        chunk_size=chunk_size,        chunk_overlap=chunk_overlap,        separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""]    )    return text_splitter.split_documents(documents)

参数说明

  • chunk_size:每个文档块的最大字符数
  • chunk_overlap:相邻块之间的重叠字符数,用于保持上下文连贯性
  • separators:分割文本的分隔符列表,按优先级排序

接下来,我们使用OpenAI的嵌入模型将文档块转换为向量,并存储在Milvus中。

from langchain_milvus import Milvus, BM25BuiltInFunctionfrom langchain_openai import OpenAIEmbeddingsdef create_vector_store(documents, collection_name, uri=URI):    # 创建嵌入模型    embeddings = OpenAIEmbeddings()    # 配置BM25分析器参数(用于生成稀疏向量)    analyzer_params = {        "tokenizer": "whitespace",  # 使用空格分词        "filter": ["lowercase", {"type": "stop", "stop_words": ["the", "a", "an", "and", "or", "but", "is", "are", "in", "to", "with", "of"]}]    }    # 创建向量存储    vector_store = Milvus.from_documents(        documents=documents,        embedding=embeddings,        builtin_function=BM25BuiltInFunction(analyzer_params=analyzer_params),        vector_field=["dense", "sparse"],  # 同时存储密集向量和稀疏向量        connection_args={"uri": uri},        collection_name=collection_name,    )    return vector_store

技术说明

  • OpenAIEmbeddings:使用OpenAI的嵌入模型生成密集向量
  • BM25BuiltInFunction:使用BM25算法生成稀疏向量,用于全文检索
  • vector_field=["dense", "sparse"]:同时存储两种向量,支持混合搜索

下面是一个完整的文档处理流程,从加载到向量化存储:

def process_documents(directory, collection_name, chunk_size=1000, chunk_overlap=200):    try:        # 1. 加载文档        print("正在加载文档...")        documents = load_all_documents(directory)        print(f"成功加载 {len(documents)} 个文档")        # 2. 分割文档        print("正在分割文档...")        chunks = split_documents(documents, chunk_size, chunk_overlap)        print(f"文档已分割为 {len(chunks)} 个块")        # 3. 创建向量存储        print("正在创建向量存储...")        vector_store = create_vector_store(chunks, collection_name)        print(f"向量存储已创建,集合名称:{collection_name}")        return vector_store    except Exception as e:        print(f"处理文档时出错:{str(e)}")        raise

使用示例:

# 处理文档并创建向量存储vector_store = process_documents(    directory="./documents",    collection_name="knowledge_base",    chunk_size=1000,    chunk_overlap=200)保存向量存储配置以便后续使用vector_store.save_local("./vector_store_config")

第四步, 知识库问答系统实现

在开始实现之前,需要准备以下环境和依赖:

# 安装必要的依赖包pip install langchain langchain-openai langchain-milvus python-dotenv# 环境变量设置import osfrom dotenv import load_dotenv# 加载环境变量load_dotenv()# 确保设置了OpenAI API密钥if not os.getenv("OPENAI_API_KEY"):    raise ValueError("请设置OPENAI_API_KEY环境变量")# Milvus连接设置MILVUS_URI = os.getenv("MILVUS_URI", "http://localhost:19530")

完整实现方案

import osfrom typing import List, Dict, Any, Optionalfrom langchain_core.documents import Documentfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_milvus import Milvus, BM25BuiltInFunctionfrom langchain_openai import OpenAIEmbeddings, ChatOpenAIfrom langchain.chains import RetrievalQAfrom langchain.prompts import PromptTemplate# 1. 加载文档def load_documents(directory: str) -> List[Document]:    """    从指定目录加载所有文本文档    Args:        directory: 文档所在目录路径    Returns:        Document对象列表    """    documents = []    try:        for filename in os.listdir(directory):            if filename.endswith(('.txt', '.md', '.pdf')):                file_path = os.path.join(directory, filename)                try:                    with open(file_path, 'r', encoding='utf-8') as f:                        content = f.read()                        documents.append(Document(                            page_content=content,                            metadata={"source": filename, "path": file_path}                        ))                except Exception as e:                    print(f"读取文件 {filename} 时出错: {str(e)}")    except Exception as e:        print(f"读取目录 {directory} 时出错: {str(e)}")    print(f"成功加载了 {len(documents)} 个文档")    return documents# 2. 文档分割def split_documents(documents: List[Document],                   chunk_size: int = 1000,                   chunk_overlap: int = 200) -> List[Document]:    """    将文档分割成更小的块    Args:        documents: 要分割的文档列表        chunk_size: 每个块的最大字符数        chunk_overlap: 相邻块之间的重叠字符数    Returns:        分割后的文档块列表    """    text_splitter = RecursiveCharacterTextSplitter(        chunk_size=chunk_size,        chunk_overlap=chunk_overlap,        separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""]    )    split_docs = text_splitter.split_documents(documents)    print(f"文档被分割为 {len(split_docs)} 个块")    return split_docs# 3. 创建向量存储def create_vector_store(documents: List[Document],                       collection_name: str = "knowledge_base",                       recreate: bool = False) -> Milvus:    """    创建向量存储    Args:        documents: 要存储的文档列表        collection_name: Milvus集合名称        recreate: 是否重新创建集合    Returns:        Milvus向量存储对象    """    try:        # 配置向量存储        embeddings = OpenAIEmbeddings()        # 配置BM25分析器参数        analyzer_params = {            "tokenizer": "jieba",  # 使用结巴分词器处理中文            "filter": ["lowercase", {"type": "stop", "stop_words": ["的", "了", "是"]}],        }        # 创建向量存储        vector_store = Milvus.from_documents(            documents=documents,            embedding=embeddings,            builtin_function=BM25BuiltInFunction(analyzer_params=analyzer_params),            vector_field=["dense", "sparse"],  # 同时存储密集向量和稀疏向量            connection_args={"uri": MILVUS_URI},            collection_name=collection_name,            drop_old=recreate,  # 是否删除已存在的集合        )        print(f"成功创建向量存储,集合名称: {collection_name}")        return vector_store    except Exception as e:        print(f"创建向量存储时出错: {str(e)}")        raise# 4. 创建问答链def create_qa_chain(vector_store: Milvus,                   temperature: float = 0.0,                   search_k: int = 5) -> RetrievalQA:    """    创建检索问答链    Args:        vector_store: Milvus向量存储对象        temperature: 生成模型的温度参数        search_k: 检索的文档数量    Returns:        RetrievalQA链对象    """    try:        # 创建混合检索器        retriever = vector_store.as_retriever(            search_type="hybrid",  # 使用混合搜索模式            search_kwargs={                "k": search_k,  # 检索top-k个文档                "hybrid_search": {                    "dense_weight": 0.7,  # 密集向量权重                    "sparse_weight": 0.3,  # 稀疏向量权重                }            }        )        # 创建自定义提示模板        template = """        使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。        {context}        问题: {question}        回答:        """        prompt = PromptTemplate(            template=template,            input_variables=["context", "question"]        )        # 创建大语言模型        llm = ChatOpenAI(temperature=temperature)        # 创建问答链        qa_chain = RetrievalQA.from_chain_type(            llm=llm,            chain_type="stuff",            retriever=retriever,            return_source_documents=True,            chain_type_kwargs={"prompt": prompt}        )        return qa_chain    except Exception as e:        print(f"创建问答链时出错: {str(e)}")        raise# 5. 完整流程示例def build_knowledge_qa_system(docs_directory: str,                             collection_name: str = "knowledge_base") -> RetrievalQA:    """    构建完整的知识库问答系统    Args:        docs_directory: 文档目录        collection_name: 向量存储集合名称    Returns:        问答系统链    """    # 加载文档    documents = load_documents(docs_directory)    # 分割文档    split_docs = split_documents(documents)    # 创建向量存储    vector_store = create_vector_store(split_docs, collection_name)    # 创建问答链    qa_chain = create_qa_chain(vector_store)    return qa_chain# 6. 使用示例def query_example():    """示例:如何使用问答系统"""    # 构建问答系统    qa_system = build_knowledge_qa_system("./documents", "company_knowledge")    # 查询示例    query = "公司的年假政策是什么?"    result = qa_system({"query": query})    # 输出结果    print(f"问题: {query}")    print(f"回答: {result['result']}")    # 输出来源文档    print("\n来源文档:")    for i, doc in enumerate(result["source_documents"]):        print(f"文档 {i+1}: {doc.metadata['source']}")        print(f"内容片段: {doc.page_content[:100]}...\n")

第五步,运行效果示例。以下是系统运行的示例输出:

成功加载了 15 个文档文档被分割为 78 个块成功创建向量存储,集合名称: company_knowledge问题: 公司的年假政策是什么?回答: 根据公司政策,正式员工每年享有15天带薪年假。工作满3年的员工额外增加3天年假,满5年的员工额外增加5天年假。年假可以分多次使用,但每次至少使用半天。未使用的年假可以结转到下一年,但最多结转5天。来源文档:文档 1: company_policy.txt内容片段: # 公司员工手册 ## 休假政策 ### 年假 正式员工每年享有15天带薪年假。工作满3年的员工额外增加3天年假,满5年...文档 2: hr_faq.txt内容片段: # 人力资源常见问题 ## 休假相关 Q: 年假如何申请? A: 员工可通过OA系统提交年假申请,需提前3个工作日申请...

03

经验总结

通过上文,我们展示了一个比较初级的基于全文检索的企业知识库系统的构建,但是实操中,我们可能还会遇到一些更加具体且琐碎的问题,比如

比如全文检索基于精确的词语匹配,可能无法处理同义词(不同词语表达相同含义)和多义词(同一词语有多种含义)的问题。例如,用户搜索"汽车"时,可能无法找到只包含"轿车"或"车辆"的文档。

**我们可以有三个解决思路。(1)同义词扩展:在索引和查询时添加同义词;(2)****查询扩展:**自动扩展用户查询以包含相关术语;(3)结合语义搜索:通过密集向量捕捉语义关系。

再比如,不同语言有不同的语法结构和词形变化规则,这给全文检索带来挑战。例如,中文不像英文那样有明显的词语边界,需要特殊的分词技术。

解决方案 主要有两个。(1) 语言特定分词器:为不同语言使用专门的分词技术;(2) 多语言支持:构建支持多种语言的索引。

此外,随着数据量的增长,全文检索系统需要处理越来越多的文档,这对系统性能提出了挑战。

解决方案 有三个。(1) 分布式索引:将索引分散到多个服务器;(2)增量索引更新:只更新变化的部分;(3)冷热数据,缓存机制:缓存热门查询结果。

最后的最后

感谢你们的阅读和喜欢,作为一位在一线互联网行业奋斗多年的老兵,我深知在这个瞬息万变的技术领域中,持续学习和进步的重要性。

为了帮助更多热爱技术、渴望成长的朋友,我特别整理了一份涵盖大模型领域的宝贵资料集。

这些资料不仅是我多年积累的心血结晶,也是我在行业一线实战经验的总结。

这些学习资料不仅深入浅出,而且非常实用,让大家系统而高效地掌握AI大模型的各个知识点。如果你愿意花时间沉下心来学习,相信它们一定能为你提供实质性的帮助。

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费

大模型知识脑图

为了成为更好的 AI大模型 开发者,这里为大家提供了总的路线图。它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

经典书籍阅读

阅读AI大模型经典书籍可以帮助读者提高技术水平,开拓视野,掌握核心技术,提高解决问题的能力,同时也可以借鉴他人的经验。对于想要深入学习AI大模型开发的读者来说,阅读经典书籍是非常有必要的。

在这里插入图片描述

实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

面试资料

我们学习AI大模型必然是想找到高薪的工作,下面这些面试题都是总结当前最新、最热、最高频的面试题,并且每道题都有详细的答案,面试前刷完这套面试题资料,小小offer,不在话下

在这里插入图片描述

640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

在这里插入图片描述

这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费

<think>我们正在构建一个RAG系统,使用LangChain作为框架,Milvus作为向量数据库,bge-m3作为嵌入模型。以下是实现步骤:1.**环境准备**:-安装必要的库:langchain,pymilvus,transformers等。-下载bge-m3模型(可以从ModelScope或HuggingFace下载)。2.**文档加载与处理**:-使用LangChain的文档加载器(如`TextLoader`、`DirectoryLoader`)加载文档。-对文档进行拆分(使用`RecursiveCharacterTextSplitter`等),注意设置合适的块大小和重叠。3.**嵌入模型**:-初始化bge-m3模型,该模型能够生成稠密向量(denseembeddings)和稀疏向量(sparseembeddings)。-由于Milvus支持混合搜索(hybridsearch),我们将同时使用这两种向量。4.**向量数据库(Milvus)**:-在Milvus中创建两个集合(collections)或一个支持多向量的集合,分别存储稠密向量和稀疏向量(或者使用一个集合包含两个向量字段)。-注意:Milvus2.4及以上版本支持多向量字段,因此可以创建一个集合,包含两个向量字段:一个稠密向量字段和一个稀疏向量字段(注意Milvus对稀疏向量的支持情况,目前Milvus支持稀疏向量SPARSE_FLOAT_VECTOR)。5.**文档向量化与存储**:-将拆分后的文档通过bge-m3模型生成稠密向量和稀疏向量。-将文档块内容、元数据以及对应的两种向量存储到Milvus中。6.**检索**:-用户输入查询问题,同样使用bge-m3生成查询的稠密向量和稀疏向量。-使用Milvus的混合搜索(hybridsearch)功能,同时利用两种向量进行检索,并融合结果(例如使用RRF(ReciprocalRankFusion)或其他方法)。-返回最相关的文档块。7.**生成**:-将检索到的文档块作为上下文,与用户问题一起输入到大语言模型(LLM)中,生成回答。下面我们详细说明每一步,并给出代码示例。###步骤1:环境准备首先,安装必要的库:```bashpipinstalllangchainpymilvustransformers```然后,下载bge-m3模型。由于模型较大,建议使用ModelScope(国内更快)或HuggingFace。这里使用ModelScope:```bashgitlfsinstallgitclonehttps://www.modelscope.cn/Xorbits/bge-m3.git```###步骤2:文档加载与处理假设我们有一个文档目录,里面存放着多个文本文件。```pythonfromlangchain.document_loadersimportDirectoryLoaderfromlangchain.text_splitterimportRecursiveCharacterTextSplitter#加载文档loader=DirectoryLoader('./docs',glob="**/*.txt")documents=loader.load()#拆分文档text_splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50)texts=text_splitter.split_documents(documents)```###步骤3:初始化嵌入模型我们使用`transformers`库加载bge-m3模型。注意,bge-m3可以同时生成稠密向量和稀疏向量(稀疏向量是SPLADE表示)。```pythonfromtransformersimportAutoModel,AutoTokenizermodel_path="path/to/bge-m3"#替换为实际路径tokenizer=AutoTokenizer.from_pretrained(model_path)model=AutoModel.from_pretrained(model_path)```###步骤4:设置Milvus向量数据库确保Milvus服务已经启动。然后,我们使用`pymilvus`连接Milvus,并创建集合。```pythonfrompymilvusimportconnections,FieldSchema,CollectionSchema,DataType,Collection,utility#连接Milvusconnections.connect("default",host="localhost",port="19530")#定义集合的字段fields=[FieldSchema(name="id",dtype=DataType.INT64,is_primary=True,auto_id=True),FieldSchema(name="text",dtype=DataType.VARCHAR,max_length=1000),FieldSchema(name="dense_vector",dtype=DataType.FLOAT_VECTOR,dim=1024),#bge-m3稠密向量维度为1024FieldSchema(name="sparse_vector",dtype=DataType.SPARSE_FLOAT_VECTOR)#稀疏向量]#创建集合schema=CollectionSchema(fields,"RAGusingbge-m3andMilvus")collection_name="bge_m3_docs"ifutility.has_collection(collection_name):utility.drop_collection(collection_name)collection=Collection(collection_name,schema)#创建索引(稠密向量使用IVF_FLAT,稀疏向量使用SPARSE_INVERTED_INDEX)index_params_dense={"index_type":"IVF_FLAT","metric_type":"L2","params":{"nlist":1024}}index_params_sparse={"index_type":"SPARSE_INVERTED_INDEX","metric_type":"IP"#内积}collection.create_index("dense_vector",index_params_dense)collection.create_index("sparse_vector",index_params_sparse)```###步骤5:文档向量化与存储对每个文本块,使用bge-m3生成稠密向量和稀疏向量,然后插入Milvus。```pythonimporttorchimportnumpyasnpfrompymilvusimportMilvusClient#初始化Milvus客户端client=MilvusClient(uri="http://localhost:19530",db_name="default")#准备批量插入的数据data=[]fortextintexts:#使用bge-m3生成向量inputs=tokenizer(text.page_content,return_tensors="pt")withtorch.no_grad():outputs=model(**inputs)#获取稠密向量dense_embedding=outputs.last_hidden_state.mean(dim=1).squeeze().numpy()#获取稀疏向量(SPLADE)sparse_embedding=outputs.logits.max(dim=1)[0].squeeze().numpy()#将稀疏向量转换为字典格式(索引:值)sparse_dict={idx:valueforidx,valueinenumerate(sparse_embedding)ifvalue!=0}data.append({"text":text.page_content,"dense_vector":dense_embedding.tolist(),"sparse_vector":sparse_dict})#插入数据client.insert(collection_name,data)```###步骤6:检索用户查询时,同样生成两种向量,然后进行混合搜索。```pythondefhybrid_search(query,top_k=5):#生成查询向量inputs=tokenizer(query,return_tensors="pt")withtorch.no_grad():outputs=model(**inputs)dense_embedding=outputs.last_hidden_state.mean(dim=1).squeeze().numpy()sparse_embedding=outputs.logits.max(dim=1)[0].squeeze().numpy()sparse_dict={idx:valueforidx,valueinenumerate(sparse_embedding)ifvalue!=0}#分别进行稠密搜索和稀疏搜索#稠密搜索dense_results=client.search(collection_name=collection_name,data=[dense_embedding.tolist()],anns_field="dense_vector",param={"metric_type":"L2","params":{"nprobe":10}},limit=top_k,output_fields=["text"])#稀疏搜索(注意:Milvus的稀疏向量搜索使用内积,且返回的分数是内积值)sparse_results=client.search(collection_name=collection_name,data=[sparse_dict],anns_field="sparse_vector",param={"metric_type":"IP"},limit=top_k,output_fields=["text"])#融合结果(简单示例:使用RRF)#为每个文档分配一个融合后的分数scores={}#处理稠密结果foridx,hitinenumerate(dense_results[0]):doc_id=hit.id#RRF:1/(rank+k)这里k设为60(常用值)rrf_score=1.0/(idx+60)scores[doc_id]=scores.get(doc_id,0)+rrf_score#处理稀疏结果foridx,hitinenumerate(sparse_results[0]):doc_id=hit.idrrf_score=1.0/(idx+60)scores[doc_id]=scores.get(doc_id,0)+rrf_score#按融合分数排序sorted_scores=sorted(scores.items(),key=lambdax:x[1],reverse=True)top_doc_ids=[doc_idfordoc_id,scoreinsorted_scores[:top_k]]#获取文档内容results=client.get(collection_name,ids=top_doc_ids,output_fields=["text"])return[res['text']forresinresults]```###步骤7:生成使用LangChain的LCEL(LangChainExpressionLanguage)或简单的链式调用,将检索到的文档作为上下文,输入到LLM中。```pythonfromlangchain.llmsimportOpenAIfromlangchain.promptsimportPromptTemplatefromlangchain.schema.runnableimportRunnablePassthrough#初始化LLMllm=OpenAI(api_key="your-openai-key")#定义提示模板template="""基于以下上下文,回答用户问题:{context}问题:{question}"""prompt=PromptTemplate.from_template(template)#定义RAG链defretrieve(question):returnhybrid_search(question)chain=({"context":retrieve,"question":RunnablePassthrough()}|prompt|llm)#调用链response=chain.invoke("用户的问题")print(response)```###注意事项1.**性能考虑**:bge-m3模型较大,推理速度较慢,可以考虑使用GPU加速。2.**混合搜索的融合策略**:这里使用了简单的RRF,也可以尝试其他融合方法,如加权分数等。3.**Milvus的稀疏向量支持**:确保Milvus版本支持稀疏向量(2.4及以上)。4.**文档分块大小**:根据实际文档调整分块大小和重叠。以上是一个完整的RAG系统实现方案,结合了LangChainMilvus和bge-m3的优势。###相关问题1.如何优化bge-m3模型的推理速度?2.在Milvus中,如何调整索引参数以提高搜索性能?3.除了RRF,还有哪些融合检索结果的方法?4.如何处理大文档的嵌入,避免内存溢出?</think>### 使用 LangChain + Milvus + BGE-M3 实现 RAG 方案 以下是完整的实现方案,分为 6 个关键步骤: #### 1. **环境准备** ```python # 安装核心库 pip install langchain pymilvus sentence-transformers git clone https://www.modelscope.cn/Xorbits/bge-m3.git # 下载模型[^1] ``` #### 2. **初始化组件** ```python from langchain.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from pymilvus import connections, Collection from transformers import AutoModel, AutoTokenizer # 连接 Milvus connections.connect(host="localhost", port="19530") # 加载 BGE-M3 模型[^3] model = AutoModel.from_pretrained("./bge-m3") tokenizer = AutoTokenizer.from_pretrained("./bge-m3") ``` #### 3. **文档处理与向量化** ```python # 加载文档 loader = TextLoader("data.txt") documents = loader.load() # 文档分块(关键步骤)[^2] text_splitter = RecursiveCharacterTextSplitter( chunk_size=512, chunk_overlap=50 ) chunks = text_splitter.split_documents(documents) # 生成混合向量 def get_embeddings(text): inputs = tokenizer(text, return_tensors="pt") outputs = model(**inputs) dense_embed = outputs.last_hidden_state.mean(dim=1).squeeze().tolist() sparse_embed = outputs.logits.max(dim=1)[0].squeeze().numpy() return dense_embed, sparse_embed ``` #### 4. **Milvus 向量库配置** ```python # 创建集合(支持混合向量)[^4] collection = Collection.create( "rag_collection", fields=[ {"name": "id", "type": "INT64", "is_primary": True}, {"name": "text", "type": "VARCHAR", "max_length": 1000}, {"name": "dense_vector", "type": "FLOAT_VECTOR", "dim": 1024}, {"name": "sparse_vector", "type": "SPARSE_FLOAT_VECTOR"} ] ) # 创建双索引 collection.create_index( field_name="dense_vector", index_params={"index_type": "IVF_FLAT", "metric_type": "L2"} ) collection.create_index( field_name="sparse_vector", index_params={"index_type": "SPARSE_INVERTED_INDEX"} ) ``` #### 5. **检索增强流程** ```python from langchain.chains import RetrievalQA from langchain.llms import OpenAI # 混合检索函数 def hybrid_search(query, top_k=5): dense_emb, sparse_emb = get_embeddings(query) # 并行执行双向量检索 dense_results = collection.search( data=[dense_emb], anns_field="dense_vector", limit=top_k ) sparse_results = collection.search( data=[sparse_emb], anns_field="sparse_vector", limit=top_k ) # 结果融合 (RRF 算法) return reciprocal_rank_fusion(dense_results, sparse_results) # 构建 RAG 链 qa_chain = RetrievalQA.from_chain_type( llm=OpenAI(temperature=0), chain_type="stuff", retriever=hybrid_search, return_source_documents=True ) ``` #### 6. **运行与优化** ```python # 插入文档向量 for chunk in chunks: dense_vec, sparse_vec = get_embeddings(chunk.page_content) collection.insert([ {"text": chunk.page_content}, {"dense_vector": dense_vec}, {"sparse_vector": sparse_vec} ]) # 执行查询 response = qa_chain("什么是量子纠缠?") print(f"答案: {response['result']}") print(f"来源: {response['source_documents'][0].page_content}") ``` ### 关键优势说明 1. **多粒度检索**:BGE-M3 支持词级/句级/段落级嵌入[^3],提升语义捕获能力 2. **混合搜索**:同时利用稠密向量的语义信息和稀疏向量的关键词信息[^4] 3. **性能优化**: - 分块大小建议 256-512 字符[^2] - 使用 `IVF_FLAT` + `SPARSE_INVERTED_INDEX` 组合索引 - 批处理文档插入(每次 500-1000 条) ### 常见问题解决 - **OOM 错误**:减小分块尺寸或启用 `model.half()` 半精度 - **低召回率**:调整分块重叠(建议 10-15%)或尝试不同分块策略 - **延迟过高**:在 Milvus 中启用 GPU 加速或增加 `nprobe` 参数 ### 相关问题 1. 如何评估 RAG 系统中检索结果的质量? 2. BGE-M3 与其他嵌入模型(如 OpenAI Embeddings)相比有何优势? 3. 在资源受限环境中如何优化 Milvus 的性能? 4. 如何处理 RAG 中的长文档上下文溢出问题? 5. 如何实现基于 RAG 的多轮对话系统? [^1]: 从 huggingface 或国内的 modelscope 下载 BGE-M3 模型 [^2]: 文档拆分的注意点,embedding model, chat model区别 [^3]: BGE-M3:Multi-Functionality Multi-Linguisticity Multi-Granularity [^4]: 混合搜索同时使用稠密向量和稀疏向量增强准确性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值