大模型学习笔记:LangChain核心组件-记忆(memory)

文章目录

一、 记忆(memory)

模型本身是没有记忆的,它记不住历史的会话内容,我们需要通过技术手段,帮助模型记住会话历史,产生记忆。

LLM 本身是 无状态(stateless) 的:

-每次对话都是独立请求

  • 上下文窗口有限(如 4k/8k/32k tokens)

  • 跨轮、跨会话、跨天会 彻底遗忘 用户偏好、历史决策、事实细节

  • 无记忆代码演示

python 复制代码
from langchain_community.chat_models import ChatTongyi
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage

model = ChatTongyi(
    model="qwen-turbo"  # 轻量免费模型,速度快
)

agent = create_agent(
    model=model,
    tools = [],
    system_prompt = "你是友好的助手,正常回应用户即可"
)



res = agent.invoke(
    {
        "messages":[
            HumanMessage(
                content="你好,我叫三爷,我喜欢老鹰"
            )
        ]
    }
)

print(res["messages"][-1].content)

res = agent.invoke(
    {
        "messages":[
            HumanMessage(
                content="你好,我是谁?"
            )
        ]
    }
)

print(res["messages"][-1].content)

运行结果

python 复制代码
你好,三爷!老鹰确实很帅气,很有力量感。你是不是也喜欢看它们在天空中翱翔?我特别佩服它们的视野和自由。你平时都做些什么来和老鹰有关呢?
你好!我是你的助手,但我还不知道你的名字。你可以告诉我你的名字,这样我们就能更亲切地交流了!😊

进程已结束,退出代码为 0
为什么要有 "记忆"?

对于Agent而言,记忆至关重要,因为它能让代理记住之前的交互情况,从反馈中学习,并适应用户的偏好。随着代理处理的任务愈发复杂,涉及的用户交互也越来越多,这种能力对于提高效率和用户满意度而言变得不可或缺。

  • 多轮连贯对话
  • 跨会话个性化
  • 长任务推理(订机票、写论文、项目管理)
  • 从历史中学习、反思、优化策略

二、记忆的核心分类

对于智能体而言,记忆分为了两类:

  • 短期记忆(short-term memory)

  • 长期记忆(long-term memory)

注意,大家不要被字面上的意思误导了,很多人看到名字就误以为:短期记忆就是临时记忆,断电就没了;长期记忆就是永久记忆,持久保存。

对于智能体而言,这是完全错误的理解!!!

简单用一句话概括的话:

  • 短期记忆:当前任务或会话的上下文(Working Memory 或 Session Memory)
  • 长期记忆:跨任务或会话的经验与知识(Persistent Memory)

比如,一个公司数据分析的Agent。

用户提出需求:

"帮我写Q1的销售分析报告"

Agent:


短期记忆:

  • 对话历史
  • 查询到Q1的销售数据
  • 任务目标及执行状态

长期记忆:

  • 公司的KPI算法
  • 用户偏好的报告形式
总结
短期记忆 长期记忆
生命周期 当前会话(短暂) 跨任务、跨会话(永久)
内容 当前任务状态 知识、经验、用户偏好
是否跨任务
存储 Redis/内存 DB/Vector DB

三、短期记忆

由于短期记忆通常生命周期是当前会话,所以我们也可以称为会话记忆。Agent的会话记忆通常包含三部分:

  • 对话历史
  • 查询结果
  • 任务状态

对于简单的Agent来说,任务没有做拆分,也就不需要记录任务状态,只用考虑会话历史和查询结果就可以了。后续我们会学习如何自定义更复杂的Agent会话记忆。

LangChain提供了自动化的记忆管理方案:

  • 首先,LangChain把会话记忆(也就是Messages列表)记录为AgentState的一部分

  • AgentState通过Checkpointer对象来保存,每一次与AI的交互都会生成一个快照,记录为一个checkpoint,把同一会话的所有checkpoint组合在一起,就是完整的会话历史了。

  • 为了区分不同的会话记忆,不同会话需要设定各自的thread_id,相同会话则使用相同thread_id

  • 向Agent发起会话时必须指定自己的thread_id以唤起对应的会话记忆

基于内存的对话持久化

  • 导入Checkpointer

  • 创建Agent,指定Checkpointer

  • 调用Agent,指定thread_id

  • 示例代码

python 复制代码
from langchain_community.chat_models import ChatTongyi
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import InMemorySaver

# 1. 指定模型
model = ChatTongyi(model="qwen-turbo")

# 2. 创建Agent
agent = create_agent(
    model=model,
    tools=[],
    checkpointer=InMemorySaver(),
    system_prompt="你是亲切友好的聊天助手,记住用户称呼和爱好,自然对话"
)

# 3. 会话 ID(不同 ID 不同对话)
config = {"configurable": {"thread_id": "user_001"}}


# -------- 第一轮 --------
res1 = agent.invoke(
    {"messages": [HumanMessage(content="你好,我叫张三,我喜欢小狗")]},
    config
)
print("第一轮:", res1["messages"][-1].content)

# -------- 第二轮(自动记住名字+爱好) --------
res2 = agent.invoke(
    {"messages": [HumanMessage(content="我是谁?我喜欢什么?")]},
    config
)
print("第二轮:", res2["messages"][-1].content)

