RAG檔案QA應用 ./research paper app_huggingfaceembedding.py main.py Groq is Fast AI Inference 這邊的應用我們會開始使用groq來串接LLMs做一些inference推論。 1.app_huggingfaceembedding.py 程式碼詳解 1. 加載必要的庫和模組 將所需的庫和模組導入,包括 Streamlit、LangChain 的相關模組等。 2. 加載環境變數 使用 dotenv 加載 OpenAI 的 API 金鑰。 3. 初始化嵌入模型和語言模型 使用 HuggingFace 的嵌入模型來將文本轉換為向量。 初始化語言模型(此處使用 OpenAI 的模型,可替換為您使用的 LLM)。 4. 定義提示模板 使用 PromptTemplate 定義模型生成回答時的提示。 5. 創建向量嵌入函數 加載 PDF 文檔並將其拆分為較小的段落。 使用嵌入模型將文本轉換為向量,並使用 FAISS 創建向量資料庫。 將向量資料庫和文檔存儲在 st.session_state 中,以便在會話中持久化。 6. 設置 Streamlit 用戶界面 設置應用的標題和輸入框,讓用戶輸入查詢問題。 提供一個按鈕來生成文檔嵌入。 7. 處理用戶查詢 檢查向量資料庫是否已經創建。 創建檢索器,然後使用 RetrievalQA 建立檢索問答鏈。 chain_type="stuff" 表示使用 "stuff" 方法來合併文檔。 prompt 使用我們之前定義的提示模板。 計算回答所花費的時間,並顯示回答結果。 使用 st.expander 展開區域,顯示相關的文檔內容。 8. 關於 RetrievalQA 和 chain_type RetrievalQA:這是 LangChain 提供的一個方便的類,用於結合檢索器和問答鏈。它接受一個語言模型、檢索器以及其他參數,並在用戶輸入查詢時,先檢索相關文檔,再使用語言模型生成答案。 chain_type:在 RetrievalQA.from_chain_type 中,chain_type 參數指定了如何處理檢索到的文檔。 "stuff":將所有檢索到的文檔內容「塞入」到提示中,適合於文檔較短的情況。 "map_reduce"、"refine" 等其他選項可以處理較長的文檔,但可能需要更多的計算資源。 1. create_stuff_documents_chain 的用途 create_stuff_documents_chain 是用於創建一個將多個文檔整合(或「填充」)到一起的鏈,然後傳遞給語言模型進行處理。這個函數通常用於將檢索到的相關文檔合併,並讓語言模型基於這些文檔生成回答。 2. create_retrieval_chain 的用途 create_retrieval_chain 是用於建立一個檢索鏈,該鏈結合了檢索器和文件處理鏈。當用戶輸入問題時,檢索器會根據問題檢索相關的文檔,然後將這些文檔傳遞給文件處理鏈(由 create_stuff_documents_chain 創建)進行回答生成。 import streamlit as st # 引入 Streamlit 用來建置網頁應用程式 import os # 操作系統相關功能,用來存取環境變數 from langchain_groq import ChatGroq # 引入 Groq 模型來進行語言生成 from langchain_openai import OpenAIEmbeddings # 引入 OpenAI 的嵌入向量工具 from langchain_community.embeddings import OllamaEmbeddings # 引入 Ollama 的嵌入向量工具 from langchain.text_splitter import RecursiveCharacterTextSplitter # 引入文字分割器,用來將文件分割成可處理的部分 from langchain.chains.combine_documents import create_stuff_documents_chain # 用來建立多文件處理鏈的工具 from langchain_core.prompts import ChatPromptTemplate # 引入提示模板,用來設計 AI 的輸入格式 from langchain.chains import create_retrieval_chain # 創建文件檢索鏈的功能 from langchain_community.vectorstores import FAISS # 引入 FAISS,用來進行向量檢索 from langchain_community.document_loaders import PyPDFDirectoryLoader # 引入 PDF 加載器,從目錄中讀取 PDF 文件 import openai # 引入 OpenAI API 相關功能 from dotenv import load_dotenv # 用來加載 .env 檔案中的環境變數 load_dotenv(dotenv_path="") # 加載 .env 檔案 ## 加載 Groq API Key,從環境變數中取得 OpenAI API 和 Groq API 金鑰 os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY") os.environ['GROQ_API_KEY'] = os.getenv("GROQ_API_KEY") groq_api_key = os.getenv("GROQ_API_KEY") ## 如果你沒有 OpenAI 的 API Key,可以使用 HuggingFace 的嵌入向量 os.environ['HF_TOKEN'] = os.getenv("HF_TOKEN") # Huggingface 的 API 金鑰 from langchain_huggingface import HuggingFaceEmbeddings # 引入 HuggingFace 的嵌入向量工具 embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") # 使用 HuggingFace 提供的嵌入模型 # 初始化 Groq 模型,這裡使用 Llama3-8b 模型,支援多達 8192 個 token llm = ChatGroq(groq_api_key=groq_api_key, model_name="Llama3-8b-8192") # 設置 AI 回答問題的提示模板,基於上下文來提供準確的回答 prompt = ChatPromptTemplate.from_template( """ 請根據提供的上下文回答問題。 只能根據上下文提供最準確的回應: ###draft_code_symbol_lessthen###context> {context} ###draft_code_symbol_lessthen###context> 問題:{input} """ ) # 建立向量嵌入的功能,將文檔資料轉換成可檢索的向量數據 def create_vector_embedding(): if "vectors" not in st.session_state: # 檢查是否已經建立向量嵌入 # 初始化嵌入模型 st.session_state.embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") # 載入 PDF 檔案,這裡從名為 "research_papers" 的目錄中讀取所有的 PDF 文件 st.session_state.loader = PyPDFDirectoryLoader("research_papers") # 資料讀取步驟 st.session_state.docs = st.session_state.loader.load() # 文件加載步驟 # 將文件分割成較小的部分,方便後續的處理。這裡設置每個塊大小為 1000 個字符,且每個塊之間會有 200 個字符的重疊 st.session_state.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) # 將加載的前 50 份文件分割處理 st.session_state.final_documents = st.session_state.text_splitter.split_documents(st.session_state.docs[:50]) # 將分割好的文檔轉換成向量,並存儲在 FAISS 向量檢索系統中 st.session_state.vectors = FAISS.from_documents(st.session_state.final_documents, st.session_state.embeddings) # 設置網頁標題 st.title("使用 Groq 和 Llama3 的 RAG 文件問答系統") # 使用者輸入問題的地方,讓他們可以基於已上傳的研究文件進行問題詢問 user_prompt = st.text_input("請輸入你想從研究文件中查詢的問題") # 文件嵌入的按鈕,當按下後,會啟動 create_vector_embedding 函數來處理向量化過程 if st.button("建立文件向量嵌入"): create_vector_embedding() # 執行向量嵌入創建步驟 st.write("向量資料庫已準備就緒") # 回傳一個訊息表示向量資料庫已經準備好 import time # 引入時間模組,用來測量回應時間 # 當使用者輸入問題後,系統將執行檢索和回答步驟 if user_prompt: # 創建文件處理鏈,將 LLM 與提示模板結合起來 document_chain = create_stuff_documents_chain(llm, prompt) # 從向量中檢索相關文件的檢索器 retriever = st.session_state.vectors.as_retriever() # 創建一個檢索鏈,結合檢索器與文件處理鏈來回答問題 retrieval_chain = create_retrieval_chain(retriever, document_chain) # 計算回應所花費的時間 start = time.process_time() # 針對使用者的問題進行檢索與生成答案 response = retrieval_chain.invoke({'input': user_prompt}) print(f"回應時間: {time.process_time() - start}") # 打印出回應所需的時間 # 將回應顯示在網頁上 st.write(response['answer']) ## 展示文件的相似性檢索結果 with st.expander("文件相似性檢索結果"): # 顯示檢索到的文件內容,並列出每份文件的頁面內容 for i, doc in enumerate(response['context']): st.write(doc.page_content) # 顯示每份文件的內容 st.write('------------------------') # 分隔線 2.main.py 這段程式碼與之前的版本相比,主要差異在於: 使用了 OpenAIEmbeddings 替代 HuggingFaceEmbeddings 來處理向量嵌入,這意味著嵌入模型的來源不同。 刪除了 Hugging Face 的環境變數配置部分(HF_TOKEN),因此只需使用 OpenAI 和 Groq 的 API 金鑰。 其餘部分功能幾乎相同,都是用來實現文件問答系統。 import streamlit as st # 引入 Streamlit 來建立網頁應用程式 import os # 引入操作系統相關功能,主要用來存取環境變數 from langchain_groq import ChatGroq # 引入 Groq 模型,用於生成大型語言模型 from langchain_openai import OpenAIEmbeddings # 引入 OpenAI 提供的嵌入向量工具 from langchain_community.embeddings import OllamaEmbeddings # 引入 Ollama 的嵌入向量工具(這裡實際上未使用) from langchain.text_splitter import RecursiveCharacterTextSplitter # 引入文字分割器,將文檔分割成較小的部分進行處理 from langchain.chains.combine_documents import create_stuff_documents_chain # 用來將文檔資料結合成文件處理鏈的工具 from langchain_core.prompts import ChatPromptTemplate # 用來設計 AI 生成回應的提示模板 from langchain.chains import create_retrieval_chain # 用於創建文件檢索與回應生成的功能鏈 from langchain_community.vectorstores import FAISS # 引入 FAISS 向量檢索庫,用來儲存與檢索嵌入向量 from langchain_community.document_loaders import PyPDFDirectoryLoader # 用來讀取目錄中的 PDF 文件 import openai # 引入 OpenAI 的 API,提供語言模型服務 from dotenv import load_dotenv # 用來讀取 .env 檔案中的環境變數 load_dotenv() # 加載 .env 檔案中的環境變數 ## 加載 Groq API 金鑰,從 .env 檔案中讀取 OpenAI 與 Groq API 金鑰 os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY") # 讀取並設置 OpenAI API 金鑰 os.environ['GROQ_API_KEY'] = os.getenv("GROQ_API_KEY") # 讀取並設置 Groq API 金鑰 groq_api_key = os.getenv("GROQ_API_KEY") # 從環境變數中取得 Groq API 金鑰 # 初始化 Groq 模型,這裡使用 "Llama3-8b-8192" 模型,支持多達 8192 個 token 的輸入 llm = ChatGroq(groq_api_key=groq_api_key, model_name="Llama3-8b-8192") # 設計提示模板,讓 AI 根據上下文回答問題,模板內部結構確保 AI 僅根據提供的上下文做出回應 prompt = ChatPromptTemplate.from_template( """ 請根據提供的上下文回答問題。 只能根據上下文提供最準確的回應: ###draft_code_symbol_lessthen###context> {context} ###draft_code_symbol_lessthen###context> 問題:{input} """ ) # 這是一個函數,用來創建向量嵌入,將文件資料轉換為可檢索的向量 def create_vector_embedding(): if "vectors" not in st.session_state: # 檢查是否已經建立了向量嵌入 # 使用 OpenAI 的嵌入工具,初始化嵌入模型 st.session_state.embeddings = OpenAIEmbeddings() # 使用 PyPDFDirectoryLoader 從名為 "research_papers" 的資料夾中讀取 PDF 文件 st.session_state.loader = PyPDFDirectoryLoader("research_papers") # 資料導入步驟 st.session_state.docs = st.session_state.loader.load() # 加載文件 # 使用文字分割器將文檔分割成較小的塊,塊大小設為 1000 個字符,重疊部分為 200 個字符 st.session_state.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) # 分割前 50 份文檔,並轉換成可處理的文件結構 st.session_state.final_documents = st.session_state.text_splitter.split_documents(st.session_state.docs[:50]) # 使用 FAISS 向量檢索庫將文檔轉換為向量,存儲於向量資料庫中 st.session_state.vectors = FAISS.from_documents(st.session_state.final_documents, st.session_state.embeddings) # 設定 Streamlit 的頁面標題,讓使用者知道這是一個 RAG 文件問答系統 st.title("使用 Groq 和 Lama3 的 RAG 文件問答系統") # 使用者可以在這裡輸入他們想從研究文件中查詢的問題 user_prompt = st.text_input("請輸入你想從研究文件中查詢的問題") # 文件嵌入按鈕,當使用者按下此按鈕時,會啟動 `create_vector_embedding` 函數,建立文件的向量化過程 if st.button("建立文件向量嵌入"): create_vector_embedding() # 創建文件向量嵌入 st.write("向量資料庫已準備就緒") # 顯示向量資料庫已建立的提示訊息 import time # 引入時間模組,用來測量回應所花費的時間 # 當使用者輸入查詢的問題時,會執行檢索與生成答案的流程 if user_prompt: # 創建文件處理鏈,結合 LLM 與提示模板,用於生成對話答案 document_chain = create_stuff_documents_chain(llm, prompt) # 從向量資料庫中檢索相關的文檔 retriever = st.session_state.vectors.as_retriever() # 創建檢索鏈,將檢索結果與文件處理鏈結合起來 retrieval_chain = create_retrieval_chain(retriever, document_chain) # 計算回應時間,開始計時 start = time.process_time() # 調用檢索鏈來處理使用者的查詢,並生成答案 response = retrieval_chain.invoke({'input': user_prompt}) print(f"回應時間: {time.process_time() - start}") # 打印回應所需的時間 # 在頁面上顯示 AI 回應的答案 st.write(response['answer']) # 展示文件的相似性檢索結果 with st.expander("文件相似性檢索結果"): # 顯示檢索到的相關文件內容,並將每份文件顯示出來 for i, doc in enumerate(response['context']): st.write(doc.page_content) # 顯示每份文件的內容 st.write('------------------------') # 分隔線 RAG QA 聊天模式conversation ./app.py ./temp.pdf app.py這段程式的目的是構建一個具有檢索增強生成 (RAG) 能力的問答系統,使用者可以上傳 PDF 檔案,並基於這些文檔與聊天歷史進行對話式問答。以下是程式的詳細解釋和台灣語系的註解: 程式運作流程: 上傳 PDF 檔案:使用者可以上傳一個或多個 PDF 文件,系統會讀取這些文件,並將其轉換為可檢索的文件結構。 聊天記錄管理:透過 session ID,管理每個 session 的聊天歷史,確保每次提問時都能保留之前的上下文。 問題檢索與生成答案:系統根據使用者的問題以及聊天記錄中的上下文,檢索相關的文檔,並生成精簡的答案。 會話式 RAG:系統結合聊天記錄和最新的檢索結果來回答問題,並顯示完整的聊天歷史。 註解重點: 使用了 Groq 的模型來進行語言生成。 使用 Chroma 向量檢索來高效處理 PDF 文件的內容。 系統根據歷史聊天記錄來生成更符合上下文的答案,且允許使用者進行多次互動。 import streamlit as st # 引入 Streamlit 來構建網頁應用程式 from langchain.chains import create_history_aware_retriever, create_retrieval_chain # 用於建立包含歷史記錄的檢索鏈 from langchain.chains.combine_documents import create_stuff_documents_chain # 文件處理鏈,用於回答問題 from langchain_chroma import Chroma # 引入 Chroma,作為向量資料庫來儲存與檢索嵌入向量 from langchain_community.chat_message_histories import ChatMessageHistory # 管理聊天記錄的功能 from langchain_core.chat_history import BaseChatMessageHistory # 聊天記錄的基本類別 from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder # 用於設計提示模板與占位符 from langchain_groq import ChatGroq # 引入 Groq 模型,用於進行生成式問答 from langchain_core.runnables.history import RunnableWithMessageHistory # 引入可執行的歷史記錄運行工具 from langchain_huggingface import HuggingFaceEmbeddings # 使用 Hugging Face 的嵌入向量模型 from langchain_text_splitters import RecursiveCharacterTextSplitter # 文字分割工具,將文件分割為較小塊以便處理 from langchain_community.document_loaders import PyPDFLoader # 用來讀取 PDF 檔案 import os # 用來操作系統功能(如讀取環境變數) from dotenv import load_dotenv # 用來載入 .env 檔案中的環境變數 load_dotenv(dotenv_path="/Users/lo-ai/github_items/Langchain_course_code/.env") # 設定 .env 檔案路徑,讀取其中的變數 # 從 .env 檔案中載入 Hugging Face 的 API 金鑰,並初始化嵌入向量模型 os.environ['HF_TOKEN'] = os.getenv("HF_TOKEN") embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") # 使用 Hugging Face 的 all-MiniLM 模型 ## 建立 Streamlit 的標題與簡單說明 st.title("具有 PDF 上傳與聊天記錄的會話式 RAG 問答系統") st.write("上傳 PDF 並與其中的內容進行對話") ## 讓使用者輸入 Groq 的 API 金鑰 api_key = st.text_input("請輸入你的 Groq API 金鑰:", type="password") ## 檢查是否輸入了 Groq 的 API 金鑰 if api_key: # 如果有輸入金鑰,初始化 Groq 模型,這裡使用 "Gemma2-9b-It" 模型 llm = ChatGroq(groq_api_key=api_key, model_name="Gemma2-9b-It") ## 設定聊天的 session ID session_id = st.text_input("Session ID", value="default_session") ## 在 Streamlit 中管理聊天歷史,確保每個 session 都有各自的歷史記錄 if 'store' not in st.session_state: st.session_state.store = {} ## 讓使用者上傳 PDF 檔案 uploaded_files = st.file_uploader("選擇一個 PDF 檔案", type="pdf", accept_multiple_files=True) ## 如果使用者上傳了 PDF,進行處理 if uploaded_files: documents = [] for uploaded_file in uploaded_files: temppdf = f"./temp.pdf" # 暫時儲存 PDF 檔案 with open(temppdf, "wb") as file: file.write(uploaded_file.getvalue()) # 將上傳的 PDF 檔案寫入 temp.pdf file_name = uploaded_file.name # 使用 PyPDFLoader 讀取 PDF 文件 loader = PyPDFLoader(temppdf) docs = loader.load() documents.extend(docs) # 將文件添加到文件列表中 # 使用 RecursiveCharacterTextSplitter 將文檔切割成較小的部分 text_splitter = RecursiveCharacterTextSplitter(chunk_size=5000, chunk_overlap=500) splits = text_splitter.split_documents(documents) # 分割文件 # 使用 Chroma 建立向量資料庫,基於文件分割後的內容 vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings) retriever = vectorstore.as_retriever() # 建立文件檢索器 ## 設定系統提示模板,讓系統重新表述問題並檢索相關文檔 contextualize_q_system_prompt = ( "根據聊天記錄和最新的用戶問題," "重構一個不依賴聊天歷史的完整問題," "不要回答問題,只需要重構它。如果不需要重構,則直接返回原問題。" ) contextualize_q_prompt = ChatPromptTemplate.from_messages( [ ("system", contextualize_q_system_prompt), MessagesPlaceholder("chat_history"), # 聊天記錄的占位符 ("human", "{input}"), # 用戶問題的占位符 ] ) # 使用 Groq 模型和檢索器來建立歷史感知的檢索器 history_aware_retriever = create_history_aware_retriever(llm, retriever, contextualize_q_prompt) ## 用來回答問題的提示系統,基於檢索到的文檔內容生成答案 system_prompt = ( "你是一個用來回答問題的助手。" "使用以下檢索到的內容來回答問題。" "如果你不知道答案,請告訴我你不知道。" "答案請盡量保持簡短,最多三句。" "\n\n" "{context}" # 檢索的文檔內容作為上下文 ) qa_prompt = ChatPromptTemplate.from_messages( [ ("system", system_prompt), MessagesPlaceholder("chat_history"), # 聊天記錄的占位符 ("human", "{input}"), # 用戶問題的占位符 ] ) # 創建文件處理鏈來生成答案 question_answer_chain = create_stuff_documents_chain(llm, qa_prompt) # 將檢索鏈與問答鏈結合 rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain) ## 獲取 session 的聊天歷史,並且在 session_state 中管理 def get_session_history(session: str) -> BaseChatMessageHistory: if session_id not in st.session_state.store: st.session_state.store[session_id] = ChatMessageHistory() # 如果沒有歷史,則初始化 return st.session_state.store[session_id] # 建立可與聊天歷史互動的 RAG 問答系統 conversational_rag_chain = RunnableWithMessageHistory( rag_chain, get_session_history, input_messages_key="input", # 用戶輸入的消息鍵 history_messages_key="chat_history", # 聊天歷史的鍵 output_messages_key="answer" # 系統回答的鍵 ) # 使用者輸入問題 user_input = st.text_input("你的問題:") if user_input: session_history = get_session_history(session_id) # 獲取聊天歷史 response = conversational_rag_chain.invoke( {"input": user_input}, # 將使用者的問題傳入 config={ "configurable": {"session_id": session_id} } ) st.write(st.session_state.store) # 顯示 session_state 中的內容 st.write("助理回應:", response['answer']) # 顯示助理生成的答案 st.write("聊天記錄:", session_history.messages) # 顯示聊天歷史 else: st.warning("請輸入 Groq API 金鑰") # 如果沒有輸入 API 金鑰,提醒使用者 總結: 這段程式實現了一個具有檢索增強生成(RAG)的會話式問答系統,使用者可以上傳 PDF 檔案,並基於這些文檔進行對話式查詢。系統通過 Groq 的大型語言模型來生成回應,同時使用嵌入向量模型(如 Hugging Face 提供的 all-MiniLM-L6-v2)來建立向量資料庫,用於從文檔中檢索相關內容。系統還支援聊天歷史的管理,讓每次查詢可以參考過去的聊天記錄來生成更加準確的回答。 主要功能包括: 上傳 PDF 文件並轉換為可檢索的向量形式。 利用 Groq 模型結合聊天歷史進行問答。 系統具備聊天歷史管理功能,保持每個 session 的上下文。 知識點: 1.Streamlit 互動界面: 使用 streamlit 建立簡單的網頁應用,讓使用者可以上傳 PDF、輸入問題並獲取系統回應。 st.session_state 用來管理多個 session 的狀態,尤其是聊天歷史的儲存。 2. 環境變數管理: 使用 dotenv 來加載 .env 檔案中的 API 金鑰,確保敏感資訊不會暴露在程式碼中。 環境變數主要用於 Hugging Face 嵌入模型和 Groq API 的初始化。 3. PDF 文檔處理與嵌入向量生成: 使用 PyPDFLoader 讀取上傳的 PDF 檔案。 使用 RecursiveCharacterTextSplitter 將文檔分割成較小的部分,方便處理。 嵌入向量使用 Hugging Face 的 all-MiniLM-L6-v2 模型來生成。 4. 向量資料庫與檢索: 使用 Chroma 來創建向量資料庫,將處理過的文檔轉換為向量,供後續檢索使用。 向量檢索器使用 vectorstore.as_retriever() 將向量資料庫轉換為檢索工具。 5. 會話式問答系統與歷史感知檢索: 使用 ChatGroq 初始化 Groq 模型來處理自然語言生成(NLG)。 create_history_aware_retriever 創建一個歷史感知的檢索器,將過去的聊天記錄納入考慮,讓系統根據上下文回答問題。 使用提示模板 ChatPromptTemplate 設計系統和使用者之間的交互,確保 AI 只根據檢索到的內容回答問題。 6. 聊天歷史管理: 透過 ChatMessageHistory 來管理每個 session 的聊天歷史,確保每次問題都能參考之前的對話記錄,讓回答更符合上下文。 7. 可配置的 RAG 生成鏈: create_stuff_documents_chain 將 Groq 的問答生成鏈與檢索結果結合。 RunnableWithMessageHistory 將問答鏈與聊天歷史綁定,形成一個完整的 RAG 問答系統,並且支援多次互動。