本文手把手教你用Python搭建RAG系统。详细介绍了基于LangChain框架的技术选型(PyPDF解析、bge向量化、Faiss存储、通义千问生成),并提供了从环境搭建、索引流程、检索流程到生成流程的完整代码实现及测试脚本,助你快速掌握RAG实战开发。

@ 目录

  • 一、技术框架与选型
  • 1.1、技术框架与选型
  • 1.2、技术框架与选型的流程图
  • 二、开发环境与技术库
  • 2.1、创建并激活虚拟环境
  • 2.2、安装技术依赖库
  • 三、RAG 核心流程代码
  • 3.1、模块库的引入
  • 3.2、索引流程
  • 3.3、检索流程
  • 3.4、生成流程
  • 3.5、测试脚本

一、技术框架与选型

  • 这篇文章中的选型并非适用于所有场景的最佳方案,而是基于当前广泛应用和流行的技术模块。关于这些模块的具体特点以及可能的替代选型,我们会在后续文章中进行详细分析与解读。

1.1、技术框架与选型

1.1.1、LangChain
  • LangChain 是专为开发基于大型语言模型(LLM)应用而设计的全面框架,其核心目标是简化开发者的构建流程,使其能够高效创建 LLM 驱动的应用。
1.1.2、索引流程 (文档解析模块:pypdf)
  • pypdf 是一个开源的 Python 库,专门用于处理 PDF 文档。pypdf 支持 PDF 文档的创建、读取、编辑和转换操作,能够有效提取和处理文本、图像及页面内容。
1.1.3、索引流程 (文档分块模块:RecursiveCharacterTextSplitter)
  • 采用 LangChain 默认的文本分割器 -RecursiveCharacterTextSplitter。该分割器通过层次化的分隔符(从双换行符到单字符)拆分文本,旨在保持文本的结构和连贯性,优先考虑自然边界如段落和句子。
1.1.4、索引 / 检索流程 (向量化模型:bge-small-zh-v1.5)
  • bge-small-zh-v1.5 是由北京人工智能研究院(BAAI,智源)开发的开源向量模型。虽然模型体积较小,但仍然能够提供高精度和高效的中文向量检索。该模型的向量维度为 512,最大输入长度同样为 512。
1.1.5、索引 / 检索流程 (向量库:Faiss)
  • Faiss 全称 Facebook AI Similarity Search,由 Facebook AI Research 团队开源的向量库,因其稳定性和高效性在向量检索领域广受欢迎。
1.1.5、生成流程 (大语言模型:通义千问 Qwen)
  • 通义千问 Qwen 是阿里云推出的一款超大规模语言模型,支持多轮对话、文案创作、逻辑推理、多模态理解以及多语言处理,在模型性能和工程应用中表现出色。采用云端 API 服务,注册有 1,000,000 token 的免费额度。

1.2、技术框架与选型的流程图

  • 流程图在这里插入图片描述

    LangChain:提供用于构建 LLM RAG 的应用程序框架。

    索引流程:使用 pypdf 对文档进行解析并提取信息;随后,采用RecursiveCharacterTextSplitter 对文档内容进行分块(chunks);最后,利用 bge-small-zh-v1.5 将分块内容进行向量化处理,并将生成的向量存储在 Faiss 向量库中。

    检索流程:使用 bge-small-zh-v1.5 对用户的查询(Query)进行向量化处理;然后,通过 Faiss 向量库对查询向量和文本块向量进行相似度匹配,从而检索出与用户查询最相似的前 top-k 个文本块(chunk)。

    生成流程:通过设定提示模板(Prompt),将用户的查询与检索到的参考文本块组合输入到 Qwen 大模型中,生成最终的 RAG 回答。

二、开发环境与技术库

  • 我们采用 Python 编程语言,Python 版本 3.8 及以上,运行于 Linux 操作系统下。结合上述的技术框架与选型,我们的开发环境需要按照以下步骤进行准备:

2.1、创建并激活虚拟环境

  • 目的是隔离项目依赖,避免项目之间冲突。先在命令行窗口中执行指令定位到具体的 RAG 项目文件夹,然后在命令行中执行以下指令:

    # 创建名为rag_env的虚拟环境  
    python3 -m venv rag_env  
    # 激活虚拟环境  
    source rag_env/bin/activate
    

2.2、安装技术依赖库

