1、引言:Agent 的"金鱼记忆"问题
大模型是无状态的:每次推理,它拿到的只有当前塞进上下文窗口的那点 token;请求一结束,说过什么、对方是谁、聊到哪,统统清零。这就是 Agent 的**"金鱼记忆"**------问题不在记性短,而在每次会话都从一张白纸重新开始。
单轮问答时代,这无关痛痒;但 Agent 的目标远不止于做一个随问随答的"工具"。一旦它要长期驻场、持续陪伴、独立推进任务,就必须成为真正意义上的长期协作者------懂你的偏好、记得共同的上下文、能接着上次的进度继续干。而能否撑起这个角色,全系于记忆:
- 答非所问:不记得用户上一句刚说的背景,反复追问;
- 重复劳动:不记得昨天已经得出的结论,重新推导一遍;
- 缺乏个性化:不记得用户偏好,每次都给出"千人一面"的答案;
- 知识断层:模型自身训练数据过时或缺失,回答不了私有知识。
因此,记忆系统是 Agent 从"一次性工具"跃迁为"长期协作者"的分水岭。它要回答四个朴素却根本的问题:记什么、存哪里、怎么取、何时忘。
本文不从某一框架的实现讲起,而是先建立一套通用的"记忆层次"坐标系,再对齐业界三大主流范式,最后落到 XXL-AI 的工程实现------它实际上已经实现了一套完整的"巩固式长期记忆",这是本文最值得读的部分。
2、原理:从认知科学拆解记忆层次(纵轴)
设计 Agent 记忆时,最自然的参照是人脑的记忆模型。认知科学将记忆按"信息能存多久"切分为若干层次,这一"生命周期"维度,正是本文用来组织全部内容的纵轴。
记忆层次(纵轴:信息的生命周期)
┌───────────────────────────────────────────────────────────────┐
│ 短期 / 工作记忆 │
│ = 上下文窗口内的即时上下文(易失、容量受限、随请求结束而丢) │
├───────────────────────────────────────────────────────────────┤
│ 长期记忆(跨会话持久,按内容性质再细分) │
│ ├─ 语义记忆 semantic 事实 / 知识 → 知识库(向量检索) │
│ ├─ 情景记忆 episodic 画像 / 偏好 / 经历 → 长期记忆实体 │
│ └─ 程序记忆 procedural 技能 / 流程 → 技能沙箱 │
└───────────────────────────────────────────────────────────────┘
2.1、短期 / 工作记忆(Short-term / Working Memory)
短期记忆是 Agent 维护"当前对话与任务"的即时上下文,一般就是上下文窗口里装的那些东西:本轮历史消息、中间推理、工具调用结果。
它的特点决定了它的局限:
- 易失:请求一结束就丢失,跨请求不保留;
- 容量受限:受模型上下文窗口上限(几 K 到几 M token)与成本双重约束,不可能无限塞;
- 即时可用:无需检索,直接参与生成,是最"快"的一类记忆。
业界(Letta、AWS 等)的主流口径里,短期记忆 = 工作记忆 = 上下文窗口内的消息缓冲,两者不作区分。少数资料会再把"当前上下文(RAM)"与"单会话历史"切开,但工程上合并处理更实用,本文采用合并口径。
2.2、长期记忆的三种形态(Long-term Memory)
长期记忆是跨会话、跨任务持久保存的那部分,对应人脑中稳定存续的记忆。按"内容性质"可再细分为三种,这种细分在工程上极具指导意义,因为它直接对应三种不同的存储与检索手段:
- 语义记忆(semantic) :事实与知识,如"公司报销制度""产品参数""行业术语"。特点是面向全体用户、可共享、相对静态 ,工程上对应知识库 + 向量检索(RAG)。
- 情景记忆(episodic) :某个具体用户的画像、偏好、过往经历,如"张三是后端工程师""偏好精简回答""上周在推进 A 项目"。特点是面向个体、随交互持续更新 ,工程上对应用户长期记忆实体。
- 程序记忆(procedural) :怎么做事,如"调用某接口的流程""使用某工具的步骤"。特点是可复用、可执行 ,工程上对应技能 / 工具沙箱。
把这三者分开,是为了避免一个常见的混乱:很多人把"知识库"当成"记忆"的全部,其实知识库只覆盖了语义记忆这一小块,用户的个性化情景记忆、可执行的程序记忆同样不可或缺。
3、业界三大范式(横轴)
如果说"记忆层次"回答的是存多久(纵轴) ,那么"范式"回答的则是怎么构建与存取(横轴)。按"记忆如何被构建、如何与模型交互"这条主线,业界方案可以收敛为三大范式。
说明:关于范式划分,学界有更细的分类(如 arXiv:2606.06448 将结构化记忆再拆成 append-only 与 consolidating 两小类,合计四类;Atlan 归纳为五种模式)。本文为聚焦主线,将"结构化记忆"统一为一大类,收敛为"三大",读者可据此再向下钻取。
3.1、范式一:上下文内记忆(Long-context / Prompt Stuffing)
核心思想:把历史内容原样(或经摘要压缩)塞进上下文窗口,模型上下文本身就是记忆载体,不引入任何外部存储。
- 优点:零架构、零依赖,开箱即用;
- 缺点:受窗口长度与 token 成本硬约束,历史一长就"装不下",且每次都要为全部历史付费。
这是最原始、也最普遍的起点。几乎所有 Agent 的第一版都长这样,随后都会因"窗口装不下"而被迫演进。
3.2、范式二:外部检索记忆(Flat RAG)
核心思想:记忆仍以原始片段(chunk)落外部存储(向量库或倒排索引),查询时按相关性召回 Top-K 注入上下文,而不是全量塞入。
- 代表:embedRAG、BM25 等;
- 优点:突破窗口上限,可扩展到海量知识,按需付费(只召回相关片段);
- 缺点:只做了"平铺式"检索,缺乏结构;对"事实性、关系性、时序性"等复杂记忆查询力不从心。
这一范式是当前"知识库问答"的主流,但它本质上仍是查询驱动的被动召回------记忆本身没有被"整理",只是被"索引"了。
3.3、范式三:结构化自管理记忆(Structured + Consolidating)
核心思想 :不再被动索引原始片段,而是主动从交互流中抽取、提炼、结构化 出记忆,并由 Agent(或后台进程)自我维护与更新------即"巩固(consolidation)"。
-
代表:Mem0、Letta(MemGPT)、GraphRAG、HippoRAG;
-
经典参照是 MemGPT(arXiv:2310.08560)提出的 OS 式三层记忆:
MemGPT / Letta 的 OS 式三层记忆(类比计算机存储)
┌───────────────────────────────────────────────────────────────┐
│ Core Memory(核心记忆,类比 RAM) │
│ = 常驻上下文的定长块:角色设定 + 用户画像 + 当前任务 │
├───────────────────────────────────────────────────────────────┤
│ Recall Memory(召回记忆,类比磁盘缓存) │
│ = 可检索的近期对话历史,按需换入上下文 │
├───────────────────────────────────────────────────────────────┤
│ Archival Memory(归档记忆,类比冷存储) │
│ = 长期压缩存储,向量检索按需换入 │
└───────────────────────────────────────────────────────────────┘ -
优点:把"记忆"从"原始片段"升级为"结构化、可巩固、自我更新"的知识,更接近人脑;
-
代价:抽取需额外调用模型(有延迟与幻觉污染风险),且引入了巩固时机、一致性、遗忘策略等新复杂度。
这一范式的关键词是**"巩固式"**------它不是简单地"存对话",而是"把对话蒸馏成记忆"。XXL-AI 的长期记忆正落在这个范式上。
3.4、三范式对照与取舍
| 维度 | 范式一 上下文内 | 范式二 外部检索(RAG) | 范式三 结构化自管理 |
|---|---|---|---|
| 记忆载体 | 模型上下文窗口 | 外部向量/倒排索引 | 结构化记忆实体 |
| 记忆形态 | 原始历史片段 | 原始 chunk(索引) | 抽取/巩固后的结构化记忆 |
| 交互方式 | 全量注入 | 查询时 Top-K 召回 | 主动抽取 + 按需注入 |
| 突破窗口上限 | ✗ | ✓ | ✓ |
| 个性化 / 跨会话 | 弱 | 弱(偏知识) | 强 |
| 实现复杂度 | 极低 | 中 | 高 |
| 代表 | 原生对话 | embedRAG / BM25 | Mem0 / Letta / GraphRAG |
需要特别强调:三者并非互斥的"三选一",而是可叠加的。一个成熟的 Agent 通常三者并用------范式一承载即时上下文、范式二承载共享知识、范式三承载个体化的长期记忆。XXL-AI 正是这样一个混合体。
4、桥梁:记忆的消费------上下文工程(Context Engineering)
记忆"存"下来只是第一步,真正决定 Agent 表现的是**"怎么用"。Letta 有一句精辟的论断:记忆的本质,是决定在每一个时刻往上下文窗口里放哪些 token**。
这就引出了"上下文工程(Context Engineering)"------它是记忆系统的"调度员",负责在生成前,把记忆检索、组织、注入到上下文里:
- 检索触发:什么时候去取记忆?取多少?(如知识库的 Top-K、相似度阈值)
- 注入组织:取回的记忆以什么形式拼进上下文?(追加到系统指令?还是作为参考资料?)
- 冲突消解:记忆与用户当前表述冲突时以谁为准?
- 压缩与遗忘:上下文超限时如何摘要、卸载、淘汰?
XXL-AI 对"注入"的处理很有代表性(下文第 5 节):把长期记忆以 Markdown 追加到系统指令末尾,并明确约定**"若与用户当前表述冲突,以当前表述为准"**------这一句看似简单,却是防止"记忆反噬"的关键护栏。
5、XXL-AI 落地实践
下面落到 XXL-AI 的真实实现。它的记忆体系可以概括为一句话:短期记忆靠会话消息、长期记忆靠"巩固式"实体 + 异步归档、语义记忆靠知识库向量检索、程序记忆靠技能沙箱,四者各归其位,通过 harness 运行时层统一调度。
5.1、整体架构
css
┌─────────────────────────────────────────────┐
│ 对话请求(用户 / 访客) │
└──────────────────────┬──────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ ChatStreamTool(对话生成 worker,harness) │
│ ① 历史消息 SessionMsg(短期记忆,按会话装载) │
│ ② 系统指令 Agent.systemPrompt │
│ ③ 长期记忆注入 MemoryTool.loadMemory + mergePrompt │
│ ④ 工具装配 MCP / Skill / 终端·文件沙箱 │
│ ⑤ RAG Advisor RagTool.buildAdvisor(知识库语义检索) │
└──────────────┬──────────────────────────────┬────────────────┘
▼ ▼
┌────────────────────┐ ┌─────────────────────────────────┐
│ LLM 流式生成 │ │ Milvus 向量库(语义记忆) │
└──────────┬─────────┘ └─────────────────────────────────┘
▼
┌────────────────────┐
│ SessionMsg 落库 │ 短期记忆持久化(reasoning+content)
└──────────┬─────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ MemoryArchiveTool(异步归档,60s 心跳 + Redis 抢锁) │
│ 新消息 > 水位线?→ MemoryTool.generate → upsert + 推进水位线│
└────────────────────────┬─────────────────────────────────────┘
▼
┌──────────────────────────────────┐
│ Memory 长期记忆(Agent×访客) │
│ 画像 / 偏好 / 事实 / 目标 / 进展│
└──────────────────────────────────┘
整条链路分三环:对话侧(同步注入记忆并生成)→ 存储侧(消息落库)→ 归档侧(异步把消息蒸馏成长记忆)。记忆的生成与消费解耦------对话时只"读"记忆,绝不阻塞生成去做"写"记忆,后者交给后台定时器异步完成。
5.2、短期记忆:会话消息(SessionMsg)
短期记忆由会话消息承载。每条消息记录角色、正文、思考过程与状态:
bash
SessionMsg 实体(短期记忆)
id 消息ID
sessionId 对话ID
role 角色:user-用户 / assistant-助手
reasoning 思考过程(推理模型 reasoning_content)
content 消息内容
status 状态:0-生成中 / 1-完成 / 2-失败
对话发起时,ChatStreamTool 按会话装载历史消息(角色序列 + 内容序列)作为短期记忆,与系统指令、长期记忆、工具、RAG 上下文一起拼成最终的生成输入。
5.3、长期记忆:实体模型 + 运行时工具
长期记忆是本文的"巩固式"核心。它由两部分构成:元数据 CRUD(业务层)+ 运行时生成/注入(harness 层)。
(1)实体模型 :以 Agent × 访客 为唯一维度,单字段 Markdown 维护记忆内容,并按固定维度分节:
shell
Memory 实体(长期记忆,Agent × 访客 维度)
id 记忆ID
agentId AgentID
visitorId 访客标识
content 记忆内容(Markdown,按维度分节,见下)
lastMsgId 最后归档处理的消息ID(增量水位线)
addTime 新增时间
updateTime 更新时间
content 的固定维度(无内容的维度整节省略):
## 用户画像 身份、称呼、职业、所在地等
## 偏好习惯 沟通偏好、回答风格、常用语言与工具等
## 关键事实 用户明确告知、需长期记住的事实性信息
## 目标与意图 核心诉求、长期目标与待办事项
## 近期进展 最近对话中已推进的事项、结论与下一步
之所以用"单字段 Markdown + 固定维度",而非拆成多张表、多个字段,是因为:记忆要直接拼进系统指令给模型看,Markdown 天然就是模型的"母语",且维度枚举约束了抽取的稳定性,可读可控。
(2)运行时工具 MemoryTool(harness 层):承载三项底层能力,与业务解耦:
loadMemory(agentId, visitorId):按 Agent×访客 读取已归档记忆;mergePrompt(basePrompt, memory):把记忆追加到系统指令末尾(记忆为空则原样返回);generate(agent, oldMemory, newMsgs, maxLen):以「历史记忆 + 最新对话」为输入,调模型按固定维度抽取、合并、去重、淘汰过时内容,并定长截断(默认 2000 字符)。
注入与抽取的提示词关键约束如下(节选),它保证了抽取结果的格式稳定:
你是长期记忆归档助手。请基于【历史记忆】与【最新对话】,按下列维度抽取并更新用户长期记忆。
要求:合并历史记忆与新对话信息,删除重复与已过时内容,保持精炼;总长度不超过 {maxLen} 字。
5.4、记忆归档:增量水位线 + 定时调度
记忆的"写"由 MemoryArchiveTool(harness 层)异步完成,是一条不打扰对话主流程的后台流水线:
ini
调度节奏:CyclicThread 固定心跳(60s)
互斥竞争:Redis SETNX 抢锁(TTL=心跳间隔,仅作互斥;抢不到直接跳过)
增量判断:遍历开启记忆的 Agent → 每个访客 → 比较 max_msg_id 与水位线 last_msg_id
抽取生成:取水位线之后的新消息 → MemoryTool.generate(历史记忆 + 新消息) → 定长记忆
落库推进:MemoryMapper.upsert(按 agent_id + visitor_id 唯一键)→ 推进 last_msg_id
其精髓在"增量水位线":
ini
水位线增量归档:
SessionMsg: ... [id=100] [id=101] [id=102] [id=103] ... (时间正序)
上次归档水位线 last_msg_id = 101
└──────────┬──────────┘
本次归档:读取 id > 101 的新消息
generate(历史记忆 + 新消息) → upsert
推进水位线 last_msg_id = 103
last_msg_id 记的是"已处理到哪",于是每次归档只增量处理新增消息,而不是重扫全量历史;upsert(按 agent_id + visitor_id 唯一键)则保证同一用户只有一份最新记忆,天然幂等。这套设计在成本与正确性之间取得了很好的平衡。
5.5、语义记忆:知识库 RAG(Milvus)
语义记忆(共享知识)走知识库 + 向量检索,由 RagTool(harness 层)统一收敛,底层是 spring-ai + Milvus:
- 隔离 :每个知识库一个集合
kb_base_{baseId},向量存储实例按知识库 ID 缓存复用(LRU 上限 128); - 向量化:分片(chunkSize / chunkOverlap)→ Document(含 docId / chunkIndex 元数据)→ 写入 Milvus(先清旧向量,支持重复向量化);
- 检索 :
similaritySearch按 COSINE 相似度召回 Top-K,返回{text, docId, chunkIndex, score}; - 注入 :为对话装配
QuestionAnswerAdvisor,检索上下文自动注入系统提示。
值得注意的是降级策略:单库 RAG 装配失败(如向量库不可达)时跳过该库继续对话,并把降级提示写入思考流------这与长期记忆"注入失败则降级为原始系统指令"是同一套"记忆永不阻塞主流程"的容错哲学。
5.6、落地小结:XXL-AI 属于哪种范式
把 XXL-AI 的记忆体系对回第 3 节的坐标系,结论很清晰------它是一个以"范式三(巩固式)"为主、范式二(知识 RAG)为辅的混合架构:
| 记忆类型 | XXL-AI 落点 | 对应范式 |
|---|---|---|
| 短期/工作记忆 | SessionMsg(含 reasoning、状态机) | 范式一(上下文内) |
| 语义记忆(知识) | knowledge 模块 + RagTool + Milvus | 范式二(外部检索) |
| 情景记忆(画像/偏好) | Memory 实体 + MemoryTool + MemoryArchiveTool | 范式三(巩固式) |
| 程序记忆(技能) | skill 模块 + SkillToolFactory 沙箱 | 范式三(可执行) |
6、权衡与展望
巩固式记忆并非没有代价,值得在每个工程决策时反复掂量:
- 抽取延迟:归档需额外调模型,故 XXL-AI 选择异步定时而非对话内同步,牺牲实时性换取主流程流畅;
- 幻觉污染:模型抽取记忆可能"编造"或"误解",因此用固定维度 + 定长截断 + Markdown 规范化来约束,但仍需人工可干预(管理端支持查看/修改/删除记忆);
- 定长截断丢信息:2000 字符上限意味着"近期进展"会不断覆盖旧的,本质是"滚动窗口式遗忘",这是有意的取舍而非缺陷;
- 一致性:记忆与事实可能漂移,故注入时约定"冲突以当前表述为准",但更长期的纠偏仍是开放问题。
展望几个方向:遗忘策略 (按重要性/时效主动淘汰,而非简单定长截断)、多 Agent 共享记忆 (团队级 memory,如 2026 年兴起的 fleet-memory / RCLL 等共享记忆层)、记忆一致性治理(来源可追溯、可回滚)。这些也正是 Agent 记忆从"能用"走向"可靠"的下半场。
记忆系统的本质,是把 Agent 从"无状态函数"改造成"有状态的长期协作者"。而一条清晰的坐标轴------纵轴看层次、横轴看范式------是设计任何记忆系统时都值得先铺好的那张地图。
资料
- XXL-AI(GitHub):github.com/xuxueli/xxl...
- XXL-AI(官方文档):www.xuxueli.com/