思維導圖
📚 引言
大型語言模型(如ChatGPT)雖然功能強大,但它們存在一些明顯的局限性。這些模型的知識庫更新較慢,無法實時學習最新內容,而且對私有數據或特定領域的專業知識了解有限。例如,ChatGPT的知識截止到特定時間點,無法感知用戶本地電腦或內網中的數據。這就是為什么當我們詢問非常具體或專業的內容時,它的回答可能顯得泛泛而談。
那么,如何讓大模型變得更"聰明",能夠獲取最新知識并回答更專業的問題呢?這就是本文要介紹的RAG(檢索增強生成)技術。
🔍 RAG技術概述
什么是RAG?
RAG(Retrieval-Augmented Generation,檢索增強生成)是一種結合檢索和生成兩種技術的方法,旨在幫助計算機更好地理解和回答問題。簡單來說,它讓AI模型能夠在回答問題前先"查閱資料",從而提供更準確、更專業的回答。
RAG的基本流程
RAG技術的典型流程包括:
- 加載數據(Loader):從各種來源加載文檔數據
- 處理文檔(Transform):對文檔進行切割、整理等處理
- 向量化(Embedding):將文本轉換為向量表示
- 存儲(Store):將向量數據存儲在向量數據庫中
- 檢索(Retrieve):根據問題檢索相關文檔片段
- 生成回答(Generate):基于檢索結果生成答案
🔧 LangChain中的RAG實現
Loader:讓大模型具備實時學習的能力
LangChain包裝了各種Loader,使大模型能夠加載各種格式的文檔:
- CSV Loader:加載表格數據
- Directory Loader:加載整個目錄的文件
- HTML Loader:加載網頁內容
- JSON Loader:加載JSON格式數據
- Markdown Loader:加載Markdown文檔
- PDF Loader:加載PDF文件
除此之外,LangChain還支持超過100種不同的數據源接口,包括B站、YouTube、GitHub等平臺的數據。
# 加載Markdown文件示例
from langchain.document_loaders import UnstructuredMarkdownLoaderloader = UnstructuredMarkdownLoader("path/to/file.md")
data = loader.load()# 加載CSV文件示例
from langchain.document_loaders import CSVLoaderloader = CSVLoader("path/to/file.csv")
data = loader.load()# 加載目錄中的文件
from langchain.document_loaders import DirectoryLoaderloader = DirectoryLoader("path/to/directory", glob="**/*.pdf")
data = loader.load()
文檔轉換:切割、總結和翻譯
加載文檔后,通常需要對其進行處理,以便更好地利用文檔內容:
文檔切割
文檔切割的目的是:
- 降低成本,適應大模型的上下文窗口限制
- 將文檔轉換為結構化數據,便于查詢
LangChain提供了多種切割器:
# 字符串分割
from langchain.text_splitter import CharacterTextSplittertext_splitter = CharacterTextSplitter(separator="\n\n",chunk_size=1000,chunk_overlap=200,length_function=len
)
docs = text_splitter.split_documents(documents)# 按代碼分割
from langchain.text_splitter import RecursiveCharacterTextSplitterpython_splitter = RecursiveCharacterTextSplitter.from_language(language="python",chunk_size=1000,chunk_overlap=200
)
文檔總結、精煉和翻譯
LangChain還提供了文檔總結、精煉和翻譯的功能:
# 文檔總結
from langchain.chains.summarize import load_summarize_chain
from langchain_openai import ChatOpenAIllm = ChatOpenAI(temperature=0)
chain = load_summarize_chain(llm, chain_type="map_reduce")
summary = chain.run(docs)# 文檔翻譯
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplatetemplate = """Translate the following text from {source_language} to {target_language}:
{text}"""prompt = PromptTemplate(input_variables=["source_language", "target_language", "text"],template=template
)chain = LLMChain(llm=llm, prompt=prompt)
translated = chain.run(source_language="English", target_language="Chinese", text=text)
解決"Lost in the Middle"問題
研究表明,當處理長文本時,檢索性能會出現"迷失在中間"的現象:當相關信息位于輸入上下文的開頭或結尾時,模型表現最佳;而當相關信息位于中間位置時,即使對于專門設計的上下文模型,性能也會顯著下降。
解決方案包括:
- 通過檢索找到相關文本塊
- 對檢索結果進行重新排序,將最相關的內容放在開頭和結尾
- 使用排序后的文本進行生成
# 排序檢索結果的示例代碼
def reorder_results(results):# 按相關性排序sorted_results = sorted(results, key=lambda x: x.score, reverse=True)# 重新排列,將高相關度的放在頭尾reordered = []for i in range(len(sorted_results)):if i % 2 == 0:reordered.append(sorted_results[i//2])else:reordered.append(sorted_results[len(sorted_results) - 1 - i//2])return reordered
文本向量化
為什么需要文本向量化?主要有兩個原因:
- 純文本形式難以進行語義相關性搜索
- 文本處理效率低且占用空間大
文本向量化(Embedding)將文本轉換為高維向量空間中的點,使得語義相似的文本在向量空間中距離更近。
# 使用OpenAI的文本嵌入模型
from langchain_openai import OpenAIEmbeddingsembeddings = OpenAIEmbeddings()
query_vector = embeddings.embed_query("How does RAG work?")# 使用HuggingFace的文本嵌入模型
from langchain_huggingface import HuggingFaceEmbeddingsembeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
向量數據庫:存儲和檢索
向量數據庫是RAG技術中不可或缺的組成部分,它能夠高效地存儲和檢索高維向量數據。向量數據庫的主要作用包括:
- 管理數據:以原始形式有效管理數據
- 存儲向量:存儲AI需要的高維數據
- 高效檢索:快速找到語義相似的內容
市面上有多種向量數據庫可供選擇:
- Milvus:開源向量數據庫,功能完善,性能強大
- Pinecone:云原生向量數據庫,易于使用
- Chroma:輕量級向量數據庫,適合本地開發
- FAISS:Facebook AI開發的相似性搜索庫
- Qdrant:開源向量搜索引擎
# 使用Chroma向量數據庫
from langchain_community.vectorstores import Chromavectordb = Chroma.from_documents(documents=docs,embedding=embeddings,persist_directory="./chroma_db"
)# 檢索相似文檔
docs = vectordb.similarity_search("What is RAG technology?", k=3)
🛠? 實戰項目:ChatDoc智能文檔助手
下面,我們將學習如何構建一個名為ChatDoc的智能文檔助手,它能夠:
- 加載PDF、DOCX或Excel等格式的文檔
- 對文檔進行適當切分
- 使用OpenAI進行向量化
- 使用Chroma DB實現本地向量存儲
- 實現與文檔的對話功能
實現步驟
1. 初始化ChatDoc類
import os
from langchain.document_loaders import PDFMinerLoader, CSVLoader
from langchain.document_loaders import Docx2txtLoader, UnstructuredExcelLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQAclass ChatDoc:def __init__(self):self.file_path = Noneself.file_content = Noneself.db = Noneself.embeddings = OpenAIEmbeddings()self.llm = ChatOpenAI(temperature=0)
2. 實現文件加載功能
def get_file(self, file_path):self.file_path = file_path_, file_extension = os.path.splitext(file_path)try:if file_extension == '.pdf':loader = PDFMinerLoader(file_path)elif file_extension == '.csv':loader = CSVLoader(file_path)elif file_extension == '.docx':loader = Docx2txtLoader(file_path)elif file_extension == '.xlsx' or file_extension == '.xls':loader = UnstructuredExcelLoader(file_path)else:print(f"不支持的文件格式: {file_extension}")return Noneself.file_content = loader.load()print(f"文件 {file_path} 加載成功!")return self.file_contentexcept Exception as e:print(f"加載文件時出錯: {e}")return None
3. 文本切分與向量化
def separate_and_embed(self):if self.file_content is None:print("沒有加載文件內容,請先加載文件。")return None# 文本切分text_splitter = CharacterTextSplitter(separator="\n",chunk_size=1000,chunk_overlap=200,length_function=len)chunks = text_splitter.split_documents(self.file_content)# 向量化存儲self.db = Chroma.from_documents(documents=chunks,embedding=self.embeddings,persist_directory="./chroma_db")print(f"文本已切分為 {len(chunks)} 個塊并完成向量化存儲。")return self.db
4. 實現基本問答功能
def ask_and_find(self, question):if self.db is None:print("向量數據庫未初始化,請先處理文檔。")return None# 檢索相關文檔docs = self.db.similarity_search(question, k=3)# 構建上下文context = "\n\n".join([doc.page_content for doc in docs])# 創建提示prompt = f"""根據以下信息回答問題:{context}問題: {question}"""# 生成回答response = self.llm.predict(prompt)return response
5. 使用多重查詢優化檢索效果
from langchain.retrievers.multi_query import MultiQueryRetrieverdef improved_ask_and_find(self, question):if self.db is None:print("向量數據庫未初始化,請先處理文檔。")return None# 創建多重查詢檢索器retriever = MultiQueryRetriever.from_llm(retriever=self.db.as_retriever(),llm=self.llm)# 進行檢索docs = retriever.get_relevant_documents(question)# 構建上下文context = "\n\n".join([doc.page_content for doc in docs])# 創建提示prompt = f"""根據以下信息回答問題:{context}問題: {question}"""# 生成回答response = self.llm.predict(prompt)return response
6. 實現對話功能
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChaindef chat_with_doc(self):if self.db is None:print("向量數據庫未初始化,請先處理文檔。")return None# 創建對話記憶memory = ConversationBufferMemory(memory_key="chat_history",return_messages=True)# 創建對話鏈qa_chain = ConversationalRetrievalChain.from_llm(llm=self.llm,retriever=self.db.as_retriever(),memory=memory)# 開始對話print("開始與文檔對話,輸入'exit'退出")while True:question = input("你: ")if question.lower() == 'exit':breakresult = qa_chain({"question": question})print(f"AI: {result['answer']}")
使用示例
# 使用ChatDoc
chat_doc = ChatDoc()# 加載文檔
chat_doc.get_file("example.pdf")# 處理文檔
chat_doc.separate_and_embed()# 問答示例
response = chat_doc.ask_and_find("這個文檔主要講了什么?")
print(response)# 啟動交互式對話
chat_doc.chat_with_doc()
📈 RAG技術的優化方法
多重查詢優化
多重查詢(Multi-query)通過擴展原始問題,從不同角度生成多個相關查詢,然后綜合檢索結果。這種方法可以提高檢索的召回率和準確性。
# 多重查詢示例
from langchain.retrievers.multi_query import MultiQueryRetrieverretriever = MultiQueryRetriever.from_llm(retriever=vectordb.as_retriever(),llm=ChatOpenAI(temperature=0)
)# 原始問題
question = "如何實現RAG技術?"# 生成的多重查詢可能包括:
# 1. "RAG技術的實現步驟是什么?"
# 2. "如何在項目中使用檢索增強生成?"
# 3. "實現RAG需要哪些組件?"
# 4. "RAG的代碼實現示例"
上下文壓縮
上下文壓縮(Context compression)通過篩選和壓縮檢索到的文檔,確保只有最相關的內容被包含在上下文中,從而提高回答質量并降低成本。
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.retrievers import ContextualCompressionRetriever# 創建壓縮器
compressor = LLMChainExtractor.from_llm(llm)# 創建壓縮檢索器
compression_retriever = ContextualCompressionRetriever(base_retriever=vectordb.as_retriever(),doc_compressor=compressor
)# 使用壓縮檢索器獲取文檔
compressed_docs = compression_retriever.get_relevant_documents(query)
基于重排序的優化
針對"迷失在中間"問題,可以對檢索結果進行重新排序,將最相關的文檔放在上下文的開頭和結尾:
def rerank_documents(docs, query):# 計算每個文檔與查詢的相關性分數# 這里可以使用更復雜的相關性評分方法scored_docs = [(doc, doc.metadata.get("score", 0)) for doc in docs]# 按分數排序sorted_docs = sorted(scored_docs, key=lambda x: x[1], reverse=True)# 重新排列文檔,使最相關的在開頭和結尾result = []for i in range(len(sorted_docs)):if i % 2 == 0 and i//2 < len(sorted_docs):result.append(sorted_docs[i//2][0])elif i//2 < len(sorted_docs) // 2:result.append(sorted_docs[len(sorted_docs) - 1 - i//2][0])return result
🌟 總結與展望
通過本文,我們深入學習了RAG技術及其在LangChain中的實現。RAG技術通過將檢索與生成相結合,有效解決了大模型知識時效性和專業性不足的問題,使AI能夠訪問最新的、專業的、私有的信息。
我們詳細探討了RAG的實現步驟:
- 使用各種Loader加載文檔
- 通過文本切割、總結等方法處理文檔
- 將文本向量化并存儲在向量數據庫中
- 根據用戶問題檢索相關文檔
- 基于檢索結果生成回答
同時,我們還介紹了多重查詢、上下文壓縮和重排序等優化技術,以提高RAG系統的性能。
最后,我們通過構建ChatDoc智能文檔助手,將學到的知識應用到實踐中,實現了與任意文檔對話的功能。
未來,隨著大模型和向量數據庫技術的不斷發展,RAG技術將變得更加高效、準確,并在更多領域發揮作用。通過掌握RAG技術,我們可以充分發揮大模型的潛力,構建更智能、更專業的AI應用。
💡 小提示:如果你想要進一步提升RAG系統的性能,可以考慮:
- 嘗試不同的文本嵌入模型
- 優化文檔切割策略
- 使用混合檢索方法
- 引入用戶反饋機制
希望本文能夠幫助你理解和應用RAG技術,構建屬于自己的智能文檔助手!
🔗 參考資料:
- AI Agent智能應用從0到1定制開發
- LangChain官方文檔:https://python.langchain.com/docs/
- “Lost in the Middle: How Language Models Use Long Contexts” 研究論文
- 向量數據庫官方文檔:Milvus、Chroma、Pinecone等