LangChain 內存(Memory)

1. 為什么需要內存?

大型語言模型(LLM)本身是無狀態的。這意味著每次你向 LLM 發送一個請求(Prompt),它都會獨立處理這個請求,完全不記得之前任何的交互。這在構建一次性問答應用時沒問題,但在需要多輪對話(比如聊天機器人、智能客服)的場景中,LLM 需要“記住”之前的對話內容,才能理解上下文并做出連貫的回復。

內存就是 LangChain 解決這個問題的方案。它允許你在 LLM 應用中注入和管理對話歷史,讓 LLM 具備“長期”記憶的能力。

簡單來講就是存儲歷史記錄,然后在每次使用 LLM 時,不僅傳入當前輸入,還有歷史記錄

2. 內存的基本工作原理

在 LangChain 中,內存通常扮演著連接用戶輸入LLM 提示的橋梁。

  1. 保存歷史:每次用戶和 LLM 進行交互后,內存組件都會捕獲并存儲這次對話的輸入和輸出。
  2. 加載歷史:在下一次 LLM 調用之前,內存會根據需要從其存儲中加載相關的對話歷史。
  3. 注入提示:加載的對話歷史會被格式化并注入到 LLM 的提示(Prompt)中,作為上下文的一部分。這樣,LLM 就能“看到”之前的對話內容,從而理解當前問題的背景。

3. 最簡單的內存:對話緩沖區(ConversationBufferMemory)

ConversationBufferMemory 是 LangChain 中最基礎也是最常用的內存類型。它非常簡單粗暴:記住所有對話的原始文本。它會將完整的對話歷史原封不動地存儲起來,并在每次調用時將所有歷史添加到提示中。

ConversationBufferMemory 流程
發送給LLM
保存到內存
從內存加載
添加到Prompt
LLM Chain/Agent
用戶輸入
LLM
LLM 輸出
ConversationBufferMemory
# memory_buffer_example.pyfrom langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import os# --- 配置部分 ---
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"if not os.getenv("OPENAI_API_KEY"):print("錯誤:請設置環境變量 OPENAI_API_KEY 或在代碼中取消注釋并設置您的密鑰。")exit()print("--- ConversationBufferMemory 示例開始 ---")# 1. 定義 LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)# 2. 定義內存
# memory_key 是在 Prompt 中用來引用對話歷史的變量名
# return_messages=True 表示內存返回的是消息對象列表,而不是單個字符串
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
print("已創建 ConversationBufferMemory。")# 3. 定義帶有歷史占位符的 Prompt
# MessagesPlaceholder 用于告訴 Prompt 在這里插入消息列表 (chat_history)
prompt = ChatPromptTemplate.from_messages([("system", "你是一個友好的AI助手,擅長進行多輪對話。"),MessagesPlaceholder(variable_name="chat_history"), # 聊天歷史的占位符("human", "{input}") # 當前用戶輸入
])
print("已創建包含 chat_history 占位符的 Prompt。")# 4. 創建 ConversationChain
# ConversationChain 是 LangChain 提供的一個預構建的鏈,專為處理對話而設計
# 它會自動處理內存的注入和更新
conversation = ConversationChain(llm=llm,memory=memory,prompt=prompt,verbose=True # 打印詳細日志,可以看到內存注入到 Prompt 的過程
)
print("已創建 ConversationChain。")# 5. 進行多輪對話
print("\n--- 開始對話 ---")# 第一輪
print("\n用戶: 你好,我叫小明。你叫什么名字?")
response1 = conversation.invoke({"input": "你好,我叫小明。你叫什么名字?"})
print(f"AI: {response1['response']}")# 第二輪
print("\n用戶: 很高興認識你!我之前告訴你我叫什么名字了?")
response2 = conversation.invoke({"input": "很高興認識你!我之前告訴你我叫什么名字了?"})
print(f"AI: {response2['response']}")# 第三輪
print("\n用戶: 幫我寫一句關于編程的詩。")
response3 = conversation.invoke({"input": "幫我寫一句關于編程的詩。"})
print(f"AI: {response3['response']}")print("\n--- 對話結束 ---")
print("\n--- ConversationBufferMemory 示例結束 ---")
  • ConversationBufferMemory 將所有交互都作為 HumanMessageAIMessage 存儲起來。
  • MessagesPlaceholder 是一個非常關鍵的組件,它告訴 LangChain 在構建最終發送給 LLM 的提示時,應該將 chat_history 這個變量的內容(即內存中的消息列表)插入到這里。
  • ConversationChain 是一個便利的鏈,它自動處理了內存的讀寫,簡化了對話應用的構建。

