# 15. Memory:统一概念模型
# 15.1 原理
Memory 不应只是一张向量表。面试中可以使用以下分类:
| 类型 | 内容 | 生命周期 | 典型存储 |
|---|---|---|---|
| Episodic | 某次任务发生了什么 | 中长期 | Rollout/事件日志、任务摘要 |
| Semantic | 稳定事实、项目知识 | 长期 | 文档、KV、图、向量索引 |
| Procedural | 如何完成一类任务 | 长期 | Skill、工作流、策略 |
| Working | 当前计划、近期观察 | Turn/Session | 内存状态、短期摘要 |
| Prospective | 将来要做的事 | 到期/完成前 | Task、Reminder、队列 |
Memory 的生命周期:capture → normalize → validate → store → index → retrieve → inject → observe usage → update/expire。
# 15.2 关键设计
- 来源可追踪:每条记忆关联原始 Thread、事件、文件或用户确认。
- 作用域明确:用户级、项目级、组织级不能混用。
- 权限继承:召回记忆的可见范围不能超过当前 Session 身份。
- 时间与失效:配置、依赖版本、路径等会过期;需要 TTL 或验证时间。
- 使用反馈:记录召回是否被引用、是否帮助完成任务,作为后续排序依据。
Codex 的长期记忆 Phase 2 会按 usage_count、last_usage/generated_at 选择输入,并记录引用使用;Claude Code 抽取侧以增量游标和实际写入文件作为成功证据。这说明“Memory 迭代”不仅是生成摘要,还包括选择、使用和更新反馈。
# 15.3 伪代码
function retrieveMemory(query, principal, project): // 为当前身份和项目检索可注入的长期记忆
candidates = hybridSearch(keyword, vector, graph, query) // 组合关键词、向量与关系图召回候选记忆
candidates = candidates.filter(m => aclAllows(principal, m.scope)) // 先按身份和作用域过滤,防止跨租户泄漏
candidates = candidates.filter(m => notExpired(m)) // 移除超过 TTL 或已明确失效的事实
ranked = rerank(candidates, // 对安全候选进行面向任务的二次排序
relevance + authority + freshness + usageSuccess - conflictPenalty) // 综合相关性、权威、新鲜度、使用效果和冲突惩罚
return fitTokenBudget(ranked) // 按排序结果裁剪到记忆注入预算
function observeMemoryUse(memoryId, outcome): // 记录某条记忆在实际任务中的使用反馈
update usage_count, last_usage // 增加使用次数并刷新最后使用时间
if outcome contradicted: // 工具证据或用户反馈推翻了记忆内容
markNeedsValidation(memoryId) // 标记为待验证,后续召回时降权或隔离
# 15.4 面试题
问:为什么 Memory 与 Context 要分开?
Memory 是可持久化、可检索的候选知识;Context 是某个 Step 实际提供给模型的有界视图。Memory 需要经过检索、权限、新鲜度和预算筛选后才能进入 Context。
# 15.5 原理深化:Memory 的类型决定写入权、失效和召回方式
五类 Memory 不是标签装饰,而是不同一致性模型。Episodic Memory 追加新事件而不覆盖旧事实;Semantic Memory 需要版本、冲突和有效时间;Procedural Memory 类似代码与 Skill,需要评审、版本和执行权限;Working Memory 可由当前事件重算;Prospective Memory 则以完成、取消或到期作为终态。若全部放进同一向量库并使用同一“相似度 Top-K”,计划提醒会与过期项目知识一起被当成普通文本召回。
源码中的原始会话记录(Rollout)、第一阶段原始记忆、第二阶段合并产物和最终 Context 注入,共同形成一条可追溯的来源链。每次抽取只能新增一个派生版本,不能抹掉来源;合并产物应记录涵盖哪些 Thread 和 Task,读取时再按当前主体、项目和记忆模式过滤。Codex 把单个 Thread 的抽取结果建成带 generated_at 的独立状态记录,并用全局合并租约管理跨 Thread 合并,正是为了把“原始经历”和“稳定知识”分开:memories.rs:8、memories.rs:54。
删除也应沿信息的来源与派生关系传播:用户删除会话或撤销敏感事实时,原始事件、派生摘要、索引向量和缓存都要通过删除标记(tombstone)和来源关系(lineage)找到并失效。仅从展示文件删除文本却保留向量索引,会产生“已经删除却仍能被召回的记忆”。因此 Memory 平台除了 content + embedding,还需要记录来源、可见范围、有效时间、保留期限和删除状态。