突破LLM的token限制:多塊上下文保留的實用系統(含code)
大語言模型(LLMs)取得了令人矚目的進展,已廣泛應用于文本生成、翻譯、問答等諸多場景。然而,LLMs存在的一些局限性,如有限的上下文窗口(令牌限制)和缺乏長期記憶,限制了其在處理復雜任務時的表現。本文將深入探討一種實用的解決方案,旨在克服這些限制,提升LLMs的性能。
一、大語言模型概述
LLMs是基于Transformer架構構建的深度學習模型,通過在海量文本數據集上進行訓練來學習語言模式和知識。Transformer模型的核心機制是自注意力機制,它能夠讓模型在無需人工監督的情況下,自動學習輸入文本中各部分之間的相關性。在處理文本時,LLMs會將文本拆分為詞元(token),這些詞元可以是子詞或字符,隨后將其輸入到Transformer模型中進行處理,最終輸出每個詞元的嵌入表示,用于各種自然語言處理任務。
在實際應用中,GPT-4以其廣泛的通用性、出色的指令遵循能力和強大的代碼生成能力而聞名;Claude注重安全性和對話應用場景;Gemini旨在實現推理和多模態應用;LLaMA作為開源權重的大語言模型,在研究和微調方面應用廣泛。這些模型雖然功能強大,但仍面臨一些挑戰。
二、大語言模型的局限性
(一)長期記憶和個性化問題
大多數LLMs在默認情況下是無狀態的,它們不會自動記住過去的對話內容。除非專門為其設計記憶機制,否則每個輸入提示(prompt)都會被獨立處理。這意味著在連續對話場景中,模型無法利用之前的交互信息,導致對話缺乏連貫性和上下文感知能力。例如,在多輪問答中,用戶詢問了一系列相關問題,但模型無法結合之前的回答進行更準確、更連貫的回應。
(二)知識局限性
基于靜態數據集訓練的LLMs,無法獲取當前事件或實時數據。如果沒有與網絡搜索工具或API集成,模型的知識將局限于訓練數據的時間范圍,對于新出現的信息無法知曉。比如,當詢問關于最新科技成果或時事新聞時,模型可能會給出過時的答案。
(三)上下文窗口限制(提示大小約束)
每個LLM都有一個固定的上下文窗口,即一次能夠處理的最大詞元數量。雖然一些先進的模型能夠支持多達100萬個詞元,但在實際應用中,這個限制仍然會對處理大型代碼庫或長篇文檔造成阻礙。此外,HTTP請求的大小限制也會進一步約束發送給模型的有效負載,導致無法一次性將全部輸入內容發送給模型進行處理。
三、解決令牌限制和長期記憶問題的方法
(一)核心思路
為了解決缺乏長期記憶和有限提示窗口這兩個關鍵問題,一種有效的方法是將提示詞元進行結構化處理,分為用戶輸入(用戶給出的任務)和上下文窗口(相關的支持數據片段)。具體而言,就是將分配給用戶輸入的總詞元劃分為分塊提示詞元和上下文詞元。由于模型的詞元限制,需要將大型輸入(如完整的代碼庫或長篇文檔)拆分成較小的塊,然后按順序處理這些塊,并將每個塊的響應摘要作為下一個塊的上下文。通過這種方式,既可以模擬長期記憶,又能夠突破提示窗口的限制。
(二)具體實現步驟
- 拆分用戶提示:首先,將用戶的輸入文本按單詞進行拆分。然后,依次遍歷每個單詞,將其添加到當前塊中。當當前塊的大小達到為單個提示設定的詞元限制(由開發者自行定義)時,將該塊確定為可執行的塊,并將其添加到塊列表中。接著,清空當前塊,繼續添加下一個單詞,重復上述過程,直到整個提示被拆分成合適大小的塊。例如,使用Python代碼實現如下:
def split_text_into_chunks(self, text, max_chunk_tokens=2000):
words = text.split()
chunks = []
current_chunk = []
for word in words:
current_chunk.append(word)
current_text = " ".join(current_chunk)
if self.num_tokens(current_text) > max_chunk_tokens:
current_chunk.pop()
chunks.append(" ".join(current_chunk))
current_chunk = [word]
if current_chunk:
chunks.append(" ".join(current_chunk))
return chunks
- 處理大型提示:在拆分用戶提示后,初始化一個空的overall_summarized_context,用于保存所有先前響應的單一摘要。對于每個分塊,將當前的overall_summarized_context附加到該分塊上,并發送給LLM以生成響應。然后,使用LLM對響應進行摘要(如果需要),并更新overall_summarized_context,使其包含最新響應的上下文。重復這個過程,直到處理完所有分塊。最后,將所有的響應合并成一個最終的統一響應,并返回給用戶。以下是Python代碼示例:
def process_large_prompt(self, user_prompt):
chunks = self.split_text_into_chunks(user_prompt)
running_context = []
overall_summarized_context = ""
results = []
for i, chunk in enumerate(chunks):
prompt = f"""
You are reading a large document split into chunks.
Here is chunk {i+1}/{len(chunks)}:
{chunk}
Previous context:
{overall_summarized_context}
Please analyze this chunk in light of the previous context.
"""
messages = [
{"role": "system", "content": "You are a smart assistant that reads the provided software code, and provide the detailed explanation of what business purpose the code is providing and how different components are connected, and interacting with each other."},
{"role": "user", "content": prompt}
]
response = self.chat(messages, temperature=0.3, max_tokens=1000)
results.append(response.strip())
context_messages = [
{"role": "system", "content": "You are a smart assistant that summarizes the given content below within 1000 words so that it can be used as a context for remaining analysis."},
{"role": "user", "content": response.strip()}
]
overall_summarized_context = self.chat(context_messages, temperature=0.3, max_tokens=1000)
running_context.append(overall_summarized_context)
return {
"final_summary": overall_summarized_context,
"individual_context_summaries": running_context,
"individual_results": results,
}
- 完整代碼可在GitHub上獲取:https://github.com/akshit04/PromptSlicerLlmWrapper。用戶可以按照README.md文件中的設置說明進行操作,通過配置GitHub倉庫URL和OpenRouter API密鑰,將任何GitHub倉庫作為OpenRouter LLM的提示源。
四、關鍵考慮因素
(一)示例提示的GitHub項目
在實際測試中,使用了https://github.com/akshit04/StackOverflow 這個GitHub項目作為示例提示。該項目是一個基本的StackOverflow風格的項目,允許用戶注冊、提問、提交答案以及對現有答案進行點贊或點踩。為了確保測試的公正性,故意從README文件中排除了項目描述,這樣最終的摘要就能真正反映模型從代碼塊和運行上下文中構建的理解,而不是簡單地從項目描述中進行總結。
(二)單個用戶提示塊的最大詞元限制
在本示例中,為單個用戶提示塊設置的最大詞元限制為2000。需要注意的是,這個限制僅適用于用戶提示,不包括上下文詞元,發送給LLM的總提示詞元數為用戶提示詞元數加上上下文詞元數。這個2000的詞元限制可以在openrouter_wrapper.py文件中進行配置。由于本示例只是一個概念驗證(POC),故意設置了較低的詞元限制,相較于大多數LLMs通常支持的數量要低很多。這樣做的目的是在小規模項目上更便于手動分析結果的質量。
五、結果分析
通過對代碼的試運行,得到了相應的結果,并將其中一個示例響應直接包含在倉庫中(response1.md)。該文件主要分為三個部分:
(一)單個結果
這部分包含了發送給LLM的每個請求的原始輸出,包括對單個提示塊的部分響應,以及在每個步驟中使用的整體總結上下文。通過這些原始輸出,可以清晰地看到模型對每個分塊的具體處理結果。
(二)單個上下文摘要
在每次LLM響應后,都會生成一個摘要以捕獲關鍵點。這些摘要隨著時間的推移不斷積累上下文信息,因為每個響應都是在訪問運行上下文的情況下生成的,它們有效地將從第一個提示中獲得的知識傳遞到當前提示。這種上下文的積累使得模型能夠更好地理解整個輸入的連貫性和相關性。
(三)最終摘要
這是通過組合單個上下文摘要而創建的綜合摘要,等同于上一部分中提到的最后一個上下文摘要,代表了模型對所有分塊處理后的最終理解。以測試的項目代碼為例,最終摘要準確地總結出代碼是一個使用Ruby on Rails構建的Web應用程序結構,涵蓋了用戶管理、問答發布和用戶關系等功能模塊,以及各個模塊的具體實現細節和它們之間的關聯。盡管是通過分塊處理的方式逐步構建的,但最終摘要有效地捕捉了整個項目的上下文信息。
六、局限性與未來展望
(一)當前局限性
雖然這種多塊上下文保留的方法在一定程度上克服了LLMs的令牌限制和長期記憶問題,但也存在一些局限性。由于將初始用戶輸入拆分成多個塊,并對每個塊和運行上下文多次調用LLM,多個請求會導致總處理時間增加。此外,當前代碼假設輸入提示是來自GitHub的代碼庫,如果要支持其他輸入類型,如語音、圖像或純文本,需要對代碼進行修改。不過,這只是實現層面的局限性,而非方法本身的問題。
(二)未來展望
在未來的研究和開發中,可以進一步優化算法,減少請求次數或提高每次請求的效率,以降低處理時間。例如,可以探索更智能的分塊策略,根據文本的語義結構進行分塊,而不僅僅是基于詞元數量;或者開發更高效的上下文摘要算法,減少不必要的信息傳遞。同時,針對不同輸入類型的支持擴展也將是一個重要的研究方向,通過開發通用的預處理模塊,將各種類型的輸入轉換為適合LLMs處理的格式,從而擴大該方法的應用范圍。此外,結合其他技術,如知識圖譜、外部知識庫等,與多塊上下文保留方法相結合,有望進一步提升LLMs的性能和智能水平,使其能夠更好地應對復雜多樣的自然語言處理任務。