跳到主要内容

短期记忆

本节定位

很多人一提“Agent 记忆”,脑子里先想到“长期存档”。
但真实系统里,最先决定体验的,往往反而是短期记忆:

系统能不能稳稳记住“这次任务正在发生什么”。

这一节讲的就是这层“工作记忆”。

学习目标

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

一、短期记忆到底是什么?

1.1 一句话理解

短期记忆可以先理解成:

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

它通常包括:

  • 最近几轮对话
  • 当前任务目标
  • 已执行步骤
  • 临时中间结果

1.2 和长期记忆有什么不同?

类型关注什么
短期记忆当前这次任务要用的信息
长期记忆跨任务、跨会话仍然有价值的信息

例如:

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

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

2.1 因为上下文窗口不是无限的

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

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

2.2 信息越多,不一定越好

很多新人会觉得:

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

其实不一定。

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

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

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

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


三、短期记忆最常见的三种形态

3.1 对话窗口(sliding window)

最简单的方式:

  • 只保留最近 N 轮消息

优点:

  • 简单
  • 实现成本低

缺点:

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

3.2 运行态状态(task state)

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

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

这类状态对 Agent 特别重要。

3.3 摘要记忆(summary memory)

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

例如:

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

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


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

4.1 可运行示例

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)

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

它教你一件最本质的事:

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

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


五、但仅靠消息窗口还不够

5.1 为什么不够?

看这组对话:

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

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

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

5.2 所以 Agent 还要有结构化状态

例如:

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

print(task_state)

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

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


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

下面这个例子同时管理:

  • 最近几轮消息
  • 当前任务状态
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())

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

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

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

这在 Agent 系统里非常重要。


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

7.1 一种常见策略

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

  • 最近几轮消息原样保留
  • 更早历史压缩成摘要

7.2 一个简化版示例

old_messages = [
"用户先问了退款政策",
"之后问了证书要求",
"最后又回到退款条件"
]

summary = "用户本轮主要目标是判断自己是否满足退款条件,中间顺带问过证书。"

recent_messages = [
{"role": "user", "content": "那我学习进度 30% 还能退吗?"}
]

memory_package = {
"summary": summary,
"recent_messages": recent_messages
}

print(memory_package)

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


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

它主要解决三件事:

8.1 保持当前任务连贯性

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

8.2 让多步执行不丢状态

例如:

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

8.3 控制上下文成本

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

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

九、短期记忆最常见的失效方式

9.1 记得太少

表现:

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

9.2 记得太多

表现:

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

9.3 只存消息,不存状态

表现:

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

9.4 只存状态,不存对话原文

表现:

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

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


十、初学者最常踩的坑

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

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

10.2 以为消息窗口越大越稳

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

10.3 忽略结构化状态

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


小结

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

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

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


练习

  1. 把本节的 ShortTermMemory 扩展成支持 summary 字段。
  2. 把最大消息窗口从 3 改成 5,观察 snapshot() 输出怎样变化。
  3. 想一想:如果一个 Agent 经常忘掉“当前已经调过哪个工具”,你会优先补消息窗口,还是补结构化状态?
  4. 用自己的话解释:为什么说短期记忆解决的是“当前任务连贯性”,而不是“长期用户画像”?