【LangChain】P19 LangChain Memory(一):让 AI 拥有“记忆力“的秘密
目录
- LangChain Memory
- Memory 到底是什么?
- 图解:Memory 是如何工作的?
- 手动管理对话历史示例
- Memory 组件的四层进化
- ChatMessageHistory - 最基础的存储器
- ConversationBufferMemory - 自动化管理
- 实战:记忆模块与提示词模板结合
- Memory 与 PromptTemplate
- Memory 与 ChatPromptTemplate(推荐)
- 关键设计细节:LangChain 的智慧
一个有趣的问题:AI 真的能记住你说的话吗?
想象一下,你正在和 ChatGPT 聊天:
你:我叫小明,今年25岁。
AI:你好小明~很高兴认识你。
你:我今年多大了?
AI:你今年25岁。
看起来 AI 记住了你的年龄信息,但真相可能会让你惊讶 —— 大语言模型本身其实没有记忆能力。他就像一个每次见面都会失忆的朋友,模型每次接收你的问题时,都是与你的初遇。那它为什么能回答出你的年龄呢?
答案,是有人在背后偷偷把你们之前的对话的信息,塞给了大模型。它的其中一个实现方式,也就是我们今天要介绍的主角 ——LangChain 的 Memory 组件。
LangChain Memory
Memory 到底是什么?
Memory(记忆)是 LangChain 中负责“记录对话历史”的组件。它的工作原理非常朴素:
- 记录: 每次对话后,把“你说了什么”和“AI 回复了什么”都保存下来
- 回放: 下次对话时,把之前的记录一起发给 AI
- AI 视角: AI 看到完整对话,就能“假装”记得你
就像你考试时偷偷翻小抄,AI 每次回答前都在“翻历史记录”。
图解:Memory 是如何工作的?
让我们用一个完整流程来理解 Memory:
┌───────────────────────────────────────────────┐
│ 第一轮对话 │
├───────────────────────────────────────────────┤
│ 👤 用户: "我叫小明" │
│ 🤖 AI: "你好,小明!" │
│ 💾 Memory 存储: [用户:我叫小明, AI:你好,小明!] │
└───────────────────────────────────────────────┘┌───────────────────────────────────────────────┐
│ 第二轮对话 │
├───────────────────────────────────────────────┤
│ 1️⃣ 👤 新问题: "我叫什么名字?" │
│ 2️⃣ 📖 Memory读取: [用户:我叫小明, AI:你好,小明!] │
│ 3️⃣ 🔀 组装完整提示词: │
│ "历史对话: │
│ 用户:我叫小明 │
│ AI:你好,小明! │
│ 当前问题:我叫什么名字?" │
│ 4️⃣ 🤖 AI 回答: "你叫小明" │
│ 5️⃣ 💾 更新 Memory: │
│ [用户:我叫小明, AI:你好1小明!, │
│ 用户:我叫什么名字, AI:你叫小明] │
└───────────────────────────────────────────────┘
核心流程六步走:
- 用户提问
- 从 Memory 读取历史消息
- 把历史+当前问题组装成完整提示词
- 大模型处理并生成回复
- 解析输出给用户
- 新对话写入 Memory
手动管理对话历史示例
在学习 Memory 组件之前,我们先自己动手实现一个简单的“记忆功能”,这样有助于理解 Memory 的整体策略。
llm 大模型部分:
import os
import dotenv
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAIdotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("OPENAI_BASE_URL")llm = ChatOpenAI(model=os.environ.get("CHAT_MODEL")
)
记忆模块部分:
def chat_with_model(answer):# 提供提示词模板,使用 ChatPromptTemplateprompt_template = ChatPromptTemplate.from_messages([("system", "你是一个人工智能的助手"),("human", "{question}")])while True:chain = prompt_template | llmresponse = chain.invoke({"question": answer})print(f"模型回复:{response.content}")# 退出或继续询问user_input = input("你还有其他问题吗,没有更多问题,请输入退出")if user_input == "退出":break# 将上述新生成的消息存放到提示词模板的消息列表中prompt_template.messages.append(AIMessage(content=response.content))prompt_template.messages.append(HumanMessage(content=user_input))if __name__ == '__main__':chat_with_model("你好,很高兴认识你!")
Memory 组件的四层进化
LangChain 针对不同场景,提供了不同复杂度的 Memory 方案:
ChatMessageHistory - 最基础的存储器
ChatMessageHistory
是一个用于存储和管理对话消息的基础类,它直接操作消息对象(如 HumanMessage
、AIMessage
等),是其他记忆组件的底层存储工具。
实例化部分:
from langchain.memory import ChatMessageHistory# ChatMessageHistory 实例化
history = ChatMessageHistory()# 添加相关的消息进行存储
history.add_user_message("你好")
history.add_ai_message("很高兴认识你")
history.add_user_message("帮我计算1+2*3=?")# 打印存储的消息
print(history.messages)
print(type(history.messages))# 调用大模型处理
response = llm.invoke(history.messages)
print(response)
模型结果:
[HumanMessage(content='你好', additional_kwargs={}, response_metadata={}), AIMessage(content='很高兴认识你', additional_kwargs={}, response_metadata={}), HumanMessage(content='帮我计算1+2*3=?', additional_kwargs={}, response_metadata={})]
<class 'list'>
content='根据数学运算的优先级,乘法优先于加法,所以:\n\n1 + 2 × 3 \n= 1 + 6 \n= 7 \n\n答案是 **7**。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 37, 'prompt_tokens': 20, 'total_tokens': 57, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 20}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_ffc7281d48_prod0820_fp8_kvcache', 'id': '9de4e158-d8cd-46a9-93af-b90fa8dbf299', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--da3289a2-b023-4c95-8e09-d3081b865729-0' usage_metadata={'input_tokens': 20, 'output_tokens': 37, 'total_tokens': 57, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
ConversationBufferMemory - 自动化管理
ConversationBufferMemory 是最常用的 Memory 组件,它会自动保存完整对话历史。
实例化部分:
from langchain.memory import ConversationBufferMemory# ============ 方式一:以字符串的方式返回存储信息 ============# ConversationBufferMemory 实例化
memory = ConversationBufferMemory()# 存储相关消息
memory.save_context(inputs={"input": "你好,我叫小明"}, outputs={"output": "很高兴认识你"})
memory.save_context(inputs={"input": "帮我回答一下1+2*3=?"}, outputs={"output": "7"})# 打印存储的信息
print(memory.load_memory_variables({}))
print(type(memory.load_memory_variables({})))# =========== 方式二:以消息的方式返回存储的信息 ===============# ConversationBufferMemory 实例化
memory = ConversationBufferMemory(return_messages=True) # 以消息列表形式返回# 存储相关消息
memory.save_context(inputs={"input": "你好,我叫小明"}, outputs={"output": "很高兴认识你"})
memory.save_context(inputs={"input": "帮我回答一下1+2*3=?"}, outputs={"output": "7"})# 打印存储的信息
print(memory.load_memory_variables({}))
print(type(memory.load_memory_variables({})))
模型结果:
{'history': 'Human: 你好,我叫小明\nAI: 很高兴认识你\nHuman: 帮我回答一下1+2*3=?\nAI: 7'}
<class 'dict'>
{'history': [HumanMessage(content='你好,我叫小明', additional_kwargs={}, response_metadata={}), AIMessage(content='很高兴认识你', additional_kwargs={}, response_metadata={}), HumanMessage(content='帮我回答一下1+2*3=?', additional_kwargs={}, response_metadata={}), AIMessage(content='7', additional_kwargs={}, response_metadata={})]}
<class 'dict'>
由上述示例,可以发现,ConversationBufferMemory
支持两种输出格式:
- 格式1:纯文本字符串(默认)
memory = ConversationBufferMemory() memory.load_memory_variables({}) # 返回: {'history': 'Human: ...\nAI: ...'}
- 格式2:消息对象列表
memory = ConversationBufferMemory(return_messages=True) memory.load_memory_variables({}) # 返回: {'history': [HumanMessage(...), AIMessage(...)]}
- 或者,我们可以直接应用如下方法展示为消息列表
print(memory.chat_memory.messages)
实战:记忆模块与提示词模板结合
Memory 的真正威力在于与 LangChain 的其他组件协作。
Memory 与 PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate# 创建带历史占位符的模板
template = """
以下是历史对话:
{history}当前问题: {input}
你的回答:
"""prompt = PromptTemplate(template=template,input_variables=["history", "input"]
)# 创建记忆
memory = ConversationBufferMemory(memory_key="history")# 创建链
chain = LLMChain(llm=llm,prompt=prompt,memory=memory
)# 对话
print(chain.run("我叫小明"))
# 输出: 你好,小明!print(chain.run("我叫什么名字?"))
# 输出: 你叫小明
关键点:
memory_key="history"
必须与模板中的{history}
匹配- Memory 会自动把对话填充到
{history}
位置
Memory 与 ChatPromptTemplate(推荐)
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder# 创建聊天模板
prompt = ChatPromptTemplate.from_messages([("system", "你是一个专业的AI助手"),MessagesPlaceholder(variable_name="history"), # 历史消息占位符("human", "{input}")
])memory = ConversationBufferMemory(memory_key="history",return_messages=True # 必须返回消息对象
)chain = LLMChain(llm=llm, prompt=prompt, memory=memory)# 开始对话
chain.run("我喜欢吃披萨")
chain.run("我喜欢吃什么?") # AI会记得你喜欢披萨
ChatPromptTemplate 的优势:
- 消息格式更规范(system/human/ai角色清晰)
- 与现代聊天API完美兼容
- 支持更复杂的对话场景
关键设计细节:LangChain 的智慧
你可能会好奇:对话是什么时候被保存到 Memory 的?
答案揭示了 LangChain 的精妙设计:
- 时机1:用户提问后立即保存问题
当你调用chain.run("问题")
时,问题会立即被记录,即使 AI 还没回答。 - 时机2:AI 回答后立即保存回复
AI 生成回复后,该回复会马上加入对话历史。
为什么这样设计?
保证与人对话逻辑的一致性:
- 用户问完问题就该被记住
- AI 回答完就该被记录
- 返回给用户的响应应反映最新状态
这种设计确保了即使发生中断,对话历史也是完整的。
下一节内容,我们将分享另外两个高价值的 Memory 方法。敬请期待。
相信读者通过上述内容,对:所谓的 AI 记忆,本质上只是在每次对话时巧妙地“翻阅历史记录”,有了更深刻的理解!
2025.10.10 金融街