← 返回教程库

Agent 为什么"健忘"——无状态的根本问题

最后更新 2026-06-25
你将学到
  • 说清楚大语言模型"无状态"的机制含义,而不只是记住结论
  • 理解"Agent 记忆"的真实来源:人工把历史拼回上下文
  • 列举上下文有限导致的三类健忘场景,对号入座自己踩过的坑
  • 明白为什么"全量塞进去"不可行,引出三层记忆体系的必要性

Agent 健忘不是 bug,是模型本质无状态——每次调用它只看你这次传进去的上下文,根本不知道"上次"发生了什么。 所谓"记忆",从来不是模型自己存着的,而是你把历史对话手动塞回去它才能"记得"。上下文窗口有限,塞不下所有历史,于是只能取舍——这就是健忘的根。

这一节专门把这件事讲透。讲透了,你才能理解后面三层记忆体系(4.2 节)在解决什么问题,而不是背一套名词。


模型到底有多"无状态"

先拆掉一个直觉上的误区:很多人用 ChatGPT 对话时觉得它"记得"前面说的话,就以为大语言模型有某种内置记忆。

没有。 那个"记得"是产品层做的,不是模型本身的能力。

从模型角度看,每一次 API 调用都是一次全新的计算:

输入 = [系统提示] + [你拼进来的所有历史消息] + [这次的用户消息]
输出 = 下一条消息

这次调用结束,模型不会在任何地方存一个"我刚才处理过什么"的状态。下次调用,还是一张白纸,只看你这次传过来的东西。

用一个比喻:模型像一个极其博学、但每次都失忆的顾问。你每次找他开会,必须把之前所有会议纪要带来,摆在他面前,他才能接着谈。你带来什么,他知道什么;你忘记带,他就不知道。

这不是临时工状态,是设计本质——Transformer 架构的推理过程是无副作用的前向计算,不会自动把本次计算结果写回某个"持久化状态"里。


"记忆"的真实来源:人工拼历史

理解了无状态,"记忆"的意思就清楚了:

你在 Agent 里看到的一切"记忆",本质上都是在调用前把历史内容拼进本次上下文。

最朴素的实现,就是维护一个 messages 列表,每轮追加:

messages = []

# 第 1 轮
messages.append({"role": "user", "content": "我叫李伟,帮我写份周报"})
response = call_llm(messages)
messages.append({"role": "assistant", "content": response})

# 第 2 轮——模型"记得"你叫李伟,是因为第 1 轮还在列表里
messages.append({"role": "user", "content": "加上本周完成了 3 个需求"})
response = call_llm(messages)  # 此刻传入 3 条消息

第 2 轮调用时,模型"知道"你叫李伟,不是因为它记得,而是因为第 1 轮的消息还躺在 messages 列表里被一起传了进去。

这个机制本身没有问题——只要历史不超限,它工作得很好。问题出在历史总会超限


上下文窗口:Health Bar 有上限

每个模型都有上下文窗口限制,单位是 token(大致理解:1 个汉字约 1-2 个 token,1 个英文单词约 1-2 个 token)。窗口大小因模型而异,以官方最新文档为准。

一轮对话消耗的 token 数大致是:

系统提示(几百到几千)
+ 所有历史消息(无上限积累)
+ 工具定义(每个工具 schema 几十到几百)
+ 本次用户输入
+ 模型输出(预留空间)
= 总计

历史消息随着对话轮数线性增长。一个在真实任务里跑几十步的 Agent,光历史消息就能轻松吃掉数万 token。窗口满了怎么办?只能截断——要么切掉最早的,要么用摘要压缩,要么策略性丢弃——无论哪种,信息都会有损失

有损失就有健忘。


健忘带来的三类真实问题

光说原理不够,看三个具体场景。

场景一:跨会话忘记用户是谁

用户 A 今天和 Agent 聊了半小时,告诉它自己是产品经理、负责 B2B 系统、最近在做权限模块。第二天再打开,Agent 完全不认识他——因为昨天的 messages 列表没有被持久化,或者这是一个全新的 API 会话,历史从零开始。

用户:"你还记得我昨天说我在做权限模块吗?"
Agent:"您好,我是 XX 助手,很高兴认识您……"

不是 Agent 傻,是历史没有传进去。

场景二:长任务忘记早期决定

Agent 执行一个 30 步的代码生成任务。前 5 步确定了技术选型(用 PostgreSQL,不用 MySQL;API 用 REST,不用 GraphQL)。执行到第 25 步,上下文窗口撑不住了,早期的选型讨论被截断——Agent 开始按自己的默认偏好生成代码,突然出现了 MySQL 的连接字符串。

你回头排查半天,才发现是上下文截断导致"忘记了约定"。

场景三:重复问同样的事

Agent 在一个长对话里已经确认过"用户的时区是 UTC+8",但因为这条信息在上下文里被压缩或截断了,它在第 20 步又一次问:"请问您所在的时区是?"用户已经告诉过它两次了。

这三类问题背后是同一个根:信息超出窗口后,模型的视野里就不存在了,和从没说过一样。


为什么不能靠"全塞进去"解决

最直觉的解法:窗口不够大,那就买更大的窗口,或者把所有历史全塞进去。

这个思路有三个硬墙:

1. token 成本不线性,会爆炸

假设一次对话平均 500 token/轮,聊了 100 轮,历史就有 50,000 token。如果每轮调用都要传入全部历史,第 100 轮单次调用就要发 50,000 token——而 token 费用通常按输入量计费。随着对话轮数增加,成本以二次方速度增长,不是线性。