4. 限制歷史長度:對話緩沖區窗口內存(ConversationBufferWindowMemory)

ConversationBufferMemory 的一個缺點是,如果對話很長,內存中的歷史會不斷增長,導致每次發送給 LLM 的提示越來越長,最終可能超出 LLM 的上下文窗口限制(Context Window Limit),并增加 API 成本。

ConversationBufferWindowMemory 解決了這個問題,它只記住最近 N 輪的對話

ConversationBufferWindowMemory 流程
發送給LLM
保存到內存 (僅保留最新N輪)
從內存加載 (僅加載最新N輪)
添加到Prompt
LLM Chain/Agent
用戶輸入
LLM
LLM 輸出
ConversationBufferWindowMemory
# memory_window_example.pyfrom langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferWindowMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import os# --- 配置部分 ---
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"if not os.getenv("OPENAI_API_KEY"):print("錯誤:請設置環境變量 OPENAI_API_KEY 或在代碼中取消注釋并設置您的密鑰。")exit()print("--- ConversationBufferWindowMemory 示例開始 ---")llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)# 定義窗口大小為 2,表示只記住最近 2 輪(4條消息:2用戶+2AI)對話
memory = ConversationBufferWindowMemory(memory_key="chat_history", return_messages=True, k=2)
print("已創建 ConversationBufferWindowMemory,窗口大小 k=2。")prompt = ChatPromptTemplate.from_messages([("system", "你是一個友好的AI助手,只記得最近的對話。"),MessagesPlaceholder(variable_name="chat_history"),("human", "{input}")
])conversation = ConversationChain(llm=llm,memory=memory,prompt=prompt,verbose=True
)
print("已創建 ConversationChain。")# 5. 進行多輪對話,觀察內存如何截斷
print("\n--- 開始對話 ---")# 第一輪
print("\n用戶: 我喜歡吃蘋果。")
response1 = conversation.invoke({"input": "我喜歡吃蘋果。"})
print(f"AI: {response1['response']}")# 第二輪
print("\n用戶: 你呢?你喜歡什么水果?")
response2 = conversation.invoke({"input": "你呢?你喜歡什么水果?"})
print(f"AI: {response2['response']}")# 第三輪 - 此時第一輪對話(用戶說“我喜歡吃蘋果”)應該被“遺忘”
print("\n用戶: 我之前喜歡什么水果來著?")
response3 = conversation.invoke({"input": "我之前喜歡什么水果來著?"})
print(f"AI: {response3['response']}") # AI可能無法準確回答,因為它忘記了第一輪# 第四輪 - 此時第二輪對話應該被遺忘
print("\n用戶: 你呢?你剛才喜歡什么水果?")
response4 = conversation.invoke({"input": "你呢?你剛才喜歡什么水果?"})
print(f"AI: {response4['response']}")print("\n--- 對話結束 ---")
print("\n--- ConversationBufferWindowMemory 示例結束 ---")
  • k=2 參數控制了窗口大小。這意味著內存將只保留最近的 2 輪完整的對話(即 2 條用戶消息和 2 條 AI 消息)。
  • 你會發現,在第三輪對話中,模型可能無法回憶起第一輪用戶提到的“蘋果”,因為它已經超出了窗口范圍。

5. 總結歷史:對話摘要內存(ConversationSummaryMemory)

ConversationBufferWindowMemory 雖然限制了歷史長度,但可能會丟失早期對話的關鍵信息。ConversationSummaryMemory 旨在解決這個問題:它不直接存儲所有歷史,而是使用一個 LLM 對對話歷史進行摘要,然后將這個摘要作為上下文提供給主 LLM。

這樣,無論對話多長,每次傳遞給 LLM 的都是一個簡潔的摘要,既能保持上下文,又不會超出令牌限制。

