【保姆级 - 大模型应用开发】DeepSeek + Faiss + langchain 从零搭建本地知识库 检索 | 理论 + 代码实战

在这里插入图片描述


RAG(Retrieval-Augmented Generation)-- 检索增强生成:

  • 一种结合信息检索(Retrieval)和文本生成(Generation)的技术:通过实时检索相关信息,并将其作为上下文输入到生成模型中,从而提高生成结果的时效性准确性 (← 优势)

    目前感觉最好用的 RAG 产品是 notebookLM,可以先上手体验下 ( BUT 需要外网,不开源)
    开源产品可以选择 Qwen-Agent ---- 本文是从头构建 RAG,也可以选择基于 Qwen-Agent 来快速实现 RAG 部署 (见主页博文)

在这里插入图片描述


⭐ RAG 的三大步骤:数据预处理、检索和生成 💜 【理论基础】

RAG 第一步:数据预处理

Indexing => 如何更好地把知识存起来
在让 AI 使用外部知识前,需要先把原始资料整理成适合检索的格式,主要包括以下几步:

  • 知识库构建:汇总各种收集到的相关资料,比如文档,建立一个“外部知识库”

  • 文档分块:把长文档按语义切成一小段一小段(叫做 chunk)

    • ✏️ 说明:分得太碎可能语义不完整,分太大又不利于检索,要平衡
  • 向量化:使用“嵌入模型”把 chunk 转成“向量”并存进“向量数据库”,方便快速搜索

    类型模型特点适用场景开源状态
    通用模型BGE-M3多语言、长文本、混合检索高精度 RAG、跨语文档✅ 开源
    text-embedding-3-large英文表现强英文场景❌ 闭源(OpenAI)
    Jina-embeddings-v2模型小,推理快实时任务、轻量场景✅ 开源
    中文模型xiaobu-embedding-v2语义理解强中文分类、检索✅ 开源
    M3E-Turbo本地部署好法律、医疗文本✅ 开源
    stella-mrl-large-zh-v3.5-1792关系抽取强NLP分析、高语义需求✅ 开源
    指令驱动 & 复杂任务gte-Qwen2-7B-instruct支持代码与跨模态多任务问答✅ 开源(协议限制)
    E5-mistral-7BZero-shot强动态语境✅ 开源
    企业级BGE-M3稳定部署、强检索企业知识库✅ 开源
    E5-mistral-7B支持微调高复杂系统✅ 开源

    ✏️ 说明:可以在 https//modelscope.cn/ 找到并下载 embedding 模型 (以下示例中部署 BGE-M3,个人级不建议部署,代价大~看下流程即可)

    # 安装依赖(只需运行一次)
    # pip install FlagEmbedding
    
    # 1️⃣ 加载模型
    from FlagEmbedding import BGEM3FlagModel
    
    # 加载 BGE-M3 模型,use_fp16=True 可以加速(需要显卡支持)
    model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)  ## 替换成你存 BGE 模型的路径
    
    # 2️⃣ 编码文本为向量
    # 设定两个句子列表,一个作为查询,一个作为候选答案
    sentences_query = ["什么是BGE M3?", "BM25的定义"]
    sentences_docs = [
        "BGE M3 是一个支持稠密检索、词匹配和多交互的嵌入模型。",
        "BM25 是一种用于排名的词袋模型。"
    ]
    
    # 使用模型编码成向量(dense_vecs)
    emb_query = model.encode(sentences_query, max_length=8192, batch_size=12)['dense_vecs']
    emb_docs = model.encode(sentences_docs)['dense_vecs']
    
    # 3️⃣ 计算相似度
    # 使用点乘计算 query 和每个文档的相似度(越高越相关)
    similarity = emb_query @ emb_docs.T
    
    # 输出相似度矩阵
    print("相似度矩阵:")
    print(similarity)  ## 形状是 [sentences_query 的数量, sentences_docs 的数量]
    

RAG 第二步:检索 Retrieval

Retrieval => 如何在大量的知识中,找到一小部分有用的,给到模型参考
让 AI 从知识库中找到与你问题相关的资料,主要包括以下几步:

  • 查询处理:将你的问题转成向量(即数字表达),然后在向量数据库中搜索,找出最相关的内容片段
  • 相关性排序:对初步搜索结果进行相关性排序,挑选最合适的片段,用于下一步生成答案

RAG 第三步:生成 Generation

Generation => 如何结合用户的提问和检索到的知识,让模型生成有用的答案
让大模型结合相关资料,生成最终答案,主要包括以下几步:

  • 上下文组装:把上一步找到的片段,与你的问题一起输入大模型,构成“增强版上下文”
  • 生成回答:大语言模型根据增强后的上下文,生成准确、有依据的回答

在这里插入图片描述

⭐ RAG 部署的三大步骤 :PDF提取、知识库创建和 Q&A💜 【代码实战】

from typing import List, Tuple
DASHSCOPE_API_KEY = ''  ## TODO: 填充你的 DASHSCOPE_API_KEY 🔍🔍🔍

1. PDF提取文本和页码信息:text, page_numbers

from PyPDF2 import PdfReader


def extract_text_with_page_numbers(pdf) -> Tuple[str, List[int]]:
    """
    从PDF中提取文本并记录每行文本对应的页码
    
    参数:
        pdf: PDF文件对象
    
    返回:
        text: 提取的文本内容
        page_numbers: 每行文本对应的页码列表
    """
    text = ""
    page_numbers = []

    for page_number, page in enumerate(pdf.pages, start=1):
        extracted_text = page.extract_text()
        if extracted_text:
            text += extracted_text
            page_numbers.extend([page_number] * len(extracted_text.split("\n")))

    return text, page_numbers


pdf_reader = PdfReader('./你准备的 PDF 文档(自行修改 🔍🔍🔍).pdf')  # 读取 PDF 文件
text, page_numbers = extract_text_with_page_numbers(pdf_reader)  # 提取文本和页码信息

2. 根据文本和页码信息创建知识库 knowledgeBase

from langchain_community.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import DashScopeEmbeddings


def process_text_with_splitter(text: str, page_numbers: List[int]) -> FAISS:
    """
    处理文本并创建 FAISS 知识库存储
    
    参数:
        text: 提取的文本内容
        page_numbers: 每行文本对应的页码列表
    
    返回:
        knowledgeBase: FAISS 知识库
    """
    # 步骤一:准备 chunks
    ## 1. 创建文本分割器
    text_splitter = RecursiveCharacterTextSplitter(
        separators=["\n\n", "\n", ".", " ", ""],
        chunk_size=1000,  		## 最大长度
        chunk_overlap=200,  	## 重叠长度
        length_function=len,
    )
    ## 2. 分割文本为 chunk
    chunks = text_splitter.split_text(text)
    # print(f"Text split into {len(chunks)} chunks.")
        
    # 步骤二:准备嵌入模型
    embeddings = DashScopeEmbeddings(
        model="text-embedding-v1",
        dashscope_api_key=DASHSCOPE_API_KEY,
    )
    
    # 步骤三:根据准备 chunks 和 嵌入模型,创建 FAISS 知识库
    knowledgeBase = FAISS.from_texts(chunks, embeddings)
    knowledgeBase.page_info = {chunk: 0 for i, chunk in enumerate(chunks)}  # 存储 chunk 对应的页码
    # TODO:暂时chunk 对应的页码都标注为了 0,待后续完善
    
    return knowledgeBase

knowledgeBase = process_text_with_splitter(text, page_numbers)
# knowledgeBase.save_local('./faiss-0804')  # 保存 (可选)

3. 根据知识库提供的 docs 进行 Q&A

from langchain_community.llms import Tongyi
from langchain.chains.question_answering import load_qa_chain
from langchain_community.callbacks.manager import get_openai_callback


# 加载问答链
llm = Tongyi(model_name="qwen-turbo", dashscope_api_key=DASHSCOPE_API_KEY) # qwen-turbo
chain = load_qa_chain(llm, chain_type="stuff")  

# 拼接查询问题 query + docs -> input_data 
query = "你的提问(自行修改 🔍🔍🔍)"
docs = knowledgeBase.similarity_search(query)
input_data = {"input_documents": docs, "question": query}
    
# 执行问答链 (同时使用回调函数跟踪 API 调用成本)
with get_openai_callback() as cost:
    response = chain.invoke(input=input_data)
    print(f"查询已处理。成本: {cost}")
    print(response["output_text"])
 
# 补充 response 回答依据
print("来源:")

unique_pages = set()  
for doc in docs:  # 获取每个文档块的来源页码,存储至 unique_pages 集合
    text_content = getattr(doc, "page_content", "")
    source_page = knowledgeBase.page_info.get(
        text_content.strip(), "未知"
    )
    unique_pages.add(source_page)
    print(f"文本块页码: {source_page}")

运行会输出类似下面的内容:

根据提供的信息,... 。具体相关内容如下:

......

来源:
文本块页码: ...

✏️ 其中,我们看到核心代码 chain = load_qa_chain(llm, chain_type="stuff")
---- LangChain 问答链的四种 chain_type 类型说明:

代码片段功能说明
chain = load_qa_chain(llm, chain_type="stuff")将所有文档拼接为一个 prompt,一次性输入给 LLM,适用于文档较小的情况
chain = load_qa_chain(llm, chain_type="map_reduce")每个 chunk 单独作为 prompt 提问,然后将结果合并做最终回答,适合并行处理
chain = load_qa_chain(llm, chain_type="refine")先从一个 chunk 得到初步回答,后续逐步 refine,适合多轮提炼答案
chain = load_qa_chain(llm, chain_type="map_rerank")每个 chunk 提问并打分,返回评分最高的那一块的答案,适合精准筛选

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

### 使用 LangChain 搭建本地知识库实战教程 #### 准备工作环境 为了创建一个基于 LangChain本地知识库,首先需要安装必要的 Python 库。这可以通过执行以下命令完成: ```bash pip install langchain pip install "langserve[all]" ``` 对于更全面的功能支持,可以考虑安装额外的扩展模块[^4]。 #### 下载项目源码 如果打算使用特定版本或分支的 `langchain-ChatGLM` 作为基础,则可通过 Git 命令克隆仓库到本地环境中: ```bash git clone https://kgithub.com/imClumsyPanda/langchain-ChatGLM.git ``` 此步骤并非强制性的,取决于个人需求和偏好[^3]。 #### 加载与处理文档资料 构建知识库的第一步是从各种来源收集所需的信息资源,并将其转换成适合进一步分析的形式。通常情况下,这些原始材料会被拆分成较小的部分以便更好地管理和索引。例如,在处理纯文本文件时,可以根据章节、段落或其他逻辑单元来进行切割[^1]。 #### 创建向量表示形式 一旦完成了初步的数据预处理之后,下一步就是利用像百度千帆这样的嵌入服务将每一片段转化为高维空间中的点——即所谓的“嵌入”。这样做不仅有助于提高检索效率,而且还能捕捉到不同片段之间的潜在关联性[^5]。 ```python from langchain_community.embeddings import QianfanEmbeddingsEndpoint embeddings_model = QianfanEmbeddingsEndpoint() text_embeddings = embeddings_model.encode(["这里是你想要编码的文字"]) ``` #### 存储至向量数据库 经过上述两阶段的工作后,现在可以把得到的结果存放到专门设计用来高效查询相似项的结构里去——也就是所谓的向量数据库。这类工具允许用户通过输入一段描述来找到最接近它的其他记录[^2]。 ```python import pinecone pinecone.init(api_key="YOUR_API_KEY", environment="us-west1-gcp") index_name = 'example-index' if index_name not in pinecone.list_indexes(): pinecone.create_index(index_name, dimension=768) index = pinecone.Index(index_name) vectors_to_insert = [(id_, vector) for id_, vector in enumerate(text_embeddings)] index.upsert(vectors=vectors_to_insert) ``` #### 查询接口实现 最后一步是定义好对外的服务端口,使得外部应用能够方便地调用内部功能。一般而言,会采用 RESTful API 或者 GraphQL 这样的协议标准来封装业务逻辑。 ```python from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class QueryRequest(BaseModel): query: str @app.post("/query") async def handle_query(request: QueryRequest): # 处理请求... pass ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值