跳转到内容

9.4.3 短期记忆

短期记忆上下文窗口与运行状态图

  • 理解短期记忆和长期记忆的区别
  • 理解为什么不能把整段历史无限塞给模型
  • 掌握对话窗口、运行态状态、摘要记忆三种常见短期记忆方式
  • 看懂一个简单的短期记忆管理器
  • 知道短期记忆最常见的失效方式

短期记忆可以先理解成:

系统为了完成当前这轮任务,暂时保留的上下文和中间状态。

它通常包括:

  • 最近几轮对话
  • 当前任务目标
  • 已执行步骤
  • 临时中间结果
类型关注什么
短期记忆当前这次任务要用的信息
长期记忆跨任务、跨会话仍然有价值的信息

例如:

  • “用户上一句说想查退款政策” -> 短期记忆
  • “这个用户喜欢简洁回答” -> 更像长期记忆

为什么不能把所有历史都一直塞给模型?

Section titled “为什么不能把所有历史都一直塞给模型?”

模型能看的上下文长度有限。 如果你把所有历史都不断塞进去,会遇到:

  • token 成本越来越高
  • 响应越来越慢
  • 重要信息被淹没

很多新人会觉得:

“多给模型一点历史,总不会错吧?”

其实不一定。

因为如果上下文里混了太多无关内容,模型反而更容易:

  • 抓错重点
  • 复读旧信息
  • 忘掉当前真正要做的事

所以短期记忆真正要解决的不是“记得越多越好”,而是:

在有限预算里,保留当前最有用的信息。


最简单的方式:

  • 只保留最近 N 轮消息

优点:

  • 简单
  • 实现成本低

缺点:

  • 太久之前的重要信息会被挤掉

不是只记聊天文本,而是明确记:

  • 当前任务目标
  • 已经查过什么
  • 下一步该做什么

这类状态对 Agent 特别重要。

当历史太长时,不是全丢掉,而是先压缩成摘要。

例如:

  • 保留最近 4 轮原文
  • 更早的内容压成一段总结

这是一种很常见的折中方式。


一个最简单的短期记忆:滑动窗口

Section titled “一个最简单的短期记忆:滑动窗口”
messages = [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好,我可以帮你做什么?"},
{"role": "user", "content": "我想了解退款政策"},
{"role": "assistant", "content": "你是想了解时间范围,还是具体条件?"},
{"role": "user", "content": "主要看时间范围"},
]
window_size = 3
short_term_memory = messages[-window_size:]
for msg in short_term_memory:
print(msg)

预期输出:

Terminal window
{'role': 'user', 'content': '我想了解退款政策'}
{'role': 'assistant', 'content': '你是想了解时间范围,还是具体条件?'}
{'role': 'user', 'content': '主要看时间范围'}

这段代码虽然简单,但已经很重要

Section titled “这段代码虽然简单,但已经很重要”

它教你一件最本质的事:

短期记忆最先是一个“保留哪些消息”的选择问题。

不是所有历史都值得继续带着走。


看这组对话:

  1. 用户说“我想查退款政策”
  2. 后面又连续问了几轮别的细节
  3. 到第 10 轮又问“那我这种情况能退吗?”

如果你只保留最近 3 轮,系统可能已经忘了:

  • 当前任务其实一直围绕“退款”

例如:

task_state = {
"goal": "帮助用户判断退款条件",
"last_tool": "search_policy",
"latest_policy_result": "购买后 7 天内且学习进度低于 20% 可退款"
}
print(task_state)

预期输出:

Terminal window
{'goal': '帮助用户判断退款条件', 'last_tool': 'search_policy', 'latest_policy_result': '购买后 7 天内且学习进度低于 20% 可退款'}

这类状态和原始聊天记录不同,它更像:

系统正在干什么的工作区。


一个更有教学意义的短期记忆管理器

Section titled “一个更有教学意义的短期记忆管理器”

下面这个例子同时管理:

  • 最近几轮消息
  • 当前任务状态
class ShortTermMemory:
def __init__(self, max_messages=4):
self.max_messages = max_messages
self.messages = []
self.state = {}
def add_message(self, role, content):
self.messages.append({"role": role, "content": content})
self.messages = self.messages[-self.max_messages:]
def update_state(self, **kwargs):
self.state.update(kwargs)
def snapshot(self):
return {
"messages": self.messages,
"state": self.state
}
memory = ShortTermMemory(max_messages=3)
memory.add_message("user", "我想查退款政策")
memory.add_message("assistant", "你更关心时间范围还是条件?")
memory.add_message("user", "先看时间范围")
memory.update_state(goal="判断退款资格", topic="退款政策")
print(memory.snapshot())

