Agentic AI:構(gòu)建長期記憶

如果你用過大型語言模型(LLMs),你就會知道它們是無狀態(tài)的。如果沒用過,可以把它們想象成沒有短期記憶的東西。
舉個(gè)例子,就像電影《記憶碎片》(Memento)里的主角,他總是需要靠便利貼上的信息來提醒自己發(fā)生了什么,拼湊出下一步該做什么。
要和LLMs對話,我們每次互動時(shí)都需要不斷提醒它們之前的對話內(nèi)容。
實(shí)現(xiàn)我們所說的“短期記憶”或狀態(tài)其實(shí)很簡單。我們只需要把之前的幾組問答對抓出來,包含在每次調(diào)用里就行。
但長期記憶完全是另一回事。
為了讓LLM能調(diào)取正確的事實(shí)、理解之前的對話并連接信息,我們需要構(gòu)建一些相當(dāng)復(fù)雜的系統(tǒng)。
圖片
這篇文章會深入探討這個(gè)問題,而不是堆砌一堆術(shù)語,分析構(gòu)建一個(gè)高效系統(tǒng)需要什么,討論不同的架構(gòu)選擇,并看看有哪些開源和云服務(wù)供應(yīng)商能幫上忙。
思考解決方案
讓我們先來梳理一下為LLMs構(gòu)建記憶的思路,以及要讓它高效需要些什么。
首先,我們需要LLM能調(diào)取舊消息,告訴我們之前說了什么。比如,我們可以問它:“你之前推薦我去斯德哥爾摩的那家餐廳叫什么名字?”這就是基本的信息提取。
如果你完全沒接觸過LLM系統(tǒng)的開發(fā),你可能首先會想到把所有對話直接塞進(jìn)上下文窗口(context window),讓LLM自己去理解。
但這種策略會讓LLM很難分辨哪些信息重要、哪些不重要,可能會導(dǎo)致它“幻覺”(hallucinate)出答案。而且一旦對話量變大,你還得限制對話的長度。
你的第二個(gè)想法可能是存儲每條消息,連同摘要一起,然后用語義搜索(比如RAG里的檢索)在有查詢時(shí)提取信息。
圖片
這跟構(gòu)建標(biāo)準(zhǔn)的簡單檢索系統(tǒng)差不多。
但問題在于,如果只是簡單地存儲消息(不做任何額外處理),一旦規(guī)模擴(kuò)大,你會遇到內(nèi)存膨脹、事實(shí)過時(shí)或矛盾、以及不斷需要清理的向量數(shù)據(jù)庫(vector database)的問題。
你可能還需要知道事情發(fā)生的時(shí)間,這樣才能問:“你什么時(shí)候跟我提過第一家餐廳?”這意味著你需要某種程度的時(shí)間推理(temporal reasoning)。
這可能會促使你加入帶有時(shí)間戳的元數(shù)據(jù)(metadata),甚至實(shí)現(xiàn)一個(gè)自我編輯系統(tǒng)(self-editing system),來更新和總結(jié)輸入內(nèi)容。
雖然更復(fù)雜,但一個(gè)自我編輯系統(tǒng)可以在需要時(shí)更新事實(shí)并使其失效。
如果你繼續(xù)深入思考,你可能還希望LLM能連接不同的事實(shí)(執(zhí)行多跳推理,multi-hop reasoning)并識別模式。
比如,你可以問:“我今年去了多少場音樂會?”或者“根據(jù)這些,你覺得我的音樂品味怎么樣?”這可能會讓你嘗試使用知識圖譜(knowledge graphs)。
組織解決方案
這個(gè)問題變得如此復(fù)雜,促使人們更好地組織它。大多數(shù)團(tuán)隊(duì)似乎把長期記憶分成兩部分:小型事實(shí)(pocket-sized facts)和之前的對話的長期記憶(long-span memory)。
圖片
對于第一部分,小型事實(shí),我們可以看看ChatGPT的記憶系統(tǒng)作為一個(gè)例子。
要構(gòu)建這種記憶,他們可能會用一個(gè)分類器(classifier)來判斷一條消息是否包含需要存儲的事實(shí)。
圖片
然后,他們會把事實(shí)分類到一個(gè)預(yù)定義的類別(比如個(gè)人資料、偏好或項(xiàng)目),如果這個(gè)事實(shí)和已有的事實(shí)類似就更新,否則就創(chuàng)建一個(gè)新的。
另一部分,長期記憶,意味著存儲所有消息并總結(jié)整個(gè)對話,以便之后可以參考。這在ChatGPT里也有,但和小型事實(shí)記憶一樣,你得手動啟用。
如果你自己來構(gòu)建這個(gè),你需要決定保留多少細(xì)節(jié),同時(shí)注意之前提到的內(nèi)存膨脹和不斷增長的數(shù)據(jù)庫問題。
標(biāo)準(zhǔn)架構(gòu)解決方案
如果我們看看其他人在做什么,這里有兩個(gè)主要的架構(gòu)選擇:向量(vectors)和知識圖譜(knowledge graphs)。
我一開始提到了一種基于檢索的方法。這通常是大家開始時(shí)的首選。檢索使用向量存儲(vector store,通常還支持稀疏搜索,sparse search),這意味著它同時(shí)支持語義搜索和關(guān)鍵詞搜索。
檢索一開始很簡單:你把文檔嵌入(embed),然后根據(jù)用戶的問題來提取。
但正如我們之前說的,如果只是簡單地這樣做,每個(gè)輸入都是不可變的。這意味著即使事實(shí)變了,文本還是會留在那兒。
這里可能出現(xiàn)的問題包括提取到多個(gè)互相矛盾的事實(shí),這會讓代理(agent)困惑。最壞的情況是,相關(guān)事實(shí)可能被埋在一堆檢索到的文本里。
代理也不知道某件事是什么時(shí)候說的,或者它指的是過去還是未來。
正如我們之前討論的,有辦法解決這些問題。
你可以搜索舊的記憶并更新它們,給元數(shù)據(jù)加上時(shí)間戳,定期總結(jié)對話來幫助LLM理解提取到的細(xì)節(jié)的上下文。
但用向量,你還會面臨數(shù)據(jù)庫不斷增長的問題。最終,你得清理舊數(shù)據(jù)或壓縮它們,這可能會讓你丟掉有用的細(xì)節(jié)。
如果我們看看知識圖譜(KGs),它們把信息表示成實(shí)體(節(jié)點(diǎn),nodes)和它們之間的關(guān)系(邊,edges)的網(wǎng)絡(luò),而不是像向量那樣的非結(jié)構(gòu)化文本。
圖片
KGs不會直接覆蓋數(shù)據(jù),而是可以給舊事實(shí)加一個(gè)“失效日期”(invalid_at date),這樣你還能追溯它的歷史。它們通過圖遍歷(graph traversals)來提取信息,讓你可以跨多個(gè)跳躍追蹤關(guān)系。
不過正如我們提到的,用向量也可以通過提取舊事實(shí)并更新來做到類似的事。
但因?yàn)镵Gs能跳躍到連接的節(jié)點(diǎn)并以更結(jié)構(gòu)化的方式保持事實(shí)更新,它們在多跳推理(multi-hop reasoning)上往往表現(xiàn)更好。
不過KGs也有自己的挑戰(zhàn)。隨著規(guī)模擴(kuò)大,基礎(chǔ)設(shè)施會變得更復(fù)雜,你可能在深層遍歷時(shí)注意到更高的延遲,因?yàn)橄到y(tǒng)需要找很遠(yuǎn)的信息。
維護(hù)成本也可能很高。
總之,不管是用向量還是KG為基礎(chǔ)的解決方案,人們通常會選擇更新記憶而不是不斷添加新的,加入我們之前看到的“小型事實(shí)”的特定分類功能。
他們還經(jīng)常用LLMs來在存儲消息前總結(jié)和提取信息。
回到最初的目標(biāo)(同時(shí)擁有小型記憶和長期記憶),你可以混合使用RAG和KG的方法來達(dá)到目的。
當(dāng)前供應(yīng)商解決方案(即插即用)
我會介紹幾種不同的獨(dú)立解決方案,幫助你設(shè)置記憶,分析它們的工作方式、使用的架構(gòu),以及它們的框架成熟度如何。

