
如何快速實現REST API集成以優化業務流程
?索引過程是為檢索過程準備數據。你應該收集你想讓你的LLM知道的一切,例如,產品文檔、產品政策、公司網站等,這取決于你想讓聊天機器人做什么。然后你會把它分解成更小的文本塊(這樣你就可以很容易地把這些塊放進LLM的上下文大小)。然后,你將通過嵌入模型將塊轉換為矢量表示(這樣以后你就可以很容易地找到類似的塊)。最后,你可以將所有這些文本嵌入對保存在索引或矢量數據庫中,以供檢索使用。
? ? ? ?檢索過程發生在用戶查詢LLM時。在用戶提出問題后,你可以保留該查詢,而不是直接將其發送到LLM。相反,你將使用索引中文本塊中的一些附加信息來豐富查詢。你將使用相同的嵌入模型對用戶的原始查詢進行編碼,然后執行相似性搜索,在數據庫中找到最相似(大多數時候也是最相關)的文本塊。
然后,為了生成,需要將文本塊插入到包括用戶原始查詢的提示中,LLM將使用檢索到的文本塊中提供的信息生成答案。
Answer the following question based on the given information only. If the given information is not enough to answer the question, simply reply "I don't know".
Question: "<user's original query>"
Given information: "<the text chunk you retrieved from the database>"
RAG是業界公認的流程,LlamaIndex[1]和LangChain[2]兩個流行的庫支持上述這些步驟用于RAG流程。RAG需要矢量數據庫來創建索引和檢索,如Pinecone[3]和Chroma[4]。
這個過程簡單有效,但在現實世界中,經常會面臨以下問題:
在下一節中,我們將介紹一些克服這些問題并提高RAG性能的技術。
? ? ? ?經過近一年的LLM使用,我學到了許多提高RAG性能的技術,并總結了一些使用RAG的經驗教訓。在本節中,我將介紹許多在檢索前、檢索中和檢索后提高RAG性能的技術。
預檢索技術包括可以在索引步驟中或在搜索數據庫中的塊之前使用的技術。
? ? ? ?第一種技術是提高索引數據的質量。在機器學習領域,有一句話叫“垃圾進,垃圾出”,我認為這也適用于RAG,但許多人只是忽略了這一步驟,并在這一非常關鍵的初始步驟之后專注于優化步驟。您不應該期望將每一個文檔(無論是否相關)都放入您的矢量數據庫,并抱著最好的希望。為了提高索引數據的質量,您應該:(1)刪除與特定任務無關的文本/文檔(2)將索引數據重新格式化為與最終用戶可能使用的格式類似的格式(3)向文檔中添加元數據,以實現高效和有針對性的檢索。
這是我自己的一個例子。我需要檢索的文本是數學問題,但關于不同概念的兩個數學問題在語義上可能相似。例如,許多問題可能使用“湯姆第一天吃了8個蘋果……”,但它們可能測試加法、乘法和除法,這是非常不同的。在這種情況下,最好使用概念和級別的元數據對它們進行標記,并在檢索它們之前檢查正確的概念。
另一個非常典型的情況是,塊在拆分時可能會丟失信息。考慮一篇典型的文章,開頭的句子通過名字介紹實體,而后面的句子只依靠代詞來指代它們。不包含實際實體名稱的分割塊將失去語義,無法通過向量搜索進行檢索。因此,在這種情況下,用實際名稱替換代詞可以提高分割塊的語義意義。
? ? ? ?第二種技術是分塊優化。根據你的下游任務是什么,你需要確定塊的最佳長度是多少,以及你希望每個塊有多少重疊。如果你的塊太小,它可能不包括LLM回答用戶查詢所需的所有信息;如果塊太大,它可能包含太多不相關的信息,從而混淆LLM,或者可能太大而無法適應上下文大小。
根據我自己的經驗,對于管道中的所有步驟,您不必拘泥于一種塊優化方法。例如,如果您的管道同時涉及高級任務(如摘要)和低級任務(如基于函數定義的編碼),則可以嘗試使用較大的塊大小進行摘要,然而使用較小的塊大小作為編碼參考。
? ? ? ?還有另一種技術是在嘗試在矢量數據庫中匹配用戶的查詢之前重寫該查詢。此步驟的本質是將用戶的查詢轉換為與矢量數據庫中的查詢格式和內容類似的格式和內容。Query2Doc技術生成偽文檔,并用這些文檔擴展查詢[5]。類似地,HyDE(假設文檔嵌入)生成與查詢相關的假設文檔[6]。
?以下是一些如何生成假設文檔的示例:
# if your reference documents are blog articles.
prompt = f"Please generate a paragraph from a blog article on {user_query}"
# if your reference documents are code documentations in markdown.
prompt = f"Please generate a code documentation for {user_query} in markdown format."
使用Query2Doc或HyDE技術時的一個陷阱是,假設文檔可能與實際文檔相矛盾或完全不一致,這可能導致不準確的檢索。為了解決這個問題,您可以檢索包含和不包含假設文檔的文檔,這樣您就可以應用我稍后將介紹的后期檢索技術來找到最佳參考文本。
? ? ? ?當用戶的查詢很復雜,可能需要多個參考文本時,可以使用LLM將其分解為多個子查詢,然后為每個查詢找到相關的文本塊。例如,如果用戶問兩個不同的問題“ChomaDB和Weaviate之間的區別是什么?”,可以分為“什么是ChromaDB?”和“什么是Weaviate?”。
? ? ? ? 下面是一個要求LLM分解查詢的示例:
Please rephrase the following query into three or fewer subqueries, so that each sub-query contains only one topic. Show each sub-query in each new line.
Query:"<original user query>"
?如果您的聊天機器人或代理可以處理多個下游任務和不同格式的用戶查詢,您可以考慮使用查詢路由,在該路由中,您可以將查詢動態路由到不同的RAG進程。例如,如果你的用戶正在詢問一個問題的特定答案,你可以將他們路由到查詢特定的塊;如果你的用戶要求一個整體的摘要,你可以將他們路由到一個遞歸創建的許多檢索到的文檔的摘要;如果您的用戶要求在兩個文檔之間進行比較,您可能需要使用上面提到的子查詢技術。您可以使用LLM本身進行路由,也可以使用關鍵字匹配/嵌入相似性進行路由。
? ? ? ?準備好查詢后,可以在RAG管道的第二步中進一步改進檢索結果。
第一種技術經常被忽視,因為人們只是跟著別人做——一直堅持向量相似性搜索。但是,您可以也應該考慮使用其他搜索方法來取代向量相似性搜索,或者通過混合搜索來補充它。盡管矢量相似性搜索在大多數情況下可以找到相關文檔,但對于某些情況或數據結構,最好使用全文搜索、結構化查詢、基于圖的搜索或混合搜索方法。
例如,如果您的文本數據包含許多語義非常相似的塊,但僅在某些關鍵字上有所不同,或者如果您的文字數據包含太多通用文字,則最好使用精確的關鍵字匹配進行搜索。例如,在電子商務中搜索僅按功能組不同的特定藥物名稱或數以萬計的類似產品名稱,可能會受益于全文匹配和過濾器。
另一個經常被忽視的技術是針對特定任務測試和使用不同的嵌入。許多人甚至不會考慮這一點,因為框架/向量數據庫有一個默認的嵌入選項,他們只是隨波逐流。但不同的嵌入模型實際上可以捕獲不同的語義信息,并可能適用于不同的任務。一個有用的嵌入模型是指導嵌入,它允許您提供關于嵌入的數據類型和任務的具體說明[7][8]。您也可以參考MTEB排行榜,它是文本嵌入模型的基準。一定要測試這些模型,因為在排行榜上排名靠前并不意味著它最適合你的特定任務[9]。
除此之外,您還可以在檢索過程中進行一些調整,使檢索更具相關性。Small2big、遞歸或上下文感知檢索是一種最初檢索較小的數據塊,由于更具體和更詳細,這些數據塊更有可能與查詢匹配,然后繼續檢索父文檔或圍繞這些較小數據塊的較大文本塊,以包括更多上下文的技術。它們確保您檢索相關的塊以及所有重要的上下文。一些框架提供了對這種檢索的支持,如LangChain[2]中的ParentDocumentRetriever、句子窗口和LlamaIndex[1]中的節點引用。
如果您可以從小到大地檢索文檔,那么您也可以用另一種方式來檢索,分層檢索從更通用到更具體。例如,您可以創建兩層數據,一層包含原始塊,另一層包含塊的摘要。您首先在摘要索引中搜索最相關的文檔,然后在這些文檔中再次搜索特定的塊。這樣,你可以在第一關快速過濾掉不相關的文檔,然后在第二關找到實際的信息進行問答。
? ? ? ?類似地,您可以將遞歸搜索與圖形搜索結合使用。該方法將相似性搜索與圖形數據結構相結合。您首先通過向量相似性搜索找到最相關的塊,然后探索與這些塊相關的節點,以探索更多潛在的有用信息。例如,如果您有一個包含Notion或Obsidian等互連文檔的數據庫,則可以通過鏈接輕松找到LLM的相關文檔。LlamaIndex通過RecursiveRetriever模塊支持類似的搜索。
還有更多的代理方式來執行檢索,方法是首先使用查詢文檔的工具/功能創建檢索器代理,并讓它決定是搜索更多信息還是僅將相關的檢索塊返回給原始代理以回答用戶的查詢。但這些技術通常需要更長的響應時間,而且可能不穩定,因此可能不是很好的生產選擇。希望通過更強大的模型和更快的推理,我們可以在這個方向上獲得更好的結果。
在從數據庫中檢索到相關的塊之后,仍然有更多的技術可以提高生成質量。根據任務的性質和文本塊的格式,您可以使用以下一種或多種技術。
? ? ? ?如果你的任務與一個特定的塊更相關,一種常用的技術是重新排序或評分。正如我前面提到的,向量相似性搜索中的高分并不意味著它總是具有最高的相關性。你應該進行第二輪重新排序或評分,找出對生成答案真正有用的文本塊。對于重新排序或評分,您可以要求LLM對文檔的相關性進行排序,也可以使用一些其他方法,如關鍵字頻率或元數據匹配,在將這些文檔傳遞給LLM以生成最終答案之前,對選擇進行細化。
? ? ? ?另一方面,如果你的任務與多個塊有關——比如摘要或比較。您可以在將信息傳遞給LLM之前進行一些信息壓縮作為后處理,以減少噪聲或上下文長度。例如,您可以首先從每個塊中總結、轉述或提取關鍵點,然后將聚合的、濃縮的信息傳遞給LLM進行生成。
? ? ? ?我發現還有一些其他技巧可以幫助改進和平衡生成質量和延遲。在實際生產中,您的用戶可能沒有時間等待多步驟RAG過程完成,尤其是當存在LLM調用鏈時。如果您想提高RAG管道的延遲,以下選擇可能會有所幫助。
? ? ? ?第一種是在某些步驟中使用更小、更快的模型。對于RAG過程中的所有步驟,您不一定需要使用最強大的模型(通常是最慢的)。例如,對于一些簡單的查詢重寫、假設文檔的生成或文本塊的匯總,您可能可以使用更快的模型(如7B或13B本地模型)。這些模型中的一些甚至能夠為用戶生成高質量的最終輸出。
? ? ? 如果你感興趣,你可以閱讀更多關于如何運行本地模型的信息[11],并在這個GitHub Repo[12]中查看我對一些小型LLM的評級。
? ? ? ?下一個技術是使一些中間步驟并行運行。你不必總是等到一步完成后再進入第二步。您可以進行一些中間步驟,如并行混合搜索或多個塊并行的摘要。要做到這一點,您可能需要大量修改RAG框架或自己創建RAG管道,但這可以大大減少最終輸出的時間。
? ? ??第三種技術是如果可能的話,讓LLM做出多項選擇,而不是生成長文本。例如,在重新排序/評分時,您可以要求LLM僅列出文本塊的分數/排名,而不是將其生成或包括詳細解釋。
? ? ? ?另一個有用的提示是為常見問題或常見查詢實現緩存。如果新查詢與舊查詢非常相似或幾乎相同,則系統可以提供即時答案,而無需每次都經過整個RAG過程。如果新查詢有點相似,但仍然相關,您甚至可以將上一個查詢的答案包含到LLM中,作為生成新答案的參考。
? ? ? ?在本文中,我介紹了許多可以在LLM支持的應用程序中改進RAG管道的技術,包括:
-RAG的基本過程:索引、檢索和生成
-預檢索技術:
-檢索技術
-后期檢索技術
-平衡質量和延遲
? ? ? 您可以在RAG管道中使用其中一種或多種技術,使其更加準確和高效。我希望這些技術可以幫助你為你的應用程序構建一個更好的RAG管道。
[1] http://www.llamaindex.ai/
[2] http://www.langchain.com/
[3] http://www.pinecone.io/
[4] http://www.trychroma.com/
[5] https://browse.arxiv.org/abs/2303.07678
[6] https://browse.arxiv.org/abs/2212.10496
[7] https://browse.arxiv.org/abs/2212.09741
[8] https://github.com/xlang-ai/instructor-embedding
[9] https://huggingface.co/spaces/mteb/leaderboard
[10] https://doi.org/10.48550/arXiv.2312.10997
[11] https://medium.com/design-bootcamp/a-complete-guide-to-running-local-llm-models-3225e4913620
[12] https://github.com/Troyanovsky/Local-LLM-Comparison-Colab-UI
[13]?https://bootcamp.uxdesign.cc/how-to-improve-rag-results-in-your-llm-apps-from-basics-to-advanced-822818014144
本文章轉載微信公眾號@ArronAI