LangChain + BGE模型实现PDF知识库RAG问答

作者:青云 发布时间: 2026-09-06 阅读量:3 评论数:0

一、前言

最近系统学习了 LangChain 最新版本 RAG 检索增强生成 技术,从零实现了一套「PDF 本地知识库问答系统」。

本次实战我完全使用 新版 LangChain(0.3+),摒弃网上大量过时教程,解决了大量新版本兼容报错、模型下载卡死、向量检索失效、Prompt 不生效等经典问题。

本文记录我今日完整学习成果:PDF解析 → 文本分块 → BGE 向量化 → Chroma 向量数据库 → 自定义Prompt → 大模型问答 全套链路。

二、项目技术栈

  • PDF解析:pymupdf(新版fitz)

  • 文本切分:RecursiveCharacterTextSplitter 智能中文分块

  • 嵌入模型:BAAI/bge-small-zh-v1.5(最强开源中文Embedding模型)

  • 向量数据库:Chroma(langchain-chroma 新版独立包)

  • 大模型调用:通义千问 DashScope 兼容 OpenAI 接口

  • 问答链路:RetrievalQA 经典RAG链路(适配新版LangChain)

三、RAG核心原理简述

RAG(检索增强生成)解决的是大模型幻觉、知识滞后、私有数据无法问答的问题。

整体流程:

  1. 文档解析:读取本地PDF全文

  2. 文本分块:长文本切分成适合向量检索的小块

  3. 文本向量化:BGE模型将文本转为向量

  4. 向量入库:存入Chroma向量数据库

  5. 语义检索:用户问题向量匹配最相似文档块

  6. 大模型生成:根据检索到的真实文档,严格依据上下文回答

四、今日重点踩坑总结(全网最实用)

本次实战最大的收获不是代码,而是 新版LangChain 各种坑的彻底解决

1. langchain-community 弃用警告 #674

新版 LangChain 彻底拆分包,Chroma 不再属于 community,必须安装独立包:langchain-chroma,否则持续警告、未来直接报错。

2. HuggingFace 模型下载卡死 / 401 报错

很多人混淆模型仓库:

❌ 错误:sentence-transformers/bge-small-zh-v1.5(401无权限)

✅ 正确:BAAI/bge-small-zh-v1.5

同时必须配置国内镜像或代理,否则 Python 进程无法下载模型。

3. BGE模型必须开启归一化

BGE系列模型官方要求:normalize_embeddings=True

关闭归一化会导致向量相似度错乱,检索完全失效!

4. 文本分块过小导致RAG回答“不知道”

最初我设置 chunk_size=50,文本被切得支离破碎,语义断裂,检索召回残缺片段,LLM 直接触发兜底话术。

✅ 修正:中文最佳参数 chunk_size=600,chunk_overlap=120

5. RetrievalQA 新版调用方式变更

废弃 qa_chain({}),必须使用 qa_chain.invoke({})

6. 自定义Prompt不生效问题

必须在 RetrievalQA 传入 chain_type_kwargs={"prompt": prompt},否则永远使用系统默认提示词,自定义规则完全失效。

五、完整可运行工程代码

以下是我今日最终调试通过、无报错、检索精准的完整代码:

import os
import pymupdf
from langchain_chroma import Chroma
from langchain_core.documents import Document
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_classic.chains.retrieval_qa.base import RetrievalQA
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate

from dotenv import load_dotenv
load_dotenv()  # 读取项目下的.env文件

def extract_text_pdf(pdf, start=1, end=None):
    '''加载pdf文件的内容'''
    text = ''
    page_numbers = []
    for pn, page in enumerate(pdf):
        if pn < start:  # 跳过前几页
            continue
        if end is not None and pn == end:  # 控制读取的页数
            break
        extract_text = page.get_text("text")
        if extract_text:
            text += extract_text
            page_numbers.extend([pn] * len(extract_text.split('\n')))
    return text, page_numbers


# 线上加载BGE中文嵌入模型(新版标准写法)
embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-small-zh-v1.5",
    model_kwargs={'device': 'cpu'},
    encode_kwargs={'normalize_embeddings': True}  # BGE必须开启归一化
)
print("✅线上模型加载完成")


