LangChain快速筑基(带代码)P3-连续对话Memory

怎么让大模型"记住"之前的对话内容呢?怎么在多轮对话呢中保持上下文连贯呢?

LangChain提供了Memory组件。

Memory:记忆

大模型本身通常是无状态的,Memory通过存储与加载历史信息解决了这个问题。

  • 存储 Memory记录对话的每一轮交互(用户的输入和模型的输出)。
  • 加载 在下一次调用 LLM 之前,将相关的历史对话信息加载出来,并以某种形式(通常是作为提示的一部分)提供给 LLM。 比如使用ChatPromptTemplate提示类时,使用MessagesPlaceholder允许在对话模板中动态插入消息列表(如对话历史、用户输入或其他角色消息)。

通过存储、加载对话,LLM 在生成新的回复时,就能"看到"之前的对话历史,从而理解当前的语境,给出更相关、更连贯的回答。

基础 Memory 类型:ConversationBufferMemory

ConversationBufferMemory最简单也最常用的 Memory 类型之一。它会把所有历史对话原封不动地存储起来,并在下次调用时全部传递给 LLM。

ConversationBufferMemory文档:python.langchain.com/api_referen...

下面,让我们创造一个带记忆的对话。

python 复制代码
import os

# 基础对话所需
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import SystemMessage, HumanMessage

# 提示模板 - ConversationChain 有自己的默认模板,但我们也可以自定义
from langchain_core.prompts import (
    ChatPromptTemplate,
    MessagesPlaceholder,  # 非常重要,用于在提示中为历史消息占位
    HumanMessagePromptTemplate
)
# Memory 组件
from langchain.memory import ConversationBufferMemory

# 链 - 我们会使用 ConversationChain
from langchain.chains import ConversationChain

# 环境key
os.environ["DEEPSEEK_API_KEY"] = 'sk-xx'


def get_deepseek_key():
    key = os.getenv('DEEPSEEK_API_KEY')
    if key is None:
        raise ValueError(
            "DEEPSEEK_API_KEY not found in environment variables.")
    return key

# --- LLM 初始化 ---


def create_deepseek_llm():
    api_key = get_deepseek_key()
    if not api_key:
        raise ValueError("没有ds key")
    return ChatDeepSeek(
        model="deepseek-chat",
        temperature=0.1,  # 温度,更具备确定性
        max_tokens=1024,
        timeout=None,
        max_retries=2,
        api_key=api_key
    )

# --- 带记忆的对话测试 ---


def test_conversation_with_buffer_memory():
    print("使用ConversationBufferMemory测试带记忆的对话")
    llm = create_deepseek_llm()

    """
    1.初始化 Memory
    `memory_key="history"` 是 ConversationBufferMemory 默认存储历史记录的变量名。
    `return_messages=True` 表示 memory 将以消息对象列表的形式返回历史记录,这对于 ChatModels 更好。
    """
    memory = ConversationBufferMemory(
        memory_key="history", return_messages=True)

    """
    2. 为ConversationChain自定义一个提示模板
    ConversationChain 有一个默认的提示模板,但我们可以自定义以加入系统消息等。
    使用MessagesPlaceholder,它告诉模板在哪里插入历史消息
    `input` 是 ConversationChain 期望的用户输入变量名。
    """
    prompt_template = ChatPromptTemplate.from_messages(
        [
            SystemMessage("你是一个乐于助人的LangChain专家,擅长简洁明了地解释概念。"),
            MessagesPlaceholder(variable_name="history"),  # Memory中的历史消息会插入这里
            HumanMessagePromptTemplate.from_template("{input}")  # 用户输入会插入这里
        ]
    )

    """
    3. 初始化 ConversationChain
    verbose=True 表示 ConversationChain 会打印出每个消息的处理结果。
    可以让我们看到链的执行过程,包括发送给 LLM 的完整提示
    """
    conversation_chain = ConversationChain(
        llm=llm,
        memory=memory,
        prompt=prompt_template,# 使用自定义的提示模板
        verbose=True
    )
    
    # --- 开始多轮对话 ---
    print("\n 第一轮对话")
    response1 = conversation_chain.invoke({"input": "你好,我是啾啾。请问LangChain中的Memory组件是做什么用的?"})
    # response1 是一个字典,通常包含 'input', 'history', 'response' (LLM的回答)
    print(f"AI 回答: {response1['response']}")

    print("\n--- 查看当前 Memory 内容 ---")
    # load_memory_variables({}) 会加载所有存储的变量,这里主要是 history
    print(memory.load_memory_variables({}))


    print("\n第二轮对话:")
    response2 = conversation_chain.invoke({"input": "很好,那我刚才告诉你我的名字了吗?如果说了,是什么?"})
    print(f"AI 回答: {response2['response']}")

    print("\n--- 再次查看当前 Memory 内容 (应该包含第一轮和第二轮) ---")
    print(memory.load_memory_variables({}))

    print("\n第三轮对话 (测试 AI 是否还记得我叫啾啾):")
    response3 = conversation_chain.invoke({"input": "你觉得我提出的关于Memory组件的问题怎么样?"}) 
    print(f"AI 回答: {response3['response']}")
    
    print("\n--- 测试结束 ConversationBufferMemory ---")
    
