LangChain 记忆模块完全指南:从 ChatMessageHistory 到滑动窗口

版本:Python 3.9+、LangChain ≥ 0.1
目标:一篇文章掌握所有「记忆」组件,复制即可运行


0. 统一环境(仅需一次)

pip install -U langchain langchain-openai python-dotenv

.env 文件(同目录)

OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.openai.com/v1
# 0_env.py
import os, dotenv
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

下面所有代码均默认已 from 0_env import llm,不再重复。


1. ChatMessageHistory:最原始的消息列表

文件:1_raw_history.py

from langchain.memory import ChatMessageHistory

history = ChatMessageHistory()
history.add_user_message("你好,我是小明")
history.add_ai_message("很高兴认识你,我是人工智能助手")
history.add_user_message("帮我解释一下 1 + 2 * 3 = ?")

print(history.messages)          # List[BaseMessage]
print(llm.invoke(history.messages).content)

终端打印:

[SystemMessage(content='你好,我是小明'), 
 AIMessage(content='很高兴认识你,我是人工智能助手'), 
 HumanMessage(content='帮我解释一下 1 + 2 * 3 = ?')]
结果是 7,因为先乘除后加减。

2. ConversationBufferMemory:自动拼接历史字符串

文件:2_buffer_str.py

from langchain.memory import ConversationBufferMemory
from langchain.chains import LLMChain
from langchain_core.prompts import PromptTemplate

template = """
历史对话:{history}
当前问题:{question}
"""
prompt = PromptTemplate.from_template(template)

memory = ConversationBufferMemory(memory_key="history")   # 默认 return_messages=False
chain = LLMChain(llm=llm, prompt=prompt, memory=memory)

print(chain.invoke({"question": "1 + 3 = ?"})["text"])
print(chain.invoke({"question": "我刚才问了什么?"})["text"])

输出:

1 + 3 等于 4。
你刚才问的是“1 + 3 = ?”。

3. 使用 ChatPromptTemplate + MessagesPlaceholder(推荐)

文件:3_buffer_messages.py

from langchain.memory import ConversationBufferMemory
from langchain.chains import LLMChain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是人工智能助手,可以详细回答问题"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{question}"),
])

memory = ConversationBufferMemory(
    memory_key="history",
    return_messages=True               # 必须 True,才能喂给 MessagesPlaceholder
)
chain = LLMChain(llm=llm, prompt=prompt, memory=memory)

print(chain.invoke({"question": "太阳为什么发光?"})["text"])
print(chain.invoke({"question": "我上一个问题是什么?"})["text"])

4. ConversationBufferWindowMemory:只记最近 k 轮

文件:4_window_memory.py

from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import LLMChain
from langchain_core.prompts import PromptTemplate

template = """
历史最近 3 轮:{history}
当前问题:{question}
"""
prompt = PromptTemplate.from_template(template)

memory = ConversationBufferWindowMemory(k=3)   # 只保留最近 3 轮
chain = LLMChain(llm=llm, prompt=prompt, memory=memory)

chain.invoke({"question": "你好,我的名字叫小明"})
chain.invoke({"question": "今天适合做啥?"})
chain.invoke({"question": "我考了 681,不去北大,去福耀科大"})
reply = chain.invoke({"question": "我的名字叫啥?"})
print(reply["text"])

当 k=1 时模型会“失忆”,k=3 能记住“小明”。


5. 记忆选型速查表

类是否保留全部历史是否支持 MessagesPlaceholder适用场景
ChatMessageHistory是(手动)手动调试、最小 demo
ConversationBufferMemory是✔通用对话,历史不长
ConversationBufferWindowMemory否(只留 k 轮)✔长对话、控制 token

6. 最佳实践小结

  1. 新代码优先用 ChatPromptTemplate + MessagesPlaceholder + return_messages=True
  2. 历史太长 → 改用 WindowMemory 或后续 SummaryMemory
  3. 一条链即可同时支持 sync / async / stream,无需改代码:
async for chunk in chain.astream({"question": "讲个笑话"}):
    print(chunk, end="", flush=True)

复制以上文件即可直接 python xxx.py 运行,把你的 LangChain 应用加上“记忆”吧!
点赞 + 收藏,下篇带你用 LCEL 重构记忆链,再会!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