# 通义千问大模型初始化(兼容OpenAI接口)
llm = ChatOpenAI(
    model=os.getenv("DASHSCOPE_MODEL"),
    base_url=os.getenv("DASHSCOPE_BASE_URL"),
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    temperature=0,
)


# 自定义RAG提示词(严格限制只能根据文档回答)
prompt_template = '''
        你是一个问答机器人。
        你的任务是根据下述给定的已知信息回答用户问题。
        
        已知信息:
        {context} # 检索出来的原始文档
        
        用户问题:
        {question} # 用户的问题
        
        如果已知信息中不包含用户问题的答案,或者已知信息无法回答用户问题,请直接返回"俺不知道!你找别的人吧!"。
        请不要输出已知信息中不包含的信息或者答案。
        请用中文回答用户问题。
    '''

prompt = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)


if __name__ == "__main__":
    # 1.读取PDF
    pdf = pymupdf.open('43-显存优化策略篇.pdf')
    text, page_numbers = extract_text_pdf(pdf)

    # 2.封装文档对象
    documents = [Document(page_content=text,
                          metadata={'source': '43-显存优化策略篇.pdf'})]

    # 3.中文智能分块(最优参数)
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=600,
        chunk_overlap=120,
        separators=["\n\n", "\n", "。", ",", "!", "?"]
    )
    splits = text_splitter.split_documents(documents)
    print(f"分块总数量:{len(splits)}")

    # 4.向量化并入库
    db = Chroma.from_documents(
        documents=splits,
        embedding=embeddings,
        persist_directory="./chroma_db4"
    )

    # 5.构建RAG问答链
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        retriever=db.as_retriever(search_kwargs={"k": 3}),
        return_source_documents=True,
        chain_type_kwargs={'prompt': prompt}
    )

    # 6.提问测试
    query = "介绍一下 gradient checkpointing 显存优化方式?"
    result = qa_chain.invoke({'query': query})
    print('答案: ', result['result'])

    # 打印检索上下文,用于调试
    print("\n=======召回的文档片段======")
    for idx, doc in enumerate(result["source_documents"]):
        print(f"【片段{idx}】\n{doc.page_content}\n")

六、核心代码解析

1. PDF文本解析

使用 pymupdf 替代废弃的 fitz,逐页提取文本,支持自定义跳过前页、截止页,适配教程PDF、书籍PDF。

2. BGE嵌入模型加载

采用工业级中文向量模型 bge-small-zh-v1.5,开启归一化,保证语义相似度计算精准。

3. 文本分块策略

针对中文句号、逗号、换行进行切割,配合重叠分块,解决知识点被切割断裂问题,是RAG效果好坏的核心关键。

4. 自定义Prompt约束

强制模型只能基于检索到的文档回答,无对应知识直接兜底回复,彻底杜绝大模型幻觉。

5. 多路检索增强

设置 k=3,一次召回3条相似文本,丰富上下文,大幅提升问答准确率。

七、最终效果

✅ 成功解析 PDF 显存优化技术文档

✅ 精准召回 gradient checkpointing 梯度检查点原理文档

✅ 严格依据文档内容回答,无幻觉

✅ 未知问题自动兜底,符合预期

✅ 完全适配最新 LangChain 版本,无弃用报错

八、学习总结

本次实战让我彻底吃透了 新版LangChain RAG 完整落地流程,不再被过时教程误导。

RAG 项目效果好坏,模型权重占20%,文本分块+检索策略+Prompt工程占80%

后续可以继续扩展:

  • 自动判断向量库是否存在,避免重复入库

  • 接入本地 Ollama 开源大模型,完全离线部署

  • 实现对话记忆、多轮问答

  • 支持多PDF文件知识库

九、环境依赖安装命令

pip install langchain langchain-core langchain-text-splitters langchain-community langchain-chroma langchain-huggingface langchain-classic chromadb pymupdf sentence-transformers transformers torch python-dotenv langchain-openai

评论