如何讓AI Agent在多輪對(duì)話中保持長期記憶?7種關(guān)鍵優(yōu)化方法解析 原創(chuàng)
在基于大模型的 Agent 中,長期記憶的狀態(tài)維護(hù)至關(guān)重要,在 OpenAIAI 應(yīng)用研究主管 Lilian Weng 的 博客《基于大模型的 Agent 構(gòu)成》中,將記憶視為關(guān)鍵的組件之一,下面我將結(jié)合 LangChain 中的代碼,分享7 種不同的Agent記憶維護(hù)方式在不同場(chǎng)景中的應(yīng)用。
獲取全量歷史對(duì)話
在電信公司的客服聊天機(jī)器人場(chǎng)景中,如果用戶在對(duì)話中先是詢問了賬單問題,接著又談到了網(wǎng)絡(luò)連接問 題,ConversationBufferMemory 可以用來記住整個(gè)與用戶的對(duì)話歷史,可以幫助 AI 在回答網(wǎng)絡(luò)問題時(shí)還 記得賬單問題的相關(guān)細(xì)節(jié),從而提供更連貫的服務(wù)。
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
memory.save_context({"input": "你好"}, {"output": "怎么了"})
print(memory.load_memory_variables({}))滑動(dòng)窗口獲取最近部分對(duì)話內(nèi)容
在一個(gè)電商平臺(tái)上,如果用戶詢問關(guān)于特定產(chǎn)品的問題(如手機(jī)的電池續(xù)航時(shí)間),然后又問到了配送方 式,ConversationBufferWindowMemory 可以幫助AI 只專注于最近的一兩個(gè)問題(如配送方式),而不是整個(gè)對(duì)話歷史,以提供更快速和專注的答復(fù)。
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input": "iphone15續(xù)航"}, {"output": "續(xù)航一般"})
memory.save_context({"input": "配送"}, {"output": "很快"})
# {'history': 'Human: 配送\nAI: 很快'}
print(memory.load_memory_variables({}))ConversationBufferWindowMemory 這個(gè)類在存儲(chǔ)message還是全量存儲(chǔ)的,只是在讀數(shù)據(jù)的時(shí)候只讀k個(gè)窗口。
獲取歷史對(duì)話中實(shí)體信息
在法律咨詢的場(chǎng)景中,客戶可能會(huì)提到特定的案件名稱、相關(guān)法律條款或個(gè)人信息(如“我在去年的交通 事故中受了傷,想了解關(guān)于賠償?shù)姆山ㄗh”)。 ConversationEntityMemory可以幫助 AI 記住這些關(guān)鍵 實(shí)體和實(shí)體關(guān)系細(xì)節(jié),從而在整個(gè)對(duì)話過程中提供更準(zhǔn)確、更個(gè)性化的法律建議。
llm = ChatOpenAI(temperature=0, model="gpt-4o")
memory = ConversationEntityMemory(
llm=llm,
return_messages=True,
)
print(memory.load_memory_variables(inputs={"input": "good! busy working on Langchain. lots to do."}))
memory.save_context({"input": "good! busy working on Langchain. lots to do."}, {"output": "That sounds like a lot of work! What kind of things are you doing to make Langchain better?"})
print(memory.load_memory_variables(inputs={"input": "i'm trying to improve Langchain's interfaces, the UX, its integrations with various products the user might want ... a lot of stuff"}))
memory.save_context(inputs={"input": "i'm trying to improve Langchain's interfaces, the UX, its integrations with various products the user might want ... a lot of stuff"}, outputs={"output": "that sounds great job"})
print(memory.load_memory_variables(inputs={"input": "what is langchain"}))在會(huì)話過程中,需要從memory load 變量時(shí):
- 根據(jù)history和用戶的提問(也就是最新一句話)提取實(shí)體,注意這里提取的是用戶最新提問的query的實(shí)體
- 從entity_store這個(gè)大字典查詢之前是否存在對(duì)應(yīng)實(shí)體的描述信息,如果有對(duì)應(yīng)的描述信息,則把對(duì)應(yīng)的實(shí)體和描述信息作為entities字段返回
- 如果之前提取了實(shí)體,但是最新一句話
當(dāng)一次會(huì)話結(jié)束之后,需要save_contexts:
- 保存human message和ai message到 messages列表
- 因?yàn)锳I message 可能補(bǔ)充了human 提到的實(shí)體信息,所以使用LLM更新當(dāng)前query提到的實(shí)體的描述信息
- 如果在當(dāng)前會(huì)話之前提取了實(shí)體,但是當(dāng)前會(huì)話只是簡(jiǎn)單的問候,那么就不會(huì)更新實(shí)體的描述信息,本質(zhì)還是因?yàn)閷?shí)體信息是綁定在當(dāng)前的query的
利用知識(shí)圖譜獲取歷史對(duì)話中的實(shí)體及其聯(lián)系
在醫(yī)療咨詢中, 一個(gè)病人可能會(huì)描述多個(gè)癥狀和過去的醫(yī)療歷史(如“我有糖尿病史,最近覺得經(jīng)常口渴 和疲勞”)。 ConversationKGMemory 可以構(gòu)建一個(gè)包含病人癥狀、疾病歷史和可能的健康關(guān)聯(lián)的知識(shí)圖譜,從而幫助 AI 提供更全面和深入的醫(yī)療建議。
from langchain_community.memory.kg import ConversationKGMemory
llm = ChatOpenAI(temperature=0, model="gpt-4o")
memory = ConversationKGMemory(llm=llm)
memory.save_context({"input": "say hi to sam"}, {"output": "who is sam"})
memory.save_context({"input": "sam is a friend"}, {"output": "okay"})
print(memory.load_memory_variables({"input": "who is sam"})) # {'history': 'On Sam: Sam is a friend.'}
print(memory.get_current_entities("what's Sams favorite color?")) # ['Sam']當(dāng)每次會(huì)話結(jié)束的時(shí)候,會(huì)利用LLM從history中抽取知識(shí)的三元組,并存儲(chǔ)到NetworkxEntityGraph圖對(duì)象中。
當(dāng)新的會(huì)話開始需要從memory load數(shù)據(jù)的時(shí)候,從當(dāng)前Query中利用LLM抽取實(shí)體,并從NetworkxEntityGraph圖對(duì)象中獲取這個(gè)實(shí)體的knowledge, 把所有實(shí)體的知識(shí)信息返回。
對(duì)歷史對(duì)話進(jìn)行階段性總結(jié)摘要
在一系列的教育輔導(dǎo)對(duì)話中,學(xué)生可能會(huì)提出不同的數(shù)學(xué)問題或理解難題(如“我不太理解二次方程的求解方法”)。 ConversationSummaryMemory 可以幫助 AI 總結(jié)之前的輔導(dǎo)內(nèi)容和學(xué)生的疑問點(diǎn),以便在隨后的輔導(dǎo)中提供更針對(duì)性的解釋和練習(xí).
llm = ChatOpenAI(temperature=0, model="gpt-4o")
memory = ConversationSummaryMemory(llm=llm)
memory.save_context({"input": "hi"}, {"output": "whats up"})
print(memory.load_memory_variables({})) # {'history': 'The human greets the AI with "hi," and the AI responds with "what\'s up."'}ConversationSummaryMemory 有個(gè)buffer的屬性,存放summary信息。每次會(huì)話結(jié)束的時(shí)候,用新生成的會(huì)話和之前的summary生成新的summary存儲(chǔ)在buffer屬性中。
ConversationSummaryMemory 特點(diǎn):
- 只存儲(chǔ)摘要,不存儲(chǔ)原始對(duì)話
- 每次對(duì)話后都會(huì)更新摘要
- 適合長期對(duì)話,節(jié)省 token
- 可能丟失細(xì)節(jié)信息
需要獲取最新對(duì)話,又要兼顧較早歷史對(duì)話
在處理一個(gè)長期的技術(shù)問題時(shí)(如軟件故障排查),用戶可能會(huì)在多次對(duì)話中提供不同的錯(cuò)誤信息和反 饋。ConversationSummaryBufferMemory 可以幫助 AI 保留最近幾次交互的詳細(xì)信息,同時(shí)提供歷史問 題處理的摘要,以便于更有效地識(shí)別和解決問題。
llm = ChatOpenAI(temperature=0, model="gpt-4o")
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
# {'history': 'System: The human greets with "hi." The AI responds with "what\'s up," and the human replies with "not much, you?"\nAI: not much'}
print(memory.load_memory_variables({}))ConversationSummaryBufferMemory 會(huì)暫存不會(huì)超過max_token_limit的會(huì)話歷史,當(dāng)歷史長度超過這個(gè)大小的時(shí)候,會(huì)截?cái)嘀暗臅?huì)話歷史以使得會(huì)話現(xiàn)存的會(huì)話長度不超過max_token_limit,并把截?cái)嗟闹暗臅?huì)話歷史和之前的moving_summary_buffer更新moving_summary_buffer信息。
ConversationSummaryBufferMemory 特點(diǎn):
- 存儲(chǔ)最近的對(duì)話 + 早期對(duì)話的摘要
- 結(jié)合了完整對(duì)話和摘要的優(yōu)勢(shì)
- 保持最近對(duì)話的細(xì)節(jié),壓縮早期對(duì)話
- 適合中等長度的對(duì)話
基于向量檢索對(duì)話信息
用戶可能會(huì)對(duì)特定新聞事件提出問題,如“最近的經(jīng)濟(jì)峰會(huì)有什么重要決策?? VectorStoreRetrieverMemory 能夠快速從大量歷史新聞數(shù)據(jù)中檢索出與當(dāng)前問題最相關(guān)的信息,即使這些信息在整個(gè)對(duì)話歷史中不是最新的,也能提供及時(shí)準(zhǔn)確的背景信息和詳細(xì)報(bào)道。
import faiss
from langchain.docstore import InMemoryDocstore
from langchain.vectorstores import FAISS
embedding_size = 1536 # Dimensions of the OpenAIEmbeddings
index = faiss.IndexFlatL2(embedding_size)
embedding_fn = OpenAIEmbeddings().embed_query
vectorstore = FAISS(embedding_fn, index, InMemoryDocstore({}), {})
# the vector lookup still returns the semantically relevant information
retriever = vectorstore.as_retriever(search_kwargs=dict(k=1))
memory = VectorStoreRetrieverMemory(retriever=retriever)
# When added to an agent, the memory object can save pertinent information from conversations or used tools
memory.save_context({"input": "My favorite food is pizza"}, {"output": "thats good to know"})
memory.save_context({"input": "My favorite sport is soccer"}, {"output": "..."})
memory.save_context({"input": "I don't the Celtics"}, {"output": "ok"})總結(jié)
在實(shí)際項(xiàng)目中,記憶方案的選擇需要綜合考量以下因素:
- 業(yè)務(wù)場(chǎng)景的信息生命周期要求
- 對(duì)話復(fù)雜度和上下文依賴程度
- 系統(tǒng)資源與響應(yīng)延遲限制
在實(shí)際項(xiàng)目里,我常跟團(tuán)隊(duì)說:"別一上來就整最復(fù)雜的,先想清楚你的AI到底需要記住什么。"有時(shí)候簡(jiǎn)單的滑動(dòng)窗口就夠用,非得加個(gè)知識(shí)圖譜反而把簡(jiǎn)單問題復(fù)雜化。最近我在做一個(gè)客服系統(tǒng),就用了混合記憶的方案,效果還不錯(cuò)。
本文轉(zhuǎn)載自???AI 博物院??? 作者:longyunfeigu

















