搜尋結果

×

LangChain RAG進階教學,結合 Ollama 和 Chroma 持久化向量資料庫,讓LLM讀取PDF文件,並分析產生回應

實現一個基於 LangChain 的檢索增強生成(RAG,Retrieval-Augmented Generation)模型,讓你可以對 PDF 文檔進行智能問答,並支援動態更新數據庫

本教學好崴寶Weibert將帶你一步步實現一個基於 LangChain 的檢索增強生成(RAG,Retrieval-Augmented Generation)模型,讓你可以對 PDF 文檔進行智能問答,並支援動態更新數據庫。

需要知識

需要文件下載

本文是使用到裡面的langchain_rag_pdf_chroma_advanced.py檔案。

教學目標

實現功能

  • 載入 PDF 文檔。
  • 將 PDF 文檔分割為小段進行嵌入生成。
  • 構建向量數據庫(Vector Database)來支持快速檢索。
  • 使用檢索增強生成(RAG)模型回答用戶問題。
  • 動態檢測 PDF 更新,自動重建數據庫,保證查詢內容的準確性。

技術架構

使用工具與框架

  • LangChain:構建檢索與生成的核心框架。
  • Chroma 向量數據庫:用於存儲和檢索嵌入向量。
  • Ollama LLM:生成自然語言回應。
  • PDF 處理與文本分割:提取 PDF 文件中的文本並分段存儲。

成果展示

root@4be643ba6a94:/app# python3 langchain_rag_pdf_chroma_advanced.py
---PDF 文件已更新,正在重新生成數據庫...
---正在刪除舊的向量數據庫...
---正在載入文件,請稍候...
---PDF 加載完成,共 3 頁
第 1 頁內容(部分):
崴寶  Weibert Weiberson - AI 軟體工程師
關於崴寶
🎓 崴寶,資訊科
第 2 頁內容(部分):
 Instagram : @weibert_coding
 YouTube : Well崴寶
第 3 頁內容(部分):
工具與技術
 Git 版本控制
 Docker
 Linux / Ubuntu

---正在分割文本...
---分割後的文檔數量: 3
第一段內容(部分):
崴寶  Weibert Weiberson - AI 軟體工程師
關於崴寶
🎓 崴寶,資訊科

---正在生成向量數據庫...
/app/langchain_agents_React-docstore.py:87: LangChainDeprecationWarning: Since Chroma 0.4.x the manual persistence method is no longer supported as docs are automatically persisted.
vectorstore.persist()
---向量數據庫已保存!

請輸入查詢內容(輸入 'bye' 退出):崴寶的Threads帳號是?
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
@weibert_coding

請輸入查詢內容(輸入 'bye' 退出):崴寶的專長?
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
崴寶的專長包括:

1. NLP(自然語言處理):熟悉 LangChain、RAG、HuggingFace、BERT 等技術。
2. CV(電腦視覺):精通 OpenCV、PyTorch、YOLOR / YOLOv7、PRB-FPN 和 
Attention Mechanism 等工具和技術。

請輸入查詢內容(輸入 'bye' 退出):介紹一下崴寶
Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
崴寶(Weibert Weiberson)是一位 AI 軟體工程師,目前在台灣某上市公司擔任。
他具有資訊科學碩士學歷,並涉足多個領域,如電腦視覺、機器學習、大型語言模型、手機遊戲開發、
網站架設和 APP 開發等。崴寶的使命是攜手程式設計愛好者一起學習、成長,實現他們的程式夢想。

他在社群媒體上提供各種工具與技術,如 Git 版本控制、Docker、Linux / Ubuntu、
SQLite, MySQL, Firebase, NoSQL 等。他也是一位活躍的 YouTuber,
在頻道「Weibert好崴寶程式」分享更多程式教學影片。
崴寶擅長 NLP(自然語言處理)和 CV(電腦視覺),
使用了 LangChain、RAG、HuggingFace、BERT 等工具。
他是一位多才多藝的程式設計師,對於想要學習程式的人來說,是個很好的夥伴。

前置條件

  1. 確保本地環境已安裝 Ollama
  2. 下載所需的 LLM 模型,例如:kenneth85/llama-3-taiwan:8b-instruct
  3. 準備 PDF 測試文檔,將其命名為 pdf_test.pdf 並放置於程式的根目錄。

本文有在GitHub放了我寫的PDF測試檔案叫做pdf_test.pdf,大家也可以拿來測試用。

1. 初始化環境

首先,我們需要初始化 LangChain 中的主要組件:

  • Ollama:作為生成模型(LLM)。

  • OllamaEmbeddings:生成向量嵌入,用於文本檢索匹配。

  • Chroma:用於存儲和檢索嵌入向量。

      from langchain_community.llms import Ollama
      from langchain_community.embeddings import OllamaEmbeddings
      from langchain.vectorstores import Chroma
      from langchain.callbacks.manager import CallbackManager
      from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
    
      # 初始化 Callback Manager
      callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
    
      # 初始化 Ollama LLM
      llm = Ollama(
          model="kenneth85/llama-3-taiwan:8b-instruct",
          callback_manager=callback_manager
      )
    
      # 初始化 Ollama Embeddings
      embeddings = OllamaEmbeddings(model="kenneth85/llama-3-taiwan:8b-instruct")
    

2. 設置文件與向量數據庫存儲路徑

