第 05 课:记忆与流式传输
第 05 课:记忆与流式传输
配套代码仓库:https://gitee.com/wang-zeyong111/lang-chain-lecture
本课定位
第 05 课关注交互体验。一个能用的 LLM 应用,通常不仅要能回答,还要能记住当前会话,并在任务较慢时持续给用户反馈。记忆解决上下文连续性,流式传输解决等待体验和可观察性。
学习目标
- 理解短期记忆的本质。
- 使用 thread_id 隔离不同会话。
- 理解 checkpointer 的作用。
- 掌握流式传输的常见模式。
- 学会控制上下文长度。
一、记忆不是模型永久记住
很多初学者会误以为“模型记住了用户”。实际情况是:模型每次调用都是基于当前输入上下文生成结果。所谓记忆,是应用把之前的重要消息重新传给模型。
短期记忆通常保存:
- 用户之前说过的话;
- 模型之前的回答;
- 工具调用结果;
- 当前任务状态;
- 用户偏好。
二、线程级记忆
LangChain v1 中,Agent 的短期记忆通常通过 checkpointer 管理,并用thread_id区分会话。
同一个用户可以有多个线程:
thread_id="support-ticket-1001";thread_id="support-ticket-1002";thread_id="student-001-langchain";
不同线程的消息应该隔离,避免 A 用户看到 B 用户的上下文。
三、checkpointer 的作用
checkpointer 负责保存和恢复 Agent 状态。
开发阶段可以用:
fromlanggraph.checkpoint.memoryimportInMemorySaver生产阶段应使用数据库型 checkpointer,例如 Postgres。原因:
- 进程重启后内存会丢失;
- 多实例部署需要共享状态;
- 需要审计和回放;
- 需要长期保存关键会话。
四、短期记忆的风险
记忆不是越多越好。长历史会带来:
- token 成本增加;
- 响应变慢;
- 模型被旧信息干扰;
- 命中上下文窗口限制;
- 隐私和合规风险。
因此需要上下文管理策略。
五、上下文裁剪策略
常见策略:
- 只保留最近 N 轮;
- 保留系统消息和最近消息;
- 把旧对话总结成摘要;
- 删除无关工具结果;
- 对长文档只保留引用 ID;
- 根据任务动态选择相关历史。
简单聊天可以先用“最近 N 条消息”。复杂任务再引入摘要记忆和长期记忆。
六、流式传输的价值
LLM 应用常常有延迟。用户看着空白页面等待 10 秒,体验很差。流式传输可以:
- 逐步显示模型生成文本;
- 显示 Agent 正在调用哪个工具;
- 显示检索进度;
- 显示自定义状态;
- 方便开发者调试。
七、常见 stream mode
updates
用于观察 Agent 每个步骤。例如:
- 模型节点输出工具调用;
- 工具节点返回结果;
- 模型节点输出最终答案。
适合调试 Agent 进度,也适合前端展示“正在检索”“正在计算”。
messages
用于流式输出模型消息或 token。适合聊天 UI 逐字显示。
custom
用于业务自定义事件。例如:
- 已检索 3 个文档;
- 正在调用订单系统;
- 已完成 70%。
八、记忆与流式的组合
真实聊天应用通常同时需要:
- 用户打开同一个会话时能恢复上下文;
- 模型回答时逐步输出;
- Agent 调工具时展示进度;
- 工具失败时给友好提示;
- 会话结束后保存最终状态。
这时你需要把 checkpointer、thread_id 和 stream 一起设计。
九、常见坑
- 忘记传
thread_id,导致记忆无法正确隔离。 - 使用内存 checkpointer 上生产,重启就丢状态。
- 历史消息无限增长,成本和效果同时变差。
- 流式输出只展示 token,不展示 Agent 步骤,用户不知道系统在做什么。
- 工具结果太长,污染后续上下文。
十、自测清单
- 我能解释短期记忆为什么需要应用层保存。
- 我知道
thread_id的作用。 - 我知道 checkpointer 在开发和生产中的不同选择。
- 我能说出
updates和messages两种流式模式的区别。 - 我知道至少三种控制上下文长度的方法。
十一、课后练习
- 用两个 thread_id 模拟两个学生,一个喜欢类比,一个喜欢代码。
- 对同一个问题分别调用 Agent,观察是否保留各自偏好。
- 写一个
trim_messages函数,只保留最近 6 条消息。 - 如果有真实模型,使用
stream_mode="updates"观察工具调用过程。