ConversationSummaryMemory 流程
發送給LLM
添加到歷史
LLM生成摘要
摘要添加到Prompt
LLM Chain/Agent
用戶輸入
LLM
LLM 輸出
完整對話歷史
ConversationSummaryMemory
# memory_summary_example.pyfrom langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import os# --- 配置部分 ---
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"if not os.getenv("OPENAI_API_KEY"):print("錯誤:請設置環境變量 OPENAI_API_KEY 或在代碼中取消注釋并設置您的密鑰。")exit()print("--- ConversationSummaryMemory 示例開始 ---")# 定義一個用于生成摘要的 LLM
# 摘要LLM可以是一個更小的、成本更低的模型,或者與主LLM相同
summary_llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
main_llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)# 1. 定義內存
# memory_key 和 return_messages 類似之前的內存
# llm 參數指定了用于生成摘要的 LLM
memory = ConversationSummaryMemory(llm=summary_llm, memory_key="chat_history", return_messages=True)
print("已創建 ConversationSummaryMemory。")prompt = ChatPromptTemplate.from_messages([("system", "你是一個友好的AI助手,能記住所有對話的重要摘要。"),MessagesPlaceholder(variable_name="chat_history"), # 這里傳入的是摘要("human", "{input}")
])conversation = ConversationChain(llm=main_llm,memory=memory,prompt=prompt,verbose=True
)
print("已創建 ConversationChain。")# 5. 進行多輪對話,觀察內存如何進行摘要
print("\n--- 開始對話 ---")# 第一輪
print("\n用戶: 我的項目遇到了一個問題,需要你的幫助。我正在開發一個Python腳本,它需要處理大量文本數據。")
response1 = conversation.invoke({"input": "我的項目遇到了一個問題,需要你的幫助。我正在開發一個Python腳本,它需要處理大量文本數據。"})
print(f"AI: {response1['response']}")# 第二輪
print("\n用戶: 具體來說,我需要對文本進行分詞和詞性標注。你有什么建議的庫嗎?")
response2 = conversation.invoke({"input": "具體來說,我需要對文本進行分詞和詞性標注。你有什么建議的庫嗎?"})
print(f"AI: {response2['response']}")# 第三輪
print("\n用戶: 好的,我會試試 NLTK 和 SpaCy。你認為哪個更適合大型項目?")
response3 = conversation.invoke({"input": "好的,我會試試 NLTK 和 SpaCy。你認為哪個更適合大型項目?"})
print(f"AI: {response3['response']}")# 第四輪 - 此時內存會包含前三輪的摘要
print("\n用戶: 好的,謝謝你的建議。我的項目主要是關于中文文本處理。")
response4 = conversation.invoke({"input": "好的,謝謝你的建議。我的項目主要是關于中文文本處理。"})
print(f"AI: {response4['response']}")print("\n--- 對話結束 ---")
# 打印最終的摘要內容
print("\n當前內存中的摘要:")
print(memory.buffer)print("\n--- ConversationSummaryMemory 示例結束 ---")

說明:

  • ConversationSummaryMemory 需要一個 llm 參數來執行摘要任務。這個 llm 可以是與主 LLM 相同的模型,也可以是專門為摘要優化的模型。
  • 通過 verbose=True 觀察輸出,你會發現每次調用時,LLM 接收到的 chat_history 變量會是一個不斷更新的摘要字符串,而不是原始的完整消息列表。

6. 其他常用內存類型

LangChain 還提供了其他更高級或更具體用途的內存類型:

  • ConversationSummaryBufferMemory: 結合了 ConversationBufferWindowMemoryConversationSummaryMemory 的特點。它會保留最近 N 輪的完整對話,而將 N 輪之前的對話進行摘要。這樣可以在短期內提供精確上下文,同時長期保持摘要記憶。
  • VectorStoreRetrieverMemory: 這種內存將對話歷史存儲在向量數據庫中。當需要回憶信息時,它會像 RAG 那樣,根據當前查詢在向量數據庫中檢索最相關的歷史片段,而不是全部或摘要。這對于需要記憶超長對話或從大量歷史中檢索特定信息非常有用。
  • EntityMemory: 專注于從對話中識別和記憶特定的實體(如人名、地點、概念),并將其存儲在一個結構化(通常是 JSON)的知識圖中。當對話中再次提到這些實體時,LLM 可以直接引用其存儲的信息。