長期記憶供應(yīng)商 —— 我總是把資源收集在這個(gè)倉庫里 | 圖片由作者提供
構(gòu)建高級LLM應(yīng)用還是很新的東西,所以這些解決方案大多在過去一兩年才發(fā)布。
剛開始時(shí),了解這些框架的構(gòu)建方式會很有幫助,能讓你知道自己可能需要什么。
正如之前提到的,它們大多分為KG優(yōu)先或向量優(yōu)先兩類。

記憶供應(yīng)商功能 —— 我總是把資源收集在這個(gè)倉庫里 | 圖片由作者提供
先看看Zep(或Graphiti),一個(gè)基于KG的解決方案,他們用LLMs來提取、添加、使失效和更新節(jié)點(diǎn)(實(shí)體)和邊(帶時(shí)間戳的關(guān)系)。
圖片
當(dāng)你提問時(shí),它會執(zhí)行語義和關(guān)鍵詞搜索來找到相關(guān)節(jié)點(diǎn),然后遍歷到連接的節(jié)點(diǎn)來提取相關(guān)事實(shí)。
如果有新消息帶來矛盾的事實(shí),它會更新節(jié)點(diǎn),同時(shí)保留舊事實(shí)。
這和Mem0不同,Mem0是一個(gè)基于向量的解決方案,它會把提取的事實(shí)疊加起來,并用一個(gè)自我編輯系統(tǒng)來識別并完全覆蓋無效事實(shí)。
Letta的工作方式類似,但還包括額外的功能,比如核心記憶(core memory),它會存儲對話摘要以及定義需要填充內(nèi)容的塊(或類別)。
所有解決方案都能設(shè)置類別,我們可以定義系統(tǒng)需要捕獲什么。比如,如果你建一個(gè)冥想應(yīng)用,一個(gè)類別可以是用戶的“當(dāng)前心情”。這些就是我們之前在ChatGPT系統(tǒng)里看到的小型事實(shí)分類。
我之前提到過,向量優(yōu)先的方法在時(shí)間推理和多跳推理上有問題。
比如,如果我說兩個(gè)月后要搬到柏林,但之前提到住在斯德哥爾摩和加利福尼亞,幾個(gè)月后我問系統(tǒng)時(shí),它能明白我現(xiàn)在住在柏林嗎?
它能識別模式嗎?用知識圖譜,信息已經(jīng)結(jié)構(gòu)化,LLM更容易利用所有可用上下文。
用向量,隨著信息增長,噪聲可能會變得太大,系統(tǒng)難以連接信息點(diǎn)。
Letta和Mem0雖然總體上更成熟,但這兩個(gè)問題還是可能出現(xiàn)。
對于知識圖譜,關(guān)注點(diǎn)在于規(guī)模擴(kuò)大時(shí)的基礎(chǔ)設(shè)施復(fù)雜性,以及如何管理越來越多的信息。
雖然我還沒徹底測試所有方案,也還缺一些數(shù)據(jù)(比如延遲數(shù)字),但我想提一下它們在企業(yè)安全方面的處理,以防你想在公司內(nèi)部使用。

