RAG(Retrieval-Augmented Generation,檢索增強生成)無疑是當今開發大型語言模型(LLM)產品時,不可或缺的技術。由於可以確保 LLM 生成的真實性,在企業 AI 應用與搜尋場景相當受到重視。
為此,我也試著實作一個簡單的 RAG 應用。多年累積下來,我的 Apple Notes 已經有 7000 多則筆記,搜尋愈來愈不準確,常在寫新筆記時想參考舊筆記,卻怎麼也找不到。
於是,我決定替 Apple Notes 加上 RAG:每當輸入一則新的筆記時,便自動推薦相似度最高的五則筆記,效果可參考下方影片。
隨著我在文件上輸入、修改,底下會出現一排依照相似度排序的推薦筆記。而由於我的筆記包含廣泛的領域,當我輸入的是「行銷規劃」,底下只會出現關於行銷的筆記;當切換到「樂理」,則只會推薦音樂相關的筆記。
本文會先簡單介紹 RAG 的原理與優勢,接著分享我的實作經驗(相關的教學文章連結我整理在文末),並討論 RAG 應用產品化過程中可能面臨的問題。在文章最後,則會進一步探討:當 LLM 具備長文本理解能力,可以讓我們把資料全部塞進 prompt 裡,此時還需要 RAG 嗎?
RAG 是什麼?
RAG 解決了大型語言模型(LLM)實際應用時的兩大侷限:幻想(hallucination)與資料時限。RAG 結合「資訊檢索(retrieval)」和「生成(generation)」:在文本生成之前,先從資料庫中檢索相關的資料放入上下文,以確保 LLM 可依照正確的最新資訊生成結果。
看似複雜,其實相當直覺——既然大型語言模型受限於缺乏最新資料,那我們就在生成時,提前準備好「小抄」,讓它照著回答。
這裡借用並小幅修改 InfuseAI 工程師劉詩雁的比喻:1想像在參加考試前,如果我們完全不清楚範圍,現場很可能因為沒有讀過,只好胡謅一通;然而如果老師已經事先列出會考的頁數,並開放考試時可以「開書考」,我們就可以現場從書中找到資料並寫下答案。這個「指定範圍+開書考」的流程,就是 RAG。
RAG 的優勢
如同前面提到的,RAG 可以解決幻想與資料時限問題,進而提高回答內容的正確性。這可以提高 LLM 生成應用在一些場景的可靠度,例如我們會希望 AI 客服不要以幻想出來的解決方案回應客戶問題。
此外,RAG 是在生成回答時才去搜尋資料,且可以使用現成的大型語言模型,成本低、靈活度高。與之相對,另一個做法「微調(fine tune)」不僅成本高,且每當有新資料就需要重新訓練。
RAG 的另一個好處是權限更易於管理,這點對於重視機密隱私的企業產品而言相當重要。由於資料庫本就具備權限管理功能,企業可以限制敏感資料的授權,只供特定使用者取得,從源頭避免大型語言模型「看見」並意外泄露機密。
若採用「微調」的方式,則需要針對不同層級的資料權限,微調出不同的模型。RAG 相較之下更加簡單,資料亦可以保留在客戶端,不需要上傳至其他地方訓練,進一步確保資料隱私。
(編按:如果你對於 RAG 的運作原理、優點與發展趨勢感興趣,可以閱讀這篇〈RAG 是什麼?不用微調模型就能低成本讓 LLM 理解專業知識的 AI 技術〉。)
Apple Notes + RAG 的實作流程
實作上,我們怎麼提供「小抄」給大型語言模型呢?目前最常見的 RAG 流程如下:
- 使用者輸入一段文字
- 將輸入的文字,透過 embedding 轉換成一個 vector(向量)
- 從向量資料庫中,找到與該向量座標最接近的資料
- 將提取出來的資料(小抄)放入提示詞的上下文,並要求 LLM 根據小抄生成回答
對此,我們需要存放向量的「vector database(向量資料庫)」,與一套 embedding 演算法。以我的「Apple Notes + RAG」應用為例,採用 Cloudflare 的 Vectorize 向量資料庫,並用 BAAI(北京智源人工智能研究院)的 bge-m3 模型做 embedding。
在挑選模型時需要注意,有些模型只能針對單一語言 embedding,而 bge-m3 不僅開源、具備多語言 embedding 能力,透過 Hugging Face 統一的 API,只需要簡短的程式碼就可以下載與執行模型,實作起來非常有效率。(如果想進一步瞭解 Hugging Face,可以參考我先前寫的文章〈什麼是 Hugging Face?開源 AI 平台介紹 4 點加速 AI 開發〉。)
在執行 RAG 前,我們得先逐一對筆記做 embedding ,並上傳至向量資料庫。我只上傳向量與一個索引用的筆記 ID,將筆記內容保留在本機端,以確保隱私。
前端介面,我則寫了簡易的 Next.js 網站,並在每次輸入一小段文字後,重新做 embedding 並與向量資料庫比對,找出相似度最高的五則筆記。如此一來,便完成簡易的 RAG 了。
由於此應用不需要 LLM 幫我撰寫筆記內容,嚴格來說,這是只有 R(Retrieval)、沒有 G(Generation)的 RAG,更像是語意搜尋的一種變形。我在實作過程中,發覺多增加一個 LLM 生成回答,並沒有提升使用體驗,但實務上可以改寫為針對筆記提問或要求生成摘要。
RAG 產品化的挑戰
如同這個「Apple Notes + RAG」的小應用,利用市面上常見的模型、產品與開源專案,可以很快製作出一套 RAG 的基本框架。然而,若想要進一步提升使用體驗與產品化,RAG 常常會面臨以下四種挑戰:
一、資料清理
在準備小抄的過程中,如果讀取出來的都是低品質的資料,自然會影響生成的結果品質。因此,在放入向量資料庫前,需要先進一步篩選資料。
以本次的應用為例,有些筆記只是一段網址、購物清單或當天提醒自己的待辦事項。將這些沒有太多內容的筆記也塞入資料庫,只會在搜尋時被過多雜訊影響,導致生成的結果變差。
二、如何切割文本
當文本資料很長時(例如:書籍或文章),由於 embedding 有資料長度上限,必須先將文本切割成許多片段。
常見的做法是依照字數切割,並在不同片段前後加上些許重疊。這種做法的問題是,每個片段間並不「獨立」。一個概念可能會在同一篇文章前後出現,導致在檢索時引用到重複的知識片段;或是同一個知識脈絡被切成了兩段,導致兩段都不夠完整,而無法被正確檢索。
切割片段的大小也很重要。小文本容易檢索,因為當資料被壓縮成向量時不容易失真;然而小文本卻也容易喪失脈絡,導致最終生成的回答不夠完整。因此,光是如何切割,就需要考慮語意,並實驗不同片段大小對於結果的影響。
三、如何搜尋
單純比較向量座標間的距離,並不是最細緻的搜尋方式。Google 至今仍然在持續改進其搜尋演算法,因此想透過單一的參數就找到最好的結果是不實際的。檢索時,不一定要限制自己只能用向量比對,可以多考慮其他「傳統」的搜尋方法,例如資料的標籤、分類與時間,甚至加入一定比重的關鍵字搜尋。
針對本次應用,由於我已經將筆記放入不同資料夾,優先推薦同資料夾內的筆記,或許是合理的做法。又或者在我搜尋時,可能更希望找到比較近期的筆記,而非塵封已久的舊筆記,因此將時間納入權重,也可能讓體驗更佳。
四、LLM 生成品質
在導入 RAG 時,不只需要重視「檢索」,也需要透過 prompt engineering,來確保「生成」這段的產品體驗。
此外,ChatGPT 的爆紅,導致許多產品預設為聊天界面。但在產品設計時,很值得思考什麼才是最適合的介面。對於本次專案,我並不需要 LLM「幫我生成筆記」,只需要它幫我找到相關的筆記。此時如果將介面做成對話框,硬讓 LLM 生成一段解釋,反而會造成分心與體驗下滑。
更多關於 AI 產品化過程的思考點,也可以讀讀我過去曾寫過的文章:〈6 個開發 AI 應用時該思考的問題,幫助你讓 Side Project 走向產品化〉。
有了「長上下文 LLM」(Long-Context LLM),還需要 RAG 嗎?
Google 在 2024 年 2 月 15 日,發表了有穩定一百萬 token 數、長上下文的 Gemini 1.5。此時我們還需要利用 RAG 將資料切開後搜尋嗎?
在 X(Twitter)上有一個相關討論,認為當 LLM 具備長文本理解能力時,大多數 RAG 的應用場景都將消失。畢竟,如果可以把任意長的資料都塞進 prompt,似乎就沒必要多此一舉「製作小抄」。值得注意的是,根據 Google 的技術報告,將資料直接放進 Gemini 1.5 Pro 的上下文,效果也比使用 RAG 的 Gemini 1.0 Pro 更好。2
不過,另一則回覆則持反對意見,認為當有一百萬個 tokens 時,貪心的人類就會希望「引用盡可能多的資料片段」——最好多到把 prompt 塞滿。例如,若想知道「好萊塢超級英雄電影公式」,我們可能不會希望 LLM 只看過一、兩部電影就回答,而會先透過 RAG 檢索一千部電影劇情,並全部放進 LLM 的 prompt 中。此時,RAG 仍然是提供資料來源的有效手段。
綜合雙方意見,RAG 固然是為了解決現行 LLM 上下文過短、回應不真實的過渡方案,但與此同時,RAG 也能提供更精確的內容參照,讓 LLM 更進一步理解使用者的意圖,並強化論述能力。在長上下文的 LLM 普及後,我想 RAG 的使用情境或許會改變,但仍會是重要的技術之一。
本文從實作出發,探討 RAG 應用產品化時需要注意的問題。隨著 AI 的發展,RAG 的實作細節也可能有所變化,就讓我們繼續關注趨勢吧。不過,目前 RAG 實作的成本與門檻並不高,如果你也想嘗試自己實作看看,以下是我在這次實作中曾讀過的文章,提供給你參考: