万物皆有声,关注即共鸣...
在 AI Agent 的实际应用中,最常见的瓶颈往往不是模型能力不够强,而是“记忆”不够聪明。传统的记忆方案把记忆简单等同于历史消息的拼接,导致长会话成本失控、跨会话完全失忆、知识冲突无法解决。
01 为什么 Agent 必须具备“记忆”
上下文窗口线性膨胀:对话越长,token 消耗越高,推理成本就越不可控;
跨会话完全失忆:用户每次都需要重复地描述职业、偏好、背景等信息;
知识无法演化:旧信息与新信息并存、甚至矛盾冲突(如“用户用 Python” vs “用户已切换 Go”);
检索能力较弱:检索依赖关键词匹配,语义召回能力弱,难以理解“喜欢简洁回复”和“请用短回答”之间的语义关联;
Tool 调用风险:硬截断可能拆散 AIMessage (tool_calls) 与 ToolMessage,导致 LLM API 直接报错。
追究其本质问题:系统只有存储,没有认知结构。它记得“说过什么”,却不理解“你是谁”和“你想要什么”。一个真正可用的 AI Agent,必须拥有类似人类的记忆分层与自我演化机制。
02 如何让记忆成为可演化的认知
分层记忆:不同时间尺度的信息各司其职。当下的对话、今天的会话、跨月的偏好,用不同的策略管理。
语义检索:真正理解用户意图,而非字面匹配。“写后端”能找到“Python”,“喜欢简洁”能命中“别啰嗦”。
记忆演化:自动去重、解决冲突、动态更新。“用 Java”变成“用 Go”时,系统知道该信哪条。
用户建模:把零散的偏好和事实,聚合成对用户的整体认知——一千条碎片不如一段凝练的画像。
最终愿景简单地“记住更多对话记录”,而是让记忆像人的认知一样,能够自我更新、消解冲突、聚合成型。用户不用反复解释自己是谁、喜欢什么,系统却能自然地给出越来越贴合的回应。更重要的是,这种理解不是黑盒——用户能随时查看系统记住了什么,能纠正,能优化。
03 L1~L3 三级记忆框架
感觉记忆:你走过街角,余光扫到一张广告牌。这几百毫秒内,信息还在,但转眼就忘了——因为它没进入下一步加工。容量巨大,但转瞬即逝;
短期记忆:也被称为工作记忆,如你心算 37 × 8。先算 7×8=56,记着“56”和进位“5”,再算 3×8=24 加上进位得 29,最后拼成 296。这个过程中你临时“记住”的那几个数字,就是工作记忆;
长期记忆:你记得小学班主任的名字、记得 Python 的语法、记得上周和朋友的约定。它又分为两类——情景记忆(如上周三我和谁吃了饭)和语义记忆(如北京是中国的首都)。
感觉记忆过滤掉 99% 的噪音,工作记忆处理当下的任务,长期记忆提供背景知识。用最小的即时成本,保留最有价值的长期信息。
近两年来,随着AI Agent技术的爆发期,这套模型被快速地搬进了 AI 系统,成为了AI Agent领域中一种常见的分层记忆设计方案。许多Agent记忆综述论文(如2025-2026年的《AI智能体时代下的记忆》)也都明确引用了这些认知科学理论作为分层设计的理论基础。它主要用于解决大语言模型(LLM)上下文窗口有限、长期交互中记忆丢失、检索效率低等一系列问题。
在实际的系统设计中,我们采用了"基座层(L1~L3) + 演进层(P1~P5)"的渐进式增强架构。L1~L3 是核心基座,每层可独立启用,其中:
3.1 L1 工作记忆(Working Memory)
人脑的工作记忆只能同时处理 7±2 个组块。AI 的上下文窗口也一样,token 上限也是硬约束。L1 的职责就是把对话历史“修剪”到能塞进窗口的程度,控制 token 成本,同时还要保证关键信息不丢失、上下文完整。比如工具调用的请求和结果必须成对保留,拆散了 API 直接报错。
在L1层的一些可参考的关键设计策略如下:
1)分离 SystemMessage:所有 SystemMessage 始终保留,不参与裁剪;
2)Tool 调用成组保留:当 AI 决定调用工具时,会先输出一条带 tool_calls 的消息,然后工具执行完返回一条 ToolMessage。这两条消息必须成对出现——如果裁剪算法把 tool_calls 留下了却把 ToolMessage 裁掉了,LLM API 会直接报错。所以裁剪逻辑要先做分组:
AIMessage(tool_calls=[...]) + ToolMessage + ToolMessage → 一组(不可拆散)HumanMessage → 独立一组AIMessage(无 tool_calls) → 独立一组
3)从最新向前保留:逆序遍历分组,遇到一组就整组拿下,直到逼近 token 上限。
4)注入 L2 摘要:若有 memory_context,可以作为额外 SystemMessage 注入。
3.2 L2 情景记忆(Episodic Memory)
人L1 解决了单次请求的裁剪问题,但如果用户一口气聊了 50 轮,token 消耗还是会线性攀升——每轮都把 50 轮历史全带上,谁也扛不住。
因此,L2 的思路是渐进式摘要:即对单个会话的对话历史进行渐进式摘要,将冗长的早期对话压缩为紧凑文本,注入后续请求的上下文中。这样,新消息来的时候只带摘要 + 最近的对话。
触发机制为:每攒够 N 轮(比如 10 轮),就对新增部分做一次摘要,和已有的旧摘要合并:
第 10 轮:消息[0:10] → LLM → 摘要 v1第 20 轮:消息[10:20] + 摘要 v1 → LLM → 摘要 v2第 30 轮:消息[20:30] + 摘要 v2 → LLM → 摘要 v3
每次只对增量部分调用 LLM,token 成本恒定,不会随着对话轮数增长。这段摘要会作为额外的系统消息注入到后续每一轮对话中,让 AI 始终记得“我们之前在聊什么”,即:
1)渐进式摘要流程
第 1 次摘要(sort_order=10):snapshot_messages[0:] → LLM → summary_v1persist(message_range_end=len(snapshot_messages))第 2 次摘要(sort_order=20):range_end = 上次存储的 message_range_endsnapshot_messages[range_end:] → LLM(带 existing_summary=summary_v1)→ summary_v2persist(message_range_end=len(snapshot_messages))
这类需要注意的是:message_range_end 存储的是 checkpoint snapshot_messages 数组的长度,而非 DB sort_order;增量切片只对新增消息调用 LLM,避免 token 成本随对话轮次线性增长。
2)Prompt 模板
首次摘要:
请对以下对话内容生成一段简洁的中文摘要,保留关键信息、决策和结论。摘要应包含:1. 讨论的主要主题2. 达成的关键决策或结论3. 重要的上下文信息(如用户偏好、约束条件)
渐进式合并:
请将以下已有摘要与新增对话合并,生成一段更简洁的中文摘要。保留最重要的信息,去除冗余。
3.3 L3 语义记忆(Semantic Memory)
L2 解决了单次会话内的记忆问题,但用户关掉页面明天再来,一切又归零了。
L3 要做的就是跨会话的记忆(这次说过的话,下次还记得):即从对话中自动抽取用户事实、偏好、习惯和长期指令,跨会话存储,在后续会话中通过语义检索按需召回。
1)事实抽取:从对话中抽丝剥茧
每次对话结束后,系统会在后台异步运行一个抽取流程(不阻塞用户响应)。抽取器拿到最近几轮对话(这里一般仅对最近 几 条消息进行调用抽取,避免全量历史 token 浪费;如果消息数 < 2 时,可以跳过抽取,以节省 LLM 调用),调用 LLM 提取出结构化的“事实”:
偏好:用户喜欢简短的回答;
事实:用户是一名 Python 后端开发;
指令:“回答时尽量附带代码示例”
抽取的结果必须带着一个初始置信度(如默认 0.8)。
2)语义去重与冲突消解
同一个事实可能被反复提及(“我用 Python”和“我的主力语言是 Python”),系统不应该存两遍。这里可以使用类似向量相似度等方法来进行判断:如果新事实和已有记忆的相似度超过 0.92,就认为是同一件事,不新增记录,而是给原有记忆的置信度 +0.05。
更有意思的是冲突消解:假如用户三个月前说“我用 Java”,今天说“我改用 Go 了”,相似度在 0.7 到 0.92 之间——像是相关但可能矛盾。这时系统会调用 LLM 做一次判定:“这两条信息是否矛盾?”如果矛盾,旧记忆被标记为“已取代”,新记忆带着更高的版本号入库。
记忆不再是只增不改的静态列表,而是有了生命周期。以向量存储为例,整个存储流程如下:
store_facts(facts):1. 过滤低置信度2. 容量控制 — 超出用户上限时淘汰低价值记忆3. 批量生成 embedding(MemoryEmbeddingService.embed_batch)4. 对每条事实 → _smart_store():a. find_similar_memories(向量搜索 top 3)b. sim > 0.92 → 语义去重:_dedup_boost(增强置信度 +0.05,采用更长表述)c. 0.7 < sim < 0.92 + 同类型 → _detect_conflict(LLM 判定)→ YES → _resolve_conflict(插入 version+1,旧记忆 superseded)d. 无匹配 → 插入新记录(含 embedding)
3)检索:精准找到相关的记忆
下次用户提问时,系统把问题转成向量,在用户的历史记忆里做语义搜索,找回最相关的几条,格式化后注入到系统提示词里。当然,未启用 embedding 时也可以使用 n-gram 关键词匹配等方式。
举个例子:用户问“帮我写个排序函数”,向量检索可能召回“用户偏好 Python”“用户喜欢简洁的代码”“用户讨厌递归写法”——这些信息让 LLM 的回答一下子有了针对性。
4)记忆衰减:让记忆库保持“苗条”与健康
如果说容量控制是应对“爆发性增长”的急刹车,那么记忆衰减就是一套维持长期健康的“新陈代谢”机制。它的核心逻辑很简单:记忆的价值不是永恒的,长期不用或质量存疑的记忆应当被归档或遗忘。
系统可以通过一个定期运行 decay_memories 任务来执行这套标准。它不会“一刀切”地删除旧数据,而是根据两条动态规则来判定一条记忆是否应该“退役”(标记为 is_active=False):
过期且低频:创建时间 > max_age_days 且 access_count <= 2
这条规则处理的是“沉默的旧事”。一条记忆即使存了很久(例如超过90天),但如果它经常被检索到(access_count 高),说明它依然有用,系统会保留它。只有当它既老旧又几乎没被想起过,才判定为失去了参考价值。这避免了将用户偶尔提及但长期相关的核心信息(如“我是后端开发”)误删。
低置信度:confidence < decay_min_confidence
这条规则是反馈闭环的延伸。如果一条记忆因为用户多次点踩(负反馈)或者长期被新事实挑战,导致置信度跌破阈值(例如 0.3),这意味着系统本身已经对它产生了怀疑。无论这条记忆是昨天刚创建的,还是上个月创建的,只要置信度低到“不可信”的程度,衰减机制就会介入,将其打入“冷宫”。
通过这套组合拳,记忆库不需要人工干预就能完成自我净化:有价值的记忆常驻内存,无用的记忆随风而逝,错误的记忆自动淘汰。
值得注意的是:当前 L3 本质上是一个 "只追加、不演化" 的扁平事实列表,检索召回的也只是“点状”的记忆碎片,它不能回答:
- 用户上个月喜欢 Java,这个月改用 Go 了 → 哪个是最新偏好?- "用户是后端开发" 和 "用户做 Python 后端开发" → 应该合并还是并存?- 抽取了 200 条记忆 → 哪 5 条对当前对话最相关?- 系统记住的偏好对不对 → 用户如何纠正?
有些场景下,AI 需要对用户有一个整体认知。鉴于篇幅原因,下一篇文章我们将重点阐述如何通过“五阶段增强演进”来解决这些问题... ...
夜雨聆风