記憶云安全 —— 我總是把資源收集在這個(gè)倉庫里 | 圖片由作者提供
我找到的唯一SOC 2 Type 2認(rèn)證的云選項(xiàng)是Zep。不過,很多方案可以自托管,這種情況下安全性取決于你自己的基礎(chǔ)設(shè)施。
這些解決方案還很新。你可能最終會自己構(gòu)建,但建議先試試這些,看看它們?nèi)绾翁幚磉吘壡闆r。
使用供應(yīng)商的經(jīng)濟(jì)性
為LLM應(yīng)用添加功能很棒,但你得記住這也會增加成本。
我總是會提到實(shí)施某項(xiàng)技術(shù)的經(jīng)濟(jì)性,這次也不例外。這是我在添加任何東西時(shí)首先會檢查的。我需要知道這對應(yīng)用的單位經(jīng)濟(jì)性(unit economics)會有什么長期影響。
大多數(shù)供應(yīng)商解決方案會讓你免費(fèi)開始。但一旦消息量超過幾千條,成本就會迅速增加。

“估算”每條消息的記憶定價(jià) —— 我總是把資源收集在這個(gè)倉庫里 | 圖片由作者提供
記住,如果你的組織每天有幾百次對話,通過這些云解決方案發(fā)送每條消息時(shí),定價(jià)會開始累積。
一開始用云解決方案可能比較理想,然后隨著規(guī)模擴(kuò)大轉(zhuǎn)到自托管。
你也可以嘗試混合方式。
比如,自己實(shí)現(xiàn)一個(gè)分類器來決定哪些消息值得存為事實(shí),以降低成本,同時(shí)把其他內(nèi)容推到自己的向量存儲里,定期壓縮和總結(jié)。
話雖如此,在上下文窗口里使用小型事實(shí)應(yīng)該比直接粘貼5000個(gè)token的歷史記錄要好。提前給LLM相關(guān)事實(shí)也有助于減少幻覺(hallucinations),總體上降低LLM生成成本。
注意事項(xiàng)
重要的是要注意,即使有了記憶系統(tǒng),也別指望完美。這些系統(tǒng)有時(shí)還是會產(chǎn)生幻覺或漏掉答案。
與其追求100%的準(zhǔn)確率,不如接受不完美,這樣能省去很多挫敗感。
目前沒有哪個(gè)系統(tǒng)能達(dá)到完美準(zhǔn)確,至少現(xiàn)在還沒有。研究顯示,幻覺是LLMs的固有問題。即使加了記憶層,也無法完全消除這個(gè)問題。



































