引言
如果你曾经深入使用过ChatGPT或Claude,一定会注意他们的记忆功能:他们似乎记得你,能够在新的对话中回忆起你的偏好、背景信息,甚至几周前讨论过的话题细节。这种跨会话的记忆能力,远远超出了单次对话中的上下文窗口管理,背后是一套精心设计的长期记忆系统。
本文基于多篇逆向工程博客,结合我长期使用ChatGPT和Claude的实践经验,对两者的长期记忆实现方式进行了系统性的梳理和分析。需要说明的是,这篇文章更侧重于从产品和工程实现的角度进行剖析,而非底层代码实现。即使你没有深厚的理工背景,也能够理解这些系统的设计思路和工程实践价值。
由于CLoseAI在system prompt中包含了明确的防injection指令,并且专门训练防止被套取内部实现细节,因此不同于Claude在博客上公开分享的诸多技术细节,OpenAI的记忆系统大多依赖逆向工程推测。本文将综合这些研究成果,揭示这两个主流AI助手在长期记忆上的异同。
从API到客户端的上下文管理
在深入讨论长期记忆系统之前,我们需要先理清一些基础但容易混淆的概念,懂的佬麻烦直接跳过。Session、thread、prompt、chat等术语在不同的上下文中往往有不同的含义,业界并没有统一的定义标准。我们在这里将session和thread看作你开的一个对话窗口里所有消息的集合。prompt和chat则对应你发的每一条消息或模型的回复。
从API层面来看,OpenAI和Anthropic采用了不同的数据格式。OpenAI使用JSON格式的chat completion API,将每条消息分为system、user和assistant三种角色;而Anthropic则采用XML风格的格式,将user消息称为human消息。这些是我们在调用API时直接面对的接口层。
然而,真正的复杂性在于客户端实现。当你在ChatGPT或Claude的Web界面中进行对话时,发送给后端的并不是简单的消息数组,而是经过精心设计的上下文管理结果,也就是我们常说的context engineering。这个工程化的过程决定了哪些信息会被注入到实际的API请求中,以及如何组织这些信息以达到最佳效果。
以ChatGPT为例,每次API调用时,上下文的结构大致如下:
[0] System Instructions
[1] Developer Instructions
[2] Session Metadata (ephemeral)
[3] User Memory (long-term facts)
[4] Recent Conversations Summary (past chats, titles + snippets)
[5] Current Session Messages (this session)
[6] Your latest message
其中,层级0和1包含了系统级别的配置,如juice参数、防injection指令、工具定义等就在这里;层级2则包含临时的会话元信息,如用户当前时间、IP所在地区等。这些内容在许多技术博客中都有详细分析,本文不再赘述。我们将重点聚焦于层级3、4、5,也就是记忆系统的核心实现部分。
理解这个分层结构非常重要,这告诉我们,所谓的"长期记忆"并不是模型本身的能力,而是通过巧妙的prompt engineering和数据管理实现的。每次对话时,相关的历史信息会被检索出来,动态注入到上下文中,让模型看起来记得你。
ChatGPT的长期记忆架构
整体设计概览
ChatGPT的长期记忆系统采用了三层设计:一个显性维护的记忆列表(memory)、一个最近会话摘要系统(recent conversations summary),以及一个隐性的用户洞察系统(user insight)。
从上下文结构来看,显性记忆列表对应层级3的user memory,最近会话摘要对应层级4,而用户洞察系统则可能位于层级0或1中。这三个组件协同工作,共同构成了ChatGPT的记忆能力。虽然关于ChatGPT具体的prompt框架细节无需过多深究,但这套记忆系统中的诸多设计思路,非常值得我们在实际工程中借鉴。
上下文窗口的滚动管理
在讨论长期记忆之前,我们需要先理解当前会话(current session)的上下文管理机制。用传统的prompt角色概念来理解,current session messages就是assistant prompt和user prompt的交替序列。
这里的一个关键点是:模型的回复与用户的提问并不是全部无限制地保留在上下文中,而是通过滚动窗口机制进行管理。
我们知道,模型的输入长度存在物理上限。例如GPT-5.2的文档标注为40万token,但实际可用约为272k;DeepSeek V3为128k;Gemini 3flash则达到了1M,(Opus4.6好像也是,不过长上下文要加钱)。然而,更长的上下文并不总是更好——随着上下文长度增加,模型的召回率会下降,这在学术界被称为"lost in the middle"现象。过长的上下文会导致模型"降智",这是不可避免的。
ChatGPT采用的策略相对简单粗暴:当消息序列的token总和超过上下文上限时,直接从最早的消息开始裁剪。
举个具体的例子:假设某个模型的输入上限是60 token,system prompt占10 token,每个user prompt和assistant prompt也各占10 token。你与AI进行了三轮对话,产生了user prompt 1、assistant prompt 1、user prompt 2、assistant prompt 2、user prompt 3、assistant prompt 3。此时你发送第四条消息user prompt 4,总上下文变为:system prompt (10) + 三轮对话 (60) + 当前消息 (10) = 80 token,超出了20 token的限制。
此时ChatGPT会将最早的一轮对话——也就是user prompt 1和assistant prompt 1——完全移除,使剩余内容刚好满足60 token的限制。
这种机制的含义很明确:被裁剪掉的信息会永久从当前会话中消失。ChatGPT并没有采用上下文压缩或摘要技术来保留这些信息,这意味着如果某条重要信息没有被提取到层级3或4的记忆系统中,它就会随着对话的进行而丢失。
从信息论角度理解上下文管理
要理解为什么上下文管理如此重要,我们可以从信息论的角度来思考。
在信息论中,信息量与不确定性的减少程度相关。一个消息传递的信息越多,它能够排除的可能性就越多。这个概念看起来抽象,但在日常沟通中我们无时无刻不在应用。
想象这样一个场景:你和好基友约定周末线下面基,已经确定了在上海碰头。这时候,如果你说"我在人民广场星巴克等你",这句话传递的有效信息是"人民广场"和"星巴克";而如果你说"我在中国上海市黄浦区人民广场某号某栋星巴克咖啡厅等你",前面的"中国上海市"其实是冗余信息——你们都已经知道在上海见面了。
同样的道理,在与AI的对话中,如果system prompt已经注入了"今天是2026年2月6日星期五",那么你在user prompt中再次说"今天是星期五"就是信息冗余,浪费了宝贵的token空间。
信息密度的另一个维度是具体性。"我老婆昨晚没回家"这句话留下了很多不确定性——可能是加班、可能是和朋友聚会、可能是出差;而"我老婆出轨了"则是一个高信息密度的陈述,它大幅减少了可能性空间。在AI对话中,提供高信息密度的上下文,能够让模型更准确地理解你的意图,减少无关回复的可能性。
在当今的大语言模型时代,动辄数百k甚至上M的上下文窗口,对于绝大多数对话来说都是足够的。真正的挑战不在于空间大小,而在于如何在有限的空间中放入最有价值的信息。这就是为什么我们需要精心设计的记忆系统——它的作用是从海量的历史交互中,检索出与当前对话最相关的信息,而不是一股脑地把所有历史都塞进上下文。
User Memory的实现机制
现在让我们回到ChatGPT的上下文结构,其中的user memory(层级3)和recent conversations summary(层级4)构成了所谓的长期记忆。
User memory是一个相对稳定的、对用户特征进行结构化存储的系统。根据Mathan的逆向工程博客,ChatGPT在system prompt中注入了一个名为bio的工具定义。当模型检测到user prompt中包含与用户档案相关的信息时,它会主动调用这个工具来存储记忆。这些存储的记忆条目可以在设置界面中查看和管理,形成了一个用户可见、可编辑的记忆列表。
然而,实际的实现比文档中描述的更加复杂。根据我的测试,ChatGPT并不是每次都将所有的user memory全部注入上下文,而是使用RAG进行检索匹配。也就是说,系统会根据当前的user prompt,从记忆库中检索出最相关的若干条bio记录,然后注入到上下文中。
有趣的是,ChatGPT似乎并没有提供显性的工具让模型在推理过程中主动搜索记忆(也就是所谓的agentic RAG)。RAG检索发生在上下文构建阶段,而不是在模型的工作流中。这种设计更加简单可靠,但灵活性稍弱。
Conversation History的复杂系统
相比于user memory的相对简单,conversation history是一个设计更为精巧的系统。
从表面上看,recent conversation summary的实现逻辑类似于一个简单的SQL查询:SELECT * FROM chat_message WHERE user_id = ? ORDER BY created_at DESC LIMIT N,即提取最近N个会话的摘要并注入上下文。根据Mathan的测试,ChatGPT大约会保留15条最近会话的摘要,格式类似:
- <Timestamp>:<Chat Title>
|||| user message snippet ||||
|||| user message snippet ||||
然而,Macro的另一篇博文提出了不同的观点。他认为ChatGPT使用了RAG,在一个两周的时间窗口内,根据当前query检索相关的若干条session,并且模型能够逐字复述其中的信息。这与Mathan的"保存user prompt摘要"的观点在数据层面是一致的——无论是通过摘要内容索引还是通过原始user prompt索引,RAG的效果本质上并无太大区别。
但我倾向于认为,ChatGPT在这一层并没有直接使用RAG,而是采用了更巧妙的两阶段检索策略。原因在于:长期记忆的核心功能已经通过user memory和user insight(稍后讨论)完全覆盖,conversation history的主要作用是提供短期记忆的连贯性,让跨会话的对话更加自然。如果在此处再引入一次RAG检索,会与user memory的RAG产生功能重叠,显得冗余。
在实际测试中,我验证了ChatGPT确实使用了一种两阶段RAG策略。如下图所示,通过询问历史对话的细节,ChatGPT不仅回答了相关问题,还提到了一些在设置中不可见的时间标记信息。这说明存在一个更高层次的检索逻辑:
-
首先通过RAG匹配相关的bio记录,这些bio带有时间区间标记
-
然后在这些时间区间内,再次使用RAG检索top-k个相关的conversation摘要
-
同时,无论是否匹配到相关bio,都会加入固定数量的最近session摘要
这种设计非常巧妙。它既保证了时间局部性(最近的对话总是可见),又通过bio作为"索引"实现了对历史深处相关信息的精准检索。这避免了单纯按时间顺序检索导致的信息丢失,也避免了全局RAG可能带来的性能开销和不稳定性。
User Insight隐性系统
除了上述两个显性的记忆系统,Macro的博文还揭示了一个更加隐蔽的组件:user insight系统。
这是一个用户不可见、不可编辑的隐性系统,据推测是通过定时任务或特定触发条件来更新的。User insight与bio的最大区别在于,它不是简单的事实性记忆列表,而是对用户偏好、交流风格、思维特点的高层次总结。
在我的测试中,ChatGPT的反应颇为有趣。当我询问是否存在user insight系统时,它先是含糊其辞,随后在追问下间接承认了这个系统的存在,并且明确告诉我:它被禁止向用户透露user insight的存在。这可以说是此地无银三百两。
从工程角度来看,user insight的设计是合理的。Bio记录是连续的用户记忆,如"他写了一个博客文章"、”他今天吃了碗炸酱面";而user insight则是整体性的画像,如"这个用户倾向于直奔主题,不喜欢冗长的铺垫,在技术讨论中偏好深度而非广度"。这种高层次的总结可以更好地指导模型的回复风格,而不仅仅是提供事实性信息。
Claude的记忆系统分析
相比ChatGPT,Claude的记忆系统采用了截然不同的设计哲学。由于我个人对Claude客户端使用较少(GPT Teams的agent mode实在太好用了),这部分的分析主要基于Mathan的博客总结。这位作者的逆向工程工作相当透彻,感兴趣的读者可以查阅原文获取更多细节。
Claude的最大特点是完全放弃了传统的RAG自动注入方式,转而采用agentic RAG——也就是将会话搜索和记忆搜索作为工具直接暴露给模型,由模型自主决定何时、如何检索记忆。
这种设计与Claude的整体产品策略是一致的。如果你使用过Claude Code,你会发现它也是通过工具调用的方式,让模型主动进行关键词搜索。这种agentic的实现方式显然更加灵活,模型可以根据对话的上下文动态决定是否需要查阅历史记忆,甚至可以进行多轮检索来找到所需信息。
然而,这种设计也有其代价。首先,它高度依赖模型的能力——模型需要准确判断何时需要检索、使用什么关键词检索、如何整合检索结果。如果模型能力不足,或者在本地部署时使用较弱的模型,效果可能会大打折扣。其次,由于没有自动注入相关的会话摘要,完全依赖模型主动检索,可能会存在信息遗漏的风险。毕竟RAG的语义搜索能力是经过验证的,而模型的主动检索更多依赖prompt理解和关键词提取。
从工程实践的角度,我个人更倾向于ChatGPT的多层设计。特别是通过bio的时间戳索引来检索相关会话摘要的方式,在保证灵活性的同时,也提供了稳定性保障。
两种方案的选择,本质上是在模型依赖度和系统可控性之间做权衡。ChatGPT的方案更加工程化,适合需要稳定可靠表现的生产环境;Claude的方案更加前沿,适合追求极致体验并且不介意偶尔出现意外的场景。
开源客户端的实现方案
在开源领域,Cherry Studio和Open-Web-UI代表了两种不同的技术路线,它们的设计思路与ChatGPT和Claude颇为相似。
Cherry Studio采用的是直接RAG方式。每次对话结束后,系统会自动提取并存储一条记忆。经过一段时间的使用,我的记忆库已经积累了近200条记录。值得称赞的是,Cherry并不是简单粗暴地堆砌记忆,而是在存储过程中进行了去重和去矛盾处理。根据官方文档,系统会检测新记忆是否与已有记忆重复或冲突,如果存在冲突则会进行合并或更新。这种设计虽然简单,但在实践中效果不错。
Open-Web-UI则选择了agentic RAG路线,让模型自主选择何时搜索、更新或添加记忆。这种方式更加灵活,但对模型能力的要求也更高。使用较弱的模型时,可能会出现该搜索时不搜索、不该搜索时频繁搜索的情况。
两者的共同优势在于开源和透明——所有记忆都在设置中清晰呈现,支持完整的CRUD操作。用户可以随时查看、编辑、删除任何一条记忆,不存在像ChatGPT的user insight那样的"黑箱"组件。
然而,这两个客户端明显缺乏更复杂的多层架构设计。它们都只有单一的记忆列表,没有区分稳定的用户画像(bio)和动态的会话摘要,也没有类似user insight的高层次总结。对于短期使用或记忆量较少的场景,这样的设计完全够用;但对于长期使用的重度用户,或者对记忆管理有更高要求的技术爱好者来说,仍然存在改进空间。
复现与改进方案:融合最优设计
基于对ChatGPT、Claude以及开源客户端的分析,我设计了一个融合各家优势的记忆系统架构。这个方案试图在稳定性和灵活性之间找到平衡点,既保留自动化记忆管理的可靠性,又提供agentic检索的灵活性。
系统架构设计
整个系统分为三个核心组件:原子记忆(atomic memory)、用户画像(user profile)和文件夹记忆(folder memory)。
原子记忆是整个系统的基础单元。每条原子记忆包含以下字段:
-
ID:唯一标识符
-
Timestamp:创建时间戳
-
Content:记忆内容,控制在三到四句话的长度,形成一个完整的语义段落
-
Tags:若干个标签,用于分类和快速检索
原子记忆的设计借鉴了ChatGPT的bio,但存储粒度更细、频率更高。这样做的好处是能够捕捉更多的交互细节,同时每条记忆的长度适中,既不会太长导致RAG匹配不精准,也不会太短丢失上下文信息。
用户画像对应ChatGPT的user insight,是一个动态维护的结构化文档。它包含用户的回答风格偏好、语言习惯、主要的个人背景信息等。与user insight不同的是,这个画像是用户可见、可编辑的,增加了系统的透明度。
文件夹记忆是一个创新性的设计。考虑到许多用户会按照项目、主题来组织对话,文件夹级别的记忆可以捕捉特定主题下的上下文信息。例如,你可能有一个"工作项目A"的文件夹和一个"学习笔记"的文件夹,两者的记忆应该是隔离的。
在这个设计中,我舍弃了独立的会话级别摘要。原因是原子记忆的存储频率已经足够高,特别是设置了"每个新会话的首条消息必须存储记忆"的规则后,原子记忆本身就能提供足够的会话连续性。这样做的好处是减少了系统复杂度,同时降低了embedding的计算成本。
记忆检索与存储机制
在运行时,系统采用双通道检索策略:
被动RAG:每次用户发送消息时,系统自动通过RAG检索top-N条相关的原子记忆,注入到上下文中。同时,用户画像和当前文件夹的记忆文档会被自动加载。这个通道保证了基本的记忆功能,无需模型主动干预。
主动RAG(agenticRAG):将记忆搜索作为一个工具暴露给模型。模型可以使用关键词主动搜索记忆库,这在某些边缘场景下非常有用。例如,当用户的提问本身不够准确,或者自动RAG没有检索到关键信息时,模型可以通过工具调用进行补充检索。
记忆的存储采用异步机制。当用户发送消息后,系统会立即进行被动检索并生成回复,不阻塞用户体验。同时,启动一个异步的记忆处理流程:
-
分析user prompt的内容
-
如果包含值得记录的新信息,存入原子记忆
-
如果包含偏好或风格相关的信息,更新用户画像
-
如果在特定文件夹中,更新文件夹记忆
这个异步流程由单独的LLM调用完成,不会影响主对话的响应速度。
时间区间检索功能
ChatGPT通过bio时间戳索引相关会话的设计非常巧妙,但它是一个自动的、用户不可见的过程。在我的方案中,我将这个功能显性化为一个工具。
原子记忆除了文本内容外,还包含时间戳信息。当模型暴露给用户的搜索工具定义中,包含一个可选的时间范围参数。在工具的说明中,明确提示模型:如果用户询问的是某个时间段的经历或信息,需要添加时间参数进行过滤。
这个设计既保留了灵活性,又不会增加系统复杂度。对于大多数不涉及时间的查询,模型使用普通的语义检索即可;只有在明确需要时间过滤的场景下,才会使用时间参数。
可配置性与透明度
最后,所有的记忆功能都应该是用户可配置的。包括:
-
原子记忆的存储频率和长度限制
-
RAG检索时的top-N参数
-
是否启用主动检索工具
-
用户画像和文件夹记忆的开关
同时,所有记忆都支持完整的CRUD操作,用户可以随时查看、编辑、删除任何一条记忆。这种透明度是开源项目的优势,也是对用户隐私的尊重。
可视化上下文管理
记忆系统的最终目标是让上下文工程变得可视化和可控。在我设计的客户端Prompt-Tree中,所有被调用的记忆都会实时显示在一个context box中。用户可以清楚地看到哪些记忆被注入了上下文,并且可以自由编辑、移除或压缩这些内容。
这种设计完美解决了ChatGPT和Claude中滚动窗口的痛点。在传统的设计中,当上下文超出限制时,早期的消息会被无声地丢弃,用户完全无法干预。而在可视化的上下文管理中,用户可以主动删除不重要的消息,或者将长消息压缩为摘要,从而为真正重要的信息腾出空间。
此外,我还将每次对话建模为一个树节点,形成类似Git的对话树结构。用户可以在不同的对话分支间自由切换,可以从历史的某个节点开始新的分支,也可以对比多个模型在同一问题上的不同回复。这种设计极大地增强了对话的灵活性和可探索性。这个客户端的仓库如下,欢迎fork。github.com/yxp934/prom…
总结与展望
本文系统梳理了ChatGPT和Claude的长期记忆实现机制,并基于开源客户端的实践,提出了一个融合各家优势的改进方案。
从技术角度看,长期记忆系统本质上是prompt engineering和数据管理的结合。通过RAG检索、多层架构设计、异步处理等工程手段,我们可以在有限的上下文窗口内,为模型提供最相关的历史信息,从而实现跨会话的连续体验。
ChatGPT的方案更加工程化,通过多层记忆和巧妙的检索策略保证了稳定性;Claude的方案更加前沿,将更多的控制权交给模型本身。两者各有优劣,选择哪种方案取决于具体的应用场景和对模型能力的预期。
对于开发者而言,理解这些设计思路有助于我们构建更智能的AI应用。无论是开发客户端、设计AI agent,还是优化产品的用户体验,记忆管理都是一个不可忽视的环节。随着模型能力的不断提升,我相信会有更多创新的记忆系统设计涌现出来。
我的目标是打造一个集成所有优秀设计理念的开源客户端。上下文管理问题已经有了清晰的解决方案,接下来的重点是进一步增强agentic能力,让AI助手能够更加主动、智能地利用工具和记忆。欢迎大家下载、fork、issue、讨论,共同构建更强大的开源AI客户端。
参考资料
manthanguptaa.in/posts/claud… linux.do/t/topic/699… manthanguptaa.in/posts/chatg…