大型語言模型在回答問題方面表現得相當不錯,但它們有一個很大的局限性:它們無法了解用戶私密的文檔內容。

如果你上傳了像公司政策、研究論文或合同這樣的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文件進行對話吧!