大型语言模型在回答问题方面表现得相当不错,但它们有一个很大的局限性:它们无法了解用户私密的文档内容。

如果你上传了像公司政策、研究论文或合同这样的PDF文件,那么模型就无法自动读取这些内容,除非你直接将其内容提供给模型。

这时,检索增强生成,也就是RAG,就派上用场了。

RAG可以将语言模型与用户自己的数据结合起来使用。它不是让模型自行猜测答案,而是先从文档中提取出相关的信息,然后再利用这些信息来生成答案。

在这篇文章中,你将学习如何使用RAG与自己的PDF文件进行交互。你将使用LangChain来构建后端服务,并创建一个简单的React用户界面,以便用户可以提出问题并获取答案。

你需要具备基本的Python和JavaScript技能,同时还需要熟悉React和REST API。虽然对语言模型的了解以及向量搜索的掌握并不是必需的,但会很有帮助。

我们解决的是什么问题?

想象一下,你有一份包含数百页内容的PDF文件。手动搜索效率很低,而把文本复制粘贴到ChatGPT中也不现实。

你可以提出一些简单的问题,比如“公司的休假政策是什么?”或者“这份合同中关于离职的规定是什么?”

普通的语言模型无法正确回答这些问题,因为它们从未见过你的PDF文件。而RAG通过在生成答案之前先进行检索处理来解决这个问题。

系统首先找到PDF文件中相关的内容,然后将这些内容作为答案的上下文来辅助模型生成答案。

什么是检索增强生成?

检索增强生成是一种包含三个主要步骤的方法。

首先,将文档分割成小块。每个小块都被转换为向量嵌入形式,这些嵌入被存储在向量数据库中。

当用户提问时,这个问题也会被转换成嵌入形式。系统会在向量数据库中进行搜索,找出最相似的匹配项。

然后,这些匹配项会被一起发送给语言模型。模型仅基于这些上下文来生成答案。

这种方法使得答案更加真实可靠,减少了幻觉现象的发生。

该系统由四个主要部分组成:

  • PDF加载器负责读取文档。
  • 文本分割器将文档分割成适当大小的小块。
  • 嵌入模型将文本转换为向量,并将其存储到向量库中。
  • 语言模型则根据提取出的内容来生成答案。

前端是一个简单的React界面,它可以将用户的提问发送到后端API,并显示结果。

这种自定义的RAG开发方式可以帮助企业构建能够处理自己私有数据的内部工具,而不需要将其发送给大型语言模型。

使用LangChain搭建后端

我们将使用Python和LangChain来构建后端。后端将负责加载PDF文件、创建向量库,并提供一个API来回答问题。

安装依赖库

首先安装所需的库。

pip install langchain langchain-community langchain-openai faiss-cpu pypdf fastapi uvicorn

这个设置中使用FAISS作为本地向量存储,而OpenAI则用于嵌入处理和聊天功能。之后,你可以更换其他模型来满足需求。

加载和分割PDF文件

第一步是加载PDF文件,并将其分割成足够小的块,以便于进行嵌入处理。


from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

loader = PyPDFLoader("document.pdf")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = text_splitter.split_documents(documents)

分块处理非常重要。如果块太大,嵌入效果就会降低;如果块太小,则会丢失上下文信息。

创建嵌入模型和向量存储

接下来,将各个块转换为嵌入格式,并将其存储在FAISS中。


from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_document_chunks(embeddings), embeddings)

这一步通常只需要执行一次。在实际应用中,需要将向量存储持久化到磁盘上。

创建检索链

现在可以创建一个基于检索的问答链。


from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

llm = ChatOpenAI(temperature=0, model="gpt-4o-mini")
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=vectorstore.as_retriever(searchkwargs={"k": 4}),
    return_source Documents=False)

检索器会找到最匹配的块,而语言模型则仅基于这些块来生成答案。

使用FastAPI提供API接口

现在,可以将这一逻辑封装成API接口,这样React应用程序就可以直接使用它了。


from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class QuestionRequest(BaseModel):
    question: str

@app.post("/ask"),
def ask_question(req: QuestionRequest):
    result = qa_chain.run(req.question)
    return {"answer": result}

可以使用以下命令运行服务器:
“`
uvicorn main:app –reload
“`
这样,你的后端就准备好了。

构建简单的React聊天界面

接下来,可以构建一个简单的React界面,用于将问题发送到后端,并显示答案。

你可以使用任何版本的React来进行开发。简单的Vite或Create React App项目都足够了。

在主组件中,可以管理问题的输入和答案的状态。


import { useState } from "react";

function App() {
  const [question, setQuestion] = useState("");
  const [answer, setAnswer] = useState("");
  const [loading, setLoading] = useState(false);

  async function askQuestion() {
    setLoading(true);
    const res = await fetch("http://localhost:8000/ask", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ question })
    });
    const data = await res.json();
    setAnswer(data.answer);
    setLoading(false);
  }

  return (
    

与您的PDF文件进行对话吧!