HMO 分层记忆编排:Agent 不是记得多,而是想得准
最近看 HMO(Hierarchical Memory Orchestration,分层记忆编排)这个思路时,我觉得它最有价值的地方不是提出了一个多复杂的公式,而是把 Agent 长期记忆这件事讲得更像一个真正的工程问题。
很多人一说 Agent 记忆,第一反应就是"存下来":把用户说过的话做摘要,塞进向量库,下次再检索出来。
这当然是一个起点,但不是终点。
因为长期记忆真正难的地方,从来不是"能不能存",而是"什么时候该想起来"。如果一个 Agent 每次都把一大堆旧历史翻出来,它看起来是记忆力很好,实际上很容易变得又慢又啰嗦,还会把无关信息带进当前判断里。
所以 HMO 给我的启发是:
长期记忆不是仓库,而是一套注意力调度系统。
它关心的不是把所有历史都放进上下文,而是把记忆分层管理,让高价值内容留在前台,让低频内容退到后台,需要时再逐层查找。
先说问题:为什么"全都记住"反而不好
如果只做一个最简单的记忆系统,大概会长这样:
text
对话内容 → 摘要 → 向量化 → 存库 → 下次相似度检索
这个方案看起来挺自然,很多 RAG 系统也是这么起步的。
但放到长期陪伴型、协作型 Agent 里,它很快会遇到几个问题。
第一,历史越多,噪声越大。
向量检索找到的是"语义相似",不一定是"行为上重要"。比如用户曾经随口聊过一次某个新闻,后来又问了一个和行业有关的问题,这条新闻可能被召回。但它真的应该影响当前回答吗?不一定。
第二,上下文是有成本的。
每多塞一段记忆,模型就要多读一段内容。短期看只是多一点 token,长期看就是延迟、费用和判断噪声一起上升。Agent 不是资料越多越聪明,有时候是资料越多越犹豫。
第三,用户偏好有轻重。
"我喜欢 TypeScript"是一种偏好,"不要主动暴露内部排程"也是一种偏好,但后者更像边界。忘记前者只是回答没那么贴合,忘记后者可能直接破坏体验。
所以一个好的记忆系统必须回答几个更细的问题:
- 哪些内容应该每轮都影响 Agent?
- 哪些内容只是需要时再查?
- 哪些内容只应该作为历史证据保存?
- 一条旧记忆被反复用到后,要不要升权?
- 用户画像变化后,旧记忆要不要重新排序?
HMO 解决的就是这些问题。
用一个类比理解 HMO
我更愿意把 HMO 理解成一张工作台、一个资料柜和一座档案馆。
text
Tier 1: 工作台
Tier 2: 资料柜
Tier 3: 档案馆
工作台上放的是现在就要用的东西,资料柜里放的是常用但不必摊开的资料,档案馆里保存完整历史和原始证据。
这套类比比"三级缓存"更容易理解,因为 Agent 的记忆不是纯性能缓存,它还关系到行为、语气、边界和长期协作。
Tier 1:工作台
Tier 1 是最靠近当前对话的记忆层。
这里放的内容应该很少,但很关键。比如用户长期稳定的表达偏好、明确说过的边界、最近正在推进的项目、当前任务必须依赖的上下文。
它的特点是:每轮都可能直接进入上下文。
举个例子:
text
用户不喜欢主动消息暴露排程细节。
这类记忆就不应该每次都去向量库里临时搜。它是行为约束,应该长期留在工作台上。
Tier 1 追求的不是完整,而是稳定和准确。它像一个小而精的运行时状态,宁愿少,也不要乱。
Tier 2:资料柜
Tier 2 放的是重要但不必每轮都出现的内容。
比如某个长期项目的背景、用户过去一段时间关注的技术方向、某些被多次召回过但不是强约束的偏好。
它的作用很像二级缓存:大多数查询如果 Tier 1 不够,可以先来 Tier 2 找,而不是直接翻完整历史。
如果某条 Tier 2 记忆最近频繁被用到,它就可以升到 Tier 1。反过来,如果 Tier 1 里某条记忆长期没被用,也应该退回 Tier 2。
这里开始有了"记忆生命周期"的味道。
记忆不是写入后就固定不动,而是会随着使用频率、时间、用户状态变化而移动。
Tier 3:档案馆
Tier 3 保存的是完整历史、低频信息和原始证据。
它的价值不是快,而是全。
很多内容不适合默认进入上下文,但也不能随便删。因为以后可能需要回溯,尤其是在长期协作里,证据源很重要。
比如用户原话是:
text
下次对话时间不要说出来,搞得太有程序感觉了。
如果被摘要成:
text
用户不喜欢技术细节。
这就已经偏了。
用户真正表达的是"不喜欢暴露内部排程",不是"不喜欢技术细节"。如果系统只保存摘要,不保存证据,后面就很难纠偏。
所以 Tier 3 不只是冷数据,它还是记忆系统的证据底座。
记忆要带元数据,不只是文本
HMO 最工程化的一点,是它不把记忆当成一段普通文本,而是当成一条有状态的数据。
一条记忆可以长这样:
json
{
"id": "mem_20260515_hide_schedule",
"content": "用户不喜欢主动消息暴露排程细节。",
"type": "boundary",
"tier": 1,
"importance": 10,
"personaSimilarity": 0.95,
"recallCount": 4,
"createdAt": "2026-05-15T03:30:00.000Z",
"lastAccessedAt": "2026-05-15T08:00:00.000Z",
"tags": ["style", "rhythm", "boundary"],
"sourceIds": ["src_20260515_hide_schedule"]
}
这里面最值得注意的不是字段数量,而是这些字段背后的判断逻辑。
importance 代表这条记忆本身有多重要。核心身份、长期边界、重大目标,肯定比一次寒暄更重要。
personaSimilarity 代表它和当前用户画像有多相关。同样一条记忆,在不同阶段权重可能不同。比如用户最近在研究 Agent 工程,那么 HMO、MCP、Skill、Hook 相关记忆就应该更容易被召回。
recallCount 代表它被用过多少次。经常被用到的记忆,应该更难衰减。这和人类记忆很像:越常想起,越容易再次想起。
lastAccessedAt 用来处理时间衰减。很久没被用到的记忆,可以降级,但不一定删除。
sourceIds 指向原始证据。这个字段我觉得特别重要,因为 AI 总结很容易漂移。长期记忆如果只有"总结后的结论",没有"当时的上下文",后面就容易把用户意思越传越歪。
换句话说,长期记忆不应该只存 content,还要存"这条记忆为什么可信、什么时候用过、从哪里来的、现在还重不重要"。
评分公式真正想表达什么
HMO 里有一个优先级评分,大概可以理解成:
text
记忆得分 =
(初始重要性 + 与用户画像的相关度)
* 召回次数增益
* 时间衰减
更形式化一点是:
text
S_m =
(alpha * I_m + beta * Sim(m, P))
* ln(1 + C_m)
* exp(-(lambda * (t_now - t_last)) / ln(1 + C_m))
公式看着有点数学味,但它表达的想法并不复杂。
一条记忆越重要,越应该靠前;和当前用户画像越相关,越应该靠前;被反复用过,越应该抗衰减;太久没用,就慢慢往后退。
这里面最妙的是 recallCount 对时间衰减的影响:
text
被想起得越多,忘得越慢。
这比单纯的"最近使用优先"更合理。
因为有些记忆不是最近说的,却非常重要。比如用户的长期表达边界、项目协作偏好、对工作方式的稳定要求,这些内容不应该因为一个月没提就消失。
从工程角度看,这个评分就是在做一件事:让记忆的位置由"重要性、相关性、使用频率、时间"共同决定,而不是只靠向量相似度。
检索时不要一上来翻全库
普通 RAG 很容易形成一个固定动作:
text
问题来了 → 全库向量检索 → 塞 topK → 生成回答
这在知识库问答里可以用,但放到个性化 Agent 里不一定合适。
HMO 的检索更像这样:
text
先看 Tier 1
↓
不够,再查 Tier 2
↓
还不够,再查 Tier 3
也就是先问:工作台上的内容够不够回答?
如果够,就别去翻资料柜。资料柜不够,再去档案馆。
这个顺序的好处很朴素:少查一点,快一点,噪声少一点。
很多时候用户只是问一个当前任务里的问题,Tier 1 已经够了。非要全库检索,反而会把很久以前的旧信息带回来,让模型产生不必要的联想。
这里还有一个值得借鉴的小设计:可以让模型先判断当前上下文是否足够。
不是所有问题都需要深层检索。一个成熟的 Agent 应该知道什么时候查记忆,也应该知道什么时候不要查。
升级、降级和懒更新
如果一条记忆被反复召回,它应该升级。
如果一条记忆长期不用,它应该降级。
如果用户画像明显变化,相关记忆应该重新排序。
这三句话听起来简单,但落地时很重要。因为它们决定了记忆系统是"活的",还是一堆越堆越多的文本。
一个基本流程可以是:
text
新交互进入
↓
提取候选记忆
↓
评估重要性和画像相关度
↓
写入对应 Tier
↓
检索命中后更新 recallCount 和 lastAccessedAt
↓
重新计算得分
↓
必要时升降级
不过这里不能走向另一个极端:每次都全量重算。
Tier 3 可能很大,如果用户画像稍微变动一下,就把所有历史都重新评分,成本会非常高。
所以 HMO 里很值得学的一点是懒更新:
- Tier 1 / Tier 2 经常更新。
- Tier 3 不频繁全量重排。
- Tier 3 里的记忆只有被访问时才重新评分。
- 如果重新评分后很重要,再提升到前面的层级。
这就是典型的工程取舍。
不是追求理论上的全局最优,而是在真实交互里保持够快、够稳、够准。
用户画像不是一段提示词
很多 Agent 系统会维护一段用户画像,然后把它放进系统提示词里。
这有用,但还不够。
在 HMO 里,用户画像不只是"给模型看的自我介绍",而是参与记忆调度的核心变量。
它会影响新记忆的重要性判断,也会影响旧记忆的排序。
比如一个用户过去主要聊前端工程,最近开始频繁研究 Agent、MCP、长期记忆,那么相关记忆就应该上浮。反过来,一些很久不用的前端细节可以暂时后退。
但画像也不能一有变化就触发全量重排,所以可以加一个 Drift Gate:
text
如果当前画像和上一次重排时的画像差异超过阈值
才触发 active tiers 重排
否则
暂时不做昂贵更新
这个设计很现实。
用户画像需要演化,但系统不能因为一点小变化就频繁大动干戈。
如果自己做一版,需要哪些模块
如果我自己实现一个简化版 HMO,不会一开始就上很复杂的图谱结构。我会先拆成这些模块:
text
Memory Ingestion
从对话中提取候选记忆
Importance Evaluator
评估这条记忆对未来有没有价值
Persona Manager
维护用户画像,并判断画像是否发生明显漂移
Memory Index
保存记忆正文、元数据、tier、tags、embedding
Tier Manager
负责 Tier 1 / Tier 2 / Tier 3 的升降级
Retriever
按层检索,而不是默认全库搜索
Access Tracker
记录 recallCount、lastAccessedAt 和召回原因
Source Store
保存原始证据,避免摘要漂移
Consolidator
把多条零散记忆合并成更稳定的长期结论
整体链路大概是:
text
用户交互
↓
候选记忆提取
↓
重要性评分 + 用户画像相关度
↓
写入 memory-index + source
↓
Tier Manager 分层
↓
优先读取 Tier 1
↓
不足时检索 Tier 2 / Tier 3
↓
命中后更新访问元数据
↓
周期性重排和画像演化
这里最核心的不是用了哪个数据库,而是职责边界要清楚。
记忆提取、画像维护、检索召回、证据保存、升降级调度,最好不要全写在一个函数里。否则后面你想换评分策略、换向量库、加冲突处理,都会很难改。
落地时我会额外加的几个字段
HMO 的基础思路已经够用了,但如果要做成长期运行的系统,我还会补几个字段。
第一个是可信度:
json
{
"confidence": 0.78,
"status": "confirmed | inferred | tentative | contradicted"
}
用户偶尔提一次,不代表长期偏好。系统要区分"用户明确确认过"和"模型推断出来的"。
第二个是冲突关系:
json
{
"supersedes": ["mem_old_xxx"],
"supersededBy": "mem_new_xxx"
}
用户会变化,旧偏好不能永远有效。与其粗暴覆盖,不如保留演化关系。
第三个是场景:
json
{
"contexts": ["coding", "planning", "casual", "thoughts"]
}
同一个人在不同场景下会有不同偏好。写代码时可能要短平快,闲聊时可能希望更自然、更有陪伴感。
第四个是召回理由:
json
{
"memoryId": "mem_hide_schedule",
"reason": "control_active_message_style",
"time": "..."
}
这个字段很适合后期调试。你能看到一条记忆到底是不是真的被使用,还是只是躺在 Tier 1 里占位置。
HMO 真正值得学的地方
HMO 值得学的不是"我也要照抄这个公式",而是它背后的工程态度。
它没有把长期记忆当成一个无限扩容的历史仓库,而是把它当成一个会变化、会衰减、会被强化、会被用户画像牵引的运行系统。
这对 Agent 很重要。
因为 Agent 的体验不取决于它保存了多少历史,而取决于它在当前这一刻能不能想起真正有用的东西。
如果只用一句话概括 HMO,我会这么说:
好的长期记忆,不是把过去都带到现在,而是知道哪些过去值得影响现在。
一个普通记忆系统像备忘录,什么都往里记。
一个好的 HMO 系统更像编辑部,每天都在决定什么该上头版、什么放资料库、什么只留档案。
这就是 Agent 从"会记事"走向"懂上下文"的关键。