我們需要指定 PDF 文件的路徑和向量數據庫的存儲目錄,並設置一個哈希文件用於檢測 PDF 文件的變更。

  • 指定 PDF 文件的路徑和向量數據庫的存儲目錄。

      import hashlib
      import os
    
      # PDF 文件路徑
      pdf_path = "pdf_test.pdf"
    
      # 向量資料庫存儲目錄
      persist_directory = "chroma_vectorstore"
    
      # 哈希文件存儲路徑
      hash_file_path = os.path.join(persist_directory, "pdf_hash.txt")
    

為了檢測文件是否發生變更,我們計算 PDF 文件的 MD5 哈希值。

  • 設置哈希文件,用於檢測 PDF 文件的變更。

      def calculate_file_hash(file_path):
          hasher = hashlib.md5()
          with open(file_path, "rb") as f:
              buf = f.read()
              hasher.update(buf)
          return hasher.hexdigest()
    

3. 檢測 PDF 文件變更

通過比對當前 PDF 文件的哈希值和之前保存的哈希值,來確定是否需要更新向量數據庫。

  • 計算 PDF 文件的 MD5 哈希值。

  • 若哈希值與記錄不符,表示文件內容已更改,需要更新數據庫。

      # 檢查是否需要更新數據庫
      def needs_update(pdf_path, hash_file_path):
          new_hash = calculate_file_hash(pdf_path)
          if not os.path.exists(hash_file_path):
              return True, new_hash
          with open(hash_file_path, "r") as f:
              existing_hash = f.read().strip()
          return new_hash != existing_hash, new_hash
    
      update_required, new_hash = needs_update(pdf_path, hash_file_path)
    

4:生成向量數據庫

文件變更檢測與數據庫更新

  1. 如果檢測到 PDF 文件有變更,則重新生成向量數據庫。
  2. 如果文件無變更,則直接加載現有的數據庫。

清理舊數據庫

  • 若 PDF 文件有更新,需要刪除舊數據庫以保證內容一致性。

  • 刪除舊數據庫後,重新生成新的向量數據庫。

      if os.path.exists(persist_directory) and not update_required:
          print("---正在加載現有的向量數據庫...")
          vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
      else:
          print("---PDF 文件已更新,正在重新生成數據庫...")
    
          # 清理舊數據庫
          if os.path.exists(persist_directory):
              print("---正在刪除舊的向量數據庫...")
              import shutil
              shutil.rmtree(persist_directory)
    

載入 PDF 並分割文本

  1. 提取 PDF 文本
    使用 PyPDFLoader 從 PDF 文件中提取文本。

  2. 分割文本
    使用 RecursiveCharacterTextSplitter 按指定大小和重疊率分割文本:

    • chunk_size=500
    • chunk_overlap=50
  3. 完成後,記錄分割的段數以供後續使用。

     from langchain.document_loaders import PyPDFLoader
     from langchain.text_splitter import RecursiveCharacterTextSplitter
    
     # 步驟 1:載入 PDF
     loader = PyPDFLoader(pdf_path)
     documents = loader.load()
    
     # 步驟 2:分割文本
     text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
     docs = text_splitter.split_documents(documents)
    
     print(f"---PDF 分割完成,共 {len(docs)} 段")
    

生成並持久化向量數據庫

  1. 將分割後的文本轉換為向量。

  2. 使用 Chroma 持久化數據庫,存儲生成的向量。

  3. 保存新的 PDF 哈希值,以便下次對比文件變更。

     from langchain.vectorstores import Chroma
    
     # 步驟 3:生成向量數據庫
     vectorstore = Chroma.from_documents(docs, embeddings, persist_directory=persist_directory)
     vectorstore.persist()
    
     # 保存新的 PDF 哈希值
     with open(hash_file_path, "w") as f:
         f.write(new_hash)
    

5:構建檢索問答鏈

利用生成模型(LLM)和檢索數據庫構建檢索問答鏈:

  • 將數據庫設為檢索器。

  • 通過 RetrievalQA 模組,實現檢索增強生成(RAG)的功能。

      from langchain.chains import RetrievalQA
    
      retriever = vectorstore.as_retriever()
      qa_chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever)
    

6:提供查詢功能

  1. 提供一個互動式查詢介面,讓用戶輸入問題進行查詢。
  2. 功能描述:
    • 用戶輸入查詢內容。

    • 系統檢索相關內容並生成答案。

    • 當用戶輸入 bye,系統退出。

         # 提供查詢功能
         while True:
             query = input("請輸入查詢內容(輸入 'bye' 退出):")
             if query.lower() == "bye":
                 print("已退出查詢系統。")
                 break
             result = qa_chain.invoke(query)
             print(f"查詢結果:\n{result}")
      

完整流程

1. 檢測 PDF 文件變更

  • 若文件有更新,刪除舊數據庫並重新生成。
  • 若無變更,直接加載現有數據庫。

2. 生成向量數據庫

  • 分割 PDF 文本。
  • 生成嵌入向量並存儲於數據庫。

3. 檢索與生成

  • 基於用戶查詢,檢索相關內容並生成答案。

崴寶結語

之前崴寶教導大家的基礎 RAG 是使用 Faiss 向量數據庫(詳見:LangChain RAG實作教學,結合Llama3讓LLM可以讀取PDF和DOC文件,並產生回應)。

本次進階教學則使用 Chroma 向量數據庫,並藉由哈希值來達成動態檢測 PDF 文件更新,自動重建Vector Database

    喜歡 好崴寶 Weibert Weiberson 的文章嗎?在這裡留下你的評論!本留言區支援 Markdown 語法