AI 智能體記憶架構(gòu)在 LangGraph 中的落地實(shí)現(xiàn) 原創(chuàng)
記憶是一個(gè)系統(tǒng),用于記錄之前交互的信息。對于 AI 智能體(AI Agent)來說,記憶非常重要,因?yàn)樗茏?AI 智能體記住之前的交互,從反饋中學(xué)習(xí),并適應(yīng)用戶的偏好。當(dāng) AI 智能體處理更復(fù)雜的任務(wù)和大量用戶交互時(shí),這種能力對于提高效率和用戶滿意度變得至關(guān)重要。
AI 智能體中有兩種記憶類型:短期記憶和長期記憶:
- 短期記憶(Short-term memory):也稱為線程范圍記憶(thread-scoped memory),通過在會(huì)話中維護(hù)消息歷史來跟蹤正在進(jìn)行的對話。LangGraph 將短期記憶作為 AI 智能體狀態(tài)的一部分進(jìn)行管理。狀態(tài)通過檢查點(diǎn)(checkpointer)持久化到數(shù)據(jù)庫中,以便隨時(shí)恢復(fù)線程。短期記憶會(huì)在調(diào)用圖或完成步驟時(shí)更新,并且在每個(gè)步驟開始時(shí)讀取狀態(tài)。
- 長期記憶(Long-term memory):存儲跨會(huì)話的用戶特定或應(yīng)用級別的數(shù)據(jù),并在不同的對話線程中共享。它可以在任何時(shí)間、任何線程中被回憶。記憶可以被限定在任何自定義的命名空間(namespace)中,而不僅僅是在單個(gè)線程ID內(nèi)。LangGraph提供了存儲(stores),讓你可以保存和回憶長期記憶。
下文詳細(xì)剖析之。
一、AI 智能體短長期記憶架構(gòu)設(shè)計(jì)與落地
1、短期記憶架構(gòu)設(shè)計(jì)與落地
短期記憶讓你的 AI 智能體應(yīng)用能夠在單個(gè)線程或?qū)υ捴杏涀≈暗慕换?。線程會(huì)組織一個(gè)會(huì)話中的多次交互,類似于電子郵件將消息分組為單個(gè)對話的方式。
LangGraph 將短期記憶作為 AI 智能體狀態(tài)的一部分進(jìn)行管理,并通過線程范圍的檢查點(diǎn)持久化。這種狀態(tài)通常包括對話歷史以及其他狀態(tài)化數(shù)據(jù),例如:上傳的文件、檢索的文檔或生成的工件。通過將這些內(nèi)容存儲在圖的狀態(tài)中,AI 智能體可以訪問給定對話的完整上下文,同時(shí)保持不同線程之間的分離。
1.1、管理短期記憶
對話歷史是短期記憶的最常見形式,而長對話對當(dāng)前的大語言模型(LLMs)來說是一個(gè)挑戰(zhàn)。完整的對話歷史可能無法完全放入 LLM 的上下文窗口中,從而導(dǎo)致無法恢復(fù)的錯(cuò)誤。即使你的 LLM 支持完整的上下文長度,大多數(shù) LLM 在處理長上下文時(shí)表現(xiàn)仍然不佳。它們會(huì)被過時(shí)或離題的內(nèi)容“分散注意力”,同時(shí)還會(huì)導(dǎo)致響應(yīng)速度變慢和成本增加。
聊天模型通過消息接受上下文,這些消息包括開發(fā)者提供的指令(系統(tǒng)消息)和用戶輸入(人類消息)。在聊天應(yīng)用中,消息會(huì)在人類輸入和模型響應(yīng)之間交替,隨著時(shí)間推移,消息列表會(huì)變得越來越長。由于上下文窗口有限,且消息列表中的 token 數(shù)量過多可能會(huì)導(dǎo)致成本增加,因此許多 AI 智能體應(yīng)用可以從手動(dòng)刪除或忘記過時(shí)信息。
2、長期記憶架構(gòu)設(shè)計(jì)與落地
LangGraph 中的長期記憶允許系統(tǒng)在不同的對話或會(huì)話中保留信息。與線程范圍的短期記憶不同,長期記憶是保存在自定義的“命名空間(namespaces)中的。
長期記憶是一個(gè)復(fù)雜的挑戰(zhàn),沒有一種通用的解決方案。然而,以下問題提供了一個(gè)框架,幫助你了解不同的技術(shù):
- 記憶的類型是什么?人類使用記憶來記住事實(shí)(語義記憶)、經(jīng)歷(情景記憶)和規(guī)則(程序性記憶)。AI 智能體也可以以同樣的方式使用記憶。例如:AI 智能體可以使用記憶來記住關(guān)于用戶的特定事實(shí),以完成任務(wù)。
- 你希望何時(shí)更新記憶?記憶可以作為 AI 智能體應(yīng)用程序邏輯的一部分進(jìn)行更新(例如:“熱路徑”上)。在這種情況下,AI 智能體通常會(huì)在回應(yīng)用戶之前決定記住事實(shí)?;蛘?,記憶可以作為一個(gè)后臺任務(wù)進(jìn)行更新(在后臺/異步運(yùn)行的邏輯中生成記憶)。
3、記憶類型剖析
不同的 AI 智能體應(yīng)用需要不同類型的記憶。盡管類比并不完美,但研究人類記憶類型可以提供一些見解。一些研究甚至將這些人類記憶類型映射到 AI 智能體中使用的類型。
3.1、語義記憶
語義記憶,無論是人類還是 AI 智能體,都涉及對特定事實(shí)和概念的保留。在人類中,它可能包括在學(xué)校學(xué)到的信息以及對概念及其關(guān)系的理解。對于 AI 智能體,語義記憶通常用于通過記住過去的交互中的事實(shí)或用于個(gè)性化應(yīng)用的概念。
注意:語義記憶與“語義搜索”不同。語義搜索是一種通過“含義”(通常是嵌入向量)查找相似內(nèi)容的技術(shù)。語義記憶是心理學(xué)中的一個(gè)術(shù)語,指的是存儲事實(shí)和知識,而語義搜索是一種基于含義而非精確匹配檢索信息的方法。
第一、用戶畫像(Profile)
語義記憶可以通過不同的方式管理。例如,記憶可以是一個(gè)單一的、不斷更新的“用戶畫像”,其中包含關(guān)于用戶、組織或其他實(shí)體(包括 AI 智能體本身)的范圍明確且具體的信息。用戶畫像通常只是一個(gè)帶有各種鍵值對的 JSON 文檔,你選擇這些鍵值對來代表你的領(lǐng)域。
在記住用戶畫像時(shí),你需要確保每次都在更新畫像。因此,你需要傳入之前的畫像,并讓大模型生成一個(gè)新的畫像(或者生成一個(gè) JSON 補(bǔ)丁來應(yīng)用到舊畫像上)。隨著畫像變得越來越大,這可能會(huì)變得容易出錯(cuò),可能需要將畫像拆分為多個(gè)文檔,或者在生成文檔時(shí)進(jìn)行嚴(yán)格解碼,以確保記憶模式保持有效。
第二、文檔集合(Collection)
或者,記憶可以是一個(gè)隨著時(shí)間不斷更新和擴(kuò)展的文檔集合。每個(gè)單獨(dú)的記憶可以更狹窄地限定范圍,更容易生成,這意味著你不太可能隨著時(shí)間的推移丟失信息。對于 LLM 來說,生成新對象以存儲新信息比將新信息與現(xiàn)有畫像協(xié)調(diào)起來更容易。因此,使用文檔集合通常會(huì)導(dǎo)致更高的回憶率。
然而,這將一些復(fù)雜性轉(zhuǎn)移到了記憶更新上。大模型現(xiàn)在必須刪除或更新列表中的現(xiàn)有項(xiàng)目,這可能會(huì)很棘手。此外,一些大模型可能默認(rèn)過度插入,而另一些大模型可能默認(rèn)過度更新。
使用文檔集合還意味著將復(fù)雜性轉(zhuǎn)移到對列表的記憶搜索上。當(dāng)前的存儲支持語義搜索和按內(nèi)容過濾。
最后,使用記憶集合可能會(huì)使向大模型提供全面上下文變得具有挑戰(zhàn)性。盡管單個(gè)記憶可能遵循特定的模式,但這種結(jié)構(gòu)可能無法捕捉記憶之間的完整上下文或關(guān)系。因此,當(dāng)使用這些記憶來生成回應(yīng)時(shí),大模型可能會(huì)缺少在統(tǒng)一畫像方法中更容易獲得的重要上下文信息。
無論采用哪種記憶管理方法,核心要點(diǎn)是 AI 智能體將使用語義記憶來支撐其回應(yīng),這通常會(huì)導(dǎo)致更個(gè)性化和相關(guān)的互動(dòng)。
3.2、情景記憶
情景記憶,無論是人類還是 AI 智能體,都涉及回憶過去的事件或行動(dòng)。事實(shí)可以寫入語義記憶,而經(jīng)歷可以寫入情景記憶。對于 AI 智能體,情景記憶通常用于幫助 AI 智能體記住如何完成任務(wù)。
在實(shí)踐中,情景記憶通常是通過少量樣本提示(few-shot example prompting)來實(shí)現(xiàn)的,AI 智能體通過學(xué)習(xí)過去的序列來正確執(zhí)行任務(wù)。有時(shí)“展示”比“告訴”更容易,LLM 從示例中學(xué)習(xí)得很好。少量樣本學(xué)習(xí)讓你可以通過在提示中更新輸入-輸出示例來“編程”你的 LLM,以展示預(yù)期的行為。盡管可以使用各種最佳實(shí)踐來生成少量樣本示例,但挑戰(zhàn)通常在于根據(jù)用戶輸入選擇最相關(guān)的示例。
注意:記憶存儲只是存儲少量樣本數(shù)據(jù)的一種方式。如果你想更多地參與開發(fā),或者將少量樣本更緊密地與你的評估框架聯(lián)系起來,你也可以使用LangSmith 數(shù)據(jù)集來存儲你的數(shù)據(jù)。然后可以使用現(xiàn)成的動(dòng)態(tài)少量樣本示例選擇器來實(shí)現(xiàn)相同的目標(biāo)。LangSmith 會(huì)為你索引數(shù)據(jù)集,并根據(jù)關(guān)鍵詞相似性(使用類似 BM25 的算法)檢索與用戶輸入最相關(guān)的少量樣本示例。
3.3、程序性記憶
程序性記憶,無論是人類還是 AI 智能體,都涉及記住執(zhí)行任務(wù)所使用的規(guī)則。在人類中,程序性記憶就像內(nèi)化的任務(wù)執(zhí)行知識,例如:通過基本的運(yùn)動(dòng)技能和平衡感學(xué)會(huì)騎自行車。而情景記憶則涉及回憶特定的經(jīng)歷,比如:你第一次在沒有輔助輪的情況下成功騎自行車,或者一次難忘的自行車騎行經(jīng)歷。對于 AI 智能體,程序性記憶是模型權(quán)重、AI 智能體代碼和 AI 智能體提示詞的組合,這些共同決定了 AI 智能體的功能。
在實(shí)踐中,AI 智能體修改其大模型權(quán)重或重寫代碼的情況相對較少。然而,AI 智能體修改自己的提示詞則更為常見。
一種有效的改進(jìn) AI 智能體指令的方法是通過“反思”或元提示(meta-prompting)。這涉及將 AI 智能體當(dāng)前的指令(例如:系統(tǒng)提示詞)以及最近的對話或明確的用戶反饋提示詞給 AI 智能體。然后,AI 智能體根據(jù)這些輸入來改進(jìn)自己的指令。這種方法特別適用于那些難以提前指定指令的任務(wù),因?yàn)樗试S代理從交互中學(xué)習(xí)和適應(yīng)。
例如:我們構(gòu)建了一個(gè)使用外部反饋和提示詞重寫來生成高質(zhì)量推文摘要的推文生成器。在這種情況下,具體的摘要提示詞很難提前指定,但用戶可以很容易地批評生成的推文,并提供關(guān)于如何改進(jìn)摘要過程的反饋。
以下是使用 LangGraph 記憶存儲實(shí)現(xiàn)此功能的偽代碼示例。使用存儲來保存提示,??update_instructions?
??節(jié)點(diǎn)獲取當(dāng)前提示(以及捕獲在??state["messages"]?
??中的與用戶的對話反饋),更新提示詞,并將新提示詞保存回存儲。然后,??call_model ?
?從存儲中獲取更新后的提示詞并使用它來生成回應(yīng)。
# Node that *uses* the instructions
def call_model(state: State, store: BaseStore):
namespace = ("agent_instructions", )
instructions = store.get(namespace, key="agent_a")[0]
# Application logic
prompt = prompt_template.format(instructinotallow=instructions.value["instructions"])
...
# Node that updates instructions
def update_instructions(state: State, store: BaseStore):
namespace = ("instructions",)
current_instructions = store.search(namespace)[0]
# Memory logic
prompt = prompt_template.format(instructinotallow=instructions.value["instructions"], cnotallow=state["messages"])
output = llm.invoke(prompt)
new_instructions = output['new_instructions']
store.put(("agent_instructions",), "agent_a", {"instructions": new_instructions})
...
4、寫入記憶架構(gòu)設(shè)計(jì)與落地
AI 智能體寫入記憶主要有兩種方法:“熱路徑”(in the hot path)和“后臺”(in the background)。
4.1、在熱路徑中(熱更新)
在運(yùn)行時(shí)創(chuàng)建記憶既有優(yōu)點(diǎn)也有挑戰(zhàn)。優(yōu)點(diǎn)是,這種方法可以實(shí)現(xiàn)實(shí)時(shí)更新,使新記憶能夠立即用于后續(xù)交互。它還可以實(shí)現(xiàn)透明性,因?yàn)橛脩艨梢栽谟洃洷粍?chuàng)建和存儲時(shí)得到通知。
然而,這種方法也存在挑戰(zhàn)。如果 AI 智能體需要一個(gè)新工具來決定要記住什么內(nèi)容,這可能會(huì)增加復(fù)雜性。此外,推理要保存到記憶中的內(nèi)容可能會(huì)對 AI 智能體的延遲產(chǎn)生影響。最后,AI 智能體需要在創(chuàng)建記憶和其他職責(zé)之間進(jìn)行多任務(wù)處理,這可能會(huì)影響記憶的數(shù)量和質(zhì)量。
例如,ChatGPT 使用一個(gè) ??save_memories ?
?工具將記憶作為內(nèi)容字符串插入或更新,每次用戶發(fā)送消息時(shí),它都會(huì)決定是否以及如何使用這個(gè)工具。
4.2、在后臺(冷更新)
將創(chuàng)建記憶作為一個(gè)單獨(dú)的后臺任務(wù)有諸多優(yōu)點(diǎn)。它可以消除主應(yīng)用中的延遲,將應(yīng)用邏輯與記憶管理分離,并讓 AI 智能體能夠更專注地完成任務(wù)。這種方法還提供了靈活的時(shí)間安排,以避免重復(fù)工作。
然而,這種方法也有自己的挑戰(zhàn)。確定記憶寫入的頻率變得至關(guān)重要,因?yàn)楦虏活l繁可能會(huì)導(dǎo)致其他線程缺少新的上下文。決定何時(shí)觸發(fā)記憶形成也很重要。常見的策略包括在設(shè)定的時(shí)間后調(diào)度(如果發(fā)生新事件則重新調(diào)度)、使用 cron 調(diào)度,或者允許用戶或應(yīng)用邏輯手動(dòng)觸發(fā)。
5、記憶存儲架構(gòu)設(shè)計(jì)與落地
LangGraph 將長期記憶作為 JSON 文檔存儲在存儲中。每個(gè)記憶都組織在一個(gè)自定義的命名空間(類似于文件夾)和一個(gè)獨(dú)特的鍵(類似于文件名)下。命名空間通常包括用戶或組織 ID 或其他標(biāo)簽,以便更容易地組織信息。這種結(jié)構(gòu)支持記憶的層次化組織。然后通過內(nèi)容過濾支持跨命名空間搜索。
from langgraph.store.memory import InMemoryStore
def embed(texts: list[str]) -> list[list[float]]:
# Replace with an actual embedding function or LangChain embeddings object
return [[1.0, 2.0] * len(texts)]
# InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use.
store = InMemoryStore(index={"embed": embed, "dims": 2})
user_id = "my-user"
application_context = "chitchat"
namespace = (user_id, application_context)
store.put(
namespace,
"a-memory",
{
"rules": [
"User likes short, direct language",
"User only speaks English & python",
],
"my-key": "my-value",
},
)
# get the "memory" by ID
item = store.get(namespace, "a-memory")
# search for "memories" within this namespace, filtering on content equivalence, sorted by vector similarity
items = store.search(
namespace, filter={"my-key": "my-value"}, query="language preferences"
)
本文轉(zhuǎn)載自???玄姐聊AGI?? 作者:玄姐