7. 選擇合適的內存策略

  • ConversationBufferMemory: 適用于短對話、簡單場景或調試。
  • ConversationBufferWindowMemory: 適用于需要限制上下文長度、但仍需保持一定近期對話完整性的場景。
  • ConversationSummaryMemory: 適用于長對話,需要保持核心上下文但又不能超出 LLM 上下文窗口的場景。
  • ConversationSummaryBufferMemory: 結合了短期精確記憶和長期摘要記憶的優點。
  • VectorStoreRetrieverMemory: 適用于需要從海量、復雜對話歷史中智能檢索相關片段的場景,或構建具備“知識庫”的聊天機器人。
  • EntityMemory: 適用于需要記憶和跟蹤特定實體信息(如客戶檔案、產品屬性)的對話場景。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/88916.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/88916.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/88916.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于定制開發開源AI智能名片S2B2C商城小程序的社群游戲定制策略研究

摘要:本文聚焦社群游戲定制領域,深入探討以社群文化和用戶偏好為導向的定制策略。通過分析互動游戲活動、社群文化塑造等關鍵要素,結合定制開發開源AI智能名片S2B2C商城小程序的技術特性,提出針對性游戲定制方案。研究旨在提升社群…

自動駕駛決策與規劃

目錄 自動駕駛決策與規劃概述 決策與規劃體系結構 分層遞階式決策規劃 反應式體系結構 混合式體系結構 決策與規劃系統的關鍵環節 路徑規劃 軌跡規劃 行為決策 異常處理 自動駕駛的路徑規劃技術 維諾圖法 柵格法 Dijkstra算法 A*算法 自動駕駛的行為決策方法 …

C++編譯期計算:常量表達式(constexpr)全解析

在C性能優化領域,"將計算盡可能轉移到編譯期"是一條黃金法則。編譯期計算(Compile-Time Computation)能顯著減少程序運行時的開銷,提升執行效率,同時還能在編譯階段暴露潛在錯誤。C11引入的constexpr關鍵字及…

【micro:bit】從入門到放棄(一):在線、離線版本的使用

1、離線版 micro:bit 1)下載地址 https://makecode.microbit.org/offline-app 2)雙擊安裝包,makecode-microbit-setup-win64.exe,自動安裝,安裝成功后圖標如下圖所示 3)運行程序,查看版本信息 4)主界面如下 5)編程界面 點擊“新建項目”或者“導入”進入編程界…

Flink Forward Asia 2025 主旨演講精彩回顧

作為 Apache Flink 社區備受矚目的年度盛會之一,由阿里云承辦的 Flink Forward Asia 2025 于 7 月 3 日在新加坡正式拉開帷幕。From Real-Time Data Analytics to Real-Time AI,Flink 生態全面擁抱 AI本次大會上,Apache Flink 中文社區發起人…

車道偏離預警(LDW)功能介紹

車道偏離預警(LDW)功能介紹 LDW功能 通過攝像頭監測前方車道線,當車輛偏離車道線時,系統發出報警提醒。 系統框圖報警條件 最早報警線最遲報警線報警臨界線 設置在 最早/最遲報警線 之間接口定義 輸入/輸出系統算法 橫向偏離速度模…

軟考(軟件設計師)計算機網絡-網絡層

obsidian做markdown筆記太爽了,康奈爾模板筆記看起來舒服,CSDN試了幾種方式,不支持,只能貼圖了,將就看吧😏(狗頭保命)工作原理圖解 #mermaid-svg-e5Tgpt26jUftujKn {font-family:&qu…

nginx反向代理實現跨域請求

記錄一個項目中遇到的跨域請求問題,解決跨域問題無非幾種辦法:1、后端解決,推薦的做法,可以看我之前寫的文章,fastadmin的后臺配置跨域;2、前端配置proxy代理(開發環境用)&#xff1…

利用Wisdom SSH高效搭建CI/CD工作流

在軟件開發流程中,CI/CD工作流對于提升效率與確保質量起著關鍵作用。Wisdom SSH作為一款強大工具,其官網為ssh.wisdomheart.cn,借助AI助手能大幅簡化CI/CD工作流的搭建過程。假設服務器已完成基礎配置,下面為您介紹如何利用Wisdom…