2.2.1、安装技术依赖库
  • 安装langchainlangchain/_community LLM RAG 的技术框架及其扩展。
  • 安装 pypdf 处理 PDF 文档的解析库。
  • 安装 sentence-transformers 运行指定文本嵌入模型 bge-small-zh-v1.5 的模型库。
  • 安装 faiss-cpu 高效相似度搜索的 Faiss 向量库。
  • 安装 dashscope 与阿里云 Qwen 大模型 API 的集成库。
2.2.2、安装技术依赖库的命令
  • 首先,升级 pip 版本,以确保兼容性,在命令行中执行以下指令。

    # 升级pip版本以确保兼容性  
    pip install --upgrade pip
    
  • 然后,安装上述技术依赖库,在命令行中执行以下指令:

    pip install langchain langchain_community pypdf sentence-transformers faiss-cpu dashscope
    
  • 如果无法连接,可以使用国内镜像站点,在命令行中执行以下指令:

    pip install langchain langchain_community pypdf sentence-transformers faiss-cpu dashscope -i https://pypi.tuna.tsinghua.edu.cn/simple 
    
2.2.3、下载 bge-small-zh-v1.5 模型
  • 该模型的文件已包含在 Gitee 上托管的项目中的 bge-small-zh-v1.5 文件夹内,你可以直接下载到 RAG 项目的根目录中,模型大小 95.8M,在命令行中执行以下指令:

    git clone https://gitee.com/techleadcy/rag_app.git
    
  • 下载完成后,检查 rag/_app 项目中 bge-small-zh-v1.5 文件夹中是否包含 pytorch/_model.bin 文件。该库中同时包含了本文章的 RAG 核心流程代码 rag/_app/_lesson2.py 及测试 PDF 文档 test/_lesson2.pdf在这里插入图片描述

三、RAG 核心流程代码

  • 在实战过程中,你不仅可以快速构建 RAG 应用,还能够在研发过程中深入理解其背后的技术逻辑与核心原理。为此,我在代码的每一行添加了必要的注释,并对每段流程代码进行了归纳和解释,旨在通过实战代码增强你对 RAG 技术的理解。
  • 整个流程代码分为模块库的引入、索引流程、检索流程、生成流程以及测试代码几部分进行精细讲解,具体的代码位于 Gitee 项目库中的 rag/_app/_lesson2.py 文件中。

3.1、模块库的引入

  • 模块库引入的代码如下:

    from langchain_community.document_loaders import PyPDFLoader # PDF文档提取  
    from langchain_text_splitters import RecursiveCharacterTextSplitter # 文档拆分chunk  
    from sentence_transformers import SentenceTransformer # 加载和使用Embedding模型  
    import faiss # Faiss向量库  
    import numpy as np # 处理嵌入向量数据,用于Faiss向量检索  
    import dashscope #调用Qwen大模型  
    from http import HTTPStatus #检查与Qwen模型HTTP请求状态  
      
    import os # 引入操作系统库,后续配置环境变量与获得当前文件路径使用  
    os.environ["TOKENIZERS_PARALLELISM"] = "false" # 不使用分词并行化操作, 避免多线程或多进程环境中运行多个模型引发冲突或死锁  
      
    # 设置Qwen系列具体模型及对应的调用API密钥,从阿里云百炼大模型服务平台获得  
    qwen_model = "qwen-turbo"  
    qwen_api_key = "your_api_key"
    
  • 阿里云百炼大模型服务平台获取的 Qwen 大模型的 API 密钥流程如下:

    打开阿里云百炼大模型服务平台 ,点击立即开通,登陆阿里云 / 支付宝 / 钉钉账号;

    点击模型广场,搜索 通义千问 -Turbo,点击 API 调用示例;

    点击查看我的 API-KEY,继续点击弹出框中的查看,复制 API-KEY,将 API-KEY 替换代码中 qwen/_api/_key = “your/_api/_key”;

    点击模型详情,可以看到模型 Model 英文名称,赋值 qwen/_model 参数。可以看到剩余额度,当前阿里云提供 1,000,000 次的免费模型额度;

    如果需要尝试其他模型,可以对应赋值上述代码中 qwen/_model 参数及 qwen/_api/_key 参数。

  • 上代码导入了我们在 RAG 流程中需要使用的核心模块及大模型参数等配置,这些模块和配置将在后续的索引、检索和生成流程中调用使用。

