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

如果你上传了像公司政策、研究论文或合同这样的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 Documents(chunks, 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
)

retriever模块负责找到最匹配的片段,而语言模型则根据这些片段来生成答案。

使用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文件进行对话吧!