深度學習-循環神經網絡RNN

文章目錄序列模型循環神經網絡案例詞的表示輸出的表示矩陣運算表示交叉熵損失時間反向傳播算法BPTT梯度消失與梯度爆炸GRULSTM總結序列模型 循環神經網絡 st是上一個隱層的輸出,體現序列數據之間有很強的關聯性。 案例 > S0是初始輸入,一般是0&#…

【Linux網絡編程】Socket - TCP

目錄 V1版本 - Echo Server 初始化服務器 啟動服務器 客戶端 一些BUG與問題 解決服務器無法一次處理多個請求的問題 多進程版本 多線程版本 線程池版本 V2版本 - 多線程遠程執行命令 V1版本 - Echo Server 初始化服務器 TCP大部分內容與UDP是相同的,我們…

知識圖譜構建簡單分享

最近系統性地學習了知識圖譜構建的關鍵技術,并結合醫療領域知識,完成了一個醫療知識圖譜項目的實踐。以下是整理的項目架構,歡迎交流指正。說明:當前項目實際實現主要應用了數據預處理、模型設計與預訓練、模型優化與測試等核心技…

MCU中的系統控制器(System Controller)是什么?

MCU中的系統控制器(System Controller)是什么? 在微控制器(MCU)中,系統控制器(System Controller)是一個關鍵模塊,負責管理和協調MCU內部的核心功能,確保系統…

【Datawhale夏令營】用AI做帶貨視頻評論分析

文本分類、文本聚類 基礎庫: pandas, sklearn功能: 商品識別、情感分析、評論聚類商品識別: 視頻文本信息,預測推廣商品名稱 & 情感分析:四個維度(情感傾向、用戶場景、用戶疑問、用戶建議&#xff09…

[Meetily后端框架] AI摘要結構化 | `SummaryResponse`模型 | Pydantic庫 | vs marshmallow庫

第3章:摘要數據結構(Pydantic庫) 歡迎回來! 在之前的第2章:API文檔中,我們知道API網關提供了端點 而API文檔準確告訴我們如何與這些端點通信,包括需要發送的數據格式和期望接收的數據格式。 …

深度學習 tensor及其相關操作

目錄 Tensor 概念 數據類型 創建tensor 基本創建方式 1、 torch.tensor() 2、torch.Tensor() 3、torch.IntTensor() 等 創建線性張量和隨機張量 1、創建線性張量 2、創建隨機張量 切換設備 類型轉換 與 Numpy 數據轉換 1、張量轉 Numpy 2、Numpy 轉張量 tenso…

如何將FPGA設計的驗證效率提升1000倍以上(4)

本文為系列文章的完結篇。用戶應用設計中的信號,在經歷編譯器的多次迭代優化之后,在FPGA芯片內部運行時,可能已經被重新命名、“改頭換面”或“機里機氣”。要想以人工經驗進行追蹤,構建目標寄存器信號與RTL設計源碼之間的映射關系…

Linux驅動11 --- buildroot雜項驅動開發方法

目錄 一、Buildroot 1.1介紹 文件系統 1.一個完整的操作系統需要包含大量的文件 2.在嵌入式開發中目前應用最廣泛的文件系統制作工具就是 buildroot,busybox 3.buildroot 制作文件系統(了解) 二、雜項驅動編程 1.1 驅動編程做的內容 2.2…

Unity物理系統由淺入深第三節:物理引擎底層原理剖析

Unity物理系統由淺入深第一節:Unity 物理系統基礎與應用 Unity物理系統由淺入深第二節:物理系統高級特性與優化 Unity物理系統由淺入深第三節:物理引擎底層原理剖析 Unity物理系統由淺入深第四節:物理約束求解與穩定性 Unity 物理…

Docker一鍵安裝中間件(RocketMq、Nginx、MySql、Minio、Jenkins、Redis)腳步

1、Docker安裝RocketMq 2、Docker安裝Nginx 3、Docker安裝MySql 4、Docker安裝Minio 5、Docker安裝jenkins 6、Docker安裝Redis 1、Docker安裝RocketMq #!/bin/bash# 定義變量 NAMESRV_CONTAINER"rocketmq-namesrv" BROKER_CONTAINER"rocketmq-broker&quo…