3.2、索引流程

  • 索引流程的代码如下:

    def load_embedding_model():  
        """  
        加载bge-small-zh-v1.5模型  
        :return: 返回加载的bge-small-zh-v1.5模型  
        """  
        print(f"加载Embedding模型中")  
        # SentenceTransformer读取绝对路径下的bge-small-zh-v1.5模型,非下载  
        embedding_model = SentenceTransformer(os.path.abspath('rag_app/bge-small-zh-v1.5'))  
        print(f"bge-small-zh-v1.5模型最大输入长度: {embedding_model.max_seq_length}")   
        return embedding_model  
      
      
    def indexing_process(pdf_file, embedding_model):  
        """  
        索引流程:加载PDF文件,并将其内容分割成小块,计算这些小块的嵌入向量并将其存储在FAISS向量数据库中。  
        :param pdf_file: PDF文件路径  
        :param embedding_model: 预加载的嵌入模型  
        :return: 返回Faiss嵌入向量索引和分割后的文本块原始内容列表  
        """  
        # PyPDFLoader加载PDF文件,忽略图片提取  
        pdf_loader = PyPDFLoader(pdf_file, extract_images=False)  
        # 配置RecursiveCharacterTextSplitter分割文本块库参数,每个文本块的大小为512字符(非token),相邻文本块之间的重叠128字符(非token)  
        text_splitter = RecursiveCharacterTextSplitter(  
            chunk_size=512, chunk_overlap=128  
        )  
        # 加载PDF文档,提取所有页的文本内容  
        pdf_content_list = pdf_loader.load()  
        # 将每页的文本内容用换行符连接,合并为PDF文档的完整文本  
        pdf_text = "/n".join([page.page_content for page in pdf_content_list])  
        print(f"PDF文档的总字符数: {len(pdf_text)}")   
      
        # 将PDF文档文本分割成文本块Chunk  
        chunks = text_splitter.split_text(pdf_text)  
        print(f"分割的文本Chunk数量: {len(chunks)}")   
      
        # 文本块转化为嵌入向量列表,normalize_embeddings表示对嵌入向量进行归一化,用于准确计算相似度  
        embeddings = []  
        for chunk in chunks:  
            embedding = embedding_model.encode(chunk, normalize_embeddings=True)  
            embeddings.append(embedding)  
      
        print("文本块Chunk转化为嵌入向量完成")  
      
        # 将嵌入向量列表转化为numpy数组,Faiss向量库操作需要numpy数组输入  
        embeddings_np = np.array(embeddings)  
      
        # 获取嵌入向量的维度(每个向量的长度)  
        dimension = embeddings_np.shape[1]  
      
        # 使用余弦相似度创建FAISS索引  
        index = faiss.IndexFlatIP(dimension)  
        # 将所有的嵌入向量添加到FAISS索引中,后续可以用来进行相似性检索  
        index.add(embeddings_np)  
      
        print("索引过程完成.")  
      
        return index, chunks
    
  • 上述代码实现了 RAG 技术中的索引流程,

    首先使用 PyPDFLoader 加载并预处理 PDF 文档,将其内容提取并合并为完整文本。接着,利用

    接着,利用 RecursiveCharacterTextSplitter 将文本分割为每块 512 字符(非 token)、重叠 128 字符(非 token)的文本块,并通过预加载的 bge-small-zh-v1.5 嵌入模型将这些文本块转化为归一化的嵌入向量。

    最终,这些嵌入向量被存储在基于余弦相似度的 Faiss 向量库中,以支持后续的相似性检索和生成任务。

  • 为更清晰地展示 RAG 流程的各个细节,当前代码未涉及多文档处理、嵌入模型的效率优化与并行处理。此外,Faiss 向量目前仅在内存中存储,未考虑持久化存储问题。以及文档解析、文本分块、嵌入模型与向量库的技术选型,后续文章将逐步深入探讨,并以上述代码作为基础,持续优化。

