第5课:短期记忆与长期记忆原理 - 学习笔记_5

第5课:短期记忆与长期记忆原理 - 学习笔记

📚 课程核心主题

本节课通过实际代码演示,深入讲解短期记忆和长期记忆的工作原理,包括消息加载机制、消息修剪、工具调用等核心概念。


🎯 第一部分:短期记忆的工作原理

短期记忆的消息加载机制

什么是消息加载?

是什么:

  • 每次对话时,系统会从存储中自动加载历史消息
  • 这些历史消息会被放入**上下文(Context)**中
  • AI模型利用这些上下文信息来回答问题

工作原理:

  1. 开启短期记忆能力:通过Checkpoint(检查点)机制
  2. 每次对话时:从存储中加载历史信息
  3. 放入上下文:将历史消息放入对话窗口
  4. 利用上下文:AI根据历史信息回答问题

关键点:

  • 短期记忆是**线程级别(Thread-level)**的
  • 同一个线程ID(thread id)下的所有对话都会被加载
  • 不同线程之间的消息是隔离的

实际演示:消息记录示例

演示场景

场景设置:

  • 使用线程ID = 1 进行多轮对话
  • 观察消息是如何累积和加载的

对话过程:

  1. 第一轮对话:

    • 用户:"我叫什么?"
    • AI:"我无法知道你的真实姓名"
    • 消息数:2条
  2. 第二轮对话:

    • 用户:"我是KEVIN"
    • AI:"你好,KEVIN"
    • 消息数:4条(包含前一轮的2条)
  3. 第三轮对话(同一线程):

    • 用户:"我叫什么?"
    • 系统自动加载了前4条消息
    • AI:"你刚才告诉我你的名字是KEVIN"
    • 消息数:6条(前4条历史 + 当前2条)
  4. 切换到新线程(thread id = 2):

    • 用户:"我叫什么?"
    • 只加载当前线程的消息(2条)
    • AI:"我无法知道你的真实姓名"
    • 验证了短期记忆是线程级别的

关键理解:

  • 同一线程内的所有消息都会被加载
  • 切换线程后,之前的消息不会被加载
  • 消息数量会随着对话轮数增加而累积

🔧 第二部分:消息修剪(Message Pruning)

为什么需要消息修剪?

问题:

  • 随着对话增加,历史消息会越来越多
  • 如果每次都加载所有消息,会导致:
    • ❌ Token消耗过大
    • ❌ 上下文长度超限
    • ❌ 性能下降
    • ❌ 成本增加

实际场景:

  • 预订酒店时,不需要加载之前所有关于"我叫什么"的对话
  • 只需要加载与当前任务相关的消息

消息修剪的实现方式

两种修剪方案

方案1:按消息条数修剪

  • 只保留最近的N条消息
  • 例如:只保留最近4条消息
  • 从最新的消息开始,向前取4条

方案2:按Token数量修剪

  • 只保留最近生成的N个Token的消息
  • 例如:只保留最近100个Token
  • 根据Token大小动态调整
代码实现

关键代码结构:

python 复制代码
# 在Agent前加入一个节点
# 绑定回调函数进行消息修剪

def trim_messages(messages):
    # 根据消息条数修剪
    # 只保留最近4条消息
    return messages[-4:]

实际效果演示:

场景: 限制只加载最近4条消息

结果:

  • 之前有11条消息(包括"我叫KEVIN"的信息)
  • 修剪后只加载最近4条(都是关于预订酒店的)
  • 用户问:"我叫什么?"
  • AI回答:"我不知道你的名字"
  • 因为"我叫KEVIN"的消息被修剪掉了

关键点:

  • 修剪是在检索之后、放入上下文之前进行的
  • 检索还是会检索所有消息
  • 但放入上下文时,只放入修剪后的消息

🛠️ 第三部分:工具调用(Tool Calling)详解

工具调用的完整流程

实际演示:预订酒店

用户输入: "预定一个汉庭酒店"