if __name__ == '__main__':
    test_conversation_with_buffer_memory()

ConversationBufferWindowMemory

这种 Memory 只会保留最近的 K 轮对话,防止上下文过长超出 LLM 限制或消耗过多 token。

python 复制代码
from langchain.memory import ConversationBufferMemory,ConversationBufferWindowMemory
python 复制代码
# --- 另一种 Memory 类型:`ConversationBufferWindowMemory` ---
# 这种 Memory 只会保留最近的 K 轮对话,防止上下文过长超出 LLM 限制或消耗过多 token。
def test_conversation_with_window_memory():
    print("\n\n--- 开始测试 ConversationBufferWindowMemory (k=2) ---")
    llm = create_deepseek_llm()

    # 1. 初始化 Window Memory,k=2 表示只保留最近2轮对话
    # (Human输入 + AI输出 算一轮交互,但这里k指的是交互的"对数"或消息数,取决于具体实现,通常是交互对)
    # 为了清晰,我们说 k=2 保留最近2次完整的 "Human: ..." 和 "AI: ..." 交换
    window_memory = ConversationBufferWindowMemory(k=2, memory_key="history", return_messages=True)

    prompt_template = ChatPromptTemplate.from_messages([
        SystemMessage(content="你是一个简明扼要的AI助手。"),
        MessagesPlaceholder(variable_name="history"),
        HumanMessagePromptTemplate.from_template("{input}")
    ])

    conversation_chain_window = ConversationChain(
        llm=llm,
        memory=window_memory,
        prompt=prompt_template,
        verbose=True
    )

    print("\n第1轮:")
    conversation_chain_window.invoke({"input": "我喜欢蓝色。"})
    print(f"Memory: {window_memory.load_memory_variables({})}")

    print("\n第2轮:")
    conversation_chain_window.invoke({"input": "我最喜欢的食物是披萨。"})
    print(f"Memory: {window_memory.load_memory_variables({})}") # 应该包含蓝色和披萨

    print("\n第3轮:")
    conversation_chain_window.invoke({"input": "我住在北京。"})
    # 因为 k=2, "我喜欢蓝色" 这一轮对话应该被挤掉了
    print(f"Memory: {window_memory.load_memory_variables({})}") # 应该只包含披萨和北京

    print("\n第4轮 (测试AI是否还记得第一轮内容):")
    response = conversation_chain_window.invoke({"input": "我最开始说的我喜欢什么颜色?"})
    print(f"AI 回答: {response['response']}") # AI 可能不记得了,或猜一个

    print("\n--- 测试结束 ConversationBufferWindowMemory ---")
    

ConversationSummaryMemory

用 LLM 对历史对话进行总结,存储总结而不是完整对话(节省 token,但可能丢失细节,且需要额外 LLM 调用)。

ConversationSummaryBufferMemory

结合了 Buffer 和 Summary,保留最近几轮的完整对话,对更早的对话进行总结。 还有基于知识图谱的 Memory 等更高级的类型。

Memory总结

相关推荐
MrGaoGang17 小时前
AI应用开发:LangGraph+MCP
前端·人工智能·langchain
西部荒野子20 小时前
LangChain.js 中的 Runnable 系统
langchain
大尾巴青年1 天前
06 一分钟搞懂langchain的Agent是如何工作的
langchain·llm
敲键盘的小夜猫2 天前
LangChain核心之Runnable接口底层实现
langchain
疯狂的小强呀2 天前
基于langchain的简单RAG的实现
python·langchain·rag检索增强
用户711283928472 天前
LangChain(三) LCEL
人工智能·langchain
啾啾大学习2 天前
LangChain快速筑基(带代码)P0-DeepSeek对话与联网搜索
langchain
啾啾大学习2 天前
LangChain快速筑基(带代码)P7-LLM外部数据检索Retrievers
langchain
啾啾大学习2 天前
LangChain快速筑基(带代码)P1-输入控制与输出解析
langchain