3.3、检索流程

  • 检索流程的代码如下:

    def retrieval_process(query, index, chunks, embedding_model, top_k=3):  
        """  
        检索流程:将用户查询Query转化为嵌入向量,并在Faiss索引中检索最相似的前k个文本块。  
        :param query: 用户查询语句  
        :param index: 已建立的Faiss向量索引  
        :param chunks: 原始文本块内容列表  
        :param embedding_model: 预加载的嵌入模型  
        :param top_k: 返回最相似的前K个结果  
        :return: 返回最相似的文本块及其相似度得分  
        """  
        # 将查询转化为嵌入向量,normalize_embeddings表示对嵌入向量进行归一化  
        query_embedding = embedding_model.encode(query, normalize_embeddings=True)  
        # 将嵌入向量转化为numpy数组,Faiss索引操作需要numpy数组输入  
        query_embedding = np.array([query_embedding])  
      
        # 在 Faiss 索引中使用 query_embedding 进行搜索,检索出最相似的前 top_k 个结果。  
        # 返回查询向量与每个返回结果之间的相似度得分(在使用余弦相似度时,值越大越相似)排名列表distances,最相似的 top_k 个文本块在原始 chunks 列表中的索引indices。  
        distances, indices = index.search(query_embedding, top_k)  
      
        print(f"查询语句: {query}")  
        print(f"最相似的前{top_k}个文本块:")  
      
        # 输出查询出的top_k个文本块及其相似度得分  
        results = []  
        for i in range(top_k):  
            # 获取相似文本块的原始内容  
            result_chunk = chunks[indices[0][i]]  
            print(f"文本块 {i}:/n{result_chunk}")   
      
            # 获取相似文本块的相似度得分  
            result_distance = distances[0][i]  
            print(f"相似度得分: {result_distance}/n")  
      
            # 将相似文本块存储在结果列表中  
            results.append(result_chunk)  
      
        print("检索过程完成.")  
        return results
    
  • 上述代码实现了 RAG 技术中的检索流程。

    首先,用户的查询(Query)被预加载的 bge-small-zh-v1.5 嵌入模型转化为归一化的嵌入向量,进一步转换为 numpy 数组以适配 Faiss 向量库的输入格式。

    然后,利用 Faiss 向量库中的向量检索功能,计算查询向量与存储向量之间的余弦相似度,从而筛选出与查询最相似的前 top/_k 个文本块。这些文本块及其相应的相似度得分被逐一输出,相似文本块存储在结果列表中,

    最终返回供后续生成过程使用。

3.4、生成流程

  • 生成流程的代码如下:

    def generate_process(query, chunks):  
        """  
        生成流程:调用Qwen大模型云端API,根据查询和文本块生成最终回复。  
        :param query: 用户查询语句  
        :param chunks: 从检索过程中获得的相关文本块上下文chunks  
        :return: 返回生成的响应内容  
        """  
        # 设置Qwen系列具体模型及对应的调用API密钥,从阿里云大模型服务平台百炼获得  
        llm_model = qwen_model  
        dashscope.api_key = qwen_api_key  
      
        # 构建参考文档内容,格式为“参考文档1: /n 参考文档2: /n ...”等  
        context = ""  
        for i, chunk in enumerate(chunks):  
            context += f"参考文档{i+1}: /n{chunk}/n/n"  
      
        # 构建生成模型所需的Prompt,包含用户查询和检索到的上下文  
        prompt = f"根据参考文档回答问题:{query}/n/n{context}"  
        print(f"生成模型的Prompt: {prompt}")  
      
        # 准备请求消息,将prompt作为输入  
        messages = [{'role': 'user', 'content': prompt}]  
      
        # 调用大模型API云服务生成响应  
        try:  
            responses = dashscope.Generation.call(  
                model = llm_model,  
                messages=messages,  
                result_format='message',  # 设置返回格式为"message"  
                stream=True,              # 启用流式输出  
                incremental_output=True   # 获取流式增量输出  
            )  
            # 初始化变量以存储生成的响应内容  
            generated_response = ""  
            print("生成过程开始:")  
            # 逐步获取和处理模型的增量输出  
            for response in responses:  
                if response.status_code == HTTPStatus.OK:  
                    content = response.output.choices[0]['message']['content']  
                    generated_response += content  
                    print(content, end='')  # 实时输出模型生成的内容  
                else:  
                    print(f"请求失败: {response.status_code} - {response.message}")  
                    return None  # 请求失败时返回 None  
            print("/n生成过程完成.")  
            return generated_response  
        except Exception as e:  
            print(f"大模型生成过程中发生错误: {e}")  
            return None
    
  • 上述代码实现了 RAG 流程中的生成过程。

    首先,结合用户查询与检索到的文本块内容组织成大模型提示词(Prompt)。

    随后,代码通过调用 Qwen 大模型云端 API,将构建好的 Prompt 发送给大模型,并利用流式输出的方式逐步获取模型生成的响应内容,实时输出并汇总为最终的生成结果。