Agent处理流程:

  1. 意图识别

    • AI识别用户想要预订酒店
    • 判断需要调用工具
  2. 工具调用

    • 调用 book_hotel 工具函数
    • 从用户输入中提取参数(酒店名称:汉庭酒店)
  3. 工具执行

    • 工具函数执行预订逻辑
    • 返回执行结果:"成功预定了汉庭酒店"
  4. 结果返回

    • 工具返回的消息(Tool Message)给到Agent
    • Agent将结果给到AI模型
    • AI生成最终回复:"成功为您预订了汉庭酒店,如果有其他需要随时告诉我,KEVIN"

关键理解:

  • 工具调用是自动的,AI会自动判断何时需要调用工具
  • 工具返回的消息也会被记录到历史消息中
  • 整个流程是ReAct架构的体现(推理 + 行动)

💾 第四部分:长期记忆的工作原理

为什么需要长期记忆?

实际需求场景

场景: 用户偏好设置

  • 用户说:"我要预订酒店,需要有窗户,有WIFI"
  • 这是用户的个人偏好
  • 希望以后每次预订酒店时,都能自动应用这个偏好

问题:

  • ❌ 用短期记忆无法实现
  • ❌ 短期记忆只在当前会话有效
  • ❌ 关闭会话后,偏好就丢失了

解决方案: 使用长期记忆


长期记忆的实现方式

存储结构

两个存储实例:

  1. 短期记忆存储(Checkpointer)

    • 使用 PostgresSaverRedisSaver
    • 存储当前线程的对话历史
    • 通过 thread_id 区分不同会话
  2. 长期记忆存储(Store)

    • 使用 PostgresStoreRedisStore
    • 存储用户的偏好、跨会话信息
    • 通过 user_id 区分不同用户

关键区别:

特性 短期记忆(Checkpointer) 长期记忆(Store)
区分标识 thread_id(线程ID) user_id(用户ID)
存储内容 当前会话的对话历史 用户偏好、跨会话信息
生命周期 会话级别 用户级别
类名 PostgresSaver PostgresStore

长期记忆的存储和检索

存储用户偏好

步骤1:提取用户偏好

python 复制代码
# 从用户输入中提取偏好信息
user_preferences = {
    "住宿偏好": "有窗户,有WIFI"
}

步骤2:存储到长期记忆

python 复制代码
# 使用 store.put() 存储
store.put(
    user_id="1",
    data=user_preferences
)

步骤3:检索用户偏好

python 复制代码
# 使用 store.search() 检索
preferences = store.search(
    user_id="1"
)
实际演示流程

场景:跨会话使用偏好

  1. 会话1(thread_id = 4):

    • 用户:"我要预订酒店,需要有窗户,有WIFI"
    • 系统提取偏好并存储到长期记忆
    • 数据库中的 store 表新增2条记录
  2. 会话2(thread_id = 5,新会话):

    • 用户:"帮我预订酒店"
    • 系统从长期记忆中检索用户偏好
    • 检索到:"有窗户,有WIFI"
    • AI回复:"已成功为您预订了汉庭酒店,并确保房间有窗户和WIFI"

关键点:

  • ✅ 即使切换了线程(thread_id不同),也能检索到偏好
  • ✅ 因为长期记忆是基于 user_id 的,不是 thread_id
  • ✅ 实现了"让AI更懂你"的目标

🔑 第五部分:关键概念对比

短期记忆 vs 长期记忆

特性 短期记忆 长期记忆
存储类 PostgresSaver / RedisSaver PostgresStore / RedisStore
区分标识 thread_id(线程ID) user_id(用户ID)
存储内容 当前会话的对话历史 用户偏好、跨会话信息
作用范围 当前会话线程 跨所有会话
生命周期 会话关闭后可能丢失 持久保存
用途 保持对话上下文连贯 个性化服务、用户偏好
类比 RAM(内存) 硬盘(Disk)

⚙️ 第六部分:生产环境的考虑

线程ID的动态生成

问题:

  • 在实际生产中,thread_id 不能写死
  • 每次新建会话时,应该动态生成新的 thread_id

正确做法:

python 复制代码
# 每次新建会话时生成新的thread_id
thread_id = generate_new_thread_id()  # 动态生成
user_id = get_current_user_id()        # 从会话中获取

类比理解:

  • thread_id 类似于 Web 开发中的 session_id
  • 每个会话都有唯一的标识
  • 用于区分不同的对话会话

存储方案选择

PostgreSQL vs Redis

