Langchain入门到精通0x04:大模型怎么记忆?

0 阅读6分钟

问题

我们以往在使用豆包或者DeepSeek时,在一些长的上下文环境中AI模型是可以记住我们之前提供的一些信息的。那么,AI大模型是怎么记忆这些信息的。

我们先从一段简单的代码入手看看。

while True:
    user_input = input("请输入 'quit' 退出程序: ")
    if user_input == 'quit':
        print("程序结束。")
        break
    else:
        print(chain.invoke({'text': user_input}))

我先输入”你好,我是大白。你是谁?“,接着后续我又继续问”我是谁?“。。。结果歇菜了,他并不知道我是谁。可见,原生的单纯的对话式问答,AI大模型并不会自动记忆

image.png

那么,Langchain框架到底是怎么记忆的?

ChatMessageHistory

基础的内存存储。所有消息存在于程序运行时内存中,服务重启则记忆消失。

代码

#  创建消息历史记录,存储组件
# add_user_message() 添加用户的输入信息
# add_ai_message() 添加存储大模型的回复信息
# .messages 属性获取所有历史消息
chat_history = ChatMessageHistory()

while True:
    user_input = input("用户:")
    if user_input == "exit":
        break

    # 添加用户输入
    chat_history.add_user_message(user_input)
    # 访问LLM时,chat_history.messages 获取所有的历史消息
    response = chain.invoke({'messages': chat_history.messages})

    print("chat_history:",chat_history.messages)
    print(f"大模型回复》》》:{response}")

    # 将大模型的回复加入历史记录
    chat_history.add_ai_message(response)

📢⚠️:这里大模型的回复也要加入历史记录,后续大模型对之前的上下文才会有更完善的记忆。

运行

image.png

可见,AI大模型确实记住了我们之前对话的相关信息。我们继续exit程序,重新问一些上轮对话的相关内容。

image.png

可见,ChatMessageHistory确实是内存存储。当程序重新运行之后,之前的记忆已然清空。

RedisChatMessageHistory

持久化存储。将消息序列化后存入Redis,实现跨请求、跨进程甚至跨服务重启的记忆持久化。

代码

  1. 构建数据库记忆
# pip install redis
# session_id 识别用户   0.3版本中 redis_url 访问路径
# history = RedisChatMessageHistory(session_id="my_session_id", redis_url="redis://localhost:6379")
# langchain 1.0  url
history = RedisChatMessageHistory(session_id="my_session_id1", url="redis://localhost:6379")

运行

当此运行,显然是没问题的。

image.png

我们重新启动程序继续问上次相关的信息,发现这次记忆记住了。可见,RedisChatMessageHistory确实是持久化存储的。

image.png

RunnableWithMessageHistory

会话管理器。 它本身不存储消息,而是一个“装饰器”或“包装器”,其核心创新在于引入了session_id的概念,能够根据每次交互的上下文,动态地从你指定的存储后端(比如前两者)中获取或创建对应会话的历史记录,并将其注入到本次调用链中。它是构建多租户SaaS服务用户粘性型对话产品的利器,该设计将会话的逻辑管理存储的物理实现解耦,未来你可以轻松将存储从Redis切换到PostgreSQL或MongoDB,而业务代码几乎不变。

一个最简单的应用就是在多轮对话中可以用RunnableWithMessageHistory来隔离不同用户的记忆。

代码

RunnableWithMessageHistory

# 历史对话记忆
def get_session_history(session_id):
    return RedisChatMessageHistory(
        session_id=session_id,
        url="redis://localhost:6379",
        ttl=300  # 300秒表示5分钟过期,这个添加记忆持续时间 5分钟
    )

# 串联历史记录(记忆) 和 链
# 将对话历史自动集成到模型调用链中,解决了聊天机器人在上下文连续性
# 和多用户支持中的核心问题
chatbot_with_his = RunnableWithMessageHistory(
    # 执行流程
    chain,
    # 记忆存储的位置的函数
    get_session_history,
    input_messages_key="input",
)

会话模拟

# 模拟了两个用户的会话 ,"session_id": "用户的唯一标识"
# user123对话
resp_user123 = chatbot_with_his.invoke(
    {"input": "我是user123,我喜欢音乐,你能给我推荐一首轻音乐吗?"},
        # config 配置信息 configurable  {"session_id": "user123"} 配置的是用户的身份信息
        config={
            "configurable": {"session_id": "user123"},
        },

)
# 大模型回复
print("用户123的回答:", resp_user123)
print("==================")
# jack对话
resp_jack = chatbot_with_his.invoke(
    {"input": "我是jack,我热爱运动,篮球是我的最爱,你猜我喜欢哪个NBA球星?"},
    config={"configurable": {"session_id": "jack"}}
)
print("用户jack的回答:", resp_jack)
print("==================")
# user123再次提问
resp1_user123 = chatbot_with_his.invoke(
    {"input": "我喜欢什么?"},
    config={"configurable": {"session_id": "user123"}}
)
print("用户123的回答:", resp1_user123)

print("==================")
resp_jack1 = chatbot_with_his.invoke(
    {"input": "我喜欢什么?"},
    config={"configurable": {"session_id": "jack"}}
)
print("用户jack的回答:", resp_jack1)

分析

可见,RunnableWithMessageHistory隔离了不同用户的记忆。其核心机制:

  • get_session_history:灵魂函数。当遇到一个 session_id时,请调用我这个函数来获取对应的历史存储对象。

  • config:

    • {“configurable”: {“session_id”: “...”}}是传递会话上下文的标准方式。session_id是你的业务逻辑生成的唯一标识,可以是用户ID、设备ID、聊天线程ID等。
    • config对象会在调用链中向下传递,RunnableWithMessageHistory在关键时刻拦截它,取出 session_id并调用 get_session_history 函数。
  • 会话记忆的完全自动化管理,开发者只需关注业务和 session_id的生成。我们并不需要手动调用 add_message。在每次invokeRunnableWithMessageHistory自动执行以下流程:

    1. 根据 config中的 session_id, 通过 get_session_history拿到本次对话的专属 ChatMessageHistory对象。

    2. 将该对象中的所有历史消息取出,注入到 PromptTemplateMessagesPlaceholder(即 history变量)中。

    3. 执行基础链(LLM调用)。

    4. 调用成功后,自动将本次的“用户输入”和“AI输出”作为一组消息,追加回那个 ChatMessageHistory对象。

对照表

以下是三种方式的详细对比,帮助你快速决策:

特性维度ChatMessageHistoryRedisChatMessageHistoryRunnableWithMessageHistory
核心本质内存存储容器持久化存储实现会话管理装饰器
存储位置应用程序进程内存Redis数据库依赖传入的ChatMessageHistory实例(可以是前两者之一)
数据持久性无,进程退出即丢失有,可配置持久化策略由传入的底层存储实例决定
并发与分布式不支持,多进程/多实例间隔离支持,所有实例共享同一数据源完美支持,是处理多用户并发的推荐模式
应用场景1. 原型验证与快速测试
2. 单次、无状态的脚本任务
3. 不需要历史记录的简单工具链
1. 需要历史回溯的生产级单用户应用
2. 简单的、基于内存缓存的会话共享
1. 多用户对话服务(如客服机器人、AI社交产品)
2. 需要严格按会话隔离的复杂Agent应用
3. 未来需要灵活更换存储后端的架构
接入复杂度极低,开箱即用中等,需部署和维护Redis略高,需理解session_id的传递与管理机制

代码

github