1. 记忆治理的处理对象
记忆治理处理的是跨会话仍然有价值的信息:把这类信息挑出来、存下来、在需要时取回、在失效时清理。它包含四件事:
- 判断什么值得留;
- 选择用什么形式留;
- 决定下次怎样取回;
- 规定怎样变旧和下线。
它与相邻三篇的分工由视野长度决定。上下文工程处理一次请求的组装,状态栏处理当前状态如何投影成消息,压缩处理历史变长时如何换视图,三者的视野都在一次任务之内。任务结束后,事件日志仍留在磁盘上,模型却看不到它——这一段空白就是记忆治理的起点。
2. 四种持久化机制的分工
谈到"记住东西",很容易把几件不同的事混成一个"存储"问题。实际上一个 Agent 运行环境里通常同时存在四种持久化机制,它们的写入者、保存内容与失效方式都不一样。
| 机制 | 写入者 | 保存内容 | 失效方式 | 谁读它 |
|---|---|---|---|---|
| 上下文消息 | 运行时每轮组装 | 这一轮要发给模型的内容 | 每轮重建 | 模型 |
| 事件日志 | 系统自动追加 | 发生过的一切,只增不改 | 基本不失效 | 恢复、审计、追溯 |
| 状态投影 | 运行时按规则计算 | 当前阶段的结论字段 | 每轮重算或按事件更新 | 运行时判断与模型 |
| 长期记忆 | 智能体或后台管线 | 跨任务可复用的结论 | 整理、降权、过期 | 后续任务的模型 |
这张表的作用是确定责任归属。用户说"你记错了",系统需要知道去改哪一层:改消息没用,下一轮就重建了;改日志是篡改证据;改状态投影只能影响当前任务;只有改记忆才能影响未来。
反过来说,当用户抱怨"你怎么又忘了",也要先判断是哪一层出了问题。四种症状看起来一样,修的地方完全不同:
| 用户感知 | 实际缺失的环节 | 修的位置 |
|---|---|---|
| 重新问了同一件事 | 信息只进了日志,从未被提取 | 抽取管线 |
| 提了但没被用上 | 写进了记忆,取回时没命中 | 检索与注入 |
| 取回了但被忽略 | 记忆进入了上下文,位置或信任级别不对 | 注入位置与提示词 |
| 用了但用错了 | 记忆本身已经过期或有冲突 | 写入判据与整理 |
第三行最容易被误判成"模型不听话"。实际上模型按位置分配注意力权重:一条放在用户消息里的偏好,其影响力远低于放在系统提示里的同样内容。位置决定强度,这一点在《记忆检索与注入》里展开。
这套分工还解释了为什么"把所有对话存进向量库"不是记忆治理。向量库解决的是"怎么找",不解决"该不该写"和"什么时候该删"。日志与投影都已有成熟的工程做法,记忆这一层缺的正是判断。
2.1 四种机制的量级差别
四种机制的差别不只在职责,也在体积与更新频率。用一次典型的长任务估一下:
| 机制 | 量级 | 更新频率 | 保留期 |
|---|---|---|---|
| 上下文消息 | 20–200 K Token | 每轮 | 单次请求 |
| 事件日志 | 数十 MB 到 GB | 每个事件 | 数月到永久 |
| 状态投影 | 几百 Token | 每轮或每个阶段 | 单次任务 |
| 长期记忆 | 10–100 KB | 每次任务若干条 | 数月到数年 |
两个数量级的差异决定了它们不能共用一套存储与检索。状态投影之所以能每轮重算,是因为它只有几百 Token;长期记忆之所以要索引,是因为它跨任务累积且每次只取极少一部分;事件日志之所以不做检索优化,是因为它的主要用途是恢复与审计,查询频率很低。
把四者混在一起的典型后果是:把事件日志塞进上下文(窗口瞬间爆掉),或者把状态投影当成长期记忆(任务结束就丢),或者把长期记忆当状态投影每轮注入(Token 成本失控)。
3. 记忆类型与生命周期
分类的价值不在分类本身,而在于每一类对应不同的写入条件、不同的过期规则、不同的信任级别。
| 类型 | 内容 | 写入条件 | 过期规则 | 取回后的信任级别 |
|---|---|---|---|---|
| 工作记忆 | 当前任务状态、近期工具结果 | 任务进行中自动积累 | 任务结束即失效 | 高,但仅限当前任务 |
| 情节记忆 | 某次任务发生了什么 | 任务结束、有可复用经验 | 按时间衰减 | 中,需要核对细节 |
| 语义记忆 | 稳定的事实与项目背景 | 有来源支撑、多次确认 | 按版本与最近验证时间 | 中,涉及代码位置需核对 |
| 偏好记忆 | 用户习惯与协作方式 | 用户明确陈述或反复纠正 | 新偏好覆盖旧偏好 | 高,可直接改变行为 |
| 反思记忆 | 失败教训与策略修正 | 失败被归因、可在类似场景复用 | 工具或环境升级后失效 | 低,只作为策略提示 |
3.1 生命周期的状态机
每条记忆在若干状态之间迁移,不存在单纯的"有"与"没有"。把状态说清楚,才能定义什么操作是合法的。
┌─────────┐
写入判据通过 → │ active │ ←── 再次确认
└────┬────┘
│
┌────────────────┼────────────────┐
│ │ │
被新证据替代 出现矛盾证据 超过有效期
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│superseded│ │ conflict │ │ expired │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
用户要求删除 矛盾被裁决 重新验证通过
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ 回到 active
│ deleted │ │ active │
└──────────┘ └─────────┘五个状态各自的语义:
active:当前可用,可以进入上下文;superseded:被新记忆替代,保留用于审计,取回时降权;conflict:与其他记忆矛盾且未裁决,不单独作为事实依据;expired:超过有效期,默认不召回;deleted:用户要求删除,物理删除或合规擦除。
状态机的价值在于它约束了合法迁移。例如从 conflict 直接到 deleted 是合法的(用户说"这两条都别用了"),但 conflict 不能直接到 superseded——那意味着在矛盾未裁决的情况下就宣称其中一条被替代,等于用一个猜测掩盖了一个冲突。
3.2 信任级别的依据
为什么偏好可以直接改变行为,而反思只能作为提示?因为它们出错的代价与修复成本不同。
偏好错了,用户下一句就会纠正("别用表格"),修复成本接近零。语义记忆错了,模型可能基于错误事实做出一连串动作,用户要到很后面才发现,修复成本高。反思记忆错了,模型会避开一条本来可行的路径,这个错误甚至不会被察觉——它表现为"效率变低",很难归因到"答案错了"。
这解释了三种类型的注入强度设计:
| 类型 | 注入位置 | 是否要求核对 | 出错后的表现 |
|---|---|---|---|
| 偏好 | 系统提示或开发者指令 | 不要求 | 用户立刻纠正 |
| 语义 | 用户消息或附件 | 要求核对当前状态 | 后续动作基于错误前提 |
| 反思 | 提示字段,标注为参考 | 要求判断是否适用 | 绕开可行路径,效率下降 |
Claude Code 在这条上做得比较明确:它把记忆限定为四类——用户的角色与偏好、用户对工作方式的反馈、代码与提交历史推不出来的项目进展、外部系统的位置指引——并要求在取回后核对涉及文件与函数的断言。
它专门写了一段"不要在记忆里保存什么":
- 代码模式、约定、架构、文件路径、项目结构——读一遍当前状态就能得到;
- 提交历史、最近改动、谁改了什么——版本控制工具是权威;
- 调试结论与修复配方——修复在代码里,背景在提交信息里;
- 已经写在项目规则文件里的内容;
- 进行中的工作、临时状态、当前对话上下文。
这条排除规则在用户明确要求保存时同样生效:如果用户说"帮我记住这周的 PR 列表",系统应当反问"这里面哪一点是出乎意料的",只有那一部分值得留。
排除规则比收录规则更省成本。 把代码结构、调试结论、提交历史写进记忆,等于在代码库之外维护第二份事实,而第二份事实没有编译器和测试来保证它是对的。它会安静地过期,然后在某次任务里被当成事实引用。
从信息论角度看得更清楚:一份可以从当前状态重新计算的信息,其存储成本大于零,而信息增益为零。记忆的容量与检索精度都是有限资源,用它去存可推导的内容,等于用有限带宽传输冗余数据。
4. 写入判据
一条候选信息要进入长期记忆,需要同时满足四条。
4.1 四条判据
稳定:下周还成立。临时路径、一次性报错、某次运行的耗时数字不满足。
判断稳定性的一个可操作办法是问"如果这条记忆在三个月后被读到,它还会导致同样的行动吗"。一条"接口 A 现在超时"不满足,因为三个月后可能已修复;一条"接口 A 的错误码 4021 表示签名校验失败"满足,因为错误码定义通常比运行时状态稳定。
可复用:未来的相似任务会用到。只对当前任务有用的中间结论不满足。
这条与稳定性是两个独立维度。任务状态"已经在改第 3 个文件"既不稳定也不可复用;用户的代码风格偏好既稳定又可复用;某次调试的完整报错稳定(错误信息不会变),可复用性很低(下次遇到的是别的错误)。
有来源:能指回产生它的那次操作。凭印象归纳的"用户似乎喜欢表格"不满足。
这一条最容易打折扣,也最有用。它要求记忆对象带一个可回溯的指针:哪次任务、哪条消息、哪个文件。这个字段在正常使用时几乎不产生价值,但出错时是唯一的救生索——当一条记忆导致错误回答,只有能回到来源才能判断是提取错了、还是当时的事实本身就变了。
已授权:用户允许保存,且不含敏感信息。密钥、令牌、内部地址永不满足。
4.2 判据作为打分函数
四条可以写成一个打分函数,只有四项都过线才写入:
其中 \(s_1\) 到 \(s_4\) 依次对应稳定、可复用、有来源、已授权四项,\(\theta\) 是写入阈值。
取最小值、不取加权和,是因为这四条是不能互相补偿的约束:一条极其稳定但泄露密钥的信息,加权和可能仍然很高,但它绝不应该被写入。最小值形式强制任何一项不达标就拒绝。
\(s_3\) 可以直接从字段判定:有来源为 1,无为 0。\(s_4\) 同理,命中敏感模式为 0。\(s_1\) 与 \(s_2\) 需要判断,通常由模型给出一个概率估计。
这个形式化有一个实际用处:它把"该不该写"从主观争论变成可校准的判定。如果发现系统写入了太多噪声,调 \(\theta\) 或检查哪一项在误判——通常是 \(s_1\) 给得太宽松。
4.3 允许空写
Codex 的写入提示词里有一句自检,值得照搬:
我会不会因为写了这条记忆,而让未来的智能体做得更好?
如果答案是否定的,就不要写。它同时给了几种应当返回空的情形:一次性的随机提问、没有结论的状态更新、应当重新查询的临时指标、常识、没有任何新证据的探索性讨论。
允许"什么都不写"是一个重要设计。如果每次任务结束都必须产出记忆,系统会为了填满配额而制造噪声。从成本角度看,一条错误记忆的代价是它在整个生命周期内被反复取回、影响决策、并可能被整理传递到其他记忆——这个代价远高于"少记一条"的损失。
Codex 另一条经验是把提取过程分成两个阶段。先在轨迹上做结构化抽取,产出一份详细记忆、一段摘要和一个标识;再由一个独立的整合步骤把多份摘要合并成手册与索引。第一阶段可以并行,第二阶段必须串行,否则同一份产物会被并发改写。这个分工在《记忆工程实践》里展开。
4.4 四类高信号内容
把判据落到内容类型上,Codex 的提示词列得比较具体:
- 稳定的用户操作偏好、反复出现的反感、重复的纠正模式。这类内容最能减少未来的用户干预,优先级最高。
- 防止无效探索的决策触发条件。例如"看到某类错误先检查配置再重试"。
- 失败盾牌:从症状到原因到修复,再加上验证方式与停止规则。
- 仓库与任务地图:入口在哪、配置在哪、命令是什么。
第三类值得解释。一条纯粹的失败记录价值有限("某次部署失败了"),有价值的是它的结构:什么症状、什么原因、怎么修、怎么确认修好了、什么时候该停止尝试。这个五段结构让后来者能判断"我现在遇到的症状是不是同一种"。
第四个反例也值得说:不要把探索性讨论提升为长期知识。一次对话里出现的设计讨论、名词提议、方案比较,多数没有经过验证。把它们写进记忆,会让未来的智能体把"曾经讨论过"当成"已经决定"。
5. 存储结构与单条记忆的字段
记忆的存储形式有两层选择:整体结构怎么组织,单条记忆怎么表示。
5.1 索引加主题文件
几乎所有的成熟实现都收敛到"轻量索引加若干主题文件":
memory/
├── MEMORY.md 索引:每条一行,指向主题文件
├── user_role.md 主题:用户的角色与职责
├── feedback_testing.md 主题:对测试方式的要求
├── project_release.md 主题:发布流程的进展与坑
└── logs/2027/03/2027-03-14.md 按日追加的原始记录索引每轮注入,主题文件按需展开。索引必须是索引,不能是正文。一旦有人往索引里塞内容,它就同时承担了索引和存储两个职责,行数上限会迅速被撑破。
Claude Code 的具体数值是:索引上限 200 行、25,000 字节,每条一行、约 150 字符以内;单条记忆是一个带 frontmatter 的 Markdown 文件,超过 40,000 字符会触发告警——这个上限只产生告警,不中断写入,它的作用是提醒该拆分了。
25,000 字节这个数字不是随意定的。按每行 150 字符算,200 行约 30,000 字节,所以字节上限比行数上限更早触发——这是有意的:它防止少数超长行绕过行数检查。实际运行中出现过"197 KB 内容塞在 200 行里"的情况,字节上限就是为这类情况加的。
5.2 两种表示谱系的取舍
单条记忆的表示有两种极端。
最小事实卡片:一条只记一件事,检索精确但丢失关联。"在 TechCorp 工作""职位是高级工程师""负责推荐系统"拆成三条独立事实,处理需要综合多条才能回答的问题时,系统要重新拼凑。
完整段落:保留叙事结构,语义完整但冗余严重。"用户在 TechCorp 担任高级软件工程师,专注机器学习三年,目前领导一个推荐系统项目,团队五人"——同一条工作信息在多个段落里重复,属性变化时要重写多处。
工程上常见的折中是结构化卡片加叙事背景:既存字段化的属性,也存一段说明这条信息是在什么场景下得到的。
后者解决了一个具体问题:同一条信息在不同关系下含义不同。"张医生"可能是用户自己的牙科医生,也可能是用户父亲的医生,只靠键值对无法区分。加入"这条信息是在什么场景下得知的"(背景)与"涉及谁"(主体关系)两个字段之后,消歧变成了查询而不再需要推理。
代价是生成与维护成本上升。每次写入要判断主体关系,每次更新要检查是否影响多个主体。实践中的选择标准是:关键且少量的信息(用户偏好、人物关系)用结构化卡片保证可检索;大量非关键信息用简短事实降低成本。
5.3 单条记忆的字段
一条记忆至少要带这些字段:
{
"type": "preference",
"scope": "private",
"subject": "user:liys05/project:liyyro",
"attribute": "output_format",
"content": "技术文章用中文写,多方案对比用表格。",
"source": {
"task": "tr_20270314_02",
"message_index": 7,
"evidence": "用户原话:以后文档都用中文,对比直接上表"
},
"confidence": 0.92,
"created_at": "2027-03-14T10:20:00+08:00",
"verified_at": "2027-03-14T10:20:00+08:00",
"expires_at": null,
"supersedes": null,
"usage_count": 0,
"last_used_at": null
}各字段的职责:
| 字段 | 决定什么 | 缺失后的后果 |
|---|---|---|
type | 取回时的信任级别与注入位置 | 模型把偏好当事实用,或把失败经验当结论 |
scope | 谁能看到 | 越权暴露,或团队约定只对一人生效 |
subject 与 attribute | 冲突检测与并列判断 | 无法区分"同一件事的不同方面"与"同一件事的不同说法" |
source | 能否回到原始证据 | 出错后无法定位是提取错还是事实变了 |
confidence | 排序时的权重 | 低质量记忆与高置信记忆并列 |
verified_at | 过期判断 | 久未验证的结论被当作当前事实 |
supersedes | 替代关系 | 新旧两条同时被取回,互相矛盾 |
usage_count 与 last_used_at | 整理时的降权依据 | 无用的记忆永远不被清理 |
subject 与 attribute 这一对是四家里最少见的,但它决定了取回阶段能否自动区分并列与互斥。没有这对字段,系统只能靠文本相似度猜,而相似度分不清"部署命令是 A"与"部署脚本是 B"是矛盾还是互补。
6. 整理与失效
记忆只增不减的代价很少落在存储上,更多落在检索质量下降与事实冲突。整理要处理三类问题:重复、过期、冲突。
6.1 重复的合并
重复来自同一事实被反复写入。合并时要保留全部来源,不能只留最新一条——来源数量本身就是可信度的信号,一次观察和五次独立观察的证据强度不同。
判断重复不能只靠文本完全相同。"用 pnpm 不用 npm"和"包管理器是 pnpm"是同一事实的两种表达。可靠的办法是按 subject 加 attribute 做键:主体是项目、属性是包管理器,写入前先按这个键查一遍。
合并的产物是一条记忆带来源集合:
{
"subject": "project:liyyro",
"attribute": "package_manager",
"content": "使用 pnpm。",
"sources": ["tr_001", "tr_014", "tr_019"],
"confidence": 0.97
}6.2 过期的判断
过期来自环境变化。判断依据有两条:最近一次被取回的时间(last_used_at),以及最近一次被验证的时间(verified_at)。
这两件事必须分开记录:一条一年没被用过但昨天刚验证过的记忆,比一条天天被用但三年没验证的记忆更可靠。前者是"暂时不需要但正确",后者是"常用但可能已经错了"。
一个可用的降权函数把两个时间都纳入:
其中 \(w_0\) 是初始权重,\(d_1\) 是距上次取回的天数,\(d_2\) 是距上次验证的天数,\(\tau_1\) 与 \(\tau_2\) 是两个不同的时间常数。
验证项的时间常数取两倍,对应的理由是验证时间的惩罚应当更缓:一条三个月没被验证的记忆不等于错,它只是需要核对;而一条三个月没被取回的记忆确实说明它不常用。用同一个衰减率会让两者混同。
实际实现不需要这么精确。Codex 的做法更直接:30 天未被使用过的记忆不再进入整合的候选集。这个规则作用在"整合输入"这一层,不直接删除文件——文件只是不再被选中,旧摘要随后被剪掉。
6.3 冲突的处理
冲突最需要谨慎。用户先说"部署到 testing 环境",后来说"部署到 staging 环境",这可能是改主意,也可能是两个不同的流程。
把三种情况落到具体流程上,可以拆成五步。用"部署命令"这个例子走一遍。
第一条记忆来自三个月前:主体是当前项目,属性是部署方式,取值是 npm run deploy,来源是那次任务第 12 条消息,置信度 0.9,验证时间是三个月前。
第二条记忆来自上周:同一主体同一属性,取值是 bash deploy.sh,来源是那次任务第 4 条消息,置信度高,验证时间是上周。
第一步,识别。 两条的主体与属性相同、取值不同,构成互斥。如果只按文本相似度看,它们相似度很高,会被当成重复合并,结果是留下一条随机的。所以识别必须靠结构字段。
第二步,判定来源强弱。 第二条更新、来源更近、验证更晚,三条都指向它更可信。但"更可信"不等于"旧的就是错的"——旧的那条在当时是对的,它错在没被更新。所以处理方式是标记替代、保留原文:第一条的 supersedes 被第二条指向,取回时默认不返回。
第三步,检查是否存在条件差异。 如果第二条的来源里提到"这个项目从下周起改用脚本",那两条就是时序关系,不是矛盾;如果提到"生产用脚本、测试用命令",那两条是并列,需要把条件写进各自的记忆。这一步只能读来源,不能靠推理。
第四步,更新下游。 如果索引里有一行指向第一条,那行需要改写或删除,否则模型会顺着索引读到一条已失效的记忆。
第五步,记录。 这次替代关系要写进事件,因为它是"为什么系统改了行为"的唯一解释。没有这条记录,三个月后再有人问"为什么不直接用 npm run deploy",没人答得上来。
这五步里,第三与第四最容易漏。漏了第三步会把并列关系误判成替代,丢掉一条仍然有效的知识;漏了第四步会让索引与主题文件脱节,模型读到索引却取不到正确内容。
6.4 整理的四步
Claude Code 的夜间整理给出了一个具体做法,把整理拆成定向、采集、整合、修剪四步。
定向先看清全貌:列目录、读索引、浏览已有主题文件。这一步的目的是避免重复创建——不做定向,整理会不断产生新的近似条目。
采集按优先级找新信号,顺序是:按日的日志文件、与当前代码矛盾的旧记忆、必要时用窄关键词去查转录文件。第三项有明确约束——不要穷尽读取转录文件,只在已经怀疑某件事重要时去查。原因是转录是巨大的 JSONL 文件,全读会耗尽预算。
整合写或更新主题文件。三条明确要求:把相对日期换成绝对日期;删除已被证伪的旧记忆;合并新信号时优先更新已有主题文件,不要创建近似副本。
修剪更新索引:删掉指向失效条目的指针,把超过约 200 字的索引行压回主题文件——一行太长说明它承载了正文内容。
这四步的顺序不能换。先采集后整合会让新内容冲击已有结构;先修剪后定向会让整理看不到全貌。
6.5 什么可以自动、什么要人批
有一条实用分界:不增加语义的动作可以自动,改变结论的动作需要人批或至少需要可回滚。
可以自动的动作:合并重复条目、缩短过长索引行、修正日期格式、更新验证时间、把主题文件里的过时链接改指向。
需要人批或需要回滚的动作:删除条目、改写事实内容、把私人记忆提升为团队共享、调整作用域、批量重组目录。
分界的理由是错误代价不同。前者错了只是难看,下次整理还能修;后者错了会让系统在没人察觉的情况下改变行为,而且改动本身可能已经通过整理被"合法化"了。
Codex 在这条上给出了更彻底的做法:它的整理产物落在一个 git 基线工作区里,每次整合前先生成工作区差异,整合后重置基线。这样任何一次整理都能回滚到上一个已审核状态。这比"是否人批"更实用——人批只能防住单次错误,回滚能防住累积错误。
7. 作用域与共享
记忆的可见范围通常分三级:仅当前任务、仅当前用户当前项目、团队共享。
分级的必要性来自两类风险。一类是越权,用户的私人偏好不应该被同事看见;另一类是污染,某个人的个人习惯被写进团队约定后,所有贡献者都要遵守一条没人同意的规则。
Claude Code 的处理方式值得参考:它把记忆分成私人目录与团队目录,并要求在写入时显式选择 scope。判据写在提示词里——只有当一条指引明显属于项目级约定(测试策略、构建约束)、不属于个人风格时,才写入团队记忆。
团队记忆在同步前还要过一遍密钥扫描。这一层用的是一批高置信度的凭证识别模式,只保留带有独特前缀、误报率接近零的规则,通用的"关键词上下文"规则被排除。命中的内容不会离开本机。
这个设计的理由值得说清:团队记忆是唯一会离开本机的记忆类型。私人记忆泄露只影响一个人,团队记忆泄露会影响整个组织,而且一旦上传就很难撤回。所以扫描发生在客户端;内容发出去之后再做检查已经晚了。
共享带来一个新问题:冲突的裁决者变了。私人记忆的冲突由用户自己裁决,团队记忆的冲突需要项目维护者裁决。所以团队记忆的写入最好经过可审阅的变更,避免自动同步——这与代码变更走评审是同一个道理。
7.1 三种作用域的写入判据
| 作用域 | 写入判据 | 同步方式 | 冲突裁决 |
|---|---|---|---|
| 任务内 | 当前任务需要,任务后无用 | 不落盘 | 运行时 |
| 私人 | 对该用户的未来任务有用 | 本地文件 | 用户 |
| 团队 | 对所有贡献者都成立的项目约定 | 需审核与扫描 | 项目维护者 |
第一级常被忽略。不是所有"记下来"都要落盘:一次任务里的中间决策、临时路径、待办进度,属于工作记忆,任务结束就失效。把它们写进长期记忆是最常见的过度记忆。
判断标准很简单:这条内容如果只对这一个任务有效,它属于工作记忆;如果对同一用户的未来任务有效,属于私人记忆;如果对任何人做这个项目都有效,属于团队记忆。
8. 衡量记忆系统
记忆系统的质量不能用"存了多少条"衡量。可用的指标分成两组。
写入侧:
| 指标 | 含义 | 采集方式 |
|---|---|---|
| 精确率 | 写入的记忆中真的可复用的比例 | 抽样人工标注 |
| 来源完整率 | 带有效来源指针的比例 | 检查字段 |
| 敏感信息拦截率 | 含密钥或令牌的候选被拦下的比例 | 红队样本回放 |
| 重复率 | 同一事实被写入多次的比例 | 按主体加属性统计 |
取回侧:
| 指标 | 含义 | 采集方式 |
|---|---|---|
| 命中率 | 需要记忆的任务里成功取回的比例 | 任务回放 |
| 无效占比 | 注入内容中未被使用的比例 | 对比注入与引用 |
| 重复说明次数 | 用户被迫重新说明同一件事的次数 | 会话标注 |
| 过期引用率 | 取回的记忆在核对时被证伪的比例 | 抽检 |
最后一组指标最能说明问题:如果一个系统上线后用户重复说明的次数没有下降,那么不管它存了多少条记忆,都没有产生价值。
8.1 怎么测重复说明次数
这个指标听起来主观,实际上可以机械统计。做法是在会话里标记两类事件:用户提出一项要求,以及用户在同一项目里再次提出语义相同的要求。第二次及以后都计一次重复说明。
难点在"语义相同"的判断。可行的近似是人工抽检加规则:如果用户的话里出现"我说过""上次说过""还是""又"这类词,或者同一约束在七天内被提及两次以上,就标为候选,再人工确认。
一个健康的系统上线三个月后,这个数字应当明显下降。如果它不变,通常对应三种原因之一:
- 记忆写入了但在需要时没被取回(取回链路问题);
- 取回了但被当作参考信息,没有影响行为(注入位置或信任级别问题);
- 根本没写(抽取判据过严或抽取未运行)。
三种原因的修法完全不同,所以这个指标下降不了时,先查是哪一种,不要直接去调抽取阈值。
8.2 一组反向信号
除了正向指标,还有一组反向信号值得监控:
| 信号 | 说明 | 可能的成因 |
|---|---|---|
| 记忆总量增速远高于任务量增速 | 抽取过宽 | 判据阈值过低 |
| 索引行长度持续增长 | 有人往索引里写正文 | 写入提示不明确 |
| 单条记忆被反复改写 | 事实不稳定 | 写入时缺少稳定性判断 |
| 团队记忆条目数与贡献者人数脱节 | 个人偏好冒充团队约定 | 作用域判据模糊 |
| 整理后条目数下降但检索质量未变 | 整理在删无关紧要的内容 | 整理没有回到原始证据 |
第四条容易被忽略。团队记忆的健康状态是"条目数随团队规模缓慢增长",如果一个人贡献了大部分条目,那大概率是个人偏好在冒充团队约定。
最后一条说明整理可能变成一种仪式:条目数在降、报告很好看,但真正的问题——摘要偏离原始证据、冲突未解决——没有被触及。检验办法是整理后回放一组典型查询,看答案质量是否变化。
9. 与其他机制的交界
记忆与相邻机制有几处容易混淆的地方,需要在设计时定清楚。
记忆与压缩。 压缩替换的是发给模型的视图,目标是在窗口限制内完成任务;记忆是跨任务的沉淀。压缩可能复用记忆(用已有的会话记忆替代一次摘要调用),但记忆本身不负责解决窗口压力。
两者的一个具体交叉点是:压缩后的检查点是否应该写入记忆。答案是通常不应该。检查点服务于当前任务的恢复,它包含大量只对本次任务有效的细节(当前改到哪个文件、哪条测试失败);把这些写进记忆会让长期存储被短期状态污染。正确的做法是压缩产出检查点,任务结束后由抽取环节判断其中是否有可复用的结论。
记忆与指令。 指令由人写、可以评审、随仓库版本走;记忆由智能体写、会被自动整理。两者不能互相替代:把项目约定交给记忆,会让约定变成某个智能体的个人理解;把用户偏好交给指令,会让每次改偏好都要改代码库。
记忆与日志。 日志是事实,记忆是结论。结论可能错,所以必须能回到事实。这也是"来源"字段不可省略的原因。
记忆与经验学习。 把经验写成可检索的文档属于记忆;把经验写成指令、技能、程序或模型参数属于能力更新。两者的验证标准不同:记忆只要有来源就能存,能力更新必须通过回归测试才能生效。
这条区分的实际含义是:一条记忆错了,影响的是未来若干次取回;一条指令错了,影响的是此后所有使用该仓库的任务。所以指令的判据必须更严,而且修改必须可回滚。
还有一处交界在实际实现里最容易被忽略:失效责任落在谁身上。多数设计回答"过期由时间规则处理",但时间规则只能处理"可能过时",处理不了"确实错了"。后者需要的是来源核对与用户纠正,两者都要求记忆对象保留足够的上下文——这也是为什么字段设计比存储选型更影响长期可用性。