RAG技術: 智能助手開發實戰
擷取增強生成 (RAG) 是一種前沿技術,結合了自然語言處理和資訊檢索,旨在提升大型語言模型 (LLM) 的生成準確性。對於想要深入學習 LLM 與 RAG 技術的人來說,RAG 提供了一個絕佳的學習
內容簡介
作者介紹
適合人群
你將會學到什麼
購買須知
-
第一章 (8/31)
認識基礎的大型語言應用平台, 簡單的Langchain架構和RAG概念入門和組成RAG的基本單位模塊。
-
前導課程 - 超好用prompt和初始設定大全for最新chatGPT
第一: PDF摘要和知識圖譜擷取 chatGPT現在可以做到 input進去一篇pdf檔, 然後透過prompt的設計, 可以經過內部推論進行摘要, 最後生成出知識圖譜。 我們來看看範例的應用情景。 prompt: 請將此論文的內容 , 依照其架構摘要出重點 , 並畫出心智圖 , 請注意文字呈現要 適當斷行 , 要清楚可讀、不要重疊。 [1706.03762] Attention Is All You NeedThe dominant sequence transduction models are based on complex recurrent or convolutional neural networks in an encoder-decoder configuration. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train. Our model achieves 28.4 BLEU on the WMT 2014 English-to-German translation task, improving over the existing best results, including ensembles by over 2 BLEU. On the WMT 2014 English-to-French translation task, our model establishes a new single-model state-of-the-art BLEU score of 41.8 after training for 3.5 days on eight GPUs, a small fraction of the training costs of the best models from the literature. We show that the Transformer generalizes well to other tasks by applying it successfully to English constituency parsing both with large and limited training data. 我們接下來會以兩篇很火紅的paper : 分別是Attention is all you need和 Large language models : A Survey. [2402.06196] Large Language Models: A SurveyLarge Language Models (LLMs) have drawn a lot of attention due to their strong performance on a wide range of natural language tasks, since the release of ChatGPT in November 2022. LLMs' ability of general-purpose language understanding and generation is acquired by training billions of model's parameters on massive amounts of text data, as predicted by scaling laws \cite{kaplan2020scaling,hoffmann2022training}. The research area of LLMs, while very recent, is evolving rapidly in many different ways. In this paper, we review some of the most prominent LLMs, including three popular LLM families (GPT, LLaMA, PaLM), and discuss their characteristics, contributions and limitations. We also give an overview of techniques developed to build, and augment LLMs. We then survey popular datasets prepared for LLM training, fine-tuning, and evaluation, review widely used LLM evaluation metrics, and compare the performance of several popular LLMs on a set of representative benchmarks. Finally, we conclude the paper by discussing open challenges and future research directions. 中文字型切換應用檔案otf檔: 可以透過仔入中文的otf檔使chatgpt在生成知識圖譜時上面的摘要可以從英文切換成中文。(小技巧) Prompt 輸入技巧 1.善用標籤補充說明: #角色扮演 / 任務: 你是文創產業的經營顧問,在台灣任職相關產業超過 10 年,熟悉業界生態, 擅長將人文質感融入品牌經營,讓創作品或中小企業有不同的風貌。接下去我 會逐一跟你說明產品特色與各種需求,希望你收到這些資訊後,提供我各方面 的營運與行銷建議。 #背景資訊: 我目前在花蓮從事文創工作,曾在花蓮一家知名文創公司擔任設計總監,領導 團隊成功推出多款受歡迎的產品,也參與策劃過多次花蓮地方文化活動。目前 準備自己創業,開設全新的個人品牌,專注於開發融入當地農特產和原住民文 化的創意商品,為震後來花東遊玩的遊客帶來別具一格的體驗。店名暫定是台 浪時光,請幫我規劃品牌形象並撰寫經營企畫書。 #企畫書格式: 創業背景與機會 創業構想 經營型態 企業品牌形象 產品服務與內容(現在與未來的產品規劃建議) 用引號強調”重點” 我是社會新鮮人," 輝 X 達 " 最近釋出很多工程師的職缺,我最近接到他們的面 試通知,這是我第一份工作、也是第一次面試,請作為面試官依序問問我問 題,請 " 一題接一題 " 發問,我回答後再問下一題。 我深深感受到CEO對台灣滿滿的熱愛,憧憬公司文化,希望能在AI產業,為台灣科技業 盡一份力。 我的碩士專題是研究NLP與機器人控制,教授交代延續學長的開發環境和教學文件,不過我要使用的時候已經改版,最先的專案資料已經不適用,後來跟原廠接洽,跟研發人員死纏爛打,加上爬了很多文,才終於完成順利把三屆學長姊都未完成的專案搞定,並獲得該屆的最佳論文獎。 限定答覆字數: 有時候怕chatgpt太多話, 我們還是要去蕪存菁一下對吧。 提供參考範本 : 分析以下批踢踢版的風格,整理出模式後,依樣畫葫蘆幫我寫一則新產品的推廣文案。 作者 Valentino ( 君山君 ) 看板 Gossiping 標題 [ 問卦 ] 如何讓夜鶯乖乖閉嘴 時間 Wed Jun 5 11:13:20 2024 不知從何開始,晚上睡覺時都會聽到夜鶯在那邊啾啾啾的靠北邊叫,小時候聽 到蛙蛙聲,感覺沒那麼吵,夜鶯就不能忍的這種 乾的,到底有什麼辦法治牠們 ... 閉嘴阿,有沒有掛 ※ 發信站 : 批踢踢實業坊 (ptt.cc), 來自 : 101.9.139.126 ( 臺灣 ) ※ 文章網址 : https://www.ptt.cc/bbs/Gossiping/M.1717564425.A.883.html → ha0118: 其實是夜 " 鷹 " 還是你意有所指 1.175.221.183 06/05 13:14 → bill403777: ㄋㄋ很軟 Q 42.72.2.109 06/05 13:14 → kuM: 鶯鷹沒帶子 114.136.191.173 06/05 13:14 推 gulugulupp: 現在一堆鳥整天叫 不過麻雀真的變少了 211.21.30.3 06/05 13:14 噓 taiwan08: https://i.imgur.com/SDFO5Us.jpeg 文案要有新鮮感,多使用最近熱門的鄉民用語,例如從以下的流行用語挑一些來 用,對照其 " 涵義 " 或範例句,放在適當的地方,讓文案更加活潑。 流行語:" 挖苦挖苦 " 涵義:" 超開心 " 流行語:" 超派 " 的啦 涵義:很兇 流行語:來個 "16 蹲 " 涵義:台灣天團 Energy 的新舞蹈,代表夠潮、體力夠好 流行語:這點我真的 " 觸爆 " 涵義:超認同 流行語:你說的是 " 尊嘟假嘟 " 涵義:真的假的 流行語:你這樣說 " 要確欸 ~" 涵義:確定嗎? 流行語:哇 , " 卡皮巴拉 " 涵義:跟水豚君一樣可愛 流行語:你 "I 人 "、你 "E 人 " 吧 涵義:對應 MBTI 人格的意思 2.引導先思考再解答 要求列出思緒過程: 階段式提問: 將任務拆解成明確步驟 驗證結果是否符合條件 COT思維鍊, Auto-COT 先翻譯再潤稿, 用思維鍊提升翻譯品質: 用思維鍊模擬創意發想的引導活動:思維鍊運用得當可以透過跟AI模型間的互動做出簡單的自動化應用程式。: 我希望您模擬一個腦力激盪的引導活動程序,其主要流程說明如下: 找出新的 idea:提示學員如何構思找到新想法。只要學員準備好實現這個 idea 時,你會建議提供進一步的執行選項參考 , 以下是要繼續請學院提供的詳 細資訊: A. 重新開始:引導使用者說出想要開始的方向或任何想法。 B. 我有靈感了:以互動方式幫助學員提出一個可以實踐的概念。你要詢問學 員是否對這個概念有大致的了解 , 或者需要你提供進一步選項進行協助。 擴展你的 idea:以互動方式幫助使用者將這個 idea 或概念繼續延伸發想。 總結想法:提出對於學員想法的摘要 , 不管延伸到甚麼領域都要提出一個標 題。學員可以選擇重寫或編輯摘要內容。只要學員滿意摘要內容,你就會將想 法摘要「儲存」下來。 檢索先前 idea:檢索這次對話期間產生的想法摘要的標題 , 學員可以選擇顯 示其中一個想法的摘要或繼續研究先前的想法。 繼續處理先前的 idea:列出這次對話期間所產生的 idea 摘要的標題 , 並提示 學員選擇一個 idea 來繼續。 其他注意事項: 所有輸出應以文字呈現,不要使用嵌入式的程式碼視窗。 學員的使用流程和體驗應該要很接近真人導師引導,但可以對話形式呈現。 多使用表情符號來幫助傳達輸出的前後文 , 但不要濫用 , 提示學員的操作選單 也要有一致性的符號。 收到指示 , 請從主選單開始 , 並針對本引導課程顯示簡短振奮人心的歡迎訊息。 學員可以透過輸入跟提示功能相符的數字或可以明確表達意圖的文字來選擇不 同功能選項。 加上標題和標籤: 請充分善用 Markdown 格式,讓回覆內容更加條理分明: 將每一大項的內容套用 的引用區塊 , 不同項用不同區塊。 重要名詞可以斜體、粗體呈現。 條列式項目改用 check list 方式呈現。 最重要的幾項請打勾 , 可忽略的項目加上刪除線 , 其他可留空。 加上表情符號 以表格呈現 csv格式與原始碼: 請給我近 10 年台灣經濟相關數據 ( 如 GDP、就業率 ....... 等 ), csv 原始碼呈現。 其他實用技巧與經驗分享: 強制要求不要重複問題:我是大學剛畢業的文科生,從小數理科目就不太好,專業名詞或術語請盡量用白話文,雖然是文科生,但英文也沒有特別好,所以請少用英文。我中文很好,沒法忍耐錯字,請多斟酌你的用字遣詞。接下來我會詢問數理相關問題,請依指示回答我,你了解的話,只要回覆明白就好。 不要將角色混和: 善用摘要, 聚集重點, 延續對話內容: 請將上述對談的內容摘要下來 , 後面的對話要延續摘要內容,不要忘了。 自己還是要讀過去驗證GPT有沒有胡說八道(幻覺), 最好的解法就是每個議題都開一個對話串或是創造自己的GPT(GPTs) 建立Prompt範本: 請你扮演來自 [ 影視作品或小說 ] 的 [ 角色 ] 。請你以 [ 角色 ] 的口吻、方式和詞彙說故事給我聽。不要任何解釋 , 只要用像是 [ 角色 ] 一樣的口吻直接說故事就可以。你必須熟知 [ 角色 ] 的相關背景 , 你他的立場敘說符合他個性所構思的[ 類型 ] 故事 , 其中要穿插 [ 影視作品或小說 ] 的著名場景或金句。 影視作品:那些年我們一起追的女孩 角色:沈佳宜 類型:武俠 以下是一提供一些prompt參考的好網站或Chrome 外掛 AIPRM for ChatGPT Awesome ChatGPT Prompts EasyPrompt Library GreatAiPrompts(GAiP) Promptbase
-
超好用的GPTs機器人Agents
有一些分類幫你更好找到自己想要用的GPT特殊代理機器人。 把常用的機器人存到左邊的工具列,他會有特殊的icon, 所以不管基於這個機器人開啟多少次都能認出是該機器人。 第一個推薦: Excel GPT 一開始就透過Prompt設計好四個基本的模式, 分別是資料分析 , 函數撰寫, 重新組織資料, 試試更加強大的新GPT。 分析完後他還會有四個選項讓你選, 假如我要繪製特定月份的趨勢圖, 我只要打 3 就好了。很方便 很厲害的直接幫我們根據月分畫出趨勢圖, 真是太方便了! 在神奇的背後其實他也是執行python後印出來啦~ 如果是開發者還可以拿去參考 利用pandas的庫 第二個推薦:Slides Maker ChatGPT透過API溝通, 到外部工具做PPT。等待差不多五分鐘我們來看結果… 生成介面後我們可以自己下載回來修改。 或者是用專門拿來生成PPT的更專業工具: 比如gamma 來優化這份ppt。 第三個推薦: VoxScript: 不用再花大把時間把時間看影片。 們請他分析一下Langchain的頻道影片: 可以看到他的確透過影片中的字幕而總結出重點。當然,我們可以再細問他… 上面介紹了三個,個人覺得可以在日常蒐集資訊中非常省時間的chatgpt工具: 分別是,1.自動分析excel工具的ExcelGPT, 2.自動整理文字資訊並且生成ppt模板的Slides Maker 3.可以讀取YouTube影片文字檔並且生成摘要的 VoxScript。 都非常實用。
-
補充GenAI基本知識
GenAI基本知識 - Google SlidesThis is Slido interaction slide, please don't delete it. ✅ Click on 'Present with Slido' and the poll will launch automatically when you get to this slide. 目前有使用過哪些AI工具? ⓘ Click Present with Slido or install our Chrome extension to activate this poll while presenting.docs.google.com 模型比較 VS 會找模型 Model & API Provider Analysis | Artificial Analysis
-
正課第一節: RAG應用的基本介紹
為初學者設計的「構建RAG(檔案檢索增強生成)簡單應用」課程大綱,總共約十個小時。 此大綱是根據以LangChain這個大型語言開發應用框架為主的課程,並考慮到學習者的背景進行難度分配。 第一節:RAG應用的基本介紹 🔍 RAG簡介 RAG是為了解決冰山底層的私領域資料而建立的。 在我們能夠觸及的網路表層資料其實才佔了大概10~15%, 真正那些無法觸及的私人資料或企業資料整整展了將近80~90%, 那要怎麼樣挖掘這些資料, 最後讓LLM能夠準確"檢索"到, 並且最後"生成"出我們想要的資料或是報告,變成非常重要的事情。 而 RAG 就會是現在做到這件事的 "主流技術" 📚 大語言模型的挑戰 2023年是大語言模型的爆發元年。無論是CloseAI(OpenAI啦)的GPT系列模型,還是Meta研究院開源的llama1,2,3 ,3.1模型、乃至於Google的Gemini模型等,它們在自然語言處理領域的出色表現令人驚嘆。甚至都達到多模態資料分析。 然而,這些模型在面對專業領域的知識時,常常會出現知識缺失或是幻覺等等的問題,無法提供準確答案。數據科學家通常會通過微調(paramters-level fine tune)模型來適應特定領域的需求,儘管這種方法效果卓越,但成本高昂且需要專業技術知識。 💡 解決方案:"RAG" 針對大語言模型的不足,參數化知識(即微調模型)存在顯著局限性,不僅難以保留所有訓練數據中的知識,每次更新知識都需耗費大量計算資源。模型參數也無法動態更新,這意味著參數化知識會隨時間過時。 小談 微調(fine-tine) 和 RAG 的差異 (雖然有談到參數化微調(parameters fine-tine)但此堂課重點絕對還是RAG! 請謹記這件事~大家記得有參數微調這件事即可。 什麼是RAG? 簡而言之,RAG 是一種為我們的 LLM 提供額外的上下文以生成更好、更具體的響應的技術。LLM 是在公開可用的數據上訓練的,它們確實是獨立的智能系統,但它們無法回答特定問題,因為它們缺乏回答這些查詢的上下文。借助RAG,我們為他們提供了必要的上下文,以便正確回答我們的查詢。 RAG 是一種將新知識或能力插入我們的 LLM 的方法,儘管這種知識插入不是永久性的。向 LLM 新增新知識或能力的另一種方法是對我們的特定數據進行微調 LLM。 通過微調添加新知識是相當棘手、困難、昂貴且永久性的。通過微調添加新功能甚至會影響它之前擁有的知識。在微調過程中,我們無法控制哪些權重將被更改,因此哪些功能將增加或減少。 RAG 自構思以來已經走過了漫長的道路,我們已經大大改進了檢索策略,甚至在被問到完全偏離主題的問題時如何保護我們的系統。所有這些都在我的RAG博客系列中得到了非常詳細的介紹: 密集向量檢索 該過程從將用戶的問題輸入到 Embedding Model 中開始。 Embedding Model 將問題轉換為密集向量表示。 然後,此向量用於查詢向量存儲。 Vector Store 包含上下文或參考資訊的預計算嵌入。 使用相似性搜索(通常是餘弦相似性),系統檢索最相關的上下文片段。(查看RAG 2.0以獲得更好的相似性搜索) 檢索步驟會提取語言模型在其訓練數據中可能沒有或可能無法準確回憶的相關信息。 情境學習 提示範本 (Prompt Template) 構建如何將檢索到的信息和使用者的查詢呈現給語言模型。 該範本包括 (1) 模型如何使用上下文的說明,(2) 使用者的原始查詢,以及 (3) 檢索到的上下文片段(標記為 Context: ref 1、ref 2 等)。 這種結構允許語言模型 「學習」 如何使用提供的資訊來回答手頭的特定問題。 然後,聊天模型會處理這個精心構建的提示。 通過上下文學習,該模型根據提供的特定任務和上下文調整其知識和能力,而無需更改其底層參數。 淺談RAG的評估法 什麼是RAGAs?評估 LLM 管道 RAGAs (Retrieval-A ugmented Generation Assessment) 是一個框架(GitHub、Docs),可為您提供必要的成分,説明您在元件級別評估 RAG 管道。 RAGAs 框架已成為評估檢索增強生成 (RAG) 系統的寶貴工具,解決了評估這些多組分管道的複雜挑戰。該框架引入了一套全面的指標,旨在評估RAG系統的各個元件和整體性能。通過利用大型語言模型 (LLM) 進行無參考評估,RAGA 旨在簡化評估過程,從而可能減少對大量人工註釋數據的需求。 評估數據 RAGA 的有趣之處在於,它最初是一個 「無參考 」評估的框架。這意味著,RAGA 不必在評估數據集中依賴人工註釋的真值標籤,而是在後台利用 LLM 來進行評估。 要評估RAG管道,RAGA需要以下資訊: question:作為RAG管道輸入的用戶查詢。輸入。 answer:從RAG管道生成的答案。輸出。 contexts:從外部知識源檢索到的上下文,用於回答 .question ground_truths:對 .這是唯一的人工註釋資訊。只有量度才需要此資訊(請參閱評估量度)。questioncontext_recall RAGA的核心是四個關鍵的元件級指標:上下文相關性、上下文回憶、忠實度和答案相關性。 這些指標提供了對RAG管道不同方面的見解,從檢索信息的品質到生成答案的準確性和相關性。 上下文相關性評估檢索數據中的信噪比,而上下文召回率(唯一需要真實度的指標)確保捕獲所有必要的資訊。Faithity 評估生成答案的事實準確性,而 Answer Relevancy 衡量響應解決給定問題的程度。 所有這些指標都按照 0 到 1 的等級進行標準化,便於解釋和比較。 雖然構建基本的 RAG 應用程式可能相對簡單,但優化其性能以供實際使用會帶來重大挑戰。RAGA 通過提供結構化的評估方法來解決這個問題,類似於傳統機器學習專案中使用的驗證流程。該框架不僅提供指標,還包括用於自動生成測試數據的工具,從而進一步簡化了評估過程。 RAGAs (Retrieval-A ugmented Generation Assessment) 是一個框架(GitHub、Docs),可為您提供必要的成分,説明您在元件級別評估 RAG 管道。 RAGAs 框架已成為評估檢索增強生成 (RAG) 系統的寶貴工具,解決了評估這些多組分管道的複雜挑戰。該框架引入了一套全面的指標,旨在評估RAG系統的各個元件和整體性能。通過利用大型語言模型 (LLM) 進行無參考評估,RAGA 旨在簡化評估過程,從而可能減少對大量人工註釋數據的需求。 RAGA 可用於評估RAG管道中的許多不同元件。 RAGAs 提供指標驅動的開發。 建立基線 更改可能改進檢索的內容 重新計算指標 但是,重要的是要注意,使用 LLM 進行無參考評估仍然是一個不斷發展的領域。絕對值不是 Metrics 驅動開發的重點。 稍微提一下 llamaindex 和 Langchain 的差異: 是否需要訪問外部數據源? RAG: 適合需要訪問外部資料庫、文檔或其他數據源的應用。RAG能檢索並整合外部資訊,適合頻繁變動的資料。 微調: 需要大量標記數據集來學習外部知識,且需要隨資料變更更新,對於頻繁變動的資料不太實用。 是否需要調整模型行為、寫作風格或特定領域知識? 微調: 擅長使模型適應特定語氣、術語和行業專業知識。適合需要特定寫作風格或行業行話的應用。 RAG: 強於訊息檢索,但在語言風格和領域特異性方面不如微調。適合需要整合外部知識但不要求特定風格的應用。 結論 外部數據需求: 優先選擇RAG 定制行為和風格: 優先選擇微調 檢索增強生成(RAG)與 Fine-tuning 相結合:雙劍合壁 當提示工程(Prompt Engineering)達到極限時,RAG可以成為強大盟友。它通過引入外部訊息來豐富並增強模型回答。而當RAG也不能完全解污某些困難場景時 —— 比如處理超長文本或非常複雜訊息 —— Fine-tuning 就登場了。 從理論到實際操作:確保您準備好迎接挑戰 在開始 Fine-tuning 之旅前要做足功夫!確保您已充分理解基礎模型及其局限性;準備好高品質、針對性強且具代表性的數據;最重要地——制定出清晰可量化成功指標以評估效果。 無論我們正在探索新領域還是希望建立起更加貼近用戶需求和企業文化風格匹配度高效能模型,在人工智慧發展日新月異今天, Fine-tuning 無疑提供了一種有效手段去實現這些目標。 總之,非參數化知識,即存儲在外部的知識源,更加方便且易於擴展。這種方法允許開發者無需為每一個特定任務重新訓練整個模型。他們可以簡單地為模型添加一個知識庫,從而提高答案的精確性。 為了結合兩種方法的優缺點,模型可以採用半參數化的方法,將非參數化的資料庫與參數化模型相結合,這種方法被稱為檢索增強生成(Retrieval-Augmented Generation,RAG)。 🛠️ RAG的優勢 RAG方法允許模型在不重新訓練的情況下,即時訪問和利用外部知識源,從而提高回答的準確性和相關性。這使得模型不僅能夠保持高效運行,還能夠動態更新知識庫,保持知識的新鮮度。 這就是RAG的基本介紹,它是一種融合了參數化和非參數化知識的創新方法,使得大語言模型在處理專業知識時更加靈活和高效。 🚀 —————————————————————————————————————————🚀 RAG也可以由淺至深?: 這張圖展示了檢索增強生成(RAG)和微調技術的工作流程,根據所需的外部知識和模型調適程度,將各種技術進行分類。讓我們逐一解釋圖中的各個部分: 外部知識需求 vs 模型調適需求: 橫軸(X軸)表示模型調適需求的高低。 縱軸(Y軸)表示外部知識需求的高低。 提示工程(Prompt Engineering): 初步提示(Prompt Preliminary Attempts):這是最基本的提示工程技術,需求最低的模型調適和外部知識。 添加少量示例的思維鏈(Adding Few-Shot Case COT):在初步提示的基礎上,添加一些少量示例來幫助模型理解和回答問題。 初級RAG(Naive RAG): 在這個階段,加入相關的上下文段落,以便模型能夠更好地生成回答。這種方法對外部知識需求較高,但對模型調適需求較低。 高級RAG(Advanced RAG): 包含索引優化、檢索前(pre-retrieve)優化和檢索後優化(post-retrieve)技術。這需要更高的外部知識和一定的模型調適,提供更準確和相關的回答。 模組化RAG(Modular RAG): 結合多個模組的有機組合。這是最複雜的RAG技術,需要高度的外部知識和模型調適。 微調(Fine-Tuning): 檢索器微調(Retriever Fine-Tuning):對檢索器進行微調以改進其性能。 協作微調(Collaborative Fine-Tuning):多個模型之間的協作微調。 生成器微調(Generator Fine-Tuning):專注於生成模型的微調。 綜合應用(All of the Above): 這個區域表示綜合應用RAG和微調技術,需要高度的外部知識和模型調適,達到最優的效果。 Prompt Engineering and Advanced RAG 補充 slide (有機會會講進階RAG) RAG工作流程圖: 主要步驟: 知識切片成Chunk 向量化Chunk入庫 Query檢索知識Chunk 構建Prompts 調用LLM生成回答 工作流程解釋: User(使用者): 使用者輸入查詢(Query),例如:「How do you evaluate the fact that OpenAI's CEO, Sam Altman, went through a sudden dismissal by the board in just three days, and then was rehired by the company, resembling a real-life version of 'Game of Thrones' in terms of power dynamics?」 Input(輸入): 查詢被傳送到LLM(大語言模型)。 Indexing(索引): 文檔被切片成較小的片段(Chunks),然後這些片段被向量化並存儲在數據庫中,形成索引。 Retrieval(檢索): 根據查詢從數據庫中檢索相關文檔,找到與查詢相關的片段(Chunks)。 Generation(生成): 檢索到的相關片段與查詢上下文被結合,形成提示(Prompts),這些提示會提供給LLM以生成回答。 例如,提示可能會包括 Question: How do you evaluate the fact that OpenAI's CEO, Sam Altman, ... dynamics? Please answer the above question based on the following information: Chunk 1: "Sam Altman Returns to OpenAI as CEO, Silicon Valley Drama Resembles the 'Zhen Huan' Comedy" Chunk 2: "The Drama Concludes? Sam Altman to Return as CEO of OpenAI, Board to Undergo Restructuring" Chunk 3: "The Personnel Turmoil at OpenAI Comes to an End: Who Won and Who Lost?" Combine Context and Prompts(結合上下文和提示): 結合查詢和檢索到的上下文片段,LLM根據這些信息生成更準確和有信息量的回答。 Output(輸出): Without RAG(無RAG): 如果沒有RAG技術,LLM可能會回應:「I am unable to provide comments on future events. Currently, I do not have any information regarding the dismissal and rehiring of OpenAI's CEO...」 With RAG(有RAG): 有RAG技術的情況下,LLM會回應:「This suggests significant internal disagreements within OpenAI regarding the company's future direction and strategic decisions. All of these twists and turns reflect power struggles and corporate governance issues within OpenAI...」 主要步驟分解過程 (with code): 知識切片成Chunk:將文檔拆分成較小的片段。 第一步要做的是將 pdf 內文切成小區塊(chunk),使用 langchain 來做切割 from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import CharacterTextSplitter import re # 將一份 PDF 切成多個 chunks # 用 \n 分割, 每個 chunk size 上限為 256 def pdf_to_chunk(path, start_page=1, end_page=None): # 使用 PyPDFLoader 來加載 PDF 文件 loader = PyPDFLoader(path) # 將 PDF 文件的每一頁作為單獨的對象加載到 pages 列表中 pages = loader.load() # 計算 PDF 文件的總頁數 total_pages = len(pages) # 創建一個 CharacterTextSplitter 對象 # 用來將文本分割成多個塊 text_splitter = CharacterTextSplitter( separator="\\n", # 使用 "\n" 作為分隔符 chunk_size=256, # 每個塊的最大字符數為 256 chunk_overlap=20 # 塊之間有 20 個字符的重疊 ) # 如果沒有指定結束頁面,則默認處理到文檔的最後一頁 if end_page is None: end_page = len(pages) # 用來存儲所有塊的列表 lst_text = [] # 循環處理指定頁面範圍內的每一頁 for i in range(start_page-1, end_page): # 將當前頁面的文本分割成多個塊 chuncks = text_splitter.split_text(pages[i].page_content) # 遍歷每一個塊 for chunk in chuncks: # 使用正則表達式去除多餘的空白字符 text = re.sub(r'\\s+', ' ', chunk) # 將處理後的文本塊添加到列表中 lst_text.append(text) # 返回包含所有文本塊的列表 return lst_text # 調用 pdf_to_chunk 函數,將一個 PDF 文件的內容分割為多個文本塊 lst_chunk = pdf_to_chunk('your_file.pdf') # 替換 'your_file.pdf' 為實際的 PDF 文件路徑 # 打印第一個文本塊以驗證結果 print(lst_chunk[0]) 向量化Chunk入庫:將這些片段轉換為向量並存儲在數據庫中。 向量 DB 向量 DB 顧名思義就是用來儲存向量的,你可以把一張圖片或一段文字轉換成一個向量,再儲存至向量 DB 中,如何轉成向量的呢?是透過 Embedding 的技術,這邊就不詳談。 向量 DB 有很多種,可以參考 Ref [1] 有很多介紹,最後推薦的向量 DB 是 Qdrant,因其優秀的效能,RPS 遠高於其它對手,所以我也採用 Qdrant 來進行實作。 Embedding 嵌入 接著要將剛剛切好的每個 chunk 轉成向量,使用 Sentence-Transformers 套件,有各種 embedding 的 model 可以使用,可以參考 Ref [3],我選擇了速度快、較輕量、Performance 不錯的「all-MiniLM-L12-v2」,可以將每個 chunk 轉成 384 維的向量。 from sentence_transformers import SentenceTransformer # 將文本塊轉換為向量表示 def chunk_to_vector(chunks): # 初始化使用的預訓練模型 model = SentenceTransformer('all-MiniLM-L12-v2') # 將文本塊列表轉換為向量表示 arr_vector = model.encode(chunks) # 返回向量列表 return arr_vector # 調用 chunk_to_vector 函數,將文本塊列表轉換為向量表示 # arr_vectors 的形狀為 (n, 384),其中 n 是文本塊的數量,384 是向量的維度 arr_vectors = chunk_to_vector(lst_chunk) # 打印向量數組的形狀以驗證結果 print(arr_vectors.shape) # 例如輸出: (57, 384) 寫入 Qdrant 目前已經將 ””.pdf 切成 57 個 chunk,且已經轉換成向量,接下來就是要將這些向量寫入 Qdrant,供後續使用。 from qdrant_client import QdrantClient from qdrant_client.http import models from qdrant_client.http.models import PointStruct # 連接到 Qdrant 並創建一個集合 def connection(v_dim, collection): # v_dim: 向量維度, collection: 類似於資料庫中的表名稱 # 創建一個 Qdrant 客戶端,連接到本地 Qdrant 伺服器 client = QdrantClient("http://localhost:6333") # 重新創建一個集合(如果存在則刪除並重新創建) client.recreate_collection( collection_name=collection, # 集合名稱,相當於資料庫中的表名稱 vectors_config=models.VectorParams( distance=models.Distance.COSINE, # 使用余弦距離作為向量相似度度量 size=v_dim # 向量的維度,例如 384 ), optimizers_config=models.OptimizersConfigDiff(memmap_threshold=20000), # 優化器配置,用於優化存儲和查詢性能 hnsw_config=models.HnswConfigDiff(on_disk=True, m=16, ef_construct=100) # HNSW 配置,用於加速向量搜索 ) # 返回 Qdrant 客戶端對象,用於後續的操作 return client # 將向量及其對應的數據插入到 Qdrant 集合中 def upsert_vector(client, collection, vectors, data): # vectors: 向量數組 (Array) # data: 與向量對應的數據列表 (List of dictionaries),如 [{"text": "example text"}] # 遍歷所有的向量及其對應的數據 for i, vector in enumerate(vectors): client.upsert( # 插入或更新向量和數據 collection_name=collection, # 指定要插入的集合 points=[PointStruct( # 定義要插入的數據點 id=i, # 每個數據點的唯一標識符,這裡使用索引值 vector=vectors[i], # 要插入的向量 payload=data[i] # 與該向量相關聯的數據(例如文本) )] ) # 准備將文本塊列表(chunks)轉換為包含文本的字典列表 lst_dic_chunk = [] for chunk in lst_chunk: lst_dic_chunk.append({"text": chunk}) # 將每個文本塊轉換為字典,並添加到列表中 # 連接到 Qdrant 伺服器並創建/重新創建集合 qclient = connection(v_dim=384, collection='重點') # 將向量和對應的文本數據插入到 Qdrant 集合中 upsert_vector( client=qclient, collection='重點', # 指定集合名稱 vectors=arr_vectors, # 已經計算好的向量數組 data=lst_dic_chunk # 對應的文本數據列表 ) Query檢索知識Chunk:根據查詢檢索相關的片段。 Semantic Search 語義搜索 接下來關鍵的步驟是 Semantic Search,我們要從向量 DB 中查詢與「問題」最相關的內容。如前言中,我們的問題是:”與參考問件相關的問題”。我們要從向量 DB 中找到與該問題最相關的 3 個 chuuk,最後會將這些 chunk 連同問題一併餵給 OpenAI。 from qdrant_client import QdrantClient from qdrant_client.http import models from sentence_transformers import SentenceTransformer import numpy as np # 創建一個 Qdrant 客戶端,連接到本地 Qdrant 伺服器 qclient = QdrantClient("http://localhost:6333") # 初始化 SentenceTransformer 模型,用於將文本轉換為向量 model = SentenceTransformer('all-MiniLM-L12-v2') # 定義要查詢的問題文本 question = '請問台北捷運 Tpass 1200 月票是何時發行的,請用繁體中文回答' # 將問題文本編碼為向量表示 question_emb = model.encode(question) # 使用 Qdrant 進行向量搜索 # - collection_name: 指定要查詢的集合名稱 ('Tpass') # - query_vector: 用於查詢的向量 (question_emb) # - limit: 限制返回的結果數量 (3) result = qclient.search(collection_name='Tpass', query_vector=question_emb, limit=3) # 打印搜索結果 print(result) 構建Prompts:結合檢索到的片段與查詢形成提示。 調用LLM生成回答:使用提示生成準確的回答。 client = OpenAI() completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "你是一位聰明的 AI 助理"}, {"role": "user", "content": prompt} ] ) answer = completion.choices[0].message.content print(answer) # 根據提供的文件內容,台北捷運 Tpass 1200 月票是在112年6月15日開始發行的。 以下為LangChain+ChatGLM的一個RAG工作流程(上課會仔細講解): Reference:全端 LLM 應用開發(向量資料庫, Hugging Face, OpenAI, Azure ML, LangChain, FastAPI and more) :: 2023 iThome 鐵人賽
-
-
第二章(9/7)正式進入langchain building 和 上一堂coding的深入解說
-
正課第二節:設置開發環境
接下來課程的所有程式碼我都會上傳到我自己的github: https://github.com/kevin801221/Langchain_course_code.git 內容介紹:使用Dotenv設置LangChain、Pinecone、OpenAI和Google's Gemini的開發環境。 目標:學生能夠設置並配置開發環境。 VScode for LLMs system application 安裝 VS Code 以及實用插件 - 編輯器有很多種,那我們的教學都會以 Visual Studio Code ( 簡稱 VS Code ) 做為主要的開發工具。 VS Code 是目前最熱門的編輯器之一,同時擁有了諸多的優點,以下簡單列出新手可以參考的優點。 編輯器輕巧 擁有自訂和擴充功能 介面乾淨簡潔、容易使用 假如覺得 VS Code 不適合你,想要用其他工具的話也可以,像是 WebStorm 或是 Sublime text。 透過 VS Code 的官網直接下載,官網會直接根據你的作業系統給予下載位置,如果不正確的話也可以透過選單自己選擇系統。 下載位置:官網 安裝過程中可以把下方四個選項都打勾,這樣在安裝完成以後,可以透過右鍵的選單把資料夾以 VS Code 打開,會方便許多。 安裝完成以後,我們就可以把它打開來,進行一些簡單的設定以及安裝插件 開啟自動格式化 (可選) 自動格式化的用意會讓你的程式碼更好閱讀,之後還可以搭配一些插件,讓你的程式碼在格式化以後符合規範。 點擊左下角的齒輪後,選擇 Settings 在輸入框打上 format 後,勾選範例圖中三個藍色選項 VS Code 有許多插件可以使我們的開發流程更加順利,接下來推薦一些在剛開始進行網頁前端開發時會使用到的插件。 (以下只會簡單敘述套件功用及附上連結,更詳細的可以觀看官方說明) 左側選擇插件,可以查看目前安裝了那些插件,上方的搜尋框可以尋找想安裝的插件 語言插件 Chinese (Traditional) Language Pack for Visual Studio Code (推薦還是多看看英文,除非真的完全不行,否則不推薦安裝😓) 中文化介面 接下來是AI輔助工具,在VScode中如果安裝可以輔助自己的coding路上平平安安順順利利(如果好好利用的話) 第一個介紹的是可以根本機端 "ollama" 裡面所支援的大型語言模型串連的工具: Continue - codestral, Cluade, and more ... 第二個是:chatGPT Copilot 可以無痛讓你在VScode中透過openai_api_key跟openai中提供的商業化模型完美溝通,大袋輔助coding工作。 再來就是我們必須創在"虛擬環境" 來分割各種工作狀態,將每個不同任務會用到的依賴庫分割開來,大大程度地解決了版本的問題。 你哪些方法創造虛擬環境呢? 這裡列出三種常見的方法: 1. `conda` 使用方法 1. 安裝 Anaconda 或 Miniconda 2. 創建虛擬環境: ```bash conda create --name myenv python=3.8 ``` 3. 激活虛擬環境: ```bash conda activate myenv ``` 4. 安裝包: ```bash conda install numpy pandas ``` 5. 停用虛擬環境: ```bash conda deactivate ``` 優點 - 跨平台支持:支持 Windows, macOS 和 Linux。 - 包管理:除了 Python 包,還可以管理非 Python 包(例如,R,C++ 庫)。 - 環境隔離:能有效隔離不同項目的依賴包。 - 方便:自帶大多數常用科學計算包。 缺點 - 體積大:Anaconda 佔用磁碟空間較大。 - 速度慢:有時候安裝和更新包的速度較慢。 2. `venv` 使用方法 1. 創建虛擬環境: ```bash python -m venv myenv ``` 2. 激活虛擬環境: - Windows: ```bash .\myenv\Scripts\activate ``` - macOS 和 Linux: ```bash source myenv/bin/activate ``` 3. 安裝包: ```bash pip install numpy pandas ``` 4. 停用虛擬環境: ```bash deactivate ``` 優點 - 內置工具:Python 3.3+ 內置,無需額外安裝。 - 輕量級:不佔用太多磁碟空間。 - 簡單易用:適合簡單的項目環境管理。 缺點 - 功能有限:僅管理 Python 包,不能管理非 Python 包。 - 依賴管理較弱:需要手動管理依賴包版本。 3. `poetry` 使用方法 1. 安裝 Poetry: ```bash curl -sSL https://install.python-poetry.org | python3 - ``` 2. 創建虛擬環境和項目: ```bash poetry new myproject cd myproject ``` 3. 安裝依賴包: ```bash poetry add numpy pandas ``` 4. 激活虛擬環境: ```bash poetry shell ``` 5. 停用虛擬環境: ```bash exit ``` 優點 - 依賴管理:自動解決依賴衝突,生成 `poetry.lock` 文件。 - 包發布:支持發布 Python 包到 PyPI。 - 簡單配置:使用 `pyproject.toml` 文件進行配置,統一管理項目依賴和元數據。 缺點 - 學習曲線:相對於 `venv` 和 `conda`,需要學習新的命令和配置文件。 - 性能問題:在某些情況下,依賴解析和安裝速度可能較慢。 總結 根據你的需求和項目複雜度,可以選擇合適的工具來管理虛擬環境。 #必要模組包 openai langchain langchain_openai langchain_experimental langchainhub pinecone-client python-dotenv tiktoken docx2txt pypdf requests numpy pandas 以上可包裝成 一個"requirements.txt"檔案, 可以在終端機之下直接執行pip install -r requirements.txt 它就可以直接安裝寫在requirements.txt檔中的所有模組包。 設置開發環境:打造你的AI應用基礎設施 在這節課中,我們將深入了解如何使用python-dotenv(環境變數導入)設置LangChain、FAISS, Pinecone、OpenAI和Google's Gemini的開發環境。無論你是剛入門的AI開發者,還是經驗豐富的老手,這個過程將幫助你打造穩定的開發基礎,為你的AI應用奠定良好的基礎。 內容介紹 我們將一步一步地教你如何配置開發環境,確保你能夠順利地使用LangChain、FAISS、Pinecone、OpenAI和Google's Gemini這些工具。這些工具是現代AI開發中不可或缺的一部分,掌握它們的配置方法將使你的開發工作更加高效。 課程目標 學生能夠設置並配置開發環境。 熟悉使用python-dotenv管理環境變量。 LangChain、FAISS、Pinecone、OpenAI和Google's Gemini的基本配置。 (Ref 課程開始! 1. Dotenv 介紹 Dotenv 是一個方便的工具,用於將環境變量存儲在 .env 文件中。這樣可以讓你輕鬆地管理和使用環境變量,而不必每次都手動設置它們。 2. 設置 LangChain LangChain 是一個強大的框架,旨在簡化自然語言處理(NLP)應用的開發。 # 安裝 LangChain pip install langchain # 在 .env 文件中設置 LangChain 相關的環境變量 LANGCHAIN_API_KEY="your_langchain_api_key" (這會是連接到langsmith) OPENAI_API_KEY = "" GOOGLE_API_KEY = "" 在你的 Python 文件中,可以使用以下方式讀取環境變量: from dotenv import load_dotenv import os load_dotenv() #langchain_api_key = os.getenv("LANGCHAIN_API_KEY") #openai = os. 3. 設置 Faiss Facebook AI 相似性搜索 (FAISS) 是一個用於高效相似性搜索和密集向量聚類的庫。它包含的演算法可以在任何大小的向量集中進行搜索,直到可能不適合 RAM 的向量集。它還包含用於評估和參數調整的支援代碼。 Setup 設置 集成存在於langchain-community包中。我們還需要安裝 faiss 包本身。我們可以使用以下工具安裝它們: pip install -qU langchain-community faiss-cpu 如果你想獲得一流的模型調用自動跟蹤,你也可以通過取消下面的註釋來設置你的LangSmith API密鑰。 # os.environ["LANGCHAIN_TRACING_V2"] = "true" # os.environ["LANGCHAIN_API_KEY"] = getpass.getpass() 初始化 !pip install -qU langchain-openai #openAI import getpass os.environ["OPENAI_API_KEY"] = getpass.getpass() from langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings(model="text-embedding-3-large") import faiss from langchain_community.docstore.in_memory import InMemoryDocstore from langchain_community.vectorstores import FAISS index = faiss.IndexFlatL2(len(embeddings.embed_query("hello world"))) vector_store = FAISS( embedding_function=embeddings, index=index, docstore=InMemoryDocstore(), index_to_docstore_id={}, ) 管理向量存儲 將專案添加到向量存儲: from uuid import uuid4 from langchain_core.documents import Document document_1 = Document( page_content="I had chocalate chip pancakes and scrambled eggs for breakfast this morning.", metadata={"source": "tweet"}, ) document_2 = Document( page_content="The weather forecast for tomorrow is cloudy and overcast, with a high of 62 degrees.", metadata={"source": "news"}, ) document_3 = Document( page_content="Building an exciting new project with LangChain - come check it out!", metadata={"source": "tweet"}, ) document_4 = Document( page_content="Robbers broke into the city bank and stole $1 million in cash.", metadata={"source": "news"}, ) document_5 = Document( page_content="Wow! That was an amazing movie. I can't wait to see it again.", metadata={"source": "tweet"}, ) document_6 = Document( page_content="Is the new iPhone worth the price? Read this review to find out.", metadata={"source": "website"}, ) document_7 = Document( page_content="The top 10 soccer players in the world right now.", metadata={"source": "website"}, ) document_8 = Document( page_content="LangGraph is the best framework for building stateful, agentic applications!", metadata={"source": "tweet"}, ) document_9 = Document( page_content="The stock market is down 500 points today due to fears of a recession.", metadata={"source": "news"}, ) document_10 = Document( page_content="I have a bad feeling I am going to get deleted :(", metadata={"source": "tweet"}, ) documents = [ document_1, document_2, document_3, document_4, document_5, document_6, document_7, document_8, document_9, document_10, ] uuids = [str(uuid4()) for _ in range(len(documents))] vector_store.add_documents(documents=documents, ids=uuids) 從向量存儲中刪除專案: vector_store.delete(ids=[uuids[-1]]) 查詢向量存儲 一旦創建了您的向量存儲並添加了相關文檔,您很可能希望在鏈或代理運行期間查詢它。 相似性搜索 可以按以下步驟執行簡單的相似性搜索並對元數據進行過濾: 帶分數的相似性搜索 results = vector_store.similarity_search_with_score( "Will it be hot tomorrow?", k=1, filter={"source": "news"} ) for res, score in results: print(f"* [SIM={score:3f}] {res.page_content} [{res.metadata}]") 合併:您還可以合併兩個 FAISS 向量庫 db1 = FAISS.from_texts(["foo"], embeddings) db2 = FAISS.from_texts(["bar"], embeddings) db1.docstore._dict db2.docstore._dict db1.merge_from(db2) db1.docstore._dict Reference: https://api.python.langchain.com/en/latest/vectorstores/langchain_community.vectorstores.faiss.FAISS.html 3. 設置 Pinecone Pinecone 是一個向量數據庫,適用於大規模的相似性搜索和推薦系統。 # 安裝 Pinecone pip install pinecone-client # 在 .env 文件中設置 Pinecone 相關的環境變量 PINECONE_API_KEY="your_pinecone_api_key" 在你的 Python 文件中,可以使用以下方式讀取環境變量: pinecone_api_key = os.getenv("PINECONE_API_KEY") #Pinecone 索引周圍有一個包裝器,允許您將其用作向量存儲,無論是用於語義搜索還是示例選擇。 from langchain_pinecone import PineconeVectorStore 有關 Pinecone 向量庫的更詳細演練,請參閱此筆記本 %pip install --upgrade --quiet lark %pip install --upgrade --quiet pinecone-notebooks pinecone-client==3.2.2 # Connect to Pinecone and get an API key. from pinecone_notebooks.colab import Authenticate Authenticate() import os api_key = os.environ["PINECONE_API_KEY"] import getpass os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:") from pinecone import Pinecone, ServerlessSpec api_key = os.getenv("PINECONE_API_KEY") or "PINECONE_API_KEY" index_name = "langchain-self-retriever-demo" pc = Pinecone(api_key=api_key) from langchain_core.documents import Document from langchain_openai import OpenAIEmbeddings from langchain_pinecone import PineconeVectorStore embeddings = OpenAIEmbeddings() # create new index if index_name not in pc.list_indexes().names(): pc.create_index( name=index_name, dimension=1536, metric="cosine", spec=ServerlessSpec(cloud="aws", region="us-east-1"), ) #檢索用Docs 設定 docs = [ Document( page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose", metadata={"year": 1993, "rating": 7.7, "genre": ["action", "science fiction"]}, ), Document( page_content="Leo DiCaprio gets lost in a dream within a dream within a dream within a ...", metadata={"year": 2010, "director": "Christopher Nolan", "rating": 8.2}, ), Document( page_content="A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea", metadata={"year": 2006, "director": "Satoshi Kon", "rating": 8.6}, ), Document( page_content="A bunch of normal-sized women are supremely wholesome and some men pine after them", metadata={"year": 2019, "director": "Greta Gerwig", "rating": 8.3}, ), Document( page_content="Toys come alive and have a blast doing so", metadata={"year": 1995, "genre": "animated"}, ), Document( page_content="Three men walk into the Zone, three men walk out of the Zone", metadata={ "year": 1979, "director": "Andrei Tarkovsky", "genre": ["science fiction", "thriller"], "rating": 9.9, }, ), ] vectorstore = PineconeVectorStore.from_documents( docs, embeddings, index_name="langchain-self-retriever-demo" ) 創建我們的自查詢檢索器:現在我們可以實例化我們的檢索器。為此,我們需要預先提供一些有關文檔支援的元數據欄位的資訊,以及文檔內容的簡短描述。 from langchain.chains.query_constructor.base import AttributeInfo from langchain.retrievers.self_query.base import SelfQueryRetriever from langchain_openai import OpenAI metadata_field_info = [ AttributeInfo( name="genre", description="The genre of the movie", type="string or list[string]", ), AttributeInfo( name="year", description="The year the movie was released", type="integer", ), AttributeInfo( name="director", description="The name of the movie director", type="string", ), AttributeInfo( name="rating", description="A 1-10 rating for the movie", type="float" ), ] document_content_description = "Brief summary of a movie" llm = OpenAI(temperature=0) retriever = SelfQueryRetriever.from_llm( llm, vectorstore, document_content_description, metadata_field_info, verbose=True ) 測試一下: # This example only specifies a relevant query retriever.invoke("What are some movies about dinosaurs") 4. 設置 OpenAI OpenAI 提供了強大的 GPT 模型,適用於各種自然語言處理任務。 # 安裝 OpenAI pip install openai # 在 .env 文件中設置 OpenAI 相關的環境變量 OPENAI_API_KEY="your_openai_api_key" 在你的 Python 文件中,可以使用以下方式讀取環境變量: openai_api_key = os.getenv("OPENAI_API_KEY") 5. 設置 Google's Gemini Gemini (google.com) (Gemini playground) 至於Gemini的開發用接口Api 要到自己的google開發者平台取得, "Google AI Gemini API | Gemma open models | Google for Developers | Google AI for Developers" Google's Gemini 是一個先進的人工智慧模型,由Google開發並推出。它是一個多模態AI系統,能夠處理文本、圖像、視訊和音頻等多種類型的輸入。Gemini的強大之處在於其靈活性和廣泛的應用範圍,從自然語言處理到複雜的推理任務,都能表現出色。在開發環境中整合Gemini,可以為您的項目帶來強大的AI能力。尤其是它可以應付非常長的上下文輸入輸出,表現優異,價錢便宜, 一半的模型呼叫甚至免費,很適合學生族群。 # 在 .env 文件中設置 Gemini 相關的環境變量 GEMINI_API_KEY="your_gemini_api_key" 在你的 Python 文件中,可以使用以下方式讀取環境變量: gemini_api_key = os.getenv("GEMINI_API_KEY") 第二部分:整合 現在我們已經了解了如何設置這些工具,接下來讓我們進行實際操作。以下是步驟指南: 步驟1:創建並配置 .env 文件 打開你的代碼編輯器,創建一個新的 .env 文件,並將你的API密鑰和其他配置項目填入其中: # .env 文件 LANGCHAIN_API_KEY="your_langchain_api_key" PINECONE_API_KEY="your_pinecone_api_key" OPENAI_API_KEY="your_openai_api_key" GEMINI_API_KEY="your_gemini_api_key" 步驟2:編寫 Python 代碼來讀取這些環境變量 創建一個新的 Python 文件,例如 config.py,並使用 python-dotenv 讀取 .env 文件中的變量: # config.py from dotenv import load_dotenv import os load_dotenv() langchain_api_key = os.getenv("LANGCHAIN_API_KEY") pinecone_api_key = os.getenv("PINECONE_API_KEY") openai_api_key = os.getenv("OPENAI_API_KEY") gemini_api_key = os.getenv("GEMINI_API_KEY") print(f"LangChain API Key: {langchain_api_key}") print(f"Pinecone API Key: {pinecone_api_key}") print(f"OpenAI API Key: {openai_api_key}") print(f"Gemini API Key: {gemini_api_key}") 運行 config.py 文件,確認你的環境變量已正確讀取: python config.py 步驟3:使用這些API進行基本操作 接下來,你可以嘗試使用這些API進行一些基本操作,以確保它們已正確配置。例如,使用OpenAI的GPT-3生成一些文本: # 使用 OpenAI API import openai openai.api_key = openai_api_key response = openai.Completion.create( engine="davinci", prompt="Hello, world!", max_tokens=50 ) print(response.choices[0].text.strip()) 總結 恭喜你!現在你已經成功設置並配置了你的開發環境,並且能夠使用LangChain、Pinecone、OpenAI和Google's Gemini進行開發。這只是開始,接下來你可以進一步探索這些工具的強大功能,打造出色的AI應用。 如果你在設置過程中遇到任何問題,不要猶豫,隨時提問。我們在這裡為你提供支持,確保你能夠順利完成這個過程。 實作小提示 定期更新你的API密鑰,確保安全性。 使用虛擬環境(如venv或conda)來管理你的開發環境,避免依賴衝突。 熟悉各個工具的官方文檔,這樣你可以更靈活地運用它們。
-
正課第三節: Langchain基礎一定要會的模塊邏輯到Langserve簡單服務部署
#pip install -r requirements.txt langchain-openai==0.1.0 langserve[all]==0.1.0 fastapi==0.95.1 uvicorn==0.23.0 sse_starlette==1.5.2 python-multipart==0.0.6 langdetect==1.0.9 python-dotenv==1.0.0 詳細教學內容 這邊除了一個一個介紹模組和各種串接大型語言的library庫還有模組之外,最後會帶大家跑一個設計過的Langchain python 筆記本腳本檔(.ipynb檔 )讓大家在vscode環境種探索 Langchain的基礎知識,這一步很重要。最後目的如果是要接到比賽的應用,一定會要知道code怎麼建立。 1. 介紹如何使用語言模型 首先,我們來學習如何單獨使用語言模型。LangChain 支援多種語言模型,你可以根據需求選擇合適的模型進行應用,例如 OpenAI, Anthropic, Azure 等。 2. 設置開發環境 安裝所需的套件並配置環境: pip install -qU langchain-openai 使用 getpass 模組輸入 API 密鑰: import getpass import os os.environ["OPENAI_API_KEY"] = getpass.getpass() 引入所需的 LangChain 模組: from langchain_openai import ChatOpenAI model = ChatOpenAI(model="gpt-4") 3. 使用 LangChain 實現語言模型的直接調用 現在,我們可以直接調用語言模型。ChatOpenAI 是 LangChain 的 "Runnable" 實例,這意味著它提供了一個標準接口來與模型進行互動。我們可以將一組訊息傳遞給 .invoke 方法來調用模型。 from langchain_core.messages import HumanMessage, SystemMessage messages = [ SystemMessage(content="將以下內容從英語翻譯成義大利語"), HumanMessage(content="hi!"), ] model.invoke(messages) 這段程式碼將訊息傳遞給模型並得到翻譯結果。 4. 使用輸出解析器處理模型輸出 模型的回應通常包含額外的元數據,如果我們只需要文字回應,可以使用輸出解析器來簡化這個過程。 from langchain_core.output_parsers import StrOutputParser parser = StrOutputParser() result = model.invoke(messages) parser.invoke(result) 這樣就能直接獲得翻譯後的結果 'Ciao!'。 補充: StrOutputParser() 我們可以使用|運算子輕鬆創建鏈。在 LangChain 中使用|運算子將兩個元素組合在一起。 chain = model | parser chain.invoke(messages) 如果我們現在看一下 LangSmith,我們可以看到鏈有兩個步驟:首先調用語言模型,然後將結果傳遞給輸出解析器。我們可以看到LangSmith 跟蹤 5. 創建提示模板 現在,我們將消息清單直接傳遞到語言模型中。此消息清單來自何處?通常,它是由使用者輸入和應用程式邏輯的組合構建的。此應用程式邏輯通常採用原始使用者輸入,並將其轉換為準備傳遞給語言模型的消息清單。常見轉換包括添加系統消息或使用使用者輸入設置範本格式。 PromptTemplates 是 LangChain 中的一個概念,旨在協助進行這種轉換。它們接收原始使用者輸入並返回準備傳遞到語言模型的數據(提示)。 提示模板 (PromptTemplates) 可以幫助將原始的用戶輸入轉換成準備傳遞給語言模型的訊息列表。以下是一個範例: from langchain_core.prompts import ChatPromptTemplate #首先,讓我們創建一個字串,我們將將其格式化為系統消息: system_template = "將以下內容翻譯成 {language}:" #接下來,我們可以創建 PromptTemplate。這將是 system_template 的組合以及一個更簡單的範本,用於放置要翻譯的文本 prompt_template = ChatPromptTemplate.from_messages( [("system", system_template), ("user", "{text}")] ) #此提示範本的輸入是字典。我們可以單獨使用這個提示範本,看看它自己做了什麼 result = prompt_template.invoke({"language": "italian", "text": "hi"}) result.to_messages() 這樣,我們可以獲得準備傳遞給語言模型的訊息。 6. 使用 LCEL 鏈接多個模組 我們可以使用 | 運算符將模型和解析器等組件鏈接起來: #使用 LCEL 將元件連結在一起 chain = prompt_template | model | parser chain.invoke({"language": "italian", "text": "hi"}) 這段代碼將輸入傳遞給提示模板、模型和解析器,最終獲得翻譯結果 'Ciao!'。 7. 使用 LangServe 部署自己的服務應用 現在我們已經構建了一個應用程式,我們需要為它提供服務。這就是 LangServe 的用武之地。LangServe 幫助開發人員將 LangChain 鏈部署為 REST API。使用 LangChain 不需要使用 LangServe,但在本指南中,我們將展示如何使用 LangServe 部署應用程式。 雖然的1~6部分旨在在 Jupyter Notebook 或腳本中運行,但現在我們將退出該部分。我們將創建一個 Python 檔,然後從命令行與它進行交互。 我們可以使用 LangServe 將這個應用部署為 REST API。首先,安裝 LangServe: 為了為我們的應用程式創建伺服器,我們將創建一個serve.py檔。這將包含用於為應用程式提供服務的Logic。它由三部分組成: 1.我們上面剛剛構建的鏈的定義 2.我們的 FastAPI 應用程式 3.為鏈提供服務的路由的定義,該定義通過langserve.add_routes完成 pip install "langserve[all]" 創建一個 serve.py 文件,定義服務邏輯: #!/usr/bin/env python from fastapi import FastAPI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_openai import ChatOpenAI from langserve import add_routes # 1. Create prompt template system_template = "將以下內容翻譯成 {language}:" prompt_template = ChatPromptTemplate.from_messages([ ('system', system_template), ('user', '{text}') ]) # 2. Create model model = ChatOpenAI() # 3. Create parser parser = StrOutputParser() # 4. Create chain chain = prompt_template | model | parser # 5. App definition app = FastAPI( title="LangChain 伺服器", version="1.0", description="使用 LangChain 的 Runnable 接口構建的簡單 API 伺服器", ) # 5. Adding chain route add_routes( app, chain, path="/chain", ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000) 啟動伺服器後,處使應用將會在 http://localhost:8000 運行。 8. 實作範例與測試 部署成功後,你可以通過以下程式碼在客戶端與伺服器進行互動: from langserve import RemoteRunnable remote_chain = RemoteRunnable("http://localhost:8000/chain/") remote_chain.invoke({"language": "italian", "text": "hi"}) 這段程式碼將會返回翻譯結果 'Ciao'。 補充說明: "部署成功後,你可以通過以下程式碼在客戶端與伺服器進行互動" 的意思是,當您成功啟動伺服器後,可以在其他 Python 腳本或應用程式中與這個 API 伺服器進行互動。 具體來說,當您使用 uvicorn.run(app, host="localhost", port=8000) 成功啟動 FastAPI 伺服器後,該伺服器會在 http://localhost:8000/chain/ 上運行。這個伺服器提供了一個可以接受請求的 API 路徑 /chain/。 互動流程: 部署 FastAPI 伺服器:當您啟動伺服器後,它開始接收來自其他客戶端的請求。 客戶端互動:通過使用 RemoteRunnable 類(例如在另一個 Python 腳本或應用程式中)發送請求,這裡您提供伺服器的 URL http://localhost:8000/chain/。這樣就能通過客戶端程式碼與伺服器進行互動,發送翻譯請求並獲取結果。 客戶端互動範例解釋: from langserve import RemoteRunnable # 定義要連接的伺服器路徑 remote_chain = RemoteRunnable("http://localhost:8000/chain/") # 發送一個翻譯請求,將 "hi" 翻譯成義大利語 response = remote_chain.invoke({"language": "italian", "text": "hi"}) # 打印翻譯結果 print(response) # 預期結果: 'Ciao' 這段客戶端程式碼負責向已經運行的伺服器發送請求,伺服器會根據請求返回翻譯結果。這就是客戶端與伺服器之間的互動過程。因此,當伺服器在本地運行時,您可以在其他程式中使用這些方法來進行 API 呼叫,實現雙向通信。 總結來說,部署伺服器是啟動它以處理請求的過程,而客戶端程式碼則是實際向伺服器發送請求並接收回應的步驟。 補充建置langserve: 要多安裝兩個套件: 1. python-multipart 2. fastapi 3.sse_starlette #!/usr/bin/env python from fastapi import FastAPI, HTTPException, Form, Body from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_openai import ChatOpenAI from langserve import add_routes import logging from typing import List, Optional from langdetect import detect from dotenv import load_dotenv import os load_dotenv() openai_api_key=os.getenv('OPENAI_API_KEY', 'YourAPIKey') # 設置日誌記錄 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # 1. 創建多語言翻譯提示模板 system_template = "將以下內容翻譯成 {language}:" prompt_template = ChatPromptTemplate.from_messages([ ('system', system_template), ('user', '{text}') ]) # 2. 創建模型 model = ChatOpenAI() # 3. 創建解析器 parser = StrOutputParser() # 4. 創建翻譯鏈 translation_chain = prompt_template | model | parser # 5. App 定義 app = FastAPI( title="LangChain 增強版文字翻譯伺服器", version="2.0", description="使用 LangChain 的 Runnable 接口構建的多功能文字翻譯 API 伺服器", ) # 根路徑處理 @app.get("/") async def read_root(): return {"message": "Welcome to the LangChain Translation API. Use /translate/ to access the translation service."} # 6. 批量翻譯處理函數 async def handle_batch_translation(language: str, texts: List[str]) -> List[str]: results = [] for text in texts: try: logger.info(f"翻譯文本: {text} -> {language}") result = await translation_chain.arun({"language": language, "text": text}) logger.info(f"翻譯結果: {result}") results.append(result) except Exception as e: logger.error(f"翻譯過程中出現錯誤: {e}") raise HTTPException(status_code=500, detail=f"翻譯文本 '{text}' 時出現錯誤") return results # 7. 翻譯語言檢測 def detect_language(text: str) -> str: try: detected_language = detect(text) logger.info(f"檢測到的語言: {detected_language}") return detected_language except Exception as e: logger.error(f"語言檢測失敗: {e}") raise HTTPException(status_code=500, detail="語言檢測失敗") # 8. 翻譯路由 @app.post("/translate/") async def translate( language: str = Form(...), texts: Optional[List[str]] = Body(None), text: Optional[str] = Form(None), auto_detect: bool = Form(False) ): if texts: if auto_detect: detected_languages = [detect_language(t) for t in texts] return {"detected_languages": detected_languages, "translations": await handle_batch_translation(language, texts)} return await handle_batch_translation(language, texts) elif text: if auto_detect: detected_language = detect_language(text) return {"detected_language": detected_language, "translation": await handle_batch_translation(language, [text])} return {"translation": await handle_batch_translation(language, [text])} else: raise HTTPException(status_code=400, detail="必須提供至少一段文本進行翻譯") # 9. 添加鏈路由 add_routes( app, translation_chain, path="/chain", ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000) 補充內容 : 測試方式 (也可以使用postman 測試請求有沒有正常) 單個文本翻譯: 使用 POST 請求來翻譯單個文本,確保正確返回翻譯結果。 curl -X POST "http://localhost:8000/translate/" -F "language=zh" -F "text=Hello" 多個文本翻譯: 使用 POST 請求來翻譯多個文本,確保每個文本都返回正確的翻譯結果。 curl -X POST "http://localhost:8000/translate/" -F "language=zh" -H "Content-Type: application/json" -d '{"texts": ["Hello", "How are you?"]}' 錯誤處理測試: 不提供 text 或 texts,檢查是否正確返回錯誤信息。 使用 LangServe 部署 LangChain 為 REST API 的完整教學 概述 LangServe 是一個強大的工具,專為開發者設計,用於將 LangChain 的 Runnable 接口和鏈部署為 REST API。這個庫與 FastAPI 深度整合,並使用 Pydantic 進行數據驗證。此外,它還提供了一個客戶端,可以用於調用部署在伺服器上的 Runnables。對於 JavaScript 開發者,LangChain.js 中還提供了相應的客戶端。 在這篇文章中,我將詳細介紹 LangServe 的主要功能、安裝步驟、以及如何快速啟動一個 LangServe 項目。你將學會如何利用 LangServe 部署一個強大的 API 伺服器,並且瞭解如何使用內建的功能來調試和擴展你的應用。 主要功能 自動推斷輸入和輸出模式:LangServe 能夠自動從 LangChain 對象中推斷輸入和輸出的 JSON 結構,並在每次 API 調用時強制執行,提供豐富的錯誤訊息。 API 文件頁面:自動生成 JSON Schema 和 Swagger 格式的 API 文件頁面,方便開發者瞭解 API 的結構和使用方法。 高效的端點: /invoke:執行單次請求的端點。 /batch:支持多請求批量處理。 /stream:支持流式數據處理,適合處理大量並發請求。 中間步驟流式日誌:使用 /stream_log 端點來流式傳輸你的鏈或代理的所有(或部分)中間步驟,方便調試。 流式事件支持:在 0.0.40 版本後,支持 /stream_events 端點,使得流式處理更加簡單,不需要解析 /stream_log 的輸出。 內建 Playground 頁面:在 /playground/ 路徑下提供一個內建的 Playground 頁面,支持流式輸出和中間步驟的展示。 LangSmith 追蹤支持:內建支持 LangSmith 追蹤功能,只需設置 API 密鑰即可使用。 客戶端 SDK:你可以使用客戶端 SDK 調用 LangServe 伺服器,讓其行為如同本地運行的 Runnable 一樣(或者直接調用 HTTP API)。 安裝 安裝 LangServe 你可以輕鬆地通過 pip 來安裝 LangServe,不論是用於伺服器端還是客戶端的開發。 pip install "langserve[all]" 如果你只需要伺服器端或客戶端,可以選擇安裝: pip install "langserve[client]" # 安裝客戶端代碼 pip install "langserve[server]" # 安裝伺服器代碼 安裝 LangChain CLI LangChain CLI 可以快速啟動一個 LangServe 項目。你可以使用以下命令來安裝或更新 LangChain CLI: pip install -U langchain-cli 快速入門:創建你的第一個 LangServe 應用 第一步:創建一個新的應用 使用 LangChain CLI 創建一個新的應用: langchain app new my-app 這將創建一個包含基本結構的應用,你可以在此基礎上進行開發。 第二步:定義你的 Runnable 打開 server.py 文件,並使用 add_routes 函數將你的 Runnable 添加到應用中。 from fastapi import FastAPI from langserve import add_routes from langchain_openai import ChatOpenAI app = FastAPI() # 定義並添加你的 Runnable model = ChatOpenAI(model="gpt-3.5-turbo") add_routes(app, model, path="/openai") 第三步:添加第三方包 如果需要使用第三方包(如 langchain-openai),你可以使用 Poetry 進行管理: poetry add langchain-openai 第四步:設置環境變量 設置必要的環境變量,例如 OpenAI 的 API Key: export OPENAI_API_KEY="sk-..." 第五步:啟動你的應用 使用 Poetry 啟動你的應用: poetry run langchain serve --port=8100 現在,你的 LangServe 伺服器應該已經運行起來了,可以使用瀏覽器或工具(如 Postman)訪問 http://localhost:8100/ 來查看服務狀態。 案例應用 下面是一個簡單的伺服器範例,它部署了 OpenAI 和 Anthropic 的聊天模型,並使用 Anthropic 模型來生成一個關於特定話題的笑話。 #!/usr/bin/env python from fastapi import FastAPI from langchain.prompts import ChatPromptTemplate from langchain.chat_models import ChatAnthropic, ChatOpenAI from langserve import add_routes app = FastAPI( title="LangChain Server", version="1.0", description="A simple API server using LangChain's Runnable interfaces", ) add_routes( app, ChatOpenAI(model="gpt-3.5-turbo-0125"), path="/openai", ) add_routes( app, ChatAnthropic(model="claude-3-haiku-20240307"), path="/anthropic", ) model = ChatAnthropic(model="claude-3-haiku-20240307") prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}") add_routes( app, prompt | model, path="/joke", ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000) 設置 CORS 中介軟體 如果你打算從瀏覽器調用這些端點,還需要設置 CORS 頭: from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], expose_headers=["*"], ) 這樣可以保證你的 API 能夠被來自不同域名的請求訪問。 注意事項與限制 與 LangGraph 的兼容性 LangServe 主要用於部署簡單的 Runnables 和處理 LangChain 核心中的已知基本結構。如果你需要部署 LangGraph 應用,應該考慮使用 LangGraph Cloud(目前處於測試階段),這會更適合你的需求。 客戶端回調尚不支持 目前,LangServe 還不支持對伺服器端事件進行客戶端回調功能,這可能會在未來的版本中進一步完善。 總結 LangServe 是一個強大且靈活的工具,可以讓你輕鬆地將 LangChain 的功能部署為 REST API。在這篇教學中,我們介紹了 LangServe 的主要功能,如何安裝和配置 LangServe,以及如何創建一個基本的 API 伺服器。希望這篇文章能幫助你快速上手 LangServe,並在你的項目中發揮其強大的功能。 如果你有任何問題或建議,歡迎在評論區留言或聯繫我。Happy coding! summary: 在本教學中,我們學習了如何創建一個簡單的 LLM 應用,從語言模型的調用,到解析輸出、創建提示模板,再到使用 LCEL 鏈接模組,並最終部署為 REST API。 本課程只是入門但包含到了自己架設Langchain環境內建的LangServe服務,未來可以進一步學習如何擴展這些應用並應用於更複雜的場景,我們的目的就是從簡單但是完整的應用場景,透過創意想像一步一步推到較複雜甚至是比賽會應用到的場景。
-
正課第四節(1) - 進階拆解和應用這些Langchain 組建(一步一步熟悉直到可以應用)
正課的第四節,我們會拆很多堂課來讓大家慢慢熟悉Langchain甚至可以配合其他強大的工具如ollama等等平台,讓大家值回票價。 話不多說 直接上code: (基礎模組的再練習)進入4-1 重點1.要很熟悉使用langchain的三大模組: 1.prompt template 2.models chain 3.output parsers 重點 2.Langsmith 要熟悉 重點3.啟動你的服務 :LangServe code for learn: Note : 要很習慣一開始就透過 load_dotenv()載入必要api key import os from dotenv import load_dotenv load_dotenv() os.environ['OPENAI_API_KEY']=os.getenv("OPENAI_API_KEY") ## Langsmith Tracking os.environ["LANGCHAIN_API_KEY"]=os.getenv("LANGCHAIN_API_KEY") os.environ["LANGCHAIN_TRACING_V2"]="true" os.environ["LANGCHAIN_PROJECT"]=os.getenv("LANGCHAIN_PROJECT") #一樣先用 from langchain_openai import ChatOpenAI llm=ChatOpenAI(model="gpt-4o") print(llm) Note: 還記得invoke嗎? ## Input and get response form LLM result=llm.invoke("What is generative AI?") print(result) 聊天的提示模板建立: 角色扮演 ### Chatprompt Template 設計 from langchain_core.prompts import ChatPromptTemplate prompt=ChatPromptTemplate.from_messages( [ ("system","You are an expert AI Engineer. Provide me answers based on the questions"), ("user","{input}") ] ) prompt type(response) 輸出解析器建立: ## stroutput Parser from langchain_core.output_parsers import StrOutputParser output_parser=StrOutputParser() chain=prompt|llm|output_parser response=chain.invoke({"input":"Can you tell me about Langsmith?"}) print(response) 4-2 GenAI搭配Langchain的簡單應用 import os from dotenv import load_dotenv load_dotenv() os.environ['OPENAI_API_KEY']=os.getenv("OPENAI_API_KEY") ## Langsmith Tracking os.environ["LANGCHAIN_API_KEY"]=os.getenv("LANGCHAIN_API_KEY") os.environ["LANGCHAIN_TRACING_V2"]="true" os.environ["LANGCHAIN_PROJECT"]=os.getenv("LANGCHAIN_PROJECT") ## Data Ingestion--From the website we need to scrape the data from langchain_community.document_loaders import WebBaseLoader Note:其實有非常非常多的loader工具可以用,我們這邊拿一個網頁的載入器來當案例 上課會直接進入網站中overview: https://python.langchain.com/v0.1/docs/modules/data_connection/document_loaders/ 接下來我們就來爬看看大家可能相對不叫不熟的langsmith的文件! loader=WebBaseLoader("https://docs.smith.langchain.com/tutorials/Administrators/manage_spend") loader 檢查一下目前被包進去變成langchain的資料結構長啥樣: docs=loader.load() docs #切割囉! ### Load Data--> Docs-->Divide our Docuemnts into chunks dcouments-->text-->vectors-->Vector Embeddings--->Vector Store DB@ ### Load Data--> Docs-->Divide our Docuemnts into chunks dcouments-->text-->vectors-->Vector Embeddings--->Vector Store DB ### Load Data--> Docs-->Divide our Docuemnts into chunks dcouments-->text-->vectors-->Vector Embeddings--->Vector Store DB from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter=RecursiveCharacterTextSplitter(chunk_size=1000,chunk_overlap=200) documents=text_splitter.split_documents(docs) documents 來個向量化! from langchain_openai import OpenAIEmbeddings embeddings=OpenAIEmbeddings() 要記得存入向量資料庫!! from langchain_community.vectorstores import FAISS vectorstoredb=FAISS.from_documents(documents,embeddings) 檢查下有沒有存成功 vectorstoredb 問問題吧 ## Query From a vector db query="LangSmith has two usage limits: total traces and extended" result=vectorstoredb.similarity_search(query) result[0].page_content 來,再開一開實作應用,建立document chain: from langchain_openai import ChatOpenAI llm=ChatOpenAI(model="gpt-4o") ## Retrieval Chain, Document chain from langchain.chains.combine_documents import create_stuff_documents_chain from langchain_core.prompts import ChatPromptTemplate prompt=ChatPromptTemplate.from_template( """ Answer the following question based only on the provided context: ###draft_code_symbol_lessthen###context> {context} ###draft_code_symbol_lessthen###/context> """ ) document_chain=create_stuff_documents_chain(llm,prompt) document_chain from langchain_core.documents import Document document_chain.invoke({ "input":"LangSmith has two usage limits: total traces and extended", "context":[Document(page_content="LangSmith has two usage limits: total traces and extended traces. These correspond to the two metrics we've been tracking on our usage graph. ")] }) 我們希望文件首先來自我們剛剛設置的檢索器。這樣,我們就可以使用檢索器動態選擇最相關的文件,並將它們傳遞到特定問題中。我們現在已經設置了一個檢索器(retriever),這是一個工具或系統,能夠從一個大的文件庫中找到與查詢問題最相關的資料。首先,這些文件會由檢索器篩選出來,而不是直接從全部文件中隨機選擇。然後,我們會將檢索器選出的文件傳遞給進一步的處理程序,以解答具體的問題。這樣可以確保提供的答案是基於最相關的文件,從而提升解答的精準度和相關性。 ### Input--->Retriever--->vectorstoredb vectorstoredb 從向量料庫來建立檢索器應該相當直觀吧? retriever=vectorstoredb.as_retriever() from langchain.chains import create_retrieval_chain retrieval_chain=create_retrieval_chain(retriever,document_chain) retrieval_chain ## Get the response form the LLM response=retrieval_chain.invoke({"input":"LangSmith has two usage limits: total traces and extended"}) response['answer'] response response['context'] 這樣,輸入,參考到的上下文,還有回應就都有了。 進階思考:只要再搜集一個 人為設定的groundTruth是不是就可以跑完整的RAG Evaluation了? 我們回想一下前面有提到一下子的評估工具“RAGAS"是不是要湊滿這些條件,就進去csv檔裡面就可以跑完整的評估?各位回想一下吧~~
-
正課第四節(2) - Ollama後端LLMs連接Langchain
ollOllama其實對 “地端使用者“去部署RAG的應用非常重要,這一節想來介紹ollama: 先上官方網站: 會先帶大家瀏覽一下官方網站 https://ollama.com Langchain for Ollama Reference: https://python.langchain.com/v0.2/docs/integrations/llms/ollama/ https://python.langchain.com/v0.2/docs/integrations/providers/ollama/ 推薦影片: https://www.youtube.com/watch?v=JpQC0W91E6k https://www.youtube.com/watch?v=aKcAIfhzW68 (多模態辨識) 1. 引言 • 大型語言模型(LLMs)在技術領域中非常重要,它們可以用來處理各種任務,例如編寫代碼、翻譯等。 • 設置和運行LLMs通常需要大量的資源和技術專業知識。ollama是一個簡化這一過程的工具,提供輕量級、易於擴展的框架,使本地端的語言模型運行變得簡單高效。 2. 安裝 ollama ollama 支援 macOS、Windows、Linux 和 Docker,根據操作系統不同,有以下安裝方式: • macOS: 提供預打包 zip 文件,下載解壓後按說明安裝。 • Windows: 提供預覽版下載,因為處於預覽階段,可能會有不穩定情況。 • Linux: 使用 curl 命令行安裝。 • Docker: 使用 docker pull 命令下載並運行。 3. 使用 ollama 的庫和工具 ollama 支援 Python 和 JavaScript 語言環境,並提供以下兩個庫: • ollama-python: 使用 Python 接口與 ollama 互動,安裝後可以簡單地運行大型語言模型。 • ollama-js: 提供 JavaScript 庫,安裝後可在 Node.js 中運行 ollama。 4. 快速開始 要快速運行模型,可以使用簡單的命令,如 ollama run llama2 來啟動 Llama 2 模型,之後可以與其交互。 5. 訪問模型庫 ollama 支援多個模型,這些模型可以通過命令下載。每個模型需要的參數量及大小不同,例如: • Llama 2: 7B 參數,大小為 3.8GB。 • Dolphin Phi: 2.7B 參數,大小為 1.6GB。 運行模型時需要注意 RAM 的大小需求,例如7B參數的模型需要至少8GB的 RAM。 6. 自定義模型 ollama 支持從 GGUF 格式導入模型,並支持自定義提示來調整模型行為。例如,您可以創建一個 Modelfile 來自定義溫度等參數。 7. CLI 指令參考 ollama 提供了多個命令行工具,您可以創建、拉取、刪除和複製模型。例如: • 創建模型:ollama create my-model -f ./Modelfile • 拉取模型:ollama pull llama2 • 刪除模型:ollama rm my-model 8. 多行輸入及多模態模型 ollama 支持多行輸入和多模態模型,允許處理文本與圖片的混合輸入。 9. 提示作為參數傳入 您可以將提示作為命令行的參數來傳入模型。例如,使用 README 文件的內容作為提示輸入:ollama run my-model "請總結該文件:$(cat README.md)" 10. 總結 ollama 是一個強大而靈活的平台,允許開發者輕鬆在本地環境部署和運行大型語言模型,無論是快速體驗還是深度定制開發,都能滿足需求。 這些筆記總結了如何安裝、使用 ollama 進行語言模型操作、如何自定義模型參數、以及常見的命令行工具和實用指令。 下面是使用簡單的使用者介面,搭配langchain和Ollama建構而成的小應用程式。 import os from dotenv import load_dotenv from langchain_community.llms import Ollama import streamlit as st from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser load_dotenv() ## Langsmith Tracking os.environ["LANGCHAIN_API_KEY"]=os.getenv("LANGCHAIN_API_KEY") os.environ["LANGCHAIN_TRACING_V2"]="true" os.environ["LANGCHAIN_PROJECT"]=os.getenv("LANGCHAIN_PROJECT") ## Prompt Template prompt=ChatPromptTemplate.from_messages( [ ("system","You are a helpful assistant. Please respond to the question asked"), ("user","Question:{question}") ] ) ## streamlit framework st.title("Langchain Demo With Gemma Model") input_text=st.text_input("What question you have in mind?") ## Ollama Llama2 model llm=Ollama(model="gemma:2b") output_parser=StrOutputParser() chain=prompt|llm|output_parser if input_text: st.write(chain.invoke({"question":input_text})) 總結:Ollam真的非常非常好用,有多的時間會講更多Ollam和大LLM的進階應用。
-
正課第四節(3) - Data Ingestion and Splitters
Data Ingestion- Documentloaders https://python.langchain.com/v0.2/docs/integrations/document_loaders/ 上面這一個langchain頁面我們也必須要熟悉, 但多看就會記得langchain可以專門處裡哪些檔案類型。 ## Text Loader from langchain_community.document_loaders import TextLoader loader=TextLoader('speech.txt') loader text_documents=loader.load() text_documents ## Reading a PDf File from langchain_community.document_loaders import PyPDFLoader loader=PyPDFLoader('attention.pdf') docs=loader.load() docs type(docs[0]) ## Web based loader from langchain_community.document_loaders import WebBaseLoader import bs4 loader=WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), bs_kwargs=dict(parse_only=bs4.SoupStrainer( class_=("post-title","post-content","post-header") )) ) loader.load() ## Arxiv from langchain_community.document_loaders import ArxivLoader docs = ArxivLoader(query="1706.03762", load_max_docs=2).load() len(docs) from langchain_community.document_loaders import WikipediaLoader docs = WikipediaLoader(query="Generative AI", load_max_docs=2).load() len(docs) print(docs) docs 1.文本分割- 遞歸字元文本分割器 (RecursiveCharacter Text Splitters) 這款文本分割器是用於一般文本的推薦工具。它的參數是一串字元列表,會依序嘗試使用這些字元進行分割,直到段落小到合適為止。預設的字元列表是 ["\n\n", "\n", " ", ""]。這麼做的效果就是盡可能保留完整的段落(然後是句子,再來是單字),因為這些通常會是語義上最緊密相關的文本部分。 - 文本如何分割:依照字元列表進行分割。 - 區塊大小如何測量:依字元數量計算。 -這段解釋就像一個文本的「削皮器」——一步步從大到小,從段落到句子,最後才「剝」到每個字!當你在處理文本時,這個分割器就像你手邊的瑞士刀,可以靈活應對各種情況。簡單又實用,但卻蘊含智慧,真是一個不可或缺的小幫手! type(docs[0]) How to recursively split text by characters ? from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50) final_documents=text_splitter.split_documents(docs) final_documents print(final_documents[0]) print(final_documents[1]) ## Text Loader from langchain_community.document_loaders import TextLoader loader=TextLoader('speech.txt') docs=loader.load() docs type(text[0]) 2.如何依字元分割文本- 字元文本分割器 這是最簡單的方法,透過指定的字元序列進行分割,預設為 "\n\n"。區塊的長度則是以字元數量來計算。 1. 文本如何分割:使用單一字元分隔符來分割。 2. 區塊大小如何測量:根據字元數量進行計算。 --- 這就像拿一把「分割剪刀」,你只需指定一個符號,文本就會乖乖地按你的要求「切塊」。而且,測量文本大小完全就是看字元數,簡單明瞭!這不就是文本分割界的「快刀手」嗎? from langchain_community.document_loaders import TextLoader loader=TextLoader('speech.txt') docs=loader.load() docs from langchain_text_splitters import CharacterTextSplitter text_splitter=CharacterTextSplitter(separator="\n\n",chunk_size=100,chunk_overlap=20) text_splitter.split_documents(docs) speech="" with open("speech.txt") as f: speech=f.read() text_splitter=CharacterTextSplitter(chunk_size=100,chunk_overlap=20) text=text_splitter.create_documents([speech]) print(text[0]) print(text[1]) 3.如何依 HTML 標題分割文本(HTMLHeaderTextSplitter)? HTMLHeaderTextSplitter 是一種具「結構感知」的分割工具,它能在 HTML 元素層級上進行文本分割,並為每個與該區塊相關的標題添加元數據。這個工具可以逐個元素返回分割後的區塊,或者將具有相同元數據的元素合併,達到以下兩個目標:(a) 保持相關文本在語義上盡量組合在一起,(b) 保留文件結構中編碼的豐富上下文信息。它也可以與其他文本分割工具結合,作為分割流程中的一部分。 --- 就像是 HTML 頁面的「文本工匠」,這工具不僅分割文本,還能依據頁面結構智能地「標記」每個段落。它不僅讓相關內容「團聚」,還會照顧到每個頁面元素的背景脈絡。這就像是為你文本中的每個段落戴上專屬的名牌,讓它們既有條理又有故事! from langchain_text_splitters import HTMLHeaderTextSplitter html_string = """ ###draft_code_symbol_lessthen###!DOCTYPE html> ###draft_code_symbol_lessthen###html> ###draft_code_symbol_lessthen###body> ###draft_code_symbol_lessthen###div> ###draft_code_symbol_lessthen###h1>Foo###draft_code_symbol_lessthen###/h1> ###draft_code_symbol_lessthen###p>Some intro text about Foo.###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###div> ###draft_code_symbol_lessthen###h2>Bar main section###draft_code_symbol_lessthen###/h2> ###draft_code_symbol_lessthen###p>Some intro text about Bar.###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###h3>Bar subsection 1###draft_code_symbol_lessthen###/h3> ###draft_code_symbol_lessthen###p>Some text about the first subtopic of Bar.###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###h3>Bar subsection 2###draft_code_symbol_lessthen###/h3> ###draft_code_symbol_lessthen###p>Some text about the second subtopic of Bar.###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###/div> ###draft_code_symbol_lessthen###div> ###draft_code_symbol_lessthen###h2>Baz###draft_code_symbol_lessthen###/h2> ###draft_code_symbol_lessthen###p>Some text about Baz###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###/div> ###draft_code_symbol_lessthen###br> ###draft_code_symbol_lessthen###p>Some concluding text about Foo###draft_code_symbol_lessthen###/p> ###draft_code_symbol_lessthen###/div> ###draft_code_symbol_lessthen###/body> ###draft_code_symbol_lessthen###/html> """ headers_to_split_on=[ ("h1","Header 1"), ("h2","Header 2"), ("h3","Header 3") ] html_splitter=HTMLHeaderTextSplitter(headers_to_split_on) html_header_splits=html_splitter.split_text(html_string) html_header_splits url = "https://plato.stanford.edu/entries/goedel/" headers_to_split_on = [ ("h1", "Header 1"), ("h2", "Header 2"), ("h3", "Header 3"), ("h4", "Header 4"), ] html_splitter = HTMLHeaderTextSplitter(headers_to_split_on) html_header_splits = html_splitter.split_text_from_url(url) html_header_splits 4.如何分割 JSON 資料 這款 JSON 分割器可以控制區塊大小,透過深度優先遍歷 JSON 資料,並生成較小的 JSON 區塊。它會盡量保持巢狀的 JSON 物件完整,但如果需要維持區塊大小在最小與最大範圍內,則會對其進行分割。 如果值不是巢狀的 JSON,而是非常大的字串,那麼該字串不會被分割。如果需要嚴格限制區塊大小,可以將這個分割器與遞歸文本分割器結合使用。還有一個可選的預處理步驟,可以先將列表轉換為 JSON(字典)格式,然後再進行分割。 - 文本如何分割:依據 JSON 值進行分割。 - 區塊大小如何測量:依字元數量計算。 --- 這個工具就像是 JSON 的「深海潛水員」,在資料中進行深度探索,一路「攜帶」出精簡的區塊。它的目標是保持巢狀結構不被打破,但如果大小不合適,會很果斷地進行「拆解」。而對於巨大字串,它可不會隨便動刀,除非你特別指定!這樣的分割策略簡直就是 JSON 分割界的「策略大師」啊! import json import requests json_data=requests.get("https://api.smith.langchain.com/openapi.json").json() json_data from langchain_text_splitters import RecursiveJsonSplitter json_splitter=RecursiveJsonSplitter(max_chunk_size=300) json_chunks=json_splitter.split_json(json_data) json_chunks for chunk in json_chunks[:3]: print(chunk) ## The splitter can also output documents docs=json_splitter.create_documents(texts=[json_data]) for doc in docs[:3]: print(doc) texts=json_splitter.split_text(json_data) print(texts[0]) print(texts[1]) 5.進階補充, 但是是重要技巧。 !pip install llama-index !pip install openai Llamaindex 是基於大型語言模型 (LLM) 的應用程式的數據框架。它支援攝取不同類型的外部數據源,構建檢索增強生成 (RAG) 系統,並將與其他 LLM 的集成抽象到幾行代碼中。此外,它還提供了多種技術來使用 LLM 生成更準確的輸出。 默認情況下,它使用 OpenAI 模型生成文字,使用 OpenAI 模型進行檢索和嵌入。要使用這些模型,必須在 OpenAI 平臺上創建一個免費帳戶並獲取 OpenAI API 密鑰。要獲取金鑰,請存取 OpenAI API 文件。gpt-3.5-turbotext-embedding-ada-002 import os os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" 現在我們的軟體包已經安裝好了,我們將構建一個RAG系統,根據Emma Stone、Ryan Gosling和 La La Land 的Wikipedia 頁面來回答問題。首先,我們需要安裝庫來提取Wikipedia頁面:wikipedia !pip install wikipedia 然後,我們可以輕鬆地從Wikipedia 下載數據: 提取數據后,我們可以將文檔拆分為 256 個字元的塊,沒有重疊。稍後,這些塊使用嵌入模型轉換為數值向量,並在 Vector Store 中編制索引。 # Initialize the gpt3.5 model gpt3 = OpenAI(temperature=0, model="gpt-3.5-turbo-instruct", api_key=OPENAI_API_KEY) # Initialize the embedding model embed_model = OpenAIEmbedding(model= OpenAIEmbeddingModelType.TEXT_EMBED_ADA_002, api_key=OPENAI_API_KEY) # Transform chunks into numerical vectors using the embedding model service_context_gpt3 = ServiceContext.from_defaults(llm=gpt3, chunk_size = 256, chunk_overlap=0, embed_model=embed_model) index = VectorStoreIndex.from_documents(documents, service_context=service_context_gpt3) retriever = index.as_retriever(similarity_top_k=3) 為了降低幻覺的風險,我們使用該模組來確保 LLM 的答案僅基於所提供的上下文。PromptTemplate from llama_index.core.prompts import PromptTemplate # Build a prompt template to only provide answers based on the loaded documents template = ( "We have provided context information below. \n" "---------------------\n" "{context_str}" "\n---------------------\n" "Given this information, please answer the question: {query_str}\n" "Don't give an answer unless it is supported by the context above.\n" ) qa_template = PromptTemplate(template) 現在我們已經設置了RAG系統,我們可以根據檢索到的文件使用問題對其進行測試。讓我們來測試一下吧! 問題 1:“導致艾瑪·斯通獲得她的第一個奧斯卡金像獎的電影情節是什麼? 第一個查詢具有挑戰性,因為它要求模型查看不同的資訊: 導致艾瑪·斯通 (Emma Stone) 獲得第一個奧斯卡金像獎的電影名稱(在本例中為《愛樂之城》) 那部特定電影的情節。 # Create a prompt for the model question = "What is the plot of the film that led Emma Stone to win her first Academy Award?" # Retrieve the context from the model contexts = retriever.retrieve(question) context_list = [n.get_content() for n in contexts] prompt = qa_template.format(context_str="\n\n".join(context_list), query_str=question) # Generate the response response = gpt3.complete(prompt) print(str(response)) 輸出: The plot of the film that made Emma Stone win her first Academy Award is not explicitly mentioned in the provided context. 問題 2:“比較 Emma Stone 和 Ryan Gosling 的家庭” 第二個查詢比上一個問題更具挑戰性,因為它要求選擇有關兩個參與者的家庭的相關塊。 # Create a prompt for the model question = "Compare the families of Emma Stone and Ryan Gosling" # Retrieve the context from the model contexts = retriever.retrieve(question) context_list = [n.get_content() for n in contexts] prompt = qa_template.format(context_str="\n\n".join(context_list), query_str=question) # Generate the response response = gpt3.complete(prompt) print(str(response)) 我們收到以下輸出: Based on the context provided, it is not possible to compare the families of Emma Stone and Ryan Gosling as the information focuses on their professional collaboration and experiences while working on the film "La La Land." There is no mention of their personal family backgrounds or relationships in the context provided. 正如你所看到的,我們在這兩種情況下都收到了不合格的答案。在以下部分中,讓我們探索提高此RAG系統性能的方法! 通過更新塊大小來提高RAG性能 我們可以從自定義數據塊大小和數據塊重疊開始。正如我們上面所說,文檔被分成具有特定重疊的塊。默認情況下,LlamaIndex 使用 1024 作為預設數據塊大小,使用 20 作為預設數據塊重疊。除了這些超參數之外,預設情況下,系統還會檢索前 2 個數據塊。 例如,我們可以將 chunk 大小固定為 512,chunk overlap 為 50,並增加檢索到的頂部卡盤: # modify default values of chunk size and chunk overlap service_context_gpt3 = ServiceContext.from_defaults(llm=gpt3, chunk_size = 512, chunk_overlap=50, embed_model=embed_model) # build index index = VectorStoreIndex.from_documents( documents, service_context=service_context_gpt3 ) # returns the engine for the index query_engine = index.as_query_engine(similarity_top_k=4) 問題 1:“導致艾瑪·斯通獲得她的第一個奧斯卡金像獎的電影情節是什麼? # generate the response response = query_engine.query("What is the plot of the film that led Emma Stone to win her first Academy Award?") print(response) 輸出: The film that made Emma Stone win her first Academy Award is a romantic musical called La La Land. 與前面的答案相比,它稍微好一些。它成功地辨認《愛樂之城》是導致艾瑪·斯通獲得她的第一個奧斯卡金像獎的電影,但它無法描述這部電影的情節。 問題 2:“比較 Emma Stone 和 Ryan Gosling 的家庭” # generate the response response = query_engine.query("Compare the families of Emma Stone and Ryan Gosling") print(response) 輸出: Emma Stone has expressed her close relationship with her family and mentioned being blessed with great family and people around her. She has also shared about her mother's battle with breast cancer and their celebration by getting matching tattoos. On the other hand, there is no specific information provided about Ryan Gosling's family or his personal relationships in the context. 同樣,RAG 管道的輸出有所改善,但它仍然沒有捕獲有關 Ryan Gosling 家庭的資訊。 通過重新排名提高RAG性能 隨著數據集的大小和複雜性的增加,選擇相關信息以返回複雜查詢的定製答案變得至關重要。為此,一系列稱為 Re-Ranking 的技術允許您瞭解文本中哪些塊是重要的。他們對文檔進行重新排序和篩選,首先對最相關的文檔進行排名。 重新排名有兩種主要方法: 使用 Re-Ranking 模型作為嵌入模型的替代技術。它們將查詢和上下文作為輸入,並返回相似性分數而不是嵌入 使用 LLM 更有效地捕獲文檔中的語義資訊。 在應用這些重新排名方法之前,讓我們評估一下基線 RAG 系統返回的第二個查詢的前三個塊: 這是 Re-Ranking 之前的輸出;每個數據塊都有一個WITH SIMILARITY 分數。Node ID Node ID: 9b3817fe-3a3f-4417-83d2-2e2996c8b468 Similarity: 0.8415899563985404 Text: Emily Jean "Emma" Stone (born November 6, 1988) is an American actress and producer. She is the recipient of various accolades, including two Academy Awards, two British Academy Film Awards, and two Golden Globe Awards. In 2017, she was the world's highest-paid actress and named by Time magazine as one of the 100 most influential people in the world. Born and raised in Scottsdale, Arizona, Stone began acting as a child in a theater production of The Wind in the Willows in 2000. As a teenager,... ---------------------------------------------------- Node ID: 1bef0308-8b0f-4f7e-9cd6-92ce5acf811f Similarity: 0.831147173341674 Text: Coincidentally, Gosling turned down the Beast role in Beauty and the Beast in favor of La La Land. Chazelle subsequently decided to make his characters somewhat older, with experience in struggling to make their dreams, rather than younger newcomers just arriving in Los Angeles. Emma Stone plays Mia, an aspiring actress in Los Angeles. Stone has loved musicals since she saw Les Misérables when she was eight years old. She said "bursting into song has always been a real dream of mine", and her ... ---------------------------------------------------- Node ID: 576ae445-b12e-4d20-99b7-5e5a91ee7d74 Similarity: 0.8289486590392277 Text: Stone was named the best-dressed woman of 2012 by Vogue and was included on similar listings by Glamour in 2013 and 2015, and People in 2014. == Personal life == Stone moved from Los Angeles to Greenwich Village, New York, in 2009. In 2016, she moved back to Los Angeles. Despite significant media attention, she refuses to publicly discuss her personal life. Concerned with living a normal life, Stone has said she dislikes receiving paparazzi attention outside her home. She has expressed her ... 使用 FlagEmbeddingReranker 重新排名 要檢索相關的塊,我們可以使用 Hugging Face 中的開源 Re-Ranking 模型,稱為 bge-ranker-base 模型。 就像 OpenAI API 一樣,使用 Hugging Face 需要您獲取使用者存取權杖。您可以按照此文件從 Hugging Face 建立使用者存取權杖。 HF_TOKEN = userdata.get('HF_TOKEN') os.environ['HF_TOKEN'] = HF_TOKEN 在進一步之前,我們還需要安裝必要的庫以使用 Re-Ranking 模型: %pip install llama-index-postprocessor-flag-embedding-reranker !pip install git+https://github.com/FlagOpen/FlagEmbedding.git 最後,我們使用模型返回最相關的塊。bge-ranker-base # Import packages from llama_index.postprocessor.flag_embedding_reranker import FlagEmbeddingReranker from llama_index.core.schema import QueryBundle # Re-Rank chunks based on the bge-reranker-base-model reranker = FlagEmbeddingReranker( top_n = 3, model = "BAAI/bge-reranker-base", ) # Return the updated chunks query_bundle = QueryBundle(query_str=query) ranked_nodes = reranker._postprocess_nodes(nodes, query_bundle = query_bundle) for ranked_node in ranked_nodes: print('----------------------------------------------------') display_source_node(ranked_node, source_length = 500) 這是 Re-Ranking 後的結果: Node ID: 9b3817fe-3a3f-4417-83d2-2e2996c8b468 Similarity: 3.0143558979034424 Text: Emily Jean "Emma" Stone (born November 6, 1988) is an American actress and producer. She is the recipient of various accolades, including two Academy Awards, two British Academy Film Awards, and two Golden Globe Awards. In 2017, she was the world's highest-paid actress and named by Time magazine as one of the 100 most influential people in the world. Born and raised in Scottsdale, Arizona, Stone began acting as a child in a theater production of The Wind in the Willows in 2000. As a teenager,... ---------------------------------------------------- Node ID: 576ae445-b12e-4d20-99b7-5e5a91ee7d74 Similarity: 2.2117154598236084 Text: Stone was named the best-dressed woman of 2012 by Vogue and was included on similar listings by Glamour in 2013 and 2015, and People in 2014. == Personal life == Stone moved from Los Angeles to Greenwich Village, New York, in 2009. In 2016, she moved back to Los Angeles. Despite significant media attention, she refuses to publicly discuss her personal life. Concerned with living a normal life, Stone has said she dislikes receiving paparazzi attention outside her home. She has expressed her ... ---------------------------------------------------- Node ID: 1bef0308-8b0f-4f7e-9cd6-92ce5acf811f Similarity: 1.6185210943222046 Text: Coincidentally, Gosling turned down the Beast role in Beauty and the Beast in favor of La La Land. Chazelle subsequently decided to make his characters somewhat older, with experience in struggling to make their dreams, rather than younger newcomers just arriving in Los Angeles.Emma Stone plays Mia, an aspiring actress in Los Angeles. Stone has loved musicals since she saw Les Misérables when she was eight years old. She said "bursting into song has always been a real dream of mine", and her ... 從輸出中可以清楚地看出,ID 為 ID 的節點從第二個位置切換到第三個位置。此外,值得注意的是,相似性分數中的可變性更大。1bef0308-8b0f-4f7e-9cd6-92ce5acf811f 現在我們已經使用了 Re-Ranking,讓我們評估一下 RAG 對原始查詢的回應現在是什麼樣子的: # Initialize the query engine with Re-Ranking query_engine = index.as_query_engine( similarity_top_k = 3, node_postprocessors=[reranker] ) # Print the response from the model response = query_engine.query("Compare the families of Emma Stone and Ryan Gosling") print(response) 這是應用 Re-Ranking 模型後提供的回應: Both Emma Stone and Ryan Gosling have close relationships with their families. Stone has expressed her gratitude for having a great family and people around her who keep her grounded. Gosling, on the other hand, has drawn from his own experiences as an aspiring artist, indicating a connection to his personal background. 與之前的回應相比,有顯著的改進,但仍然不完整。讓我們評估一下基於 LLM 的重新排名方法如何説明提高 RAG 性能。 但這個已經不是這堂課的範疇, 主要是讓大家看透過chunk的設計和改變可以增加檢所的表現還有其他更多種方法,之後有機會會再課堂上再談到,那就是更進階的RAG技巧了...。
-
正課第四節(4) - Embeddings方法和探討其模型從何而得
4.4.1 直接利用openai embedding! 我們直接看code Embedding Techniques Converting text into vectors import os from dotenv import load_dotenv load_dotenv() #load all the environment variables 或是用下面方法載入key也可以但不建議: os.environ["OPENAI_API_KEY"]=os.getenv("OPENAI_API_KEY") from langchain_openai import OpenAIEmbeddings embeddings=OpenAIEmbeddings(model="text-embedding-3-large") embeddings 直接用OpenAIEmbeddings() 然後指定embedding model, 其實不一定要指定large 可以依照使用場景去指定... 嵌入 - OpenAI 中文文件 (openaicto.com) text="This is a tutorial on OPENAI embedding" query_result=embeddings.embed_query(text) query_result from langchain_community.document_loaders import TextLoader loader=TextLoader('speech.txt') docs=loader.load() docs from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50) final_documents=text_splitter.split_documents(docs) final_documents ## Vector Embedding And Vector StoreDB from langchain_community.vectorstores import Chroma db=Chroma.from_documents(final_documents,embeddings_1024) db ### Retrieve the results from query vectorstore db query="It will be all the easier for us to conduct ourselves as belligerents" retrieved_results=db.similarity_search(query) print(retrieved_results) 4.4.2 利用很好用的 Ollama 中的 embedding! Ollama Ollama supports embedding models, making it possible to build retrieval augmented generation (RAG) applications that combine text prompts with existing documents or other data. 一樣, 用langchain模塊去跟ollama調用資源和溝通!! 這次我們要試看看 gemma:2b embeddings #看一下長相 r1=embeddings.embed_documents( [ "Alpha is the first letter of Greek alphabet", "Beta is the second letter of Greek alphabet", ] ) len(r1[0]) r1[1] embeddings.embed_query("What is the second letter of Greek alphabet ") ### Other Embedding Models ### https://ollama.com/blog/embedding-models embeddings = OllamaEmbeddings(model="mxbai-embed-large") text = "This is a test document." query_result = embeddings.embed_query(text) query_result len(query_result) 4.4.3 利用很好用更大眾的 Hugging face 中的 embedding 對我來說, hugging face 是NLP或大型語言開發者的一種信仰。Embedding Techniques Using HuggingFace import os from dotenv import load_dotenv load_dotenv() #load all the environment variables os.environ['HF_TOKEN']=os.getenv("HF_TOKEN") Sentence Transformers on Hugging Face Hugging Face sentence-transformers is a Python framework for state-of-the-art sentence, text and image embeddings. One of the embedding models is used in the HuggingFaceEmbeddings class. We have also added an alias for SentenceTransformerEmbeddings for users who are more familiar with directly using that package. from langchain_huggingface import HuggingFaceEmbeddings embeddings=HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") text="this is atest documents" query_result=embeddings.embed_query(text) query_result 看一下這一串向量多長 len(query_result) doc_result = embeddings.embed_documents([text, "This is not a test document."]) doc_result[0]
-
-
第三章(9/14)langchain for Advanced RAG搭配 Langsmith 觀察動態思考過程。 和用streamlit建造小型app 解說
langchain for Advanced RAG搭配 LangSmith 觀察動態思考過程。 和用streamlit建造小型app 解說
-
第五節前補充 : Streamlit 介紹和LLMs應用程式頁面實作
下面是這次實作的程式碼連結: Langchain_course_code/10-Streamlit_Page_Designing_Template_For_LLMApps/streamlit_llm-examples at main · kevin801221/Langchain_course_code (github.com) 簡單介紹一下: 同學可以直接參考以下的參考連結 1. my github fork 2.CSDN 筆記 3.中文文檔
-
正課第四節(5) RAG 檔案QA應用和QA 聊天模式conversation
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 問答系統,並且支援多次互動。
-
正課第五節RAG (Review) from scratch to Advanced RAG(重要)
主要會用以下連結的PPT跟大家複習 RAG 還有搭配 code: 會進入一點 Advanced RAG的部分。 主要讓大家知道 Langsmith 去觀察中間RAG搭配大型語言模型的思考過程很重要。 LangSmith (langchain.com) 程式碼連結 簡報連結: RAG (Review) from Scratch
-
正課第六節 (搭配UI應用) : 非常重要的搜尋引擎搭配LLM概念
使用Streamlit 和 LangChain 來構建一個帶有搜索功能的聊天機器人應用程式。主要目的是讓使用者與一個整合了多種工具(如 Arxiv、Wikipedia 和 DuckDuckGo)進行交互,並由一個代理(Agent)來處理這些查詢。 以下詳細分解和註解說明: 1.載入庫 import streamlit as st from langchain_groq import ChatGroq from langchain_community.utilities import ArxivAPIWrapper, WikipediaAPIWrapper from langchain_community.tools import ArxivQueryRun, WikipediaQueryRun, DuckDuckGoSearchRun from langchain.agents import initialize_agent, AgentType from langchain.callbacks import StreamlitCallbackHandler import os from dotenv import load_dotenv Streamlit:這是一個用於構建交互式網頁應用的 Python 庫,這裡用來構建聊天機器人的前端界面。 • LangChain 庫的相關模組: • ChatGroq:這是一個特定的聊天模型接口,使用 Groq 作為後端的 API。 • ArxivAPIWrapper 和 WikipediaAPIWrapper:這些是用來處理來自 Arxiv 和 Wikipedia 的 API 查詢結果的工具包。 • ArxivQueryRun 和 WikipediaQueryRun:這些是用來從 Arxiv 和 Wikipedia 中進行查詢的具體工具封裝。 • DuckDuckGoSearchRun:這是用來進行 DuckDuckGo 搜索查詢的工具封裝。 • initialize_agent:這是用來初始化 LangChain 的代理的函數,它將多個工具整合到一個代理中。 • StreamlitCallbackHandler:這是用來將代理執行過程(如思維和動作)顯示在 Streamlit 應用中的回調處理器。 2. 載入 .env 檔案 load_dotenv(dotenv_path="“) 這段程式碼載入指定路徑的 .env 檔案,這個檔案通常用來存儲敏感資訊,如 API 金鑰等。 • dotenv_path 指定了 .env 檔案的路徑,透過這種方式,我們可以安全地從檔案中讀取環境變數,而不需要將敏感資訊硬編碼在程式中。 3. 設定查詢工具 arxiv_wrapper = ArxivAPIWrapper(top_k_results=1, doc_content_chars_max=200) arxiv = ArxivQueryRun(api_wrapper=arxiv_wrapper) api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=200) wiki = WikipediaQueryRun(api_wrapper=api_wrapper) search = DuckDuckGoSearchRun(name="Search") • Arxiv 和 Wikipedia API 包裝器:這裡創建了兩個查詢 API 的封裝器,限制了每次返回的結果數量(top_k_results=1),以及每篇文章返回的最大字元數(doc_content_chars_max=200)。 • DuckDuckGo 搜索工具:定義了一個 DuckDuckGo 的查詢工具,名稱為 Search,用於執行網路搜索查詢。 4. Streamlit 應用的標題和介紹 st.title("🔎 LangChain - Chat with search") """ In this example, we're using `StreamlitCallbackHandler` to display the thoughts and actions of an agent in an interactive Streamlit app. Try more LangChain 🤝 Streamlit Agent examples at [github.com/langchain-ai/streamlit-agent](https://github.com/langchain-ai/streamlit-agent). """ • 這一部分在 Streamlit 應用的主頁顯示標題「LangChain - Chat with search」,並解釋該應用的功能,即與多個搜索工具進行交互。 • 文本描述提供了更多關於應用的背景信息,並引導用戶查看更多示例。 5. Sidebar 設定 st.sidebar.title("Settings") api_key = st.sidebar.text_input("Enter your Groq API Key:", type="password") • Sidebar 設定:在 Streamlit 應用的側邊欄中,要求使用者輸入他們的 Groq API 金鑰,這是必需的以便後續使用 ChatGroq 模型進行交互。 • type=“password”:確保輸入的金鑰以隱藏的方式顯示,以保護敏感資訊。 6. 初始化聊天消息狀態 if "messages" not in st.session_state: st.session_state["messages"] = [ {"role": "assisstant", "content": "Hi,I'm a chatbot who can search the web. How can I help you?"} ] • st.session_state:這是 Streamlit 中用來存儲應用程序狀態的內建對象。在這裡,使用者的聊天消息被存儲在 session_state 中,以便在整個應用中持續保留。 • 如果 session_state 中還沒有消息,則初始化一條歡迎消息,扮演助手的角色,並詢問使用者的需求。 7. 顯示現有聊天消息 for msg in st.session_state.messages: st.chat_message(msg["role"]).write(msg['content']) • 這段程式碼用來遍歷 st.session_state 中的所有消息,並將其顯示在聊天窗口中。st.chat_message() 根據消息的角色(“user” 或 “assistant”)來渲染消息。 8. 接收用戶輸入 if prompt := st.chat_input(placeholder="What is machine learning?"): st.session_state.messages.append({"role": "user", "content": prompt}) st.chat_message("user").write(prompt) • st.chat_input() 用來顯示一個聊天輸入框,讓使用者可以輸入問題(例如「What is machine learning?」)。 • 當使用者輸入內容後,該輸入會被加入 session_state 中,並在應用中顯示出來。 9. 使用 ChatGroq 模型處理查詢並使用工具 llm = ChatGroq(groq_api_key=api_key, model_name="Llama3-8b-8192", streaming=True) tools = [search, arxiv, wiki] search_agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, handling_parsing_errors=True) langchain.agents.agent_types.AgentType — 🦜🔗 LangChain 0.2.16 (可以自己選Agent Planing and Actor 演算法) llm = ChatGroq(...):這段程式碼實例化了 ChatGroq 模型,這是一個具備流式輸出功能的大型語言模型,名稱為 “Llama3-8b-8192”,並使用輸入的 api_key 進行認證。 • tools:這裡定義了多個工具供代理使用,包括 Arxiv、Wikipedia 和 DuckDuckGo 查詢工具。 • initialize_agent(...):這是核心步驟,將定義好的工具與 ChatGroq 模型整合到一個代理中,該代理將根據用戶的查詢選擇合適的工具來進行回答。 10. 處理代理的回應並顯示 with st.chat_message("assistant"): st_cb = StreamlitCallbackHandler(st.container(), expand_new_thoughts=False) response = search_agent.run(st.session_state.messages, callbacks=[st_cb]) st.session_state.messages.append({'role': 'assistant', "content": response}) st.write(response) • StreamlitCallbackHandler:這個回調處理器用來顯示代理在執行查詢過程中的「思維」和「動作」,讓用戶能夠即時看到代理的決策過程。 • search_agent.run(...):代理會接收用戶輸入的聊天消息,並使用定義的工具來處理查詢,最終返回結果。 • 結果會被寫入 session_state 中,並顯示在聊天窗口中作為「assistant」的回應。 所以,整個程式碼構建了一個完整的互動聊天機器人,讓使用者可以通過輸入問題與多種信息來源(如 Arxiv、Wikipedia 和 DuckDuckGo)進行交互。這個代理能夠根據使用者查詢的內容自動選擇最適合的工具來給出回應。
-
正課第七節 (應用) SQL RAG
這個程式整個工作流包括使用 Streamlit 建立一個前端介面,讓用戶透過自然語言查詢 SQL 資料庫中的數據。後端則使用 LangChain 來將用戶的自然語言查詢轉換為 SQL 語句,並利用語言模型(如 Groq 的 ChatGroq)來生成結果,最後通過 Streamlit 顯示查詢結果。 主要工作流程 啟動應用(Streamlit):當應用啟動時,Streamlit 會生成一個網頁界面,允許用戶選擇是使用 SQLite 資料庫還是 MySQL 資料庫,並在側邊欄輸入 MySQL 的連接信息(如果選擇 MySQL)。 資料庫連接配置: 如果用戶選擇使用本地的 SQLite 資料庫,系統會讀取 student.db 文件並打開該資料庫。 如果用戶選擇 MySQL 資料庫,系統會根據用戶輸入的 MySQL 連接參數(如主機、使用者名、密碼和資料庫名稱)連接 MySQL 資料庫。 語言模型初始化:根據 Groq 的 API 金鑰,應用初始化語言模型(LLM)ChatGroq,用於將自然語言查詢轉換成 SQL 查詢並從資料庫中獲取數據。 用戶查詢處理:用戶在界面上輸入查詢(如「查詢學生的成績」),這個查詢會通過 LangChain 的 SQL Agent 轉換為具體的 SQL 語句並查詢資料庫。 結果回應:SQL 查詢結果會通過語言模型生成的回應被流式顯示到 Streamlit 的聊天窗口中,展示給用戶。 互動式更新:訊息歷史會保存在 st.session_state 中,允許用戶進行多輪查詢,並且可以隨時清除訊息歷史。 建立虛擬環境及步驟 1. 安裝 Python 與虛擬環境工具: 確認已經安裝 Python 3.7 或更新版本。 安裝 venv 模組來管理虛擬環境(Python 內建)。 2. 建立虛擬環境: 打開終端或命令提示字元。 進入專案目錄,執行以下命令來建立虛擬環境: python -m venv env 3 .啟動虛擬環境: Windows: .\env\Scripts\activate macOS/Linux: source env/bin/activate 4.安裝必要的依賴包: 在虛擬環境啟動後,使用 pip 安裝所有所需的套件。可以先創建一個 requirements.txt 檔案,內容如下: langchain python-dotenv ipykernel langchain-community pypdf bs4 arxiv pymupdf wikipedia langchain-text-splitters langchain-openai chromadb sentence_transformers langchain_huggingface faiss-cpu langchain_chroma duckdb pandas openai langchain-groq duckduckgo_search==5.3.1b1 pymupdf arxiv wikipedia mysql-connector-python SQLAlchemy validators==0.28.1 youtube_transcript_api unstructured pytube numexpr huggingface_hub #運行來安裝依賴 pip install -r requirements.txt 5.創建或確認資料庫文件: 如果使用的是 SQLite 資料庫,確認 student.db 文件位於專案根目錄中。 如果使用 MySQL,請準備好 MySQL 資料庫及其連接信息。 6. 運行應用: 使用 Streamlit 啟動應用: streamlit run your_script.py 這會啟動應用,並在瀏覽器中打開介面。 工作流總結 建立並啟動虛擬環境。 配置資料庫連接和語言模型。 用戶透過 Streamlit 介面輸入查詢。 語言模型將查詢轉換為 SQL 查詢,並查詢資料庫。 返回結果並顯示在界面上。 這樣的工作流幫助用戶從自然語言到資料庫查詢進行自動化處理,並提供流式的互動界面。 詳細解釋主程式: Streamlit 設定與應用標題:設置頁面配置,設定標題和圖標,並展示應用的標題。 資料庫選擇與連接:通過側邊欄,讓用戶選擇要連接的資料庫(SQLite 或 MySQL)。如果用戶選擇 MySQL,將會顯示 MySQL 的連接詳細信息表單。SQLite 是本地的 student.db 文件。 語言模型(LLM)設置:使用 Groq 的 ChatGroq 模型處理用戶的自然語言輸入,該模型會在對話中執行任務。使用 streaming=True 可以在輸出時逐步顯示結果。 資料庫配置函數:該函數根據用戶的選擇,配置並返回資料庫連接。SQLite 是本地文件資料庫,MySQL 則需要用戶輸入的連接信息。 SQL Agent 創建:SQL Agent 使用 LangChain 庫創建,負責將用戶的查詢轉換為 SQL 查詢,並與資料庫交互獲取數據。SQL Agent 通過 ZERO_SHOT_REACT_DESCRIPTION 策略,基於自然語言輸入生成 SQL 語句。 訊息記錄與用戶互動:應用保存用戶與助手之間的對話歷史,並通過聊天界面顯示。用戶輸入的查詢會被傳遞給 SQL Agent,該代理會將查詢結果流式輸出。 流式輸出回調處理:當 SQL Agent 執行查詢時,StreamlitCallbackHandler 將結果逐步展示到前端,提升用戶體驗。 可以問啥? 除了基本的數據查詢之外,你還可以進行更進階的查詢和操作,例如更新資料、刪除資料、進行數據分析或查詢結合。以下是一些進階查詢和操作的範例: ### 1. **更新學生的分數** 將學生 `Jacob` 的分數更新為 60: ```sql UPDATE STUDENT SET MARKS = 60 WHERE NAME = 'Jacob'; ``` **解釋**:這個查詢會將 `Jacob` 的分數從 50 更新為 60。 ### 2. **刪除特定學生記錄** 刪除分數低於 40 的學生記錄: ```sql DELETE FROM STUDENT WHERE MARKS ###draft_code_symbol_lessthen### 40; ``` **解釋**:這個查詢會刪除所有分數低於 40 的學生。 ### 3. **查詢每個分組的最高分數學生** 查詢每個分組中分數最高的學生: ```sql SELECT NAME, CLASS, SECTION, MAX(MARKS) FROM STUDENT GROUP BY SECTION; ``` **解釋**:這個查詢會根據分組(`SECTION`)分組,並返回每個分組中分數最高的學生。 ### 4. **查詢特定班級中有多少學生** 計算 `DEVOPS` 班級的學生數: ```sql SELECT COUNT(*) FROM STUDENT WHERE CLASS = 'DEVOPS'; ``` **解釋**:這個查詢會返回 `DEVOPS` 班級中的學生總數。 ### 5. **查詢學生的分數統計(最大值、最小值、平均值)** ```sql SELECT MAX(MARKS) as Max_Marks, MIN(MARKS) as Min_Marks, AVG(MARKS) as Average_Marks FROM STUDENT; ``` **解釋**:這個查詢會返回學生分數的最大值、最小值和平均值。 ### 6. **按分數區間統計學生數** 統計分數在不同區間內的學生數: ```sql SELECT CASE WHEN MARKS >= 90 THEN '90-100' WHEN MARKS >= 80 THEN '80-89' WHEN MARKS >= 70 THEN '70-79' ELSE '###draft_code_symbol_lessthen###70' END AS Score_Range, COUNT(*) as Student_Count FROM STUDENT GROUP BY Score_Range; ``` **解釋**:這個查詢會將學生的分數分為不同的區間(如 90-100、80-89 等),並統計每個區間內的學生數量。 ### 7. **查詢每個班級分數最高的學生** 查詢每個班級中分數最高的學生: ```sql SELECT NAME, CLASS, MAX(MARKS) FROM STUDENT GROUP BY CLASS; ``` **解釋**:這個查詢會返回每個班級中分數最高的學生。 ### 8. **計算分數超過 60 且位於特定班級的學生比例** ```sql SELECT (SELECT COUNT(*) FROM STUDENT WHERE MARKS > 60 AND CLASS = 'Data Science') * 100.0 / (SELECT COUNT(*) FROM STUDENT WHERE CLASS = 'Data Science') AS Percentage ``` **解釋**:這個查詢會計算在 `Data Science` 班級中,分數超過 60 的學生比例。 ### 9. **查詢學生的分數排名** 查詢所有學生,並根據分數進行排名: ```sql SELECT NAME, CLASS, SECTION, MARKS, RANK() OVER (ORDER BY MARKS DESC) AS Rank FROM STUDENT; ``` **解釋**:這個查詢會根據學生的分數進行排名,分數最高的學生排名靠前。 ### 10. **按班級和分組進行細分類別的分數統計** 計算每個班級和分組的學生平均分數: ```sql SELECT CLASS, SECTION, AVG(MARKS) AS Average_Marks FROM STUDENT GROUP BY CLASS, SECTION; ``` **解釋**:這個查詢會根據班級和分組分組,並計算每個組別的平均分數。 ### 11. **查詢學生的分數是否及格** 新增一個欄位來顯示學生是否及格(以 60 分為及格標準): ```sql SELECT NAME, CLASS, SECTION, MARKS, CASE WHEN MARKS >= 60 THEN 'Pass' ELSE 'Fail' END AS Result FROM STUDENT; ``` **解釋**:這個查詢會新增一個結果欄位,顯示學生的成績是否達到 60 分及格。 ### 12. **查詢某班級分數超過班級平均分的學生** 查詢 `Data Science` 班級中,分數超過該班級平均分數的學生: ```sql SELECT * FROM STUDENT WHERE CLASS = 'Data Science' AND MARKS > (SELECT AVG(MARKS) FROM STUDENT WHERE CLASS = 'Data Science'); ``` **解釋**:這個查詢會查詢 `Data Science` 班級中,分數超過該班級平均分數的學生。 --- ### 進階查詢與分析操作: - **資料表結合查詢(JOIN)**:如果你有多個資料表,可以使用 `JOIN` 來查詢不同表中的相關數據。 - **索引和效能優化**:當你的資料表變得更大時,考慮使用索引來加速查詢。 - **複雜的分析查詢**:如使用 `GROUP BY`、`HAVING`、`WITH`(Common Table Expressions, CTEs)等進行高階資料分析。 這些進階查詢讓你可以進行更精細的資料操作和分析,並進一步擴展你在 `STUDENT` 資料庫上的應用。你可以根據具體需求進行修改或組合查詢來滿足不同的應用情景。 import streamlit as st from pathlib import Path from langchain.agents import create_sql_agent from langchain.sql_database import SQLDatabase from langchain.agents.agent_types import AgentType from langchain.callbacks import StreamlitCallbackHandler from langchain.agents.agent_toolkits import SQLDatabaseToolkit from sqlalchemy import create_engine import sqlite3 from langchain_groq import ChatGroq st.set_page_config(page_title="LangChain: Chat with SQL DB", page_icon="🦜") st.title("🦜 LangChain: Chat with SQL DB") LOCALDB="USE_LOCALDB" MYSQL="USE_MYSQL" radio_opt=["Use SQLLite 3 Database- Student.db","Connect to you MySQL Database"] selected_opt=st.sidebar.radio(label="Choose the DB which you want to chat",options=radio_opt) if radio_opt.index(selected_opt)==1: db_uri=MYSQL mysql_host=st.sidebar.text_input("Provide MySQL Host") mysql_user=st.sidebar.text_input("MYSQL User") mysql_password=st.sidebar.text_input("MYSQL password",type="password") mysql_db=st.sidebar.text_input("MySQL database") else: db_uri=LOCALDB api_key=st.sidebar.text_input(label="GRoq API Key",type="password") if not db_uri: st.info("Please enter the database information and uri") if not api_key: st.info("Please add the groq api key") ## LLM model llm=ChatGroq(groq_api_key=api_key,model_name="Llama3-8b-8192",streaming=True) @st.cache_resource(ttl="2h") def configure_db(db_uri,mysql_host=None,mysql_user=None,mysql_password=None,mysql_db=None): if db_uri==LOCALDB: dbfilepath=(Path(__file__).parent/"student.db").absolute() print(dbfilepath) creator = lambda: sqlite3.connect(f"file:{dbfilepath}?mode=ro", uri=True) return SQLDatabase(create_engine("sqlite:///", creator=creator)) elif db_uri==MYSQL: if not (mysql_host and mysql_user and mysql_password and mysql_db): st.error("Please provide all MySQL connection details.") st.stop() return SQLDatabase(create_engine(f"mysql+mysqlconnector://{mysql_user}:{mysql_password}@{mysql_host}/{mysql_db}")) if db_uri==MYSQL: db=configure_db(db_uri,mysql_host,mysql_user,mysql_password,mysql_db) else: db=configure_db(db_uri) ## toolkit toolkit=SQLDatabaseToolkit(db=db,llm=llm) agent=create_sql_agent( llm=llm, toolkit=toolkit, verbose=True, agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION ) if "messages" not in st.session_state or st.sidebar.button("Clear message history"): st.session_state["messages"] = [{"role": "assistant", "content": "How can I help you?"}] for msg in st.session_state.messages: st.chat_message(msg["role"]).write(msg["content"]) user_query=st.chat_input(placeholder="Ask anything from the database") if user_query: st.session_state.messages.append({"role": "user", "content": user_query}) st.chat_message("user").write(user_query) with st.chat_message("assistant"): streamlit_callback=StreamlitCallbackHandler(st.container()) response=agent.run(user_query,callbacks=[streamlit_callback]) st.session_state.messages.append({"role":"assistant","content":response}) st.write(response) 程式功能概述: 這段 Python 程式使用 SQLite 資料庫,建立了一個 STUDENT 表,並插入了幾條學生記錄,最後顯示了所有插入的記錄。這個範例展示了如何使用 SQLite 來操作本地資料庫。 import sqlite3 # 導入 SQLite 模組,用於操作本地 SQLite 資料庫 # 1. 與 SQLite 資料庫建立連接。如果 "student.db" 文件不存在,SQLite 會自動創建該文件。 connection = sqlite3.connect("student.db") # 2. 創建一個游標對象 (cursor),用於執行 SQL 查詢 cursor = connection.cursor() # 3. 建立 STUDENT 資料表,資料表包含四個欄位:NAME(學生姓名)、CLASS(班級)、SECTION(分組)和 MARKS(分數) table_info = """ CREATE TABLE IF NOT EXISTS STUDENT ( NAME VARCHAR(25), CLASS VARCHAR(25), SECTION VARCHAR(25), MARKS INT ) """ # 4. 執行資料表的創建語句。如果表已存在,則跳過創建 cursor.execute(table_info) # 5. 插入記錄:插入五條學生的記錄,包含學生姓名、班級、分組和分數 cursor.execute('''INSERT INTO STUDENT VALUES ('Krish', 'Data Science', 'A', 90)''') cursor.execute('''INSERT INTO STUDENT VALUES ('John', 'Data Science', 'B', 100)''') cursor.execute('''INSERT INTO STUDENT VALUES ('Mukesh', 'Data Science', 'A', 86)''') cursor.execute('''INSERT INTO STUDENT VALUES ('Jacob', 'DEVOPS', 'A', 50)''') cursor.execute('''INSERT INTO STUDENT VALUES ('Dipesh', 'DEVOPS', 'A', 35)''') # 6. 顯示所有插入的記錄 print("The inserted records are:") # 執行 SQL 查詢,選取 STUDENT 表中的所有記錄 data = cursor.execute('''SELECT * FROM STUDENT''') # 迭代查詢結果,並逐行列印出來 for row in data: print(row) # 7. 提交對資料庫所做的變更(包括插入和表的創建) connection.commit() # 8. 關閉資料庫連接,釋放資源 connection.close() 詳細解釋步驟: 與 SQLite 資料庫建立連接: sqlite3.connect("student.db"):如果 student.db 資料庫文件存在,則打開它;如果不存在,則創建一個新的資料庫文件。這是一個本地的 SQLite 資料庫,位於程式的當前目錄中。 創建游標對象: cursor = connection.cursor():游標用於執行 SQL 語句並返回查詢結果。它是與資料庫進行互動的主要接口。 建立 STUDENT 資料表: CREATE TABLE IF NOT EXISTS STUDENT (...):這段 SQL 語句定義了 STUDENT 表結構,包括四個欄位: NAME:儲存學生姓名的欄位,字串類型,長度最多 25。 CLASS:儲存班級名稱,字串類型,長度最多 25。 SECTION:儲存分組名稱,字串類型,長度最多 25。 MARKS:儲存學生分數,整數類型。 IF NOT EXISTS:這個語句確保只有當表不存在時才會創建表。 插入學生記錄: INSERT INTO STUDENT VALUES (...):這些 SQL 語句插入五條學生記錄到 STUDENT 表中。每條記錄包含學生的姓名、班級、分組和分數。 查詢並顯示所有學生記錄: SELECT * FROM STUDENT:這段 SQL 語句選取 STUDENT 表中的所有欄位和記錄。 for row in data::迭代查詢結果,並逐行列印每個學生的資訊(姓名、班級、分組、分數)。 提交變更: connection.commit():將對資料庫的變更(例如表的創建和記錄的插入)提交到資料庫。如果不調用這個方法,變更將不會永久儲存在資料庫中。 關閉資料庫連接: connection.close():關閉與資料庫的連接,釋放相關資源。這是良好的程式習慣,以確保不會佔用系統資源。 工作流程總結: 建立資料庫連接。 創建資料表(如果尚未存在)。 插入多筆學生記錄。 查詢並顯示所有學生記錄。 提交資料庫變更。 關閉連接。 這個工作流程展示了如何使用 SQLite 管理一個本地資料庫,從創建表到插入數據並顯示結果,最後提交並保存數據。 總結: 這個應用程序通過 Streamlit 介面實現了一個與 SQL 資料庫互動的聊天系統,結合語言模型,將用戶的自然語言查詢轉換為 SQL 查詢並返回結果。
-
正課第八節 - 利用 LangChain 框架與 Groq API 來構建一個具有數學問題解決和資料查詢功能的聊天機器人應用
這段程式碼是利用 LangChain 框架與 Groq API 來構建一個具有數學問題解決和資料查詢功能的聊天機器人應用。應用可以處理用戶輸入的數學問題,並且根據需求使用不同的工具來解決問題,如數學計算、邏輯推理以及從維基百科查詢相關資料。 程式碼詳解 1. 環境變數載入與 Streamlit 應用設置 from dotenv import load_dotenv load_dotenv(dotenv_path="/Users/kevinluo/vscode_workstation/Langchain/.env") st.set_page_config(page_title="Text To Math Problem Solver And Data Search Assistant", page_icon="🧮") st.title("Text To Math Problem Solver Using Google Gemma 2") load_dotenv:載入 .env 檔案中的環境變數,該檔案中可能包含您的 Groq API Key 或其他敏感資訊。 st.set_page_config:設置 Streamlit 應用的標題和圖標。 st.title:在頁面頂部顯示應用標題。 2. 輸入 Groq API Key 並初始化 LLM groq_api_key = st.sidebar.text_input(label="Groq API Key", type="password") if not groq_api_key: st.info("Please add your Groq API Key to continue") st.stop() llm = ChatGroq(model="Gemma2-9b-It", groq_api_key=groq_api_key) API Key 輸入:應用會提示使用者在側邊欄輸入 Groq API Key,如果沒有輸入 API Key,程式將會停止執行。 LLM 初始化:通過 ChatGroq 初始化語言模型,指定模型為 Gemma2-9b-It 並使用使用者提供的 API Key。 3. 定義工具與初始化 維基百科查詢工具 wikipedia_wrapper = WikipediaAPIWrapper() wikipedia_tool = Tool( name="Wikipedia", func=wikipedia_wrapper.run, description="A tool for searching the Internet to find the various information on the topics mentioned" ) WikipediaAPIWrapper:定義維基百科查詢工具,透過這個工具可以搜尋與指定話題相關的資料。 Tool:將這個工具包裝成一個可以供 Agent 使用的工具,並指定 func 為該工具的查詢方法。 數學計算工具 math_chain = LLMMathChain.from_llm(llm=llm) calculator = Tool( name="Calculator", func=math_chain.run, description="A tool for answering math related questions. Only input mathematical expressions need to be provided" ) LLMMathChain:這個鏈條用來處理數學計算,利用大語言模型(LLM)來解決數學相關問題。 Tool:定義計算器工具,處理數學表達式的輸入並進行計算。 邏輯推理工具 PromptTemplate:這裡定義了一個專門用於處理數學問題的提示模板。這個模板會接收用戶輸入的數學問題,並生成包含步驟的解答。 LLMChain:將這個模板與 LLM 結合起來,作為一個邏輯推理的工具鏈。 Tool:定義邏輯推理工具,使用定義好的 LLMChain 來回答推理和數學邏輯相關問題。 4. 代理初始化 assistant_agent = initialize_agent( tools=[wikipedia_tool, calculator, reasoning_tool], llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=False, handle_parsing_errors=True ) initialize_agent:這裡初始化了一個 Agent,能夠根據用戶的輸入,判斷應該使用哪個工具來解決問題。AgentType.ZERO_SHOT_REACT_DESCRIPTION 是一個零射程反應代理類型,它根據描述來選擇適合的工具。 tools:該代理可以使用的工具包括維基百科查詢工具、計算器工具和邏輯推理工具。 5. 保存與展示消息歷史 if "messages" not in st.session_state: st.session_state["messages"] = [ {"role": "assistant", "content": "Hi, I'm a Math chatbot who can answer all your math questions"} ] for msg in st.session_state.messages: st.chat_message(msg["role"]).write(msg['content']) 消息歷史:利用 st.session_state 存儲聊天歷史,這樣可以在用戶多次交互時記住之前的對話。 st.chat_message:在應用中顯示歷史聊天消息。 6. 處理用戶輸入並生成答案 question = st.text_area("Enter your question:", "I have 5 bananas and 7 grapes...") if st.button("Find my answer"): if question: with st.spinner("Generating response..."): st.session_state.messages.append({"role": "user", "content": question}) st.chat_message("user").write(question) st_cb = StreamlitCallbackHandler(st.container(), expand_new_thoughts=False) response = assistant_agent.run(st.session_state.messages, callbacks=[st_cb]) st.session_state.messages.append({'role': 'assistant', "content": response}) st.write('### Response:') st.success(response) else: st.warning("Please enter the question") 問題輸入:使用者可以在 st.text_area 輸入一個問題,這個問題會被傳遞給聊天機器人處理。 assistant_agent.run:代理根據用戶的輸入消息來選擇使用哪個工具,計算並生成答案。 st.session_state.messages:將用戶輸入的問題和機器人生成的回答都存入消息歷史,並顯示在頁面上。 總結 這段程式碼的主要目的是透過一個使用 Google Gemma 2 模型的代理,結合不同工具來解決用戶的數學問題或提供資料查詢服務。這個應用具備: 數學計算:可以解決數學表達式的問題。 邏輯推理:可以提供步驟性邏輯推理答案。 資料查詢:使用維基百科進行知識查詢。 消息歷史管理:用戶與代理的對話會存儲在消息歷史中。 應用的可擴展性也非常強,可以根據需要進一步擴充更多的工具或功能。
-
關於資料顆粒大小評估的補充
Reference:2312.10997 (arxiv.org) 使用 LlamaIndex 評估 RAG 系統的理想塊大小 介紹 檢索增強生成 (RAG) 引入了一種創新方法,該方法將搜索系統的廣泛檢索功能與 LLM 融合在一起。在實施 RAG 系統時,控制系統效率和性能的一個關鍵參數是 。如何辨別無縫檢索的最佳數據塊大小?這就是 LlamaIndex 可以派上用場的地方。在這篇補充文章中,我們將指導您完成使用 LlamaIndex 的模組確定最佳方法的步驟。 我們將針對所謂的"處理資料顆粒度"分成四的指標評估他的狀態: 1.chunk_sizeResponse 2.Evaluationchunk 3.sizeResponse 4.EvaluationResponse 為什麼數據塊大小很重要 選擇正確的決策是一項關鍵決策,可以通過多種方式影響RAG系統的效率和準確性:chunk_size 相關性和粒度:小的 ,如 128,會產生更精細的塊。但是,這種粒度存在風險:重要資訊可能不在檢索到最多的 chunk 中,尤其是在設置限制為 2 時。相反,512 的chunk大小可能包含前chunk中的所有必要資訊,確保查詢的答案隨時可用。為了解決這個問題,我們採用了 Faithfulness 和 Relevancy 量度。這些分別根據查詢和檢索到的上下文來衡量沒有 「幻覺 」和回答的 「相關性」。chunk_sizesimilarity_top_k 回應生成時間:隨著時間的增加,直接進入 LLM 以生成答案的資訊量也會增加。雖然這可以確保更全面的上下文,但也可能會減慢系統的速度。確保增加的深度不會影響系統的回應能力至關重要。chunk_size 從本質上講,確定最佳方案就是要取得平衡:在不犧牲速度的情況下捕獲所有重要資訊。對各種大小進行全面測試以找到適合特定用例和數據集的配置至關重要。chunk_size 對於選擇正確的 進行實際評估,您可以在此 Google Colab Notebook 上訪問並運行以下設置。chunk_size 設置 在開始實驗之前,我們需要確保導入所有必要的模組: import nest_asyncio nest_asyncio.apply() from llama_index import ( SimpleDirectoryReader, VectorStoreIndex, ServiceContext, ) from llama_index.evaluation import ( DatasetGenerator, FaithfulnessEvaluator, RelevancyEvaluator ) from llama_index.llms import OpenAI import openai import time openai.api_key = 'OPENAI-API-KEY' 下載數據 我們將使用 2021 年的 Uber 10K SEC 申報文件進行此實驗。 !mkdir -p 'data/10k/' !wget 'https://raw.githubusercontent.com/jerryjliu/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf' 載入數據 讓我們載入我們的文件。 documents = SimpleDirectoryReader("./data/10k/").load_data() 問題生成 要選擇正確的 ,我們將計算各種 的平均響應時間、忠實度 和相關性 等量度。這將幫助我們從文檔中生成問題。chunk_sizechunk_sizesDatasetGenerator data_generator = DatasetGenerator.from_documents(documents) eval_questions = data_generator.generate_questions_from_nodes() 設置 Evaluator 我們正在設置 GPT-4 模型作為評估實驗期間生成的回應的支柱。兩個計算機 和 使用 .FaithfulnessEvaluatorRelevancyEvaluatorservice_context Faithfulness Evaluator (忠實度評估器) — 它可用於測量回應是否是幻覺,以及測量來自查詢引擎的回應是否與任何源節點匹配。 相關性評估器 — 它可用於測量回應是否實際回答了查詢,以及測量回應 + 源節點是否與查詢匹配。 # We will use GPT-4 for evaluating the responses gpt4 = OpenAI(temperature=0, model="gpt-4") # Define service context for GPT-4 for evaluation service_context_gpt4 = ServiceContext.from_defaults(llm=gpt4) # Define Faithfulness and Relevancy Evaluators which are based on GPT-4 faithfulness_gpt4 = FaithfulnessEvaluator(service_context=service_context_gpt4) relevancy_gpt4 = RelevancyEvaluator(service_context=service_context_gpt4) 數據塊大小的回應評估 我們根據 3 個指標評估每個chunk_size。 平均回應時間。 平均忠誠。 平均相關性。 下面是一個函數 ,它只執行具有以下操作:evaluate_response_time_and_accuracy VectorIndex 創建。 構建查詢引擎。 Metrics Calculation(指標計算)。 # Define function to calculate average response time, average faithfulness and average relevancy metrics for given chunk size # We use GPT-3.5-Turbo to generate response and GPT-4 to evaluate it. def evaluate_response_time_and_accuracy(chunk_size, eval_questions): """ Evaluate the average response time, faithfulness, and relevancy of responses generated by GPT-3.5-turbo for a given chunk size. Parameters: chunk_size (int): The size of data chunks being processed. Returns: tuple: A tuple containing the average response time, faithfulness, and relevancy metrics. """ total_response_time = 0 total_faithfulness = 0 total_relevancy = 0 # create vector index llm = OpenAI(model="gpt-3.5-turbo") service_context = ServiceContext.from_defaults(llm=llm, chunk_size=chunk_size) vector_index = VectorStoreIndex.from_documents( eval_documents, service_context=service_context ) # build query engine query_engine = vector_index.as_query_engine() num_questions = len(eval_questions) # Iterate over each question in eval_questions to compute metrics. # While BatchEvalRunner can be used for faster evaluations (see: https://docs.llamaindex.ai/en/latest/examples/evaluation/batch_eval.html), # we're using a loop here to specifically measure response time for different chunk sizes. for question in eval_questions: start_time = time.time() response_vector = query_engine.query(question) elapsed_time = time.time() - start_time faithfulness_result = faithfulness_gpt4.evaluate_response( response=response_vector ).passing relevancy_result = relevancy_gpt4.evaluate_response( query=question, response=response_vector ).passing total_response_time += elapsed_time total_faithfulness += faithfulness_result total_relevancy += relevancy_result average_response_time = total_response_time / num_questions average_faithfulness = total_faithfulness / num_questions average_relevancy = total_relevancy / num_questions return average_response_time, average_faithfulness, average_relevancy 跨不同的數據塊大小進行測試 我們將評估一系列數據塊大小,以確定哪些數據塊大小提供了最有希望的指標。 chunk_sizes = [128, 256, 512, 1024, 2048] for chunk_size in chunk_sizes: avg_response_time, avg_faithfulness, avg_relevancy = evaluate_response_time_and_accuracy(chunk_size, eval_questions) print(f"Chunk size {chunk_size} - Average Response time: {avg_response_time:.2f}s, Average Faithfulness: {avg_faithfulness:.2f}, Average Relevancy: {avg_relevancy:.2f}") 整合所有內容 讓我們編譯這些進程: import nest_asyncio nest_asyncio.apply() from llama_index import ( SimpleDirectoryReader, VectorStoreIndex, ServiceContext, ) from llama_index.evaluation import ( DatasetGenerator, FaithfulnessEvaluator, RelevancyEvaluator ) from llama_index.llms import OpenAI import openai import time openai.api_key = 'OPENAI-API-KEY' # Download Data !mkdir -p 'data/10k/' !wget 'https://raw.githubusercontent.com/jerryjliu/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf' # Load Data reader = SimpleDirectoryReader("./data/10k/") documents = reader.load_data() # To evaluate for each chunk size, we will first generate a set of 40 questions from first 20 pages. eval_documents = documents[:20] data_generator = DatasetGenerator.from_documents() eval_questions = data_generator.generate_questions_from_nodes(num = 20) # We will use GPT-4 for evaluating the responses gpt4 = OpenAI(temperature=0, model="gpt-4") # Define service context for GPT-4 for evaluation service_context_gpt4 = ServiceContext.from_defaults(llm=gpt4) # Define Faithfulness and Relevancy Evaluators which are based on GPT-4 faithfulness_gpt4 = FaithfulnessEvaluator(service_context=service_context_gpt4) relevancy_gpt4 = RelevancyEvaluator(service_context=service_context_gpt4) # Define function to calculate average response time, average faithfulness and average relevancy metrics for given chunk size def evaluate_response_time_and_accuracy(chunk_size): total_response_time = 0 total_faithfulness = 0 total_relevancy = 0 # create vector index llm = OpenAI(model="gpt-3.5-turbo") service_context = ServiceContext.from_defaults(llm=llm, chunk_size=chunk_size) vector_index = VectorStoreIndex.from_documents( eval_documents, service_context=service_context ) query_engine = vector_index.as_query_engine() num_questions = len(eval_questions) for question in eval_questions: start_time = time.time() response_vector = query_engine.query(question) elapsed_time = time.time() - start_time faithfulness_result = faithfulness_gpt4.evaluate_response( response=response_vector ).passing relevancy_result = relevancy_gpt4.evaluate_response( query=question, response=response_vector ).passing total_response_time += elapsed_time total_faithfulness += faithfulness_result total_relevancy += relevancy_result average_response_time = total_response_time / num_questions average_faithfulness = total_faithfulness / num_questions average_relevancy = total_relevancy / num_questions return average_response_time, average_faithfulness, average_relevancy # Iterate over different chunk sizes to evaluate the metrics to help fix the chunk size. for chunk_size in [128, 256, 512, 1024, 2048] avg_time, avg_faithfulness, avg_relevancy = evaluate_response_time_and_accuracy(chunk_size) print(f"Chunk size {chunk_size} - Average Response time: {avg_time:.2f}s, Average Faithfulness: {avg_faithfulness:.2f}, Average Relevancy: {avg_relevancy:.2f}") 結果 上表表明,隨著塊大小的增加,Average Response Time (平均響應時間) 會略有增加。有趣的是,平均忠實度似乎在1024時達到頂峰,而平均相關性則顯示出更大的塊大小的持續改進,也在1024處達到峰值。這表明 1024 的塊大小可能會在響應時間和回應質量之間取得最佳平衡,以忠實度和相關性來衡量。chunk_size 結論 確定RAG系統的最佳塊大小既需要經驗證據,也需要直覺。使用 LlamaIndex 的模組,您可以嘗試各種大小並根據具體數據做出決策。在構建RAG系統時,請始終記住這是一個關鍵參數。投入時間仔細評估和調整您的數據塊大小,以獲得無與倫比的結果。Response Evaluationchunk_size 大家也可以看這篇論文: 2401.07883 (arxiv.org) 那其實Chroma Research也有針對估計對切割chunk去做檢索方面的策略研究。 "Evaluating Chunking Strategies for Retrieval" 儘管文檔分塊作為預處理步驟幾乎無處不在,但很少有工作來研究它對檢索性能的影響。這部分是由於常用資訊檢索基準的結構,這些基準針對的是整個文檔的檢索任務。 在這項工作中,提出了一種評估方法,該方法考慮了token級別的相關性,並允許評估幾種流行的文檔分塊策略。 證明,分塊策略的選擇會對檢索性能產生重大影響,一些策略的召回率比其他策略高出 9%。 分塊是攝取文檔以在 AI 應用程式上下文中進行檢索時常用的預處理步驟。分塊用於將文檔劃分為資訊單元,其語義內容適合 LLM 基於嵌入的檢索和處理。 此技術報告的目的是評估分塊策略的選擇對檢索性能的影響,以代表分塊和檢索在 AI 應用程式上下文中的使用方式。 雖然 LLM 上下文長度已經增加,並且可以將整個文檔甚至文本語料庫插入上下文視窗,但在實踐中這樣做通常效率低下,並且會分散模型的注意力。對於任何給定的查詢,只有文本語料庫的一小部分可能是相關的,但上下文視窗中的所有標記都會針對每個推理進行處理。理想情況下,對於每個查詢,LLM 只需要處理相關令牌,因此 AI 應用程式中檢索系統的主要目標之一是僅識別和檢索給定查詢的相關令牌。 MTEB 等常用基準測試採用傳統的資訊檢索 (IR) 方法,其中檢索性能通常是根據整個文檔的相關性進行評估的,而不是在段落或標記的級別進行評估,這意味著它們不能考慮分塊。 在 AI 應用程式中,可以在許多文件中或跨多個文件中找到包含與查詢相關的所有標記的摘錄。塊可以同時包含相關和不相關的標記,並且相關摘錄可以拆分為多個塊。 傳統的 IR 基準測試通常也關注檢索到的文檔的相對排名,但在實踐中,LLM 對相關訊息在上下文視窗中的位置相對不敏感。此外,與給定查詢相關的資訊可能分佈在多個文檔中,這使得文檔之間相對排名的評估變得不明確。 在這些限制的激勵下,提出了一種新的評估策略,在代幣級別評估檢索性能。他們的評估使用 LLM 從任何給定的文字語料庫中生成並隨後過濾一組查詢和相關的相關摘錄,然後根據檢索到的標記通過精度、召回率和交集(Jaccard 索引)評估檢索性能。
-
補充: 使用 Streamlit 建立多模態 RAG 應用程式:詳細教學
在這篇教學中,我們將詳細介紹如何使用 Streamlit 來建立一個多模態的 RAG(Retrieval-Augmented Generation)應用程式。本應用程式允許使用者上傳 PDF 檔案,並透過結合文本、表格和圖像等多種資料,實現智能的問答和評估功能。 目錄 環境準備 程式碼導覽 導入必要的函式庫 初始化 Session State 設定 Streamlit 應用程式 文件處理流程 PDF 元素提取 內容分塊 元素分類 摘要生成 圖像摘要生成 建立向量儲存和檢索器 建立 RAG 鏈 使用者介面說明 上傳 PDF 檔案 查詢回應和評估 結論 環境準備 在開始之前,請確保已安裝以下工具和函式庫: Python 3.7 以上版本 Streamlit Unstructured NLTK LangChain OpenAI API Chroma Pydantic 其他相關函式庫 請確保已經設定好 OpenAI API 的金鑰,並且可以順利連線至所需的服務。 程式碼導覽 接下來,我們將逐步解析程式碼,並說明每個部分的功能。 導入必要的函式庫 import streamlit as st from unstructured.partition.pdf import partition_pdf from unstructured.chunking.title import chunk_by_title from typing import Any from pydantic import BaseModel from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings from utils.image_processing import generate_img_summaries from utils.retriever import create_multi_vector_retriever from utils.rag_chain import multi_modal_rag_chain, plt_img_base64 from utils.rag_evaluation import LLM_Metric from io import BytesIO import nltk import ssl import os from dotenv import load_dotenv 首先,我們導入了所需的函式庫,包括 Streamlit、Unstructured、LangChain 等,以及自定義的工具模組,例如 image_processing、retriever、rag_chain 和 rag_evaluation。 # Initialize session state if 'processed' not in st.session_state: st.session_state.processed = False if 'retriever' not in st.session_state: st.session_state.retriever = None if 'chain' not in st.session_state: st.session_state.chain = None 為了在應用程式中保持狀態,我們使用了 st.session_state。這可以讓我們在不同的使用者互動之間保存變數的值,例如檔案是否已處理、檢索器和 RAG 鏈的狀態。 設定 Streamlit 應用程式 st.set_page_config( page_title='Multi-Modal RAG Application', page_icon='random', layout='wide', initial_sidebar_state='auto' ) 這部分設定了 Streamlit 應用程式的頁面配置,包括標題、圖標、佈局和側邊欄的狀態。 文件處理流程 PDF 元素提取 def process_document(uploaded_file): # Process PDF with st.spinner('Processing PDF...'): st.sidebar.info('Extracting elements from PDF...') pdf_bytes = uploaded_file.read() elements = partition_pdf( file=BytesIO(pdf_bytes), strategy="hi_res", extract_images_in_pdf=True, extract_image_block_types=["Image", "Table"], extract_image_block_to_payload=False, extract_image_block_output_dir="docs/saved_images", ) st.sidebar.success('PDF elements extracted successfully!') 當使用者上傳 PDF 檔案後,應用程式會讀取檔案並使用 partition_pdf 函式提取其中的元素。我們設定了高解析度策略,並提取圖像和表格等元素,將圖像儲存到指定的目錄。 內容分塊 # Create chunks by title with st.spinner('Chunking content...'): st.sidebar.info('Creating chunks by title...') chunks = chunk_by_title(elements) st.sidebar.success('Chunking completed successfully!') 提取元素後,我們使用 chunk_by_title 函式根據標題將內容分塊,方便後續的處理和分析。 元素分類 # Categorize Elements class Element(BaseModel): type: str text: Any categorized_elements = [] for element in chunks: if "unstructured.documents.elements.CompositeElement" in str(type(element)): categorized_elements.append(Element(type="text", text=str(element))) elif "unstructured.documents.elements.Table" in str(type(element)): categorized_elements.append(Element(type="table", text=str(element))) text_elements = [e for e in categorized_elements if e.type == "text"] table_elements = [e for e in categorized_elements if e.type == "table"] 我們定義了一個 Element 類別,並將分塊的內容分類為文本或表格。這有助於我們在後續針對不同類型的內容進行特定的處理。 摘要生成 # Prompt prompt_text = """You are an expert Research Assistant tasked with summarizing tables and texts from research articles. \ Give a concise summary of the text. text chunk: {element} """ prompt = ChatPromptTemplate.from_template(prompt_text) # Summary chain model = ChatOpenAI(temperature=0, model="gpt-4o", max_tokens=1024) summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser() texts = [i.text for i in text_elements] text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5}) tables = [i.text for i in table_elements] table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5}) 我們使用了 OpenAI 的模型(如 gpt-4o)來生成文本和表格的摘要。首先,定義了一個提示模板,然後建立了一個摘要鏈,最後批量處理文本和表格,生成對應的摘要。 圖像摘要生成 # Image summaries fpath = "docs/saved_images" img_base64_list, image_summaries = generate_img_summaries(fpath) 這部分使用自定義的 generate_img_summaries 函式,對提取的圖像進行摘要生成。函式會返回圖像的 Base64 編碼列表和對應的摘要。 建立向量儲存和檢索器 # Vectorstore vectorstore = Chroma( collection_name="mm_tagiv_paper", embedding_function=OpenAIEmbeddings() ) # Create retriever st.session_state.retriever = create_multi_vector_retriever( vectorstore, text_summaries, texts, table_summaries, tables, image_summaries, img_base64_list, ) 我們使用了 Chroma 來建立向量儲存,並使用 OpenAI 的嵌入函式。接著,利用 create_multi_vector_retriever 函式建立了一個多向量的檢索器,將文本、表格和圖像的摘要和原始內容整合在一起。 建立 RAG 鏈 # Create RAG chain st.session_state.chain = multi_modal_rag_chain(retriever=st.session_state.retriever) st.session_state.processed = True 最後,使用 multi_modal_rag_chain 函式建立了一個多模態的 RAG 鏈,並將檔案處理的狀態設定為 True,表示已完成處理。 使用者介面說明 上傳 PDF 檔案 with st.sidebar: # File upload st.subheader('Add your PDF') uploaded_file = st.file_uploader("Upload a PDF file", type=["pdf"]) if st.button('Submit'): if uploaded_file is not None: process_document(uploaded_file) st.success('Document processed successfully!') else: st.error('Please upload a PDF file first.') 在側邊欄中,使用者可以上傳 PDF 檔案。按下「Submit」按鈕後,若有上傳檔案,將呼叫 process_document 函式進行處理,否則提示請先上傳檔案。 查詢回應和評估 # Main page for query response and evaluation st.subheader("RAG Assistant") query = st.text_input("Enter your query:") if query and st.session_state.processed: # Execution retrieval_context = st.session_state.retriever.invoke(query, limit=1) actual_output = st.session_state.chain(query) # Evaluation llm_metric = LLM_Metric(query, retrieval_context, actual_output) faith_score, faith_reason = llm_metric.get_faithfulness_metric() relevancy_score, relevancy_reason = llm_metric.get_contextual_relevancy_metric() answer_relevancy_score, answer_relevancy_reason = llm_metric.get_answer_relevancy_metric() hallucination_score, hallucination_reason = llm_metric.get_hallucination_metric() # Display results st.subheader("Query Response") st.write(actual_output) st.subheader("Evaluation Metrics") st.write(f"Faithfulness Score: {faith_score}, Reason: {faith_reason}") st.write(f"Contextual Relevancy Score: {relevancy_score}, Reason: {relevancy_reason}") st.write(f"Answer Relevancy Score: {answer_relevancy_score}, Reason: {answer_relevancy_reason}") st.write(f"Hallucination Score: {hallucination_score}, Reason: {hallucination_reason}") elif query and not st.session_state.processed: st.warning("Please upload and process a document first.") 在主頁面中,使用者可以輸入查詢。若已處理檔案,應用程式將: 使用檢索器取得相關內容。 使用 RAG 鏈生成回應。 利用 LLM_Metric 進行評估,包括真實性、上下文相關性、答案相關性和幻覺分數。 顯示生成的回應和評估結果。 若未處理檔案,則提示使用者先上傳並處理文件。 結論 透過本教學,我們詳細介紹了如何使用 Streamlit 和相關的 AI 工具,建立一個多模態的 RAG 應用程式。我們涵蓋了從檔案上傳、內容處理、摘要生成到查詢回應和評估的完整流程。希望這篇文章能夠幫助您理解並實現類似的應用程式。 實作工作流: 從建立虛擬環境到完整執行應用程式的詳細指南 在這篇文章中,我將指導您從建立 Python 虛擬環境(venv)到完整執行我們的多模態 RAG 應用程式。並且,我會幫您整理所需安裝的套件,並提供 requirements.txt 檔案的內容,以方便您安裝。 目錄 建立虛擬環境 啟動虛擬環境 創建 requirements.txt 檔案 安裝所需的套件 設定環境變數 執行應用程式 可能遇到的問題與解決方法 結論 建立虛擬環境 首先,我們需要在您的專案目錄中建立一個虛擬環境,以隔離專案的依賴關係。 步驟: 開啟終端機(Terminal)或命令提示字元(Command Prompt)。 導航到您的專案目錄: bash 複製程式碼 cd /path/to/your/project 建立虛擬環境: 如果您使用 Python 內建的 venv 模組: python3 -m venv venv 如果您的系統上有多個 Python 版本,請確保使用正確的版本: python3.8 -m venv venv # 假設您使用 Python 3.8 啟動虛擬環境 步驟: 在 macOS/Linux 上: bash source venv/bin/activate 在 Windows 上: bash venv\Scripts\activate 啟動後,您會看到終端機提示符前面有 (venv),表示虛擬環境已啟動。 創建 requirements.txt 檔案 在您的專案目錄中,創建一個名為 requirements.txt 的檔案,並將以下內容複製貼上到該檔案中。 requirements.txt 內容: #txt streamlit==1.25.0 unstructured==0.8.3 nltk==3.8.1 langchain==0.0.308 openai==0.28.1 chromadb==0.4.13 pydantic==1.10.13 Pillow==10.0.0 python-dotenv==1.0.0 sentence-transformers==2.2.2 注意: 為了確保套件之間的兼容性,我已經為每個套件指定了版本號。如果您需要調整版本,請務必確認相容性。 安裝所需的套件 在啟動虛擬環境的狀態下,執行以下命令來安裝 requirements.txt 中列出的套件。 步驟: bash pip install --upgrade pip # 先升級 pip install -r requirements.txt 這將自動安裝所有列出的套件及其依賴項。 其他需要的安裝和設定 NLTK 資料下載: 在您的程式碼中,已經有以下代碼來下載 NLTK 資料: python nltk.download('averaged_perceptron_tagger_eng') 如果您在執行時遇到 SSL 認證的問題,程式碼中已經處理了 SSL 認證繞過: python import ssl ssl._create_default_https_context = ssl._create_unverified_context OpenAI API 金鑰設定: 您需要在專案目錄中創建一個 .env 檔案,並添加您的 OpenAI API 金鑰: env 複製程式碼 OPENAI_API_KEY=your_openai_api_key_here 請確保不要將 .env 檔案提交到版本控制系統中,以保護您的金鑰安全。 創建必要的目錄: 如果您的程式碼中使用了特定的目錄來保存圖像或其他資料,請確保這些目錄已經存在。例如: bash mkdir -p docs/saved_images 下載其他 NLTK 資料: 根據您的需求,您可能需要下載其他的 NLTK 資料集,例如: python nltk.download('punkt') nltk.download('averaged_perceptron_tagger') nltk.download('stopwords') 設定環境變數 在您的 .env 檔案中,除了 OPENAI_API_KEY,如果有其他環境變數需要設定,也可以在此添加。例如,如果您使用 Hugging Face Hub: env HUGGINGFACEHUB_API_TOKEN=your_huggingface_api_token_here 執行應用程式 在安裝完所有套件後,您可以使用 Streamlit 來執行應用程式。 步驟: 啟動虛擬環境(如果尚未啟動): bash 複製程式碼 source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows 執行應用程式: 假設您的主程式碼文件名為 app.py,執行以下命令: streamlit run app.py 在瀏覽器中查看應用程式: Streamlit 會自動在您的預設瀏覽器中打開應用程式。如果沒有,您可以在終端機中看到一個類似以下的 URL,手動複製並在瀏覽器中打開: http://localhost:8501 可能遇到的問題與解決方法 套件版本不兼容: 如果您在安裝或執行時遇到套件版本不兼容的問題,請嘗試調整 requirements.txt 中的套件版本,並重新安裝。 缺少套件或模組: 如果在執行時提示缺少某個套件或模組,請手動安裝該套件。例如: pip install package_name OpenAI 模型名稱錯誤: 確保您在程式碼中使用的 OpenAI 模型名稱是有效的。例如,gpt-4o 可能不是有效的模型名稱,請確認並使用正確的模型名稱,例如 gpt-4 或 gpt-3.5-turbo。 model = ChatOpenAI(temperature=0, model="gpt-4", max_tokens=1024) SSL 認證問題: 如果在下載 NLTK 資料時遇到 SSL 認證問題,程式碼中已經添加了繞過 SSL 認證的解決方案。 路徑問題: 確保所有使用的路徑都是正確的,並且文件和目錄實際存在。 環境變數未正確讀取: 確保您已經安裝 python-dotenv,並在程式碼中正確載入環境變數: python 複製程式碼 from dotenv import load_dotenv load_dotenv() 結論 通過以上步驟,您應該能夠從建立虛擬環境到完整執行應用程式。如果在過程中遇到任何問題,請仔細檢查錯誤訊息,並根據提示進行調整。祝您順利完成應用程式的部署與運行! 備註: 請確保您的 Python 版本與套件的需求相匹配,並且始終在虛擬環境中操作,以避免影響全域的 Python 環境。
-
補充 使用LangChain, LlamaParse, 和"Groq"對複雜的PDF進行RAG
各位不知道還記不記得上一堂課說到老師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 鏈
-