PostgreSQL(关系型数据库):

  • ✅ 适合一般场景
  • ✅ 数据持久化可靠
  • ✅ 适合中小型应用

Redis(内存数据库):

  • 高并发场景推荐
  • ✅ 每秒10万级以上的高吞吐
  • ✅ 支持数据过期设置(TTL)
  • ✅ 可以设置哪些信息过期,哪些永不过期

选择建议:

  • 如果并发量特别大,建议用 Redis
  • 一般情况用 PostgreSQL 也可以
  • 短期记忆和长期记忆可以分开存储(一个用PostgreSQL,一个用Redis)

💡 关键概念总结

概念 简单理解
消息加载 每次对话时,从存储中加载历史消息到上下文
消息修剪(Pruning) 过滤历史消息,只保留相关的消息放入上下文
线程级别(Thread-level) 短期记忆只在当前会话线程中有效
用户级别(User-level) 长期记忆跨所有会话,基于用户ID
Checkpointer 短期记忆存储,使用Saver类
Store 长期记忆存储,使用Store类
工具调用(Tool Calling) AI自动判断并调用外部工具完成操作
ReAct架构 推理(Reasoning)+ 行动(Acting)的Agent架构
动态thread_id 生产环境中,每次会话动态生成线程ID

❓ 思考题

  1. 为什么需要消息修剪?

    • 答:随着对话增加,历史消息会越来越多,如果每次都加载所有消息,会导致Token消耗过大、上下文长度超限、性能下降。通过消息修剪,只保留与当前任务相关的消息,可以提高效率。
  2. 短期记忆和长期记忆在存储上有什么区别?

    • 答:短期记忆使用Checkpointer(Saver类),通过thread_id区分不同会话;长期记忆使用Store类,通过user_id区分不同用户。短期记忆存储对话历史,长期记忆存储用户偏好和跨会话信息。
  3. 为什么切换线程后,AI就不记得之前的信息了?

    • 答:因为短期记忆是线程级别的,只加载当前thread_id的消息。切换线程后,thread_id变了,系统只会加载新线程的消息,不会加载旧线程的消息。
  4. 如何实现跨会话的用户偏好?

    • 答:使用长期记忆(Store),将用户偏好存储到数据库中,基于user_id而不是thread_id。这样无论用户开启多少个新会话,都能检索到之前存储的偏好信息。
  5. 生产环境中,为什么thread_id不能写死?

    • 答:因为每个会话都需要唯一的标识。如果写死,所有用户都会共享同一个thread_id,导致消息混乱。应该每次新建会话时动态生成新的thread_id,类似于Web开发中的session_id。

📌 本节课重点回顾

短期记忆工作原理: 每次对话时自动加载历史消息到上下文,是线程级别的

消息修剪机制: 通过限制消息条数或Token数量,只保留相关消息,提高效率

工具调用流程: 意图识别 → 工具调用 → 工具执行 → 结果返回,是ReAct架构的体现

长期记忆实现: 使用Store类存储用户偏好,基于user_id实现跨会话记忆

存储方案选择: PostgreSQL适合一般场景,Redis适合高并发场景,可以分开使用

生产环境注意: thread_id必须动态生成,不能写死


笔记整理时间:2024年
建议:理解短期记忆和长期记忆的区别是构建智能AI系统的关键,需要通过实际代码演示加深理解

相关推荐
像风一样自由2 小时前
android native 中的函数动态注册方式总结
android·java·服务器·安卓逆向分析·native函数动态注册·.so文件分析
星期五不见面2 小时前
机器人学习!(二)ROS-基于Gazebo项目(2)2026/01/12
学习·机器人
兮动人2 小时前
Maven指定加载的类
java·maven·maven指定加载的类
日更嵌入式的打工仔2 小时前
嵌入式系统设计师软考个人笔记<1>
笔记
Freshman小白2 小时前
《智能制造系统》网课答案
学习·答案·网课答案
副露のmagic3 小时前
更弱智的算法学习 day34
python·学习
wangkay883 小时前
【Java 转运营】Day04:抖音新号起号前准备全指南
java·开发语言·新媒体运营
亲爱的非洲野猪3 小时前
Java线程池深度解析:从原理到最佳实践
java·网络·python
写点什么呢3 小时前
AD21安装激活
学习