2. 注意力被稀释,早期信息实际被忽略

研究和工程实践都发现:当上下文极长时,模型对中间段的内容注意力会显著下降(著名的"大海捞针"问题)。把 100 轮历史全塞进去,并不等于模型真的"平等看待"每一条——早期的关键决定,往往比你预期的更容易被忽略。

3. 超限就是硬报错

窗口是硬上限,不是软警告。超了就报错,或者直接截断——你不处理的话,轻则输出质量下降,重则整个调用失败。就算现在某些模型把窗口做到百万 token,也只是把极限推远,没有消除极限。

结论:你没法无限扩容来解决健忘,只能接受窗口有限的现实,转而想"怎么聪明地选择放什么进去"。


引出解法:三层记忆体系

面对无状态的本质约束,工程上的解法不是蛮力扩容,而是分层管理记忆——像人脑一样,什么放工作记忆、什么存长期记忆、什么做语义检索。

一个典型的三层设计:

存什么 放哪里 如何进上下文
短期记忆 当前会话的近 N 轮对话 内存里的 messages 列表 直接追加
结构化长期记忆 用户偏好、实体信息、已做决定 数据库 / 文件 每次召回相关字段注入系统提示
向量化语义记忆 历史文档、知识库、长对话摘要 向量数据库 语义检索相关片段插入上下文

三层各司其职,让进入上下文的每一个 token 都"物尽其用",而不是把所有东西都堆进去再听天由命。

具体每层怎么实现、怎么取舍,看下一节 三层记忆架构:短期、向量与结构化(4.2 节)。

如果你还想了解从上下文长度角度怎么做工程优化,上下文工程:管 Token,防飘移 这节(编程 L4)讲得很细,值得配合读。


小示意:无状态循环

光文字可能还有点抽象,用一个最简的示意描述 Agent 每轮调用的实际发生:

第 1 轮 ─── 发给模型 ───▶ [系统提示] + [用户消息①]
           ◀─ 返回 ────── [助手回复①]

第 2 轮 ─── 发给模型 ───▶ [系统提示] + [用户消息①] + [助手回复①] + [用户消息②]
           ◀─ 返回 ────── [助手回复②]

第 N 轮 ─── 发给模型 ───▶ [系统提示] + [全部历史(1..N-1)] + [用户消息N]
                            ↑
                     这里越来越长,最终撑不住

模型每次都是独立计算,没有任何内部状态在轮次间传递。"记忆"就是这条越来越长的输入序列本身。

这也解释了为什么 文件级记忆 Hermes Agent 的思路是把关键信息写到文件里——文件不受单次上下文限制,需要时再读进来,本质是把记忆移出了上下文窗口,存到外部持久化存储。


常见问题

Q:模型厂商不是在做更大的上下文窗口吗,以后够大了这个问题不就消失了?

窗口变大是好事,但消除不了问题。首先,成本不会消失——更大的窗口通常意味着更贵的输入费用;其次,注意力稀释问题依然存在,塞得越满,早期信息的实际影响力越低;再者,"所有历史"的增速可以超过任何固定窗口——系统运行时间够长、对话够多,总会有超出去的那一天。窗口扩容是缓解,不是根治。

Q:短对话里 Agent 表现很好,长对话就开始犯浑,是质量下降了吗?

质量没变,是上下文被截断或压缩了。检查一下你的 Agent 实现里是否有历史截断逻辑——如果有,截断了哪些内容,截断策略是否丢掉了关键信息。很多"长对话质量下降"的 bug 都是这个原因。

Q:我用的是封装好的框架,自动管理了 memory,还需要理解这些吗?

需要。框架帮你做了实现,但策略判断还是你的——何时清理历史、用什么摘要策略、哪些信息要持久化、向量检索的召回数设多少——这些都是你在配置框架时要做的决策。不理解底层机制,配置参数就是盲调。

Q:我需要 Agent 记住用户上次说的话,最简单的方案是什么?

messages 列表序列化存到文件或数据库,下次会话时加载回来接着用。这是最直接的"跨会话记忆"实现,适合用户少、对话轮数不多的场景。用户规模大了再考虑分层记忆架构。


小结

  • 大语言模型本质无状态,每次调用只看本次传入的上下文,没有跨调用的内部状态
  • "记忆"的真实含义:把历史内容手动拼进本次上下文,模型才能"看到"之前发生了什么
  • 上下文有限 → 历史增长超限 → 只能截断 → 健忘,这是三个必然递进的步骤
  • 靠扩大窗口解决不了:成本二次方增长、注意力稀释、硬上限三道墙
  • 解法不是蛮力,而是分层记忆——短期、结构化长期、向量化语义,让每个 token 物尽其用

理解了根本原因,三层记忆体系就不是神秘的技术堆砌,而是面对约束的合理工程选择。接着读 三层记忆架构:短期、向量与结构化,看怎么落地。

了解整体 Agent 学习路线,可以回到 AI 数字员工落地指南 看大图。如果关心上下文工程的 token 控制细节,上下文工程:管 Token,防飘移 是配套必读。

👉 看看 AI 数字员工落地指南,或了解 数字员工搭建实战课。需要为企业落地方案,欢迎找我们聊 企业服务

📄 来源 / 自校链接

本文为学习整理,关键步骤与代码请结合下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为学习与落地整理,AI 工具与平台更新较快,关键步骤请结合官方最新资料验证。见免责声明