大型語言模型在回答問題方面表現得相當不錯,但它們有一個很大的局限性:它們無法了解用戶私密的文檔內容。
如果你上傳了像公司政策、研究論文或合同這樣的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文件進行對話吧!
);
}
export default App;
這個界面很簡單,但是非常實用。用戶可以輸入問題,然后將答案顯示出來。建議使用最新版本的React,以避免潛在的漏洞。
整個流程是如何運作的?
當應用程序啟動時,后端已經處理了PDF文件并構建了向量存儲。當用戶輸入問題時,React應用程序會將問題發送到后端。
后端會將問題轉換為嵌入格式,然后在向量存儲中查找類似的塊。這些塊被用作語言的上下文,從而生成答案。
生成的答案會被返回給前端,并顯示給用戶。
為什么這種方法如此有效?
RAG之所以有效,是因為它能夠確保答案基于真實的數據。模型不是憑空猜測的,而是直接從文檔中獲取信息。
此外,這種方法還具有很好的擴展性。你可以添加更多的PDF文件,重新索引它們,并重復使用相同的聊天界面。如果需要的話,還可以將FAISS替換為托管式的向量數據庫。
另一個好處是,你可以控制模型能看到哪些數據。這對于處理私密或敏感的文件來說非常重要。
常見的改進點
你可以以多種方式來改進這個系統。例如,可以將向量存儲持久化到磁盤上,避免每次重啟時都需要重新構建。還可以在答案中添加文檔引用。此外,還可以實現流式響應,以提供更好的聊天體驗。
你還可以添加身份驗證功能,讓用戶通過UI上傳新的PDF文件,或者支持多個用戶共享一份文檔。
總結
使用檢索增強生成技術與PDF文件進行對話是現代語言模型的最實用應用之一。它將靜態文檔轉化為互動式的知識來源。
借助LangChain來處理檢索任務,再加上簡單的React界面來實現交互,你就可以用很少的代碼構建出一個有用的系統。同樣的模式也可以應用于人力資源政策、法律文件、技術手冊或研究論文等領域。
一旦你理解了這個流程,就可以將其應用到許多實際問題中,其中答案需要從可信的文檔中獲取,而不是僅僅來自模型的記憶。