运行效果

python 复制代码
第一轮: 你好,张三!很高兴认识你!你喜欢小狗啊,真是太棒了!小狗们总是那么可爱、忠诚,能给人带来很多快乐。你有没有养过小狗呢?或者有没有特别喜欢的小狗品种?我超喜欢和喜欢小狗的人聊天,我们可以分享很多有趣的故事哦!
第二轮: 你好,张三!你之前告诉我你喜欢小狗呢!还记得吗?你是一个很温暖的人,喜欢可爱的小动物,特别是小狗。它们一定给你带来了很多快乐吧?我真的很喜欢和你聊天,感觉你很亲切呢!

进程已结束,退出代码为 0

四、长期记忆

长期记忆让你的智能体能够在不同的对话和会话中存储和调用信息。与仅限于单个线程的短期记忆不同,长期记忆能够跨线程持久存在,并且可以随时调用。

长期记忆建立在LangGraph存储上,这些存储将数据保存为由命名空间和键组织的JSON文档。

每个内存条目都组织在一个自定义命名空间(类似于文件夹)和一个唯一键(类似于文件名)下。命名空间通常包含用户或组织ID或其他标签,以便更容易地组织信息。

这种结构实现了内存的分层组织。然后通过内容过滤器支持跨命名空间的搜索。

实际开发中,可以使用数据库存储

示例代码

python 复制代码
from dataclasses import dataclass
from langchain_community.chat_models import ChatTongyi
from langchain.agents import create_agent
from langchain.tools import ToolRuntime, tool
from langgraph.store.memory import InMemoryStore

# 定义上下文结构
@dataclass
class Context:
    user_id: str

# 长期记忆存储
store = InMemoryStore()

# 模型
model = ChatTongyi(model="qwen-turbo")

# 写入用户信息
store.put(
    ("users",),
    "user_123",
    {
        "name": "三爷",
        "hobby": "老鹰",
    },
)

# 工具:官方标准写法
@tool
def get_user_info(runtime: ToolRuntime[Context]) -> str:
    """获取用户的姓名和爱好信息。"""
    user_id = runtime.context.user_id
    user_info = runtime.store.get(("users",), user_id)
    return str(user_info.value) if user_info else "Unknown user"



# 创建 Agent(官方最新)
agent = create_agent(
    model=model,
    tools=[get_user_info],
    store=store,
    context_schema=Context,
)

# 用户id
context=Context(user_id="user_123")

# 运行(带 context!)
res = agent.invoke(
    {
        "messages": [{
            "role": "user",
            "content": "我是谁?我的爱好是什么?"
        }]
    },
    context=context,
)

# 输出最终回复
print("AI 回复:", res["messages"][-1].content)

运行结果

python 复制代码
AI 回复: 你叫三爷,你的爱好是老鹰。

进程已结束,退出代码为 0

五、记忆管理策略

由于会话记忆要保存会话的历史,并且在调用LLM时携带历史消息列表。而当会话越来越长时,历史消息就可能超过LLM的上下文限制。例如,DeepSeek的上下文不能超过128K.

一旦会话历史超过上下文窗口,就会出现上下文丢失的情况,从而导致丢失记忆。而且即便不丢失,太长的上下文容易让模型出现"注意力分散"问题,模型的响应速度、回答质量会大大降低。

未来解决这一问题,通常有以下几种手段:

1 修剪消息

修剪消息并不是真正的删除消息,在AgentState中的消息列表依然是完整的,只不过发送给LLM之前会进行修剪,只保留一部分消息。

2 删除消息

删除消息与修剪不同:

  • 修剪消息:只是从State中选取一部分消息发送给模型
  • 删除消息:直接删除State中保存的消息,也就是说消息历史中不再存在!
3 总结消息

不管是修剪还是删除,都会导致一部分消息丢失,从而丢失记忆。所以就有了第三种策略:总结消息(Summarize Messages)

它的思路很简单,就是把历史的消息利用大模型总结出摘要,然后把最新的消息拼接在一起作为新的消息列表发送给大模型,这样既不会超出模型的上下文窗口限制,还能尽量保留所有的记忆。

相关推荐
jieyucx1 小时前
Go语言通透教程:结构体定义与方法
服务器·数据库·golang·结构体
m0_690825822 小时前
c++ RAII机制详解 c++如何利用RAII管理资源
jvm·数据库·python
JunLa2 小时前
L angGraph vs 链式调用
java·网络·数据库
DianSan_ERP2 小时前
抖店订单接口中消费者信息加密解密机制与安全履约全解析
前端·网络·数据库·后端·安全·团队开发·运维开发
爱码小白2 小时前
MySQL运维篇
大数据·数据库·python
wang3zc2 小时前
HTML函数能否用外接显卡坞提升性能_eGPU对HTML函数帮助【汇总】
jvm·数据库·python
難釋懷2 小时前
Redis网络模型-Redis是单线程的吗?为什么使用单线程
网络·数据库·redis
2301_781571422 小时前
mysql如何配置自增ID预留_mysql innodb_autoinc_lock_mode参数
jvm·数据库·python
解决问题no解决代码问题2 小时前
Quartz 1.6.5
数据库·servlet·oracle