1. 从“百万上下文”说起:一个被反复追问的老问题
每隔一段时间,技术圈就会冒出同一个疑问:既然模型的上下文窗口已经能塞进上百万个token,那还需要单独搞一套“记忆”系统吗?这个问题的潜台词很直白——上下文够大,把该说的都塞进去不就行了,记忆模块是不是可以退休了。
我最早接触这个话题,是在做长文档问答的时候。当时第一反应也是“窗口够大就万事大吉”,结果实测下来被现实教育得很彻底。后来陆续看过不少长上下文模型的评测报告,也自己动手搭过几套带记忆的对话系统,才慢慢理解为什么“百万上下文”和“记忆”这两件事根本不是替代关系,而是两个不同层面的问题。
这篇文章想聊的就是这件事:为什么上下文窗口一路涨到百万级别,Memory 这个方向非但没死,反而重新变成热点。我会从上下文窗口的真实能力边界讲起,拆解记忆系统到底在解决什么上下文解决不了的问题,再回顾一下这三十年里“记忆”这个概念是怎么绕了一圈又回到原点的,最后给一些实际搭建记忆系统时的选型和踩坑经验。不管你是刚接触大模型应用开发,还是已经在做 Agent、RAG、长对话系统的同学,应该都能从中找到对自己有用的部分。
需要先说明一点:下面提到的“记忆”不是指模型参数里隐式存储的知识,而是指在推理阶段、模型外部显式维护的那套状态管理机制。这个区分很重要,后面会反复用到。
2. 上下文窗口的真实能力边界:能塞进去不等于能用好
2.1 窗口大小和有效利用是两码事
很多人把“支持百万上下文”理解成“能有效处理百万上下文”,这是最大的认知偏差。模型宣称的窗口长度,指的是它能接收的token上限,但不代表它对窗口里每个位置的信息都能同等程度地利用。
业内有个被反复验证的现象叫“中间迷失”(lost in the middle):把关键信息放在长上下文的中段,模型的召回率会明显低于放在开头或结尾。也就是说,窗口是线性的,但注意力对位置的敏感度是不均匀的。你塞进去一百万token,模型真正“看得清”的可能只是头尾那部分,中间大段内容处于一种“扫过但没记住”的状态。
我自己做过一个对比实验:同样一段两万字的技术文档,把核心结论分别放在开头、中间、结尾三个位置,然后问同一个问题。放在开头和结尾时,回答准确率能到八成以上;放在正中间时,准确率掉到五成左右,而且模型经常给出一个“看起来合理但和原文有出入”的答案。这个差距在短上下文里几乎不存在,窗口一长就暴露出来了。
2.2 成本与延迟的硬约束
就算模型真能做到“塞进去就能用”,还有一个绕不开的问题:钱和时间。
上下文是按token计费的,而且是每次推理都要重新计算。你有一个持续几十轮的对话,如果每一轮都把完整历史塞进去,token消耗是随轮次线性甚至超线性增长的。聊到第五十轮的时候,光是把历史读一遍的成本就可能超过问题本身。延迟同理,输入越长,首token响应时间越长,用户体验直线下降。
这里有个容易被忽略的细节:很多模型的计费是“输入token + 输出token”分开算的,长上下文主要吃的是输入token。看起来单价不高,但架不住量大。一个日活几千的对话应用,如果每轮都带完整历史,一个月下来的账单能吓死人。
所以现实中的做法几乎都是“选择性携带”:只把和当前问题相关的历史片段放进上下文。而“选择哪些片段”这件事,本身就是记忆系统要干的活。窗口再大,也改变不了“你需要决定放什么进去”这个前提。
2.3 上下文是“工作台”,不是“仓库”
我习惯用一个类比来解释:上下文窗口就像你书桌的桌面,记忆系统就像你身后的书架。
桌面再大,也有限度,而且桌面上东西越多越乱,找东西越慢。你不可能把整个书架的书都摊在桌面上干活,正确的做法是:需要哪本从书架上拿哪本,用完放回去。桌面负责“当前正在处理的几件事”,书架负责“所有可能用到的资料”。
百万上下文相当于给你换了一张超大桌面,能同时摊开更多书了。但书架该有还得有,因为:第一,桌面再大也放不下你所有的书;第二,就算放得下,全摊开你也找不到想要的那本;第三,每次换任务都要重新摆一遍桌面,成本太高。
这个类比基本能解释为什么记忆不会因为窗口变大而消失。窗口解决的是“当前这一步能看多少”,记忆解决的是“跨步骤、跨会话该保留什么、该丢弃什么、该在什么时候取回来”。
3. 记忆系统到底在补哪几个上下文补不了的窟窿
3.1 跨会话的持久性:关掉再打开,它还认得你
上下文窗口有个天然属性:它是会话级的。会话结束,上下文清空,一切归零。下次用户再来,模型对你一无所知。
这在很多场景下是致命的。比如一个长期使用的个人助理,用户第一次告诉它“我对花生过敏”,第二次告诉它“我习惯用简洁的回答”,第三次告诉它“我下个月要搬家”。如果每次都要重新说一遍,这个助理就没有存在价值。记忆系统要做的,就是把这些跨会话的事实沉淀下来,下次会话开始时按需注入。
这里的关键词是“按需”。不是把所有历史事实一股脑塞回去,而是根据当前对话的主题,检索出相关的那几条。用户问吃饭的事,就把过敏信息调出来;用户问日程,就把搬家时间调出来。这又回到了“选择放什么进上下文”的问题。
3.2 信息的压缩与抽象:把一万字浓缩成一句话
上下文里存的是原始文本,但记忆里存的应该是“结论”。
举个例子,用户和助手聊了二十轮,讨论了一个项目的排期,涉及各种时间点、依赖关系、风险项。这二十轮原文可能有一万字。如果全部塞进上下文,又贵又容易迷失。但如果记忆系统能把它压缩成一条:“项目X预计Q3上线,关键依赖是A模块,主要风险是B资源不足”,那下次只需要注入这一条就够了。
这个压缩过程本身就是价值。它把“过程”变成了“结论”,把“原始数据”变成了“可用知识”。上下文窗口再大,也替代不了这种抽象能力,因为抽象是有损的、需要判断的,不是简单地把文本堆在一起。
3.3 结构化与可检索:让信息能被精准找到
原始对话历史是一团非结构化的文本。你想从中找“用户上次提到的那个日期”,只能靠全文检索或者让模型自己读。但记忆系统可以把信息结构化:实体、关系、时间、偏好,各归各位。
结构化的好处是可检索、可更新、可推理。用户说“我换工作了”,记忆系统能定位到之前存的“职业”字段,更新它,而不是简单追加一条新记录导致前后矛盾。这种“更新”能力,纯靠上下文堆叠是做不到的,因为上下文里会同时存在新旧两条信息,模型未必知道该信哪条。
3.4 一致性与冲突消解:新旧信息打架时听谁的
接上一条,记忆系统还要处理冲突。用户三个月前说“我住在A城市”,昨天说“我搬到B城市了”。如果两条都留在记忆里,模型可能随机选一条,或者把两条都列出来让用户自己判断,体验很差。
好的记忆系统应该有机制识别这种冲突:时间上更新的优先,或者显式标记旧信息为“已过期”。这需要一套状态管理逻辑,而不是简单的键值存储。上下文窗口本身不提供这种逻辑,它只是被动地接收你给它的内容。
4. 三十年的一次轮回:记忆这个概念是怎么绕回来的
4.1 早期符号主义时代的“记忆”是显式的
把时间拨回上世纪八九十年代,那时候的AI系统里,“记忆”是个很实在的东西。专家系统里有事实库、规则库,对话系统里有用户模型、对话状态。信息怎么存、怎么取、怎么更新,都是工程师手写的逻辑。
那个时代的记忆是显式的、结构化的、可控的。缺点是脆,覆盖不了开放场景,写规则写到崩溃。但它的核心思想——状态要外置、要管理、要跨轮次保持——一直是对的。
4.2 深度学习时代:记忆被“隐”进参数里
后来神经网络起来了,大家发现与其手写规则,不如让模型自己从数据里学。于是“记忆”这个概念被弱化了:知识存在参数里,上下文靠注意力机制处理,状态靠RNN的隐状态传递。
这个阶段的主流思路是“端到端”:所有东西都交给模型,别在外面搞一堆模块。RNN、LSTM那套隐状态机制,本质上就是一种隐式记忆,只是它不可解释、不可编辑、容量有限,而且随着序列变长会遗忘。
4.3 Transformer时代:注意力即记忆,但只是短期记忆
Transformer把“注意力”变成了核心机制,上下文窗口成了新的记忆载体。大家一度觉得,只要窗口够大,记忆问题就解决了。这就是“百万上下文杀死记忆”这个说法的来源。
但很快发现,注意力机制提供的是一种“软记忆”:它能在当前输入范围内建立关联,但出了这个范围就没了。而且它对长序列的处理是有损的,位置编码、注意力稀释都会导致信息衰减。这更像是一种“工作记忆”,而不是“长期记忆”。
4.4 现在的回归:外置记忆重新成为刚需
于是绕了一圈,大家又开始做外置记忆了。向量数据库、知识图谱、结构化用户画像、对话摘要,这些本质上都是把记忆重新外置。区别在于,这次的外置记忆是“神经+符号”的混合体:用模型来做抽取、压缩、检索,用结构化存储来做持久化和精确管理。
这不是简单的复古,而是螺旋上升。早期符号记忆的问题是太脆、太贵、覆盖不了开放场景;现在有了模型做前端处理,外置记忆的构建成本大幅下降,覆盖面也广了。而模型本身的能力提升,又让“什么时候该记、该记什么、该怎么取”这些判断变得可行。
所以“百万上下文杀死记忆”这个判断,错在把两个不同层次的东西当成了替代关系。上下文是工作台,记忆是仓库,工作台变大不会让仓库消失,只会让仓库和台面之间的搬运变得更频繁、更需要策略。
5. 实际搭建记忆系统时的选型与踩坑
5.1 先想清楚你要的是哪种记忆
记忆不是一个单一的东西,至少可以分几类,每类的实现方式完全不同:
| 记忆类型 | 存什么 | 典型实现 | 更新频率 |
|---|---|---|---|
| 事实记忆 | 用户属性、偏好、长期事实 | 结构化存储/键值对 | 低,偶尔更新 |
| 情节记忆 | 具体对话片段、事件 | 向量库+摘要 | 中,按会话追加 |
| 语义记忆 | 抽象知识、结论 | 知识图谱/摘要库 | 低,定期整理 |
| 工作记忆 | 当前任务状态 | 上下文窗口 | 高,每轮变化 |
很多团队一上来就堆向量库,把所有历史都embedding进去,结果检索质量差、更新困难、成本还高。正确的做法是先分清你要解决的是哪类问题,再选对应的方案。事实记忆用结构化存储比向量库合适得多,因为你要的是精确查询和更新,不是模糊匹配。
5.2 检索策略比存储本身更重要
记忆系统的效果,八成取决于“取得对不对”,而不是“存得多不多”。我见过太多系统存了一大堆,但检索时要么召回不相关,要么漏掉关键的。
几个实操经验:
- 混合检索:纯向量检索对精确匹配不友好,比如用户问“我上次说的那个日期”,向量检索可能找不准。加上关键词检索(BM25之类)做混合,效果会稳很多。
- 时间衰减:越近的记忆权重越高,但不能简单线性衰减,因为有些长期事实(比如过敏)不该随时间淡化。要按记忆类型区分衰减策略。
- 重排序:先粗召回一批,再用一个小的重排模型精排。这一步对最终质量影响很大,值得投入。
- 查询改写:用户当前的问题往往不能直接用来检索,需要先改写成更适合检索的形式。比如“那我那个事怎么办”这种指代,得先结合上下文补全成完整查询。
5.3 写入时机和去重是两大坑
什么时候往记忆里写,比怎么写更容易出错。
写太勤:每轮对话都写,记忆库迅速膨胀,大量冗余和噪声,检索质量崩盘。 写太懒:只在会话结束时写,可能漏掉中途的重要信息,而且会话结束时往往已经忘了细节。
我的做法是分层:重要的显式事实(用户明确说的偏好、属性)实时写;对话摘要按轮次或按主题写;原始对话片段先缓存,定期整理后再决定是否入库。
去重也是个大问题。用户可能在不同时间用不同说法表达同一件事,如果不去重,记忆库里会有一堆语义重复的条目,检索时互相挤占位置。简单的做法是写入前先做一次相似度检查,超过阈值就合并或更新而不是新增。
5.4 别忘了“遗忘”也是记忆的一部分
这一点最容易被忽略。好的记忆系统不仅要会记,还要会忘。
- 过期的信息要标记失效,而不是留着误导模型。
- 低价值的记忆要定期清理,控制库的规模。
- 冲突的信息要消解,保留最新的或最可信的。
我见过一个系统,用户半年前随口说的一句“最近在学吉他”,一直被当成长期兴趣保留着,后来用户早就不学了,但助手还老拿这个说事,体验很怪。记忆需要生命周期管理,不是只进不出的仓库。
6. 一个可落地的最小记忆架构长什么样
6.1 分层结构:短期、中期、长期
如果让我从零搭一个记忆系统,我会分成三层:
短期层就是当前会话的上下文窗口,存最近几轮的原始对话,保证连贯性。这层不需要额外存储,直接用模型的上下文即可。
中期层存当前会话的摘要和关键事实,按主题或按轮次滚动更新。会话结束时,把中期层里值得长期保留的内容沉淀到长期层。
长期层是跨会话的持久存储,分结构化部分(用户画像、偏好、事实)和非结构化部分(历史情节的向量索引)。每次新会话开始时,根据当前话题从长期层检索相关内容,注入到短期层。
这个分层的好处是每层职责清晰,成本和效果可控。短期层保证体验,中期层控制上下文长度,长期层保证持久性。
6.2 关键流程:读、写、整理
读流程:新会话开始或话题切换时,用当前上下文生成检索查询,从长期层召回相关记忆,经过重排后注入上下文。注意控制注入量,不是越多越好,一般几条到十几条足够。
写流程:对话过程中,实时抽取显式事实写入结构化存储;按轮次生成摘要写入中期层;会话结束时做一次整理,把中期层内容压缩后写入长期层。
整理流程:定期(比如每天或每周)对长期层做一次清理,合并重复、标记过期、消解冲突。这一步可以离线做,不占用在线资源。
6.3 用模型做抽取,但别全信模型
抽取事实、生成摘要这些活,现在基本都交给模型做。但要注意:模型会出错,会幻觉,会把不确定的东西说得很确定。
我的经验是:抽取结果要带置信度和来源。低置信度的不直接写入长期层,先放缓冲区观察。来源要保留,方便后续追溯和纠错。另外,抽取用的提示词要针对场景调,通用提示词的效果往往一般。
还有一个细节:抽取时最好让模型输出结构化的结果(比如JSON),而不是自由文本。结构化结果好解析、好校验、好入库。自由文本还得再解析一遍,容易出问题。
7. 几个常见误解的澄清
7.1 “窗口够大就不需要RAG了”
这是把RAG和长上下文对立起来了。实际上RAG解决的是“从海量知识里找相关内容”的问题,长上下文解决的是“找到之后能放多少”的问题。两者是配合关系,不是替代关系。窗口大了,RAG召回的内容可以放更多,但RAG本身的检索环节不会消失。
7.2 “记忆就是向量数据库”
向量库只是记忆的一种存储形式,而且只适合非结构化的情节记忆。事实记忆、状态记忆用向量库反而别扭。把记忆等同于向量库,会导致方案选型一开始就跑偏。
7.3 “记忆越多越好”
记忆的价值在于精准,不在于多。一个存了一万条但检索不准的系统,不如一个只存一百条但条条命中的系统。控制规模、提升质量,比盲目扩张重要得多。
8. 我在实际项目里踩过的几个坑
第一个坑是过度依赖向量检索。早期做用户画像,把所有信息都embedding存起来,结果用户问“我的会员等级是什么”,检索出来的是一堆语义相近但答非所问的片段。后来改成结构化存储加精确查询,问题立刻解决。教训是:能用结构化解决的,别用向量。
第二个坑是摘要丢信息。有次做长对话摘要,为了压缩比,把摘要写得太简略,结果关键的时间点和数字丢了,后续回答出错。后来调整策略,摘要里强制保留实体、时间、数字这些硬信息,压缩比降一点但准确率上来了。
第三个坑是忘记处理冲突。用户先说了A,后说了B,系统两条都留着,模型回答时随机选,用户觉得系统“精神分裂”。后来加了冲突检测和版本管理,新信息覆盖旧信息,并保留变更历史,问题才解决。
第四个坑是检索查询没改写。用户说“那个呢”,直接拿这三个字去检索,召回的全是无关内容。后来加了查询改写环节,结合上下文把指代补全,召回质量提升明显。
这些坑的共同点是:都不是模型能力问题,而是工程设计和流程设计问题。记忆系统的难点从来不在“能不能存”,而在“存什么、怎么取、怎么管”。
9. 往后看:记忆会往哪个方向走
从目前的趋势看,记忆系统正在从“外挂模块”往“模型原生能力”方向走。已经有一些工作在尝试把记忆机制直接做进模型架构里,让模型在推理时能主动读写外部记忆,而不是靠外部的检索管道硬接。
但短期内,外置记忆仍然是主流,因为它的可控性、可解释性、可编辑性是原生记忆比不了的。企业场景尤其如此,你总得知道系统记住了什么、为什么记住、怎么改。
另一个方向是记忆的自动管理。现在很多环节还需要人工设计规则,未来可能会更多交给模型自己判断:什么值得记、什么时候该忘、冲突怎么消解。但这需要模型有足够的判断力和稳定性,目前还在早期。
回到标题那个问题:百万上下文为什么没有杀死Memory?因为上下文和记忆解决的是不同层次的问题。上下文是“当前能看到什么”,记忆是“跨时间该保留什么”。窗口变大,只是让工作台更宽敞了,仓库该建还得建,而且因为台面大了,仓库和台面之间的调度反而更频繁、更需要策略。这三十年的轮回,绕的不是圆圈,是螺旋——同样的“外置记忆”概念,这次有了模型能力做支撑,能做的事情比当年多得多。