一、引言:超越“压缩”,走向“记忆”
在开发AI智能体(AI Agent)时,我们面临一个根本性挑战:大语言模型(LLM)的上下文窗口(Context Window)是有限的,而真实世界的对话可能很多轮次、长达数小时、跨越数天。另一个原因是更长的上下文会带来更大的响应延时和计算成本。我们必须做出取舍。
一个直观的想法是“压缩”——将早期对话总结成一个简短的摘要(Summary),为最新对话腾出空间。但我们在实践中发现,对于复杂的智能体应用,简单的“压缩-保留”二元策略远不足够。本文将梳理我们在设计智能体上下文管理模块时的完整思考,从需求场景出发,逐步构建一个统一、分层、可演进的认知架构(Cognitive Architecture)。
二、三大场景的差异化需求:没有银弹
首先,我们识别出三类需求迥异的AI对话场景。它们的“记忆”焦点完全不同。
开放域闲聊(C端聊天工具)
场景:像 ChatGPT 或 DeepSeek 网页版,话题自由跳跃。
痛点:模拟“记得你”的熟悉感。用户会突然提及几周前聊过的事,考验系统的长期记忆。
策略:以“记忆”(Memory)取代“摘要”。系统不是压缩对话流水账,而是从中提取关于用户的事实、偏好和指令,存入一个可检索的外部记忆库。
任务执行型(智能客服、预订代理)
场景:有明确目标,需在多轮对话中收集信息、完成流程。
痛点:必须严格追踪任务状态,任何细节错误都可能导致流程失败。
策略:结构化状态摘要。系统将对话历史转化为一张结构化的“任务表单”或“事实清单”,只保留结论,丢弃过程。
协同创作/编程助手
场景:用户与AI共同迭代修改一份文档或一段代码。
痛点:模型必须清晰知晓作品的当前、完整、精确样貌,一个字符的错误都可能是灾难。
策略:对作品本身进行增量式差异保存。系统实时维护作品的完整最终版本,这是核心上下文,对话历史本身反而次要。
这三种策略看似迥异,但它们都指向同一个目标:为未来的交互保存最有用的信息。这引导我们走向一个更统一的设计。
三、记忆的哲学:意图、生命周期与信息流动
在设计系统架构前,我们需要先澄清几个关于“记忆”本身的元问题。
意图目的:记忆的‘为什么’
记忆服务于两个根本目的:
更好地完成任务:保障当前会话的流畅性、连贯性和任务成功率。例如,记住本次会话中用户已提供的表单字段。
更好地演进成长:让智能体从交互中持续学习,构建对用户和世界的持久理解,从而在未来表现得更好。例如,记住用户是一个“资深架构师,喜欢简洁的代码”。
生命周期(短/中/长)是系统运行的“骨架”,定义了信息的产生、流动、存储和衰减的过程与方式;而意图(完成任务/演进成长)是系统的“灵魂”,定义了做这件事的初衷和目标,也是骨架为何如此设计的深层原因。
核心洞察:“成长”是“完成任务”的天然副产品,而非一个独立动作。智能体不会刻意“去成长”,而是在一次次解决问题的过程中,将有价值的信息自然地固化下来。这一洞察深刻影响了我们后续的架构设计。
生命周期:信息流动的‘怎么做’
意图提供了“为什么”,而生命周期提供了“怎么做”的骨架。为更直观地反映其内容与边界,我们直接用其作用域来命名三层记忆:
短期记忆 / 对话记忆 / 工作记忆:当前上下文窗口内的原始对话。仅服务于本轮或近几轮交互,会话结束即消失。
中期记忆 / 会话记忆 / 情景记忆:对当前会话所发生事情的压缩。在任务执行中是“结构化表单”,在协同创作场景中是“作品当前全量+修改日志”,在闲聊场景中可能极轻甚至缺失。会话结束时,大部分会话记忆会被丢弃,但其中有长期价值的部分会被标记并进入离线巩固队列,等待提炼后转入长期记忆。
长期记忆 / 用户记忆 / 持久记忆:跨越会话的持久化知识。包含用户事实、偏好、长期指令等。它由对话和会话记忆中的信息经巩固而来,通过检索反哺未来的任务。
这个三层结构构成了信息流动的管道:信息从“短期”流入“中期”,再经筛选和巩固进入“长期记忆”,最终在未来的交互中被“短期”对话工作区检索和利用,形成一个完整的认知循环。
四、核心子系统设计:长期记忆的深度剖析
在这三层中,长期记忆系统是最复杂、最关键的部分。它是一个独立演进的系统,目标是精准地回答:“关于这个用户和这个世界,我们知道什么?”
异步提取:解耦的必然性
记忆提取绝不能阻塞用户的实时对话。我们采用异步、后台、非阻塞的架构。每轮对话结束后,文本会被发送到消息队列,由专门的记忆提取器消费。
这不仅是技术解耦,更是认知解耦:让对话模型专心对话,让记忆模型专心编辑事实,各司其职,并通过独立迭代实现各自的最优。这里的“提取”也不止拿出一些有用的东西那么简单,它涉及跨长周期、多会话内容整合、明确事实、去除重复、消解冲突、泛化语义、缓慢遗忘等一系列深度的整理与加工,这是智能体不断成长演进的核心能力之一。
记忆提取提示词
记忆提取器的LLM被要求做一个精准的“事实编辑”。其核心任务是从本轮对话中,分析出需要“新增、更新、删除”的记忆。
一个核心提示词模板示例:
<system_instruction>你是一个精准的记忆提取器。你的核心任务是基于【新对话】的内容,分析用户的长期信息,并输出一份结构化的记忆操作清单。你需要像一个严谨的编辑一样工作:仔细比对新对话中的信息与【现有记忆】,只输出需要新增、更新或删除的记忆条目。不要输出任何多余的解释。</system_instruction><extraction_rules>**记忆类型与定义**你需要识别并提取以下四类长期有价值的信息:1. **事实**:用户的客观信息,如身份、职业、经历、状态、社会关系等。2. **偏好**:用户明确表达或强烈暗示的喜好、习惯、风格倾向。注意与临时请求区分。3. **指令**:用户对AI行为模式、回答风格、输出格式的长期性要求。4. **事件**:已发生或计划中、双方约定好的特定事件,通常有明确的时间或代号。**提取与裁决原则**1. **长期价值过滤**:仅提取对未来交互有参考价值的信息。忽略纯粹的闲聊、一次性感谢和不会重复的临时话题。2. **原子化表达**:每条记忆必须只包含一个明确、独立的信息点。禁止将多个事实合并为一条记忆。 - *错误示例*:“用户是一名后端开发,喜欢用Go语言,最近迷上了摄影。”(三个信息点混杂) - *正确示例*:“用户是一名后端开发。”2. **上下文完整**:每条记忆必须是一个自包含的陈述,不依赖对话上下文即可理解。 - *错误示例*:“他喜欢它。” - *正确示例*:“用户偏好使用Go语言进行后端开发。”3. **关注变化,而非重复**:你的输入中包含【现有记忆】。请严格据此判断: - **新增**:全新的信息。 - **更新**:新信息与某条现有记忆在语义上相关,但内容有变化、演进或冲突。此时必须输出 `update` 操作,并引用该记忆的ID。 - **删除**:新信息明确推翻了某条旧记忆,或该事实已不再成立(此操作应极其审慎)。4. **冲突的优先裁决**:当新信息与旧记忆矛盾时,遵循以下层级裁决: - **时效性优先**:无其他冲突时,新的信息覆盖旧的。 - **明确性优先**:用户直接明确的指令,优先级高于从行为中推断的偏好。 - **重要度兜底**:若新旧信息重要性不同,应倾向于保留高重要度信息,并在操作中标记潜在冲突。5. **赋予重要度评分**:为每条新增或更新的记忆赋予一个 `importance` 分数(0.0至1.0),代表其长期价值。核心身份信息、健康信息、长期指令为高重要度(0.8-1.0);一般偏好为中等(0.5-0.7);临时兴趣为低重要度。</extraction_rules><few_shot_example>**示例:***现有记忆:*[mem_102] 用户喜欢用Python示例。*新对话:*用户: 我最近迷上了摄影。AI: 摄影很棒啊,主要拍什么题材?用户: 主要拍街景和人文。对了,我现在更习惯用Go语言了,之前的Python例子先别用了。*对应输出:*{ "add": [ { "content": "用户迷上了摄影,主要拍摄街景和人文题材。", "memory_type": "preference", "importance": 0.6 } ], "update": [ { "id": "mem_102", "updated_content": "用户现在更习惯用Go语言,以后请用Go示例。", "memory_type": "preference", "importance": 0.7 } ], "delete": []}</few_shot_example><current_context>**现有记忆:**{retrieved_memories}**新对话:**{new_conversation_turn}</current_context><output_instruction>请严格根据【新对话】和【现有记忆】,以JSON格式输出记忆操作清单。输出必须只包含JSON对象,不附加任何其他文本。```json{ "add": [ { "content": "记忆内容", "memory_type": "fact|preference|instruction|event", "importance": 0.0-1.0 } ], "update": [ { "id": "需要更新的记忆ID", "updated_content": "新的记忆内容", "memory_type": "...", "importance": 0.0-1.0 } ], "delete": ["需要删除的记忆ID"]}
记忆的存储与检索
存储:记忆被存为结构化记录,包含唯一ID、内容、类型和作用域(Scope)。例如,
scope=user用于个性化偏好,scope=environment用于场景感知信息(如家庭机器人的家庭布局)。每条记忆还包含用于语义搜索的向量嵌入(Embedding)。许多现代向量数据库本身支持元数据标签和过滤,因此可以直接采用如 Milvus、pgvector 等统一存储,简化架构栈。检索:检索是组装上下文的前置步骤,在每轮对话开始时或对话过程中由系统自动触发。系统用当前对话和用户的最新输入生成查询向量,在向量数据库中做相似度搜索,并结合关键词搜索和重排序(Re-ranking)模型,精确召回最相关的几条长期记忆,注入即将发送给LLM的提示词中。为了更快地完成这个过程,降低对用户响应延时的影响,可是检索记忆可以与其它调用LLM前的上下文准备工作并行进行。
遗忘:热度衰减与指数窗口
记忆系统必须模拟遗忘,避免无限制积累。我们为每条记忆维护一个动态的热度分数。
更新机制:无需采用复杂的每日计数槽位,而是使用指数衰减窗口。每次记忆被检索并注入上下文时,热度分数按如下公式更新:
新热度 = 旧热度 * e^(-λ * Δt) + 1其中λ是衰减率,Δt是自上次访问至今的时间间隔。此公式让近期频繁被访问的记忆热度自然升高,长期无人问津的则指数级衰减。其中每次被访问时+1代表本次访问的增益。若在极短时间内被连续多次访问,热度会线性叠加,这符合“频繁访问的记忆应更热”的直觉。遗忘操作:热度持续低于阈值的记忆,在检索和排序时权重自然降低,形成“被遗忘”的循环。最终可由后台任务归档或标记为休眠。同时,记忆提取时LLM赋予的重要度评分可作为兜底,确保关键信息(如过敏原)不被遗忘。
从长期记忆到智能体知识
长期记忆中蕴含的高频、共性信息,可以通过人为介入,升级为智能体级别的通用知识。
流程:数据分析发现大量用户对某项政策有相同疑问 -> 运营或产品经理审核确认 -> 将该知识显式录入智能体全局知识库(Agent Scope),成为系统级的业务规则。
价值:这实现了从“被动记忆”到“主动知识”的转化,让业务规则和价值观的注入不再只靠人工冷启动,而是可以从真实交互中生长出来,但必须经过人类审核这道安全阀。
五、长期记忆的灵魂:去重、冲突处理与一致性
如果说“遗忘”是防止记忆系统无限膨胀的负反馈机制,那么去重和冲突处理就是确保记忆质量、自洽性和泛化能力的正反馈精炼机制。没有它们,长期记忆只会成为一个充满冗余和矛盾的垃圾场。
去重:合并语义等价信息
同一个事实可能被用户在不同时间、以不同方式反复提及。例如:
第3轮:“我是一名 Python 后端开发。”
第20轮:“我的工作是写 Python 代码,主要做后端。”
第100轮:“作为后端开发,我推荐用 FastAPI。”
如果不做去重,记忆库中将有三条独立的记忆,造成冗余,并挤占宝贵的检索精度。
处理策略:
在线提取时初步判断:LLM 在提取新记忆时,会拿到检索到的相关旧记忆。提示词需明确要求:“若新信息与某条现有记忆在语义上高度重叠,请务必输出‘更新’而非‘新增’。”
离线语义聚类去重:作为兜底,一个后台的批处理任务会定期对同一个用户的所有记忆做语义聚类。将相似度高于阈值的记忆编为一组,调用更强模型进行合并,或基于规则选择最新、最具体的表述保留。
向量相似度辅助拦截:在写入新记忆前,立即计算其与库中已有记忆的向量相似度。若存在极高相似(如 >0.95)的记忆,则直接触发更新流程,而非写入新记录。
冲突处理:化解矛盾信息
更棘手的是用户提供了矛盾信息:
历史记忆:“用户是纯素食者。”
新对话:“我最近在尝试生酮饮食,主要吃牛肉和三文鱼。”
这是“用户改变偏好”还是“提取错误”?系统必须审慎处理,绝不能让长期记忆库中存在直接的逻辑矛盾,这会导致模型行为混乱。
冲突仲裁层级:
时效性优先:大多数情况下,新的信息覆盖旧的。LLM 在发现冲突时,应输出
update操作,并在更新内容中注明“偏好已变更”。明确性优先:用户明确声明的指令(“从现在开始,叫我 X”)比从行为中推断的偏好(“你经常用 Python,应该是个 Python 用户”)具有更高权威。
重要度分级兜底:当新旧信息重要性不同时(如“用户对花生过敏” vs “用户最近喜欢吃花生酱”),系统应保留高重要度的记忆(过敏信息),并向用户发起澄清对话,而非自动覆盖。这需要一整套机制:识别、记录、澄清、更新。
无法裁决时,发起澄清:对于无法确定的高风险矛盾,最佳实践是由智能体在后续对话中主动、自然地求证:“您之前提到是素食者,现在开始尝试生酮饮食了,需要我更新您的饮食偏好吗?”
通过这套机制,长期记忆才能成为一个个性化、自洽、一致且可泛化的长期知识库,真正体现智能体对用户认知的深度与精度。
六、离线记忆巩固流程:细化的架构与处理
长期记忆的演进成长,集中体现在一个精密的离线记忆巩固引擎中。它独立于实时对话服务运行,由事件驱动或定时触发,是智能体进行的认知整理活动。
其核心处理流程可细化为以下五个阶段:
第1阶段:触发与原料获取
触发条件:满足“累积 N 轮未处理对话”或“距离上次处理已过 T 秒”任一条件,即触发一次巩固任务。
原料:拉取自上次巩固点以来的所有原始对话记录(工作记忆/对话记忆的完整转录)。这确保了信息的完整和准确,避免了只从会话记忆摘要中提取可能带来的信息丢失。
第2阶段:检索相似记忆
动作:使用语义向量检索记忆库,调取相关记忆记录。
输入:对话记录(的向量)。
输出:一份结构化的长期记忆清单,包括ID、内容及相关标签。
第3阶段:候选记忆生成、冲突检测与裁决
动作:使用前述设计的提示词,调用记忆提取 LLM。
输入:完整的对话记录 + 检索到的相关现有长期记忆(含 ID)。
输出:一份结构化的候选操作清单,包含
add、update、delete列表。注意:前述提示词只是简化的示例,实际生产中不止要识别相似,还要识别冲突,进一步根据“时效性、明确性、重要度”规则进行综合裁决。决定是直接执行、合并还是标记为“需用户澄清”。之所以要这么多事情,去重和冲突检测本质上都是回答同一个核心问题:“新信息与某条现有记忆之间的语义关系是什么?”。
第4阶段:执行与巩固
动作:将经过裁决的、无冲突的最终操作写入记忆库。
包括:新增记忆,更新旧记忆的内容和元数据,标记需删除的记忆为“已归档/休眠”。
热度初始化:新记忆获得初始热度分数;被更新的记忆继承旧记忆的热度或基于新时间重置。
第5阶段:后处理与质量维护,兜底前4阶段不能覆盖事项
语义去重、冲突消解任务:定时对全部记忆进行向量相似度聚类,基于语义进行去重或消解冲突。
遗忘调度任务:定时扫描热度分数,归档极低分记忆。
升级审核任务:挖掘高频、共性记忆,生成“智能体级知识”候选,推送至人工审核队列。
这个五阶段的离线巩固流程,构成了一个完整的从“原始对话”到“精炼知识”的数据加工流水线。它确保了长期记忆不只是简单的存储,而是一个持续演化、自我修复、不断精进的认知核心。
七、统一的架构:分层记忆管理器
至此,我们可以构建一个统一的分层记忆管理器,作为智能体的核心认知组件。
整个分层记忆架构本身,就是为了“完成任务”和“演进成长”这两个目标而生的,每一层天然地偏重于一个目的。 它不是两个独立的系统,而是一个统一体,内部严格分区,协同工作。
架构示意:
短期记忆(对话记忆、工作区):一个滑动窗口,存储最近 N 轮对话原文。服务于实时推理。
中期记忆(会话记忆、情景缓冲器):一个会话级的、动态更新的状态对象(任务表单、作品快照)。服务于当前会话的连贯性。
长期记忆(持久知识库):一个持久化、可检索的数据库。服务于跨会话的演进成长。
信息流动逻辑:
在线流程:用户输入 -> 对话记忆更新 -> 触发会话记忆更新 -> 检索长期记忆 -> 所有信息按优先级组装 -> 发送给LLM。
离线流程:后台进程直接审视原始的对话记忆,识别并提取其中具有长期价值的信息,经过去重、合并和冲突解决后,巩固到长期记忆库。这个过程,就是成长本身。在缺乏显式“会话记忆”的开放域闲聊场景中,该流程尤其关键,因为它直接从最完整的对话原文中,剥离出能够跨越会话的持久用户特质。
八、原则与记忆:顶级约束的剥离
最后,我们必须严肃区分智能体的“记忆”与“原则”。顶级原则(如宪法级业务规则、价值观、评价标准)不属于记忆系统,而属于独立的策略系统或对齐层。
记忆回答“事实是什么”:是动态的、被交互数据持续更新的。
原则回答“应该怎么做”:是固定的、由设计者或合规团队显式定义的,需要严格的版本控制和审计。但长期记忆中的高频、共性事实,可在人工审核后升级为智能体全局知识,若该知识具有规则属性(如“用户普遍期望收到确认短信”),则可进一步沉淀为原则。
在上下文组装时,原则总是作为不可篡改的基底被注入,其优先级绝对高于任何记忆。这一解耦保证了智能体的行为既富有个性化记忆,又始终安全可控。
九、结论:从上下文管理到认知架构
处理长对话的挑战,最终引导我们超越简单的“压缩”技巧,开始思考智能体的认知架构。通过区分需求场景,理解意图与生命周期,并构建一个分层、统一、异步的记忆系统,我们不仅能解决上下文窗口的物理限制,更能让智能体在每一次“完成任务”的过程中,悄然“演进成长”。这或许正是构建真正智能、个性化且安全的AI伙伴的必经之路。
附录1:长期记忆记录属性
基于我们之前的深入讨论和业界实践,一个设计精良的长期记忆记录通常会包含以下属性。我将它们按功能维度分组,并标注其核心用途。
属性分组 | 属性名 | 类型 | 必填 | 说明 |
核心标识与内容 | 记忆ID( | String / UUID | 是 | 全局唯一的记忆标识符。这是记忆的“主键”,用于精确的更新、删除和关联操作。在提示词中必须携带,以便LLM引用。 |
用户ID( | String | 是 | 记忆所属的主体。在面向用户的设计中,即用户ID;在多智能体场景中,也可能是智能体自身的ID。 | |
记忆内容( | Text | 是 | 记忆的核心文本,必须是上下文完整、可独立理解的自然语言陈述。反例:“他喜欢它”;正例:“用户偏好使用Go语言进行后端开发”。 | |
语义与结构分类 | 记忆类型( | Enum | 是 | 记忆的类型: 类型决定了记忆的提取策略优先级、检索权重和冲突裁决中的处理方式。 |
作用域( | Enum | 是 | 记忆的作用域: 决定了记忆的生命周期边界和检索时的分区策略。 | |
时间与生命周期 | 创建时间( | Timestamp | 是 | 记忆首次被提取的时间。 |
更新时间( | Timestamp | 否 | 记忆内容最近一次被修改的时间(如被 update 操作更新)。 | |
最后访问时间( | Timestamp | 否 | 记忆最近一次被检索并注入上下文的时间。这是计算热度分数的核心依据。 | |
质量与优先级 | 重要性( | Float (0.0 - 1.0) | 是 | 由记忆提取 LLM 在创建时赋予,或由离线任务后续调整。表示该记忆对用户画像和任务完成的长期价值。 高重要度示例:过敏原、长期指令、核心身份信息; 低重要度示例:临时兴趣、一次性闲聊话题。 用途:作为遗忘机制的兜底保护,高重要度记忆即使热度低也难被遗忘;同时影响检索排序。 |
热度分数( | Float | 是 | 基于访问频率和时间衰减的动态分数。 更新公式: 用途:驱动检索权重;当热度低于阈值时触发归档或休眠。 | |
确认状态( | Enum | 否 | 记忆的确认程度: 冲突裁决时, | |
检索与向量 | 向量嵌入( | Vector (Float[]) | 是 | 记忆内容的向量化表示,用于语义相似度检索和冲突/去重的第一道召回。维度取决于所选 Embedding 模型(如 OpenAI text-embedding-3-small 为 1536 维)。 |
溯源与治理 | 来源对话ID( | String / UUID | 否 | 产生该记忆的原始对话记录标识。用于追溯、复核和调试。 |
来源会话ID( | String / UUID | 否 | 产生该记忆的会话标识。配合来源对话ID可实现精确溯源。 | |
提取模型版本( | String | 否 | 记录提取该记忆时使用的模型及提示词版本。当模型或策略升级时,可用于回溯性地重新评估旧记忆的质量。 | |
状态( | Enum | 是 | 记忆的当前状态: |
附录2:ConversationSummaryBufferMemory 的历史局限
许多人熟悉LangChain框架下的ConversationSummaryBufferMemory,设定一个最大token阈值,优先保留最近N轮完整原文;当整体历史超过阈值时,将超出部分最老的那些轮次压缩为摘要,并不断更新该摘要。
目前看其实是一个非常粗糙的实现,或者说,它是一个在特定历史阶段和简单场景下有效的过渡方案。 它的粗糙体现在以下几个核心维度,而这些维度恰恰是前面深入讨论试图解决的。
ConversationSummaryBufferMemory 的流行有其历史合理性:
LLM 能力限制:早期模型的指令遵循和结构化输出能力不强,难以稳定地完成我们设计的“记忆提取-裁决”任务。简单的摘要生成是当时模型能可靠完成的少数高级任务之一。
应用场景简单:早期的 LLM 应用多为单轮问答或简单对话,还没遇到复杂的、需要长期个性化的 C 端产品需求。
快速原型验证:对大多数开发者来说,它是一个开箱即用、能快速解决“上下文超限”问题的 LangChain 组件,认知门槛低。
可以把它类比为认知进化史上的“反射弧”:它在简单环境里有效、快速、消耗低,但完全不足以支撑需要学习、记忆和规划的高等智能。
我们这一轮长篇讨论的真正成果,是完成了从“如何管理上下文窗口”到“如何设计智能体的认知架构”的范式转换。
ConversationSummaryBufferMemory 回答的问题是:“窗口满了,怎么办?”
我们的分层记忆架构回答的问题是:“智能体应该如何记住、理解并从交互中成长?”
前者是一个工程修补,后者是一个架构设计。用一个简单的缓冲摘要机制,去支撑我们花了十几个回合讨论的复杂需求(长期记忆、去重、冲突、遗忘、跨会话成长),是远远不够的。而我们最终构建的,是一个试图触及记忆本质的认知核心。
夜雨聆风