预期输出:

Terminal window
{'messages': [{'role': 'user', 'content': '我想查退款政策'}, {'role': 'assistant', 'content': '你更关心时间范围还是条件?'}, {'role': 'user', 'content': '先看时间范围'}], 'state': {'goal': '判断退款资格', 'topic': '退款政策'}}

短期记忆 snapshot 结果图

这个例子真正比“只存历史消息”强在哪里?

Section titled “这个例子真正比“只存历史消息”强在哪里?”

因为它把短期记忆拆成了两层:

  • 文本上下文
  • 结构化状态

这在 Agent 系统里非常重要。


摘要记忆:当消息越来越长怎么办?

Section titled “摘要记忆:当消息越来越长怎么办?”

真实系统很常见这种做法:

  • 最近几轮消息原样保留
  • 更早历史压缩成摘要
old_messages = [
"用户先问了退款政策",
"之后问了证书要求",
"最后又回到退款条件"
]
summary = "用户本轮主要目标是判断自己是否满足退款条件,中间顺带问过证书。"
recent_messages = [
{"role": "user", "content": "那我学习进度 30% 还能退吗?"}
]
memory_package = {
"summary": summary,
"recent_messages": recent_messages
}
print(memory_package)

预期输出:

Terminal window
{'summary': '用户本轮主要目标是判断自己是否满足退款条件,中间顺带问过证书。', 'recent_messages': [{'role': 'user', 'content': '那我学习进度 30% 还能退吗?'}]}

这就是最基本的“摘要 + 最近窗口”的思路。


短期记忆在 Agent 里到底解决什么?

Section titled “短期记忆在 Agent 里到底解决什么?”

它主要解决三件事:

系统不能每一步都像第一次见到用户那样重新开始。

例如:

  • 已经调用了哪个工具
  • 已经查到了什么
  • 还差哪一步

短期记忆不是只为“记住”,也是为了:

  • 少塞无关内容
  • 降低 token 成本
  • 提高响应稳定性

表现:

  • 系统突然忘了刚才正在说什么

表现:

  • 上下文又长又乱
  • 回答跑偏
  • 成本变高

表现:

  • 多步任务容易掉链子
  • 工具调用前后状态衔接差

表现:

  • 容易丢掉用户原始表达
  • 语气、约束和细节缺失

所以短期记忆通常不是“只选一种”,而是组合设计。


把短期记忆和长期记忆混在一起

Section titled “把短期记忆和长期记忆混在一起”

短期记忆解决的是当前任务,不是用户画像全集。

窗口太大也会带来噪声和成本。

这会让 Agent 一到多步任务就开始发飘。


学完这一页,至少保留这张证据卡:

记忆类型
短期、长期、情景或程序性
写入规则
在内存创建或更新时
检索规则
查询、相关性、时效性和权限检查
失败检查
过时记忆、隐私泄漏、矛盾或过度检索
清理动作
总结、合并、过期、删除或请求确认

这一节最重要的不是记住“窗口”“摘要”这些词,而是抓住这条主线:

短期记忆的目标,不是无限保留历史,而是在有限上下文里维持当前任务的连贯性。

真正设计得好的短期记忆,通常既包含最近消息,也包含任务状态,有时还会加一层摘要压缩。


  1. 把本节的 ShortTermMemory 扩展成支持 summary 字段。
  2. 把最大消息窗口从 3 改成 5,观察 snapshot() 输出怎样变化。
  3. 想一想:如果一个 Agent 经常忘掉“当前已经调过哪个工具”,你会优先补消息窗口,还是补结构化状态?
  4. 用自己的话解释:为什么说短期记忆解决的是“当前任务连贯性”,而不是“长期用户画像”?
参考实现与讲解
  1. summary 字段可以把较早轮次压缩成当前任务摘要,而原始 message window 继续保留最近细节。
  2. 把窗口从 3 改成 5 后,snapshot() 会保留更多最近消息,可能提升连贯性,但也会增加噪声和 token 成本。
  3. 如果 Agent 忘记已经调用过哪些工具,应优先加 structured state。单纯扩大 message window 更弱,也更贵。
  4. 短期记忆解决的是当前任务连贯性:目标、约束、最近修正、工具结果和下一步动作。它不应该变成永久用户画像。