主题
记忆系统
一句话:记忆系统把“这次任务的事实”“跨会话的经验”“组织知识”分开存放,并管住写入、召回、遗忘三条链路;它换来的是跨任务复用与连续体验,代价是必须处理污染、冲突和删除,否则记得越多越不可信。
适用:2026 年主流 Agent 的上下文、状态与记忆设计。向量库、图数据库与托管记忆服务的具体能力差异较大,选型以官方文档和压测结果为准。
为什么需要
用户说“按上次的方案改”,Agent 却从零再问一遍;长任务断点恢复后重复执行已完成的步骤;上周明确说过的约束,这周完全失效。另一类问题相反:Agent 把一次推测当成用户偏好,之后每次都用;把外部文档里的一句话升级成行为规则;或者把甲的记录召回给了乙。前一类是“没有记忆”,后一类是“有记忆但没有治理”。
记忆不是把聊天记录塞进向量库。聊天记录是过程,记忆是从过程里提炼出、未来还可能用到的结论;任务状态是这次任务的既成事实,不能靠语义相似度碰运气召回。把三者混在一起,短期看似什么都能查到,长期会同时失去可恢复性、可解释性和删除能力。所以先分承载对象,再谈记忆算法。
核心机制
四类状态对象,各配承载
| 对象 | 回答的问题 | 核心要求 | 常见承载 |
|---|---|---|---|
| 运行时状态 | 任务做到哪、能否接着跑 | 可恢复、可重放 | 事件日志 + 检查点 + 执行租约 |
| 工作区与产物 | 中间成果放哪、交付什么 | 可隔离、可追溯、不可变 | 分层工作区 + 写时复制 + 产物库 |
| 记忆与知识 | 跨任务能复用什么 | 可写、可召回、可遗忘 | 向量 / 关键词 / 图 + 元数据 |
| 业务语义 | 对象、关系、动作、规则是什么 | 可解释、受控变更 | 本体 / 规则库 |
四类对象的要求不同,不必塞进同一种存储;但它们必须共享一套契约:身份、版本、权限、生命周期。否则每类对象各建一套账号和删除逻辑,最后没人能回答“这条数据属于谁、从哪来、删干净了没有”。运行时状态解决“可恢复”:事件日志只追加,检查点放在外部调用前后、长计算后、人工确认前后,恢复时用执行租约和围栏标识保证只有最新实例能提交——这不是“恰好一次”,外部副作用仍要幂等和补偿。工作区解决“可交付”:基础层只读复用,共享层放协作内容,可写层由当前任务独占,写时复制让快照便宜,也避免“任务状态恢复了、文件却变了”。
记忆分层:先问看的是哪条轴
“工作 / 情景 / 语义 / 程序”是一把尺子,“工作 / 会话 / 持久”是另一把,“轮次 / 会话 / 任务 / 团队 / 跨会话”是第三把。它们回答的问题不同,不能互相替代:
| 轴 | 层 | 回答的问题 | 不能替代什么 |
|---|---|---|---|
| 用途轴 | 工作 / 情景 / 语义 / 程序 | 这条信息拿来干什么 | 生命周期与治理边界 |
| 时间尺度轴 | 工作 / 会话 / 持久 | 这条信息活多久、坏了怎么恢复 | 用途分类与权限 |
| 治理轴 | 轮次 / 会话 / 任务 / 团队 / 跨会话 | 谁可见、谁可删、保留多久 | 用途与恢复语义 |
读的时候先问“看的是哪条轴”:同一句“工作记忆”,在用途轴上指当前任务的临时结论,在时间尺度轴上指秒级上下文,在治理轴上指本轮可见的范围。三套轴可以叠加使用,但不能拿一套的层数去否定另一套。
用途轴上,工作记忆存当前任务状态和临时结论,直接注入,不走向量检索;情景记忆存过往任务的做法与结果,按相似任务和结果质量取;语义记忆存稳定事实、偏好、实体,按向量、关键词、实体取;程序记忆存已验证的成熟方法,稳定后升级为 Skill,按任务类型匹配。工作记忆不是长期记忆,它属于任务状态;把当前计划放进向量库,恢复时只能靠语义碰运气,这是很多“断点续跑”失败的根因。记忆还要看来源(用户明确表达 / Agent 行为归纳 / 外部业务事实)和稳定性(人设层 / 画像层 / 事件偏好层):人设不能因为一次对话就改写,画像可以随行为更新但要有置信度和确认机制,事件偏好只对相近情境生效;把三者混成“用户画像”,最常见的结果是把一次性要求变成永久规则。
一条事实的三态迁移与七种会话后端
| 状态 | 承载 | 生命周期 | 回答的问题 | 恢复方式 |
|---|---|---|---|---|
| messages | 工作记忆 | 秒级到会话 | 这轮说过什么 | 重建或重放上下文 |
| Checkpoint | 会话记忆 | 会话级 | 执行到哪一步 | 快照恢复,先清理中间态 |
| MEMORY.md | 持久记忆 | 跨会话 | 学到了什么 | PreReasoning 检索后注入 |
一条事实沿这三态迁移:先作为消息进入工作记忆,会话中被 Checkpoint 连同执行时间线一起快照,任务结束后由后台异步提炼进 MEMORY.md;下次会话在 PreReasoning 阶段同步检索并注入。Checkpoint≠Memory:一个记“执行到哪”,一个记“学到什么”。写异步、读同步;恢复时第一件事是清理旧 tool_call 等临时状态,否则 Agent 会以为动作没执行而重复一次。
为什么写异步、读同步:提炼要调模型做抽取和合并,秒级到十秒级,放进对话路径会直接抬高 P99;而注入必须在推理前完成,否则这一轮就用不上——一个可以迟到,一个不能。为什么恢复要先清中间态:Checkpoint 停在"tool_call 已发出、结果未落库"的裂缝上时,重放等于把副作用执行第二遍,除非工具有幂等键。
会话状态存哪,第一问不是性能,而是“换一个实例还能恢复吗”。Agent 在 A 实例中断、B 实例要加载同一 Checkpoint;单机文件做不到,关系库或内存 KV 可以。过了第一问,再按一致性、查询能力和运维成本分,不要默认只有关系库一种答案,也不要为了“以后可能扩展”一上来就选最重的后端。
| 后端类型 | 换实例能恢复 | 一致性与查询 | 运维成本 | 适用 |
|---|---|---|---|---|
| 进程内存 | 否 | 无持久化 | 最低 | 开发测试 |
| 单机文件 | 否,磁盘绑实例 | 按文件读写 | 低 | 单机、可丢会话 |
| 关系型数据库 | 是 | 强一致 + SQL 查询 | 中 | 微服务、默认选型 |
| 内存 KV | 是 | 最终一致或可配,低延迟 | 中 | 高并发短会话 |
| 已有关系库兼容 | 是 | 强一致,复用现有备份 | 低 | 已有数据库资产 |
| 文档数据库 | 是 | 灵活 schema,查询较弱 | 中高 | 半结构化快照 |
| 合规数据库 | 是 | 强一致 + 审计 | 高 | 金融、强合规 |
写入:抽取、去重与冲突
写入前先问六个问题:未来还会用到吗?是环境验证过的事实还是模型推测?归属谁、保留多久?是否含敏感信息?是新增、合并还是更新?写错后谁来纠正、派生索引怎么清?答不上来的,宁可不写。值得抽取的是用户明确说出的偏好与约束、已确认的决策、反复出现的做法、任务结束时的有效经验;寒暄、临时中间结果、未验证推断不写。
写入侧常见双阈值:相似度 ≥0.7 才判重复或合并,重要性 ≥0.6 才允许进入长期记忆。这是经验值,必须用校准集调:0.7 太低会注入语义无关的垃圾,0.9 太高会漏掉同一事实的不同措辞;0.6 重要性大致要求至少两个维度有信号。不同 embedding 模型和业务分布下,阈值不能照抄。冲突不能静默覆盖:按来源可靠度、时间新近度和权威级别选择,保留旧版本与变更原因,高影响冲突请求用户确认;每条记忆至少带来源、作用域、时间、置信度和适用条件,否则召回时无法判断该不该信。
召回:候选池、排序、按预算取 K
召回分三段:先取候选池,再统一排序,最后按窗口预算取 K。
- 候选池:按身份与作用域过滤后,向量召回语义相近、关键词召回精确匹配、实体关系召回关联对象,合并去重。
- 排序:
score = relevance × recency × confidence,再做时间衰减、类别过滤和权威性校正。 - 取 K:按当前窗口占用动态决定,取 K=5/2/0(某实现的经验值);对话历史比记忆更重要。
| 窗口占用 | K | 取舍 |
|---|---|---|
| <50% 充裕 | 5 | 记忆与对话都可容纳 |
| 50–80% 紧张 | 2 | 只保留高置信、强相关 |
| >80% 紧急 | 0 | 先保对话历史,记忆下一轮再说 |
算例:“Layout 用 React.lazy”的 0.92×0.85×0.90≈0.70,排第一;“用户用 VS Code”的 0.05×0.95×1.0≈0.048,被排除。relevance 直接决定生死,无关的记忆再新也不该注入。
排序为什么用乘法不用加权求和:加权求和允许"很新很可信"补偿"完全无关",一条无关记忆可以靠时近性挤进 Top-K;乘法结构让 relevance 成为一票否决项,符合"无关的记忆再新也不该注入"的语义。取 K 为什么看窗口占用而不是固定条数:记忆的边际收益是挤占对话历史换来的,窗口紧张时对话历史优先级更高——这是同一份稀缺预算上的竞争,不是两个独立队列。
关键词 + 向量双通道是某实现的组合,不是标准答案。通用架构可以是向量 + 关键词 + 实体关系三路:数据以精确编号为主就加重关键词,以多跳关系为主就补实体图,不要把实现细节当成架构规范。时间衰减要有例外:长期有效的约束不能因为长期没被调用就淡出。用户三个月前定的“不要自动发布”仍然有效,应标记为长期约束并提高保留权重。召回前必须先确定身份与作用域;返回结果带来源与时间,让模型能区分“用户现在说的”和“很久以前记的”。
遗忘:删除要删整条派生链
遗忘有三种动作:归档(不再参与召回但保留可查)、降权(降低排序权重)、删除(不可恢复)。混成一个“删除”按钮,要么该留的丢了,要么该删的还在索引里。
真正的删除要从稳定资产 ID 出发,沿派生链处理:禁止读取 → 删除原始记录 → 清理摘要、向量索引、缓存、图谱关系 → 更新 Manifest 与相关 Skill → 保留最小审计 → 跑验证确认召回不到。只删原始行、不删索引,下一次重建会把内容带回来。权威事实与派生索引分开提交:先可靠写入权威存储,再异步更新索引;重建以权威为准,而不是以索引为准。
污染防线也要建在写入和召回两侧:外部文本不能自动升级为行为规则;模型推测不能直接写成组织规则;跨租户内容在进入候选集之前就被过滤;高影响记忆的变更要能追溯到来源和审批。记忆数量增长本身不是问题,无法解释、无法删除、无法纠正才是。
把上面写入、召回、遗忘三段串起来,记忆是一条带反馈的闭环:
这张图的闭环说明记忆不是一次性写入:使用反馈会改变记忆的权重和保留期,遗忘又会反过来影响下一次召回。缺少反馈,低质量记忆会一直占着 Top-K;缺少删除链,错误记忆会在索引重建后反复出现。
实战场景
场景:聊天记录直接进长期记忆——一次推测被自我强化三周
现象:一个个人助理 Agent 把整段对话原样喂进 MEMORY.md。某轮模型随口说“你可能是素食者”(用户其实只是那天吃了素),这条被当事实写入后,此后 3 周推荐全是素食餐厅,用户纠正 26 次都压不回去。
排查过程(是召回层出错还是写入层出错):先看召回——每次都能稳定命中这条,说明召回没错。回到写入侧六问:“是环境验证过的事实还是模型推测?”是推测。正文口径:来源要标 user_explicit / agent_inferred / business_fact,这条实际是 agent_inferred 却被当 user_explicit 落库;重要性 ≥0.6 门槛也没起效,因为抽取时给一次性事件打了高分。更糟的是读同步——这条每轮 PreReasoning 被重新注入,模型基于它继续生成新的“确认”,形成自我强化;用户纠正只改了当前回答,没定位并更新对应持久记忆。根因在写入门槛,不在检索。
对症做法:低置信推断只进候选区,不直接参与召回;来源与置信度决定去向。
yaml
write_gate:
source: agent_inferred
confidence: 0.45
route: candidate_zone # 低于重要性门槛进候选区,二次确认才升级持久记忆
require: user_explicit_or_two_signals # 至少两个维度有信号才允许写入长期记忆
on_user_correction: update_memory_not_just_answer复盘要点:记忆是从过程里提炼、未来还可能用到的结论,不是聊天记录;把模型推测大量写入长期记忆,污染随时间复利,清理成本远高于收益。
场景:删原文没删派生记忆——合规复检召回旧数据
现象:用户按合规要求提交删除我的数据,团队删掉了会话原始表里的那几行。7 天后复检,Agent 仍能召回该用户的饮食偏好与健康备注,删除判定不通过,面临处罚风险。
排查过程(沿派生链找残留,不是只看原表):从稳定资产 ID 出发逐段验召回——原始行已删,但 MEMORY.md 里的摘要还在、向量索引还带着这条 embedding、缓存未失效、图谱关系仍指向它。跑一次召回验证直接命中旧内容。正文口径:“只删原始行、不删索引,下一次重建会把内容带回来”。根因:把归档、降权、删除混成一个删除按钮,只执行了链路第一步。
对症做法:删除走完整派生链,末尾用验证确认召回不到。
| 步 | 动作 | 验收 |
|---|---|---|
| 1 | 按资产 ID 禁止读取 | 立即不再进候选集 |
| 2 | 删原始记录 | 原表查询为空 |
| 3 | 清理摘要 / 向量索引 / 缓存 / 图谱关系 | 四路各自命中数为 0 |
| 4 | 更新 Manifest 与相关 Skill | 引用不再指向该 ID |
| 5 | 保留最小审计,跑端到端验证 | 该条召回为 0 |
权威事实先可靠写入,派生索引异步更新;重建以权威为准,不以索引为准。
复盘要点:记忆能解释、能删除、能纠正才是治理到位;删除完成时长是被监控的五类指标之一,没删净等于没删。
场景:召回排序用加法——过期高置信记忆压过事实
现象:一个 Coding Agent 的技术栈记忆里有条“2024 年确认用 Webpack 构建”,用户当时明确说过、confidence 0.99,但半年没被调用、与本轮“迁移到 Vite”的任务 relevance 只有 0.10;本轮真正相关的“上周要求改用 Vite”是新决策、confidence 只 0.62。用加权求和且置信项占重时,前者总分一直靠前、稳定挤进 Top-K,注入早已停用的构建配置,一周内方案与这条旧配置冲突 11 次。
排查过程(是模型选错还是排序结构问题):让模型解释“为什么注入这条”,它按分数照单全收,不是模型判断错。核对排序公式——加权求和允许“很新很可信”补偿“完全无关”,一条无关记忆能靠别的项挤进 Top-K。对照正文算例:乘法下“用户用 VS Code”的 0.05×0.95×1.0≈0.048 会被直接排除,加法却把它当高分留下。
| 记忆 | relevance | recency | confidence | 加权和(权重 0.15/0.15/0.70) | 乘积 |
|---|---|---|---|---|---|
| 2024 Webpack(过期高置信) | 0.10 | 0.30 | 0.99 | 0.753 | 0.0297 |
| 上周改用 Vite(本轮事实) | 0.88 | 0.85 | 0.62 | 0.694 | 0.4637 |
加法下 Webpack 0.753 高于 Vite 0.694,过期那条排前面;乘法下 Vite 0.4637 高于 Webpack 0.0297,事实胜出。根因在排序结构:relevance 该是一票否决项,加法把它降级成了可被补偿的加分项。
对症做法:改回乘法,并对长期有效约束做衰减例外。
python
score = relevance * recency * confidence # 任一因子趋零整体出局,relevance 一票否决
if memory.stability == "long_term": # 如"不要自动发布"这类长期约束
recency = max(recency, floor) # 给下限,不因久未调用而淡出窗口紧张时再按占用取 K(50% 以上降到 2 条、紧急降到 0 条),对话历史优先于记忆。
复盘要点:乘法结构让“无关的记忆再新也不该注入”成为硬语义;排序用加法等于把 relevance 从准入门槛降成加分项,过期高置信记忆靠别的项翻身的空间就是这么留出来的。
最小上手
先定义一条记忆记录,再谈检索算法:
yaml
# 示意:一条记忆的最小字段
id: mem_xxx
type: preference # fact / preference / decision / experience
content: "发布前必须先跑回归"
source: user_explicit # user_explicit / agent_inferred / business_fact
scope: tenant/project # 召回前先按此过滤
stability: long_term # persona / profile / event
confidence: 0.95
valid_from: 2026-09-01
expires_at: null
derived_from: [conv_123]跑通判据:同一问题隔一周再问,能召回正确偏好且不串租户;冲突写入不会静默覆盖,能查到两个版本和取舍原因;删除后原文、摘要、索引、缓存都查不到;任务中断后从检查点恢复,不重复已完成的副作用;记忆总量增长时,召回延迟和 Top-K 准确率仍在目标内。
生产动作
- 先修状态,再修记忆:断点恢复、幂等、工作区快照没做好之前,加长期记忆只会让问题更隐蔽。恢复路径要定期演练,不能只写在文档里。
- 监控五类指标:召回命中率、召回相关性、冲突积压、记忆增长率、删除完成时长;再加一项跨租户越权告警。指标恶化时先查作用域过滤和索引版本,再查模型。
- 写入设门槛并校准:低置信度推断只进候选区,不直接参与召回;敏感信息在写入前脱敏或拒绝;0.7 / 0.6 这类阈值用校准集调,不照抄。
- 召回做可解释:返回结果附带来源、时间和入选原因;K 随窗口动态变化;用户纠正一次,要能定位并更新对应记忆,而不是只改当前回答。
- 删除做演练,变更走发布链:定期抽一条记忆走完整删除链,验证索引、缓存、摘要和备份策略;记忆结构、抽取规则、排序权重都版本化,先离线回放再灰度。
边界
- 适合:跨会话、跨任务的连续服务,用户偏好稳定,且有明确纠正入口。不适合:一次性问答、无身份体系的匿名场景,记忆的收益低于治理成本。
- 适合:事实可验证、来源可追溯的记忆。不适合:把模型推测大量写入长期记忆,污染会随时间复利,清理成本远高于收益。
- 适合:任务状态与长期记忆分开建模。不适合:用一个向量库承载所有状态,恢复语义、权限和删除都无法保证。
- 常见误区:记得越多越懂用户(噪声会稀释召回);向量检索万能(精确编号和专有名词常失效);删除只删原表(索引和缓存会复活内容);人设可以从对话里自动学习(应由治理方显式维护);知识库等于记忆(权威来源和责任不同)。
讲清楚
记忆系统的第一问题不是“怎么存”,而是“存的是哪一类东西、看的是哪条轴”:用途轴分工作、情景、语义、程序,时间尺度轴分工作、会话、持久,治理轴分轮次、会话、任务、团队、跨会话,三套不能互相替代。一条事实从 messages 到 Checkpoint 再到 MEMORY.md,Checkpoint 记执行到哪,Memory 记学到什么,写异步、读同步。召回先取候选池,再按 relevance×recency×confidence 排序,最后按窗口占用取 K;写入用相似度和重要性双阈值挡噪音,但阈值必须校准。遗忘要区分归档、降权和删除,真删除必须清掉原文、摘要、索引、缓存和派生关系。判断标准是“需要时取得到、取到时信得过、要求删时删得净”,而不是记忆条数。
适用:2026 年主流 Agent 的上下文、状态与记忆设计 | 初稿 2026-10