3.5、测试脚本

  • 测试脚本的代码如下:

    def main():  
        print("RAG过程开始.")  
      
        query="下面报告中涉及了哪几个行业的案例以及总结各自面临的挑战?"  
        embedding_model = load_embedding_model()  
      
        # 索引流程:加载PDF文件,分割文本块,计算嵌入向量,存储在FAISS向量库中(内存)  
        index, chunks = indexing_process('rag_app/test_lesson2.pdf', embedding_model)  
      
        # 检索流程:将用户查询转化为嵌入向量,检索最相似的文本块  
        retrieval_chunks = retrieval_process(query, index, chunks, embedding_model)  
      
        # 生成流程:调用Qwen大模型生成响应  
        generate_process(query, retrieval_chunks)  
      
        print("RAG过程结束.")  
      
    if __name__ == "__main__":  
        main()
    
  • 在命令行窗口中执行指令定位到具体的 RAG 项目文件夹,在命令行中执行以下指令即可开始 RAG 应用测试:

    source rag_env/bin/activate  # 激活虚拟环境  
    python rag_app/rag_app_lesson2.py # 执行RAG应用脚本
    
  • 测试代码通过 main() 函数串联各个步骤,从索引到生成,确保 RAG 的各个环节顺畅执行,准确完成了下面报告中涉及了哪几个行业的案例以及总结各自面临的挑战?的 RAG 问答任务。

  • 正确运行结果如下所示:

    RAG过程开始.  
    加载Embedding模型中  
    bge-small-zh-v1.5模型最大输入长度token: 512  
    PDF文档的总字符数: 9135  
    分割的文本Chunk数量: 24  
    文本块Chunk转化为嵌入向量完成  
    索引过程完成.  
      
    查询语句: 下面报告中涉及了哪几个行业的案例以及总结各自面临的挑战?  
    最相似的前3个文本块:  
    文本块 0: 面的数字化转型。2.3.2面临的挑战:......相似度得分: 0.5915016531944275  
    文本块 1: ...... 相似度得分: 0.5728524327278137  
    文本块 2: ...... 相似度得分: 0.5637902617454529  
    检索过程完成.  
      
    生成模型的Prompt: 根据参考文档回答问题:下面报告中涉及了哪几个行业的案例以及总结各自面临的挑战?  
    参考文档1: 面的数字化转型。2.3.2面临的挑战...  
    参考文档2: ......  
    参考文档3: ......  
      
    生成过程开始:  
    参考文档中涉及了三个行业的案例及其面临的挑战:  
    1.### 制造业...... 2. ### 零售业......  3. ### 金融业......  
    ### 数字化转型解决方案概述  
    -**制造业**:...... -**零售业**:...... -**金融业**:......  
    这些案例强调了不同行业在数字化转型过程中面临的独特挑战......  
    生成过程完成.  
    RAG过程结束.
    

    检索过程完成.

    生成模型的Prompt: 根据参考文档回答问题:下面报告中涉及了哪几个行业的案例以及总结各自面临的挑战?
    参考文档1: 面的数字化转型。2.3.2面临的挑战…
    参考文档2: …
    参考文档3: …

    生成过程开始:
    参考文档中涉及了三个行业的案例及其面临的挑战:
    1.### 制造业… 2. ### 零售业… 3. ### 金融业…

    数字化转型解决方案概述

    -制造业:… -零售业:… -金融业:…
    这些案例强调了不同行业在数字化转型过程中面临的独特挑战…
    生成过程完成.
    RAG过程结束.

假如你从2026年开始学大模型,按这个步骤走准能稳步进阶。

接下来告诉你一条最快的邪修路线,

3个月即可成为模型大师,薪资直接起飞。
img

阶段1:大模型基础

img

阶段2:RAG应用开发工程

img

阶段3:大模型Agent应用架构

img

阶段4:大模型微调与私有化部署

img

配套文档资源+全套AI 大模型 学习资料,朋友们如果需要可以微信扫描下方二维码免费领取【保证100%免费】👇👇
在这里插入图片描述
img

img

img

img
img

配套文档资源+全套AI 大模型 学习资料,朋友们如果需要可以微信扫描下方二维码免费领取【保证100%免费】👇👇

在这里插入图片描述

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