logo
Loading...

補充 使用LangChain, LlamaParse, 和"Groq"對複雜的PDF進行RAG - RAG技術: 智能助手開發實戰 - Cupoy

各位不知道還記不記得上一堂課說到老師L習慣在自己的應用中Langchain和llamaindex和在一起使用? 今天就要來交各為怎麼樣”一起使用”。這算是比較進階一點的應用課程唷! 我們來複習一下前面...

各位不知道還記不記得上一堂課說到老師L習慣在自己的應用中Langchain和llamaindex和在一起使用? 今天就要來交各為怎麼樣”一起使用”。這算是比較進階一點的應用課程唷! 我們來複習一下前面一直提到的概念。”RAG”和其”workflow”. 檢索增強生成 (RAG) 算是一個2023年才逐漸串起來厲害的新方法!它就像是給大型語言模型(LLM)裝上了一個超強的放大鏡,讓它可以從一大堆雜亂無章的資料中自動找出重要的東西。這個方法最近變得超級紅,因為它可以讓 LLM 應用程式變得更聰明,更懂得看場合說話。RAG 的神奇魔法包含了幾個關鍵步驟: 資料進門:首先,我們要從各種地方搜刮資料,像是文件、網站或資料庫。這些資料可能是原汁原味的,也可能已經稍微整理過了。 資料大掃除:接著,我們要把這些資料好好整理一番,就像是大掃除一樣。我們會把它們洗乾淨、分類,方便之後使用。這包括把文字切成一小段一小段的,或是把不重要的字詞刪掉。 資料變身:每一條資料都要變身成為一串數字,我們叫它 embedding。這個神奇的數字串可以讓電腦更容易理解資料的意思,就像是給每條資料一個獨特的身分證。 資料的家:這些變身後的資料會住進一個叫做向量資料庫的地方。這個資料庫超厲害的,可以快速找出相似的資料,就像是有個超級效率的圖書管理員。 找資料和問問題:當我們需要生成內容時,LLM 就會跑去這個資料庫找相關的資料。這樣一來,LLM 就能給出更準確、更合適的回答,就像是有個超級聰明的助理隨時待命。 總的來說,RAG 讓 LLM 變得更厲害了!它可以有效地運用外部知識,讓 LLM 在理解人類語言、找資料、做決定等方面變得更強。這簡直就像是給 LLM 裝上了一個超級大腦,讓它變得更聰明、更懂人情世故啦! 不過呢,要把 RAG 真的應用到實際工作中還是有點小麻煩的。我們來看看有哪些挑戰: 準確度不夠高:有時候,對於一些比較特別或罕見的問題,系統可能會答不太好。就像是考試時遇到了超難的題目,連老師都要想半天。 太多東西要調整:現在還不太清楚該怎麼調整各種參數,感覺就像是在玩一個超複雜的遊戲,不知道該先升級哪個技能。 PDF 檔案超麻煩:有些文件格式亂七八糟的,要讓電腦看懂真的很頭痛。就像是要教一個外星人看懂地球人的塗鴉一樣困難。 資料一直在變:實際工作中的資料常常會更新,要一直跟上最新的資料真的很累人。就像是要追一部每天都在更新的連續劇,超燒腦的! 為了解決這些問題,LlamaIndex 在 2024 年 2 月 20 日推出了兩個超強的新工具:LalmaCloud 和 LlamaParse。這兩個工具就像是給 RAG 系統穿上了一件超級裝甲,讓它變得更強大、更好用! LalmaCloud 的設計理念超有趣的,它就是要讓你專心寫程式,不用再為了整理資料傷腦筋。它可以輕鬆處理大量的資料,立刻提升回答的品質。它主要包含兩個超厲害的功能: LlamaParse 的超強解析功能:它可以輕鬆解讀複雜的文件,就算裡面有表格或圖片也不怕。這個功能直接整合到了 LlamaIndex 中,讓你可以輕鬆處理各種難搞的文件。它簡直就像是有個超級翻譯官,可以回答以前根本不可能回答的問題! 超方便的資料處理 API:這個 API 讓你可以輕鬆地載入、處理和儲存 RAG 應用程式需要的資料,而且可以用各種程式語言來使用。它背後有 LlamaHub 的強力支援,裡面有各種資料來源和儲存方式可以用。這簡直就像是給你一個百寶箱,裡面有各種神奇的工具可以用! LlamaCloud (llamaindex.ai) → 連進去拿api key 什麼是 LlamaParse ? LlamaParse 是一種專有的解析服務,非常擅長將具有複雜表格的 PDF 解析為結構良好的 markdown 格式。 此服務以**公共預覽模式提供:**可供所有人使用,但有使用限制(每天 1k 頁),每周 7,000 個免費頁面。然後每頁 0.003 USD(每 1000 頁 3 USD)。它作為獨立服務運行,也可以插入到託管的攝取和檢索 API 中。 Groq? Groq: 革新 AI 加速技術 Groq 是一家成立於 2016 年的美國公司,專注於開發先進的 AI 加速技術。其核心產品是語言處理單元(LPU),這項創新技術顯著提升了 AI 模型的處理速度和效率。 Groq 的 LPU 技術在處理大型語言模型時表現尤為出色。例如,在 Meta AI 的 Llama-2 70B 模型上,Groq 實現了每秒處理超過 300 個代幣的驚人速度,大幅超越了傳統 GPU 的性能。 Groq 的核心技術 GroqChip 採用了稱為「張量流」的架構,不僅處理速度快,還具有高能效和高精度的特點。公司的目標是在未來兩年內生產 100 萬顆這樣的晶片,有望為 AI 行業帶來重大變革。 LPU 推理引擎:AI 處理的新標準 LPU 推理引擎是 Groq 的核心技術,專為處理高計算需求的 AI 任務而設計。它在語言處理方面表現尤為出色,為 AI 應用提供了顯著的性能提升。 LPU 相比 GPU 的優勢 LPU 在兩個關鍵方面超越了 GPU:計算密度和記憶體頻寬。這使得 LPU 能夠更快速地處理數據,尤其是在處理大型語言模型時。LPU 的計算能力優於 GPU 和 CPU,能夠更快地處理每個字元,同時解決了記憶體瓶頸問題,大幅提高了 AI 模型的處理效率。 如需深入了解 Groq 的技術細節,建議參考他們在 ISCA 發表的論文。2020 年和 2022 年的研究成果提供了更多技術洞察。 體驗 Groq 技術 Groq 目前提供免費的 API 終端節點,讓開發者能夠親身體驗他們的大型語言模型。此外,Groq 承諾提供市場上最具競爭力的價格,並且能夠根據市場情況靈活調整。除了自有模型外,Groq 還支持 Mistral 和 CodeLlama 等其他模型。 如果您有興趣嘗試 Groq 的技術,可以通過他們的官方網站註冊並獲取 API 密鑰。這是一個體驗先進 AI 加速技術的絕佳機會。 這個key上一堂課已經提了好幾次!! 就不再說明了~ 下來我們一樣複習一下Langchain的基礎概念吧,之前講過了但還是要在新的筆記上讓大家回憶一下。 什麼是 Langchain? LangChain 是一個開源框架,旨在簡化使用大型語言模型 (LLM) 的應用程式創建過程。它為鏈提供了一個標準介面,與其他工具的大量集成,以及常見應用程式的端到端鏈。 Langchain 關鍵概念: 元件:元件是模組化構建塊,隨時可用且易於使用,以構建強大的應用程式。元件包括 LLM Wrappers、Prompt Template 和 Indexes 用於相關信息檢索。 鏈條:鏈條允許我們將多個元件組合在一起以解決特定任務。鏈使其更加模組化且易於調試和維護,從而使複雜應用程式的實現變得容易。 代理:代理允許 LLM 與其環境交互。例如,使用外部 API 執行特定操作。 接下來進入程式: 1.依賴項目(庫) %%writefile requirements.txt langchain langchain-community llama-parse fastembed chromadb python-dotenv langchain-groq chainlit fastembed unstructured[md] !pip install -r requirements.txt 2.設置環境變數 import os from dotenv import dotenv_loader() #記得準備好,env檔 3.導入所需庫: ##### LLAMAPARSE ##### from llama_parse import LlamaParse from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings.fastembed import FastEmbedEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.document_loaders import DirectoryLoader from langchain_community.document_loaders import UnstructuredMarkdownLoader from langchain.prompts import PromptTemplate from langchain.chains import RetrievalQA # from groq import Groq from langchain_groq import ChatGroq # import joblib import os import nest_asyncio # noqa: E402 nest_asyncio.apply() 4.LlamaParse 參數 * api_key: str = Field( default="", description="The API key for the LlamaParse API.", ) * base_url: str = Field( default=DEFAULT_BASE_URL, description="The base URL of the Llama Parsing API.", ) * result_type: ResultType = Field( default=ResultType.TXT, description="The result type for the parser." ) * num_workers: int = Field( default=4, gt=0, lt=10, description="The number of workers to use sending API requests for parsing." ) * check_interval: int = Field( default=1, description="The interval in seconds to check if the parsing is done.", ) * max_timeout: int = Field( default=2000, description="The maximum timeout in seconds to wait for the parsing to finish.", ) * verbose: bool = Field( default=True, description="Whether to print the progress of the parsing." ) * language: Language = Field( default=Language.ENGLISH, description="The language of the text to parse." ) * parsing_instruction: Optional[str] = Field( default="", description="The parsing instruction for the parser." ) 5.用於載入和解析輸入資料的輔助函數 import os import joblib from llama_parse import LlamaParse def load_or_parse_data(): # 檔案路徑 data_file = "./data/parsed_data.pkl" pdf_file = "/content/data/Large Language Models A Survey.pdf" # 檢查是否已經存在解析好的 .pkl 檔案 if os.path.exists(data_file): try: # 嘗試從 .pkl 文件加載數據 parsed_data = joblib.load(data_file) print(f"Loaded parsed data from {data_file}") except (EOFError, KeyError, joblib.externals.loky.process_executor._RemoteTraceback) as e: print(f"Error loading the parsed data: {e}") parsed_data = None else: parsed_data = None # 如果無法從 .pkl 文件加載數據,或者文件不存在,則重新解析 PDF if parsed_data is None: print("Parsing PDF and generating data...") # 解析指令 parsing_instruction = """The provided document is a survey about Large Language Models. The document includes various technical insights, research summaries, and models' comparisons. Focus on extracting key tables and discussions regarding model performance.""" # 設置 LlamaParse 解析器 parser = LlamaParse(api_key=llamaparse_api_key, result_type="markdown", parsing_instruction=parsing_instruction, max_timeout=5000) try: # 解析 PDF 文檔 llama_parse_documents = parser.load_data(pdf_file) # 檢查解析結果是否有效 if llama_parse_documents: # 保存解析結果到 .pkl 文件 print("Saving the parse results in .pkl format...") joblib.dump(llama_parse_documents, data_file) parsed_data = llama_parse_documents else: raise ValueError("Parsed data is empty. Check the input PDF or parsing process.") except Exception as e: print(f"Error during parsing or saving: {e}") parsed_data = None return parsed_data 6.將塊載入到 vectorstore 中的輔助函數。 import os from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings.fastembed import FastEmbedEmbeddings from langchain_community.vectorstores import Chroma from langchain_community.document_loaders import UnstructuredMarkdownLoader def create_vector_database(): """ Creates a vector database using document loaders and embeddings. This function loads documents, splits them into chunks, transforms them into embeddings, and persists the embeddings into a Chroma vector database. """ # 檔案和資料夾管理 output_dir = "/content/data/" os.makedirs(output_dir, exist_ok=True) # 確保資料夾存在 markdown_file = os.path.join(output_dir, "output.md") # 調用之前定義的 load_or_parse_data 函數 llama_parse_documents = load_or_parse_data() # 確保解析結果不為空 if not llama_parse_documents: print("Error: No documents found during parsing.") return None, None print(f"First 300 characters of the parsed document: {llama_parse_documents[0].text[:300]}") # 寫入解析數據到 markdown 文件 with open(markdown_file, 'a') as f: # 追加模式 ('a') for doc in llama_parse_documents: f.write(doc.text + '\n') # 使用 UnstructuredMarkdownLoader 來載入 Markdown 文件 loader = UnstructuredMarkdownLoader(markdown_file) documents = loader.load() # 使用文本分割器來處理文件 text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=100) docs = text_splitter.split_documents(documents) print(f"Number of documents loaded: {len(documents)}") print(f"Total number of document chunks generated: {len(docs)}") # 初始化嵌入模型 embed_model = FastEmbedEmbeddings(model_name="BAAI/bge-base-en-v1.5") # 創建並持久化向量資料庫 vs = Chroma.from_documents( documents=docs, embedding=embed_model, persist_directory="chroma_db_llamaparse1", # 本地模式,資料庫儲存於記憶體中 collection_name="rag" ) print('Vector database created successfully!') return vs, embed_model 可以設計有追蹤進度的function版本: def create_vector_database(): print("開始解析文檔...") llama_parse_documents = load_or_parse_data() print(f"文檔成功解析,共有 {len(llama_parse_documents)} 個文檔") # 文檔寫入和載入 print("將文檔寫入 output.md...") with open('data/output.md', 'a') as f: for doc in llama_parse_documents: f.write(doc.text + '\n') print("正在載入並處理文檔...") loader = UnstructuredMarkdownLoader("/content/data/output.md") documents = loader.load() print(f"文檔載入成功,共 {len(documents)} 個文檔") print("正在分割文檔...") text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=100) docs = text_splitter.split_documents(documents) print(f"文檔已分割為 {len(docs)} 個塊") print("正在生成嵌入...") embed_model = FastEmbedEmbeddings(model_name="BAAI/bge-base-en-v1.5") vs = Chroma.from_documents( documents=docs, embedding=embed_model, persist_directory="chroma_db_llamaparse1", collection_name="rag" ) print('向量資料庫已成功創建!') return vs, embed_model 7.下載nltk庫必要的安裝包(很多docs都會忽略此步驟) import nltk # 下載所有 NLTK 的 punkt 資源,包括相關的表格和字典 nltk.download('punkt') nltk.download('popular') # 這會下載一些常用的 NLP 資源,應該包含 'punkt_tab' import nltk # 下載 punkt 和其他常用資源 nltk.download('punkt') nltk.download('popular') import nltk nltk.data.path.append("/content/nltk_data") # 手動指定下載路徑 # 下載 punkt nltk.download('punkt', download_dir="/content/nltk_data") import shutil # 刪除 NLTK 緩存文件夾以確保重新下載 shutil.rmtree('/root/nltk_data') nltk.download('punkt') nltk.download('averaged_perceptron_tagger_eng') 8.處理並創建vector Store vs,embed_model = create_vector_database() 9.實例化 LLM chat_model = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768", api_key=userdata.get("GROQ_API_KEY"),) 上面的代碼執行以下操作: 創建一個名為 chat_model 的新 ChatGroq 物件 將 temperature 參數設置為 0,表示回應應更具可預測性 將 model_name 參數設置為 “mixtral-8x7b-32768”,指定要使用的語言模型 10.實例化 Vectorstore vectorstore = Chroma(embedding_function=embed_model, persist_directory="chroma_db_llamaparse1", collection_name="rag") # retriever=vectorstore.as_retriever(search_kwargs={'k': 3}) 10.創建自訂提示範本 custom_prompt_template = """Use the following pieces of information to answer the user's question. If you don't know the answer, just say that you don't know, don't try to make up an answer. Context: {context} Question: {question} Only return the helpful answer below and nothing else. Helpful answer: """ 11.用於設置提示格式的 Helper Function def set_custom_prompt(): """ Prompt template for QA retrieval for each vectorstore """ prompt = PromptTemplate(template=custom_prompt_template, input_variables=['context', 'question']) return prompt # prompt = set_custom_prompt() prompt ########################### RESPONSE ########################### PromptTemplate(input_variables=['context', 'question'], template="Use the following pieces of information to answer the user's question.\nIf you don't know the answer, just say that you don't know, don't try to make up an answer.\n\nContext: {context}\nQuestion: {question}\n\nOnly return the helpful answer below and nothing else.\nHelpful answer:\n") 12.實例化檢索問答鏈 13.調用 Retriveal QA 鏈