Replies: 1 comment 1 reply
|
【示例:为什么需要 on-policy 视角】这里,我觉得在不支持版本化方案时,系统是能 trace 并知道 E1 是否影响了 agent 的执行。这里真正需要版本化的原因是 E1 的更新,比如 E1 中增加了一行关键信息,这种感知不到。 |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Memory Data Versioning 方案
日期: 2026-05-28
状态: Draft
目标
为记忆文件(memory files)增加版本化能力,满足以下需求:
data_version查看某个历史时刻的记忆状态。背景与动机
引入 memory versioning 的核心原因,不只是为了“可回溯”或“可审计”,更重要的是为了支持 on-policy memory update。
关键术语
为避免歧义,本文统一使用以下术语:
data_version下的状态,而不是当前 latest state。data_version回到某个历史 policy view,对当时的 memory state 进行读取、检索与分析。为什么需要 on-policy memory update
在 experience system 中,memory 不是静态知识,而是会随着任务反馈持续更新的策略对象(policy object)。
这意味着:
policy view;historical memory state下。如果系统只保留 latest state,而不保留 historical memory state,那么 experience consumption 的分析就会被“事后知识”污染,导致经验更新无法做真正的 on-policy 归因。换句话说,系统失去了对 memory policy view 的 time travel 能力。
示例:为什么需要 on-policy 视角
假设有一个 game agent,在挑战同一个 Boss,目标是尽快学会稳定通关。
历史过程
data_version = v1时,memory 中还没有这条经验:policy view = v1data_version = v2policy view = v2如果系统支持 on-policy memory update
系统可以明确区分:
policy view(v1)policy view(v2)这样在分析时就能得到正确结论:
如果系统不支持 on-policy memory update
如果系统只保留 latest state(即只看到 v2),那么回看 Task A 时会产生错误理解:
这就会带来几个典型问题:
历史决策被未来知识污染
经验效果归因错误
经验生成与经验消费错位
评估结果失真
图示:遵守 on-policy 与不遵守 on-policy 的区别
下面用 Boss 战例子说明,为什么 experience system 需要遵守 on-policy memory update。
场景
Agent 学到一条新经验:
情况 A:遵守 on-policy
sequenceDiagram participant TA as Task A (policy view = v1) participant M as Memory participant EU as Experience Update participant TB as Task B (policy view = v2) Note over M: v1: no E1 TA->>M: consume policy view(v1) Note over TA: Boss 抬手<br/>继续输出<br/>被大招击杀 TA->>EU: 从失败中抽取经验 EU->>M: 写入 E1<br/>Boss 抬手 1 秒后会放红圈大招,应立即闪避 Note over M: v2: with E1 TB->>M: consume policy view(v2) Note over TB: Boss 抬手<br/>识别为大招前摇<br/>立刻闪避并通关结论:
情况 B:不遵守 on-policy
也就是在事后分析时,只看 latest memory,而不回到当时的 historical memory state。
flowchart LR A[Task A 执行于 v1] --> U[写入经验 E1] U --> B[Task B 执行于 v2] A -. 事后统一拿 latest(v2) 回看 .-> X[Task A 被误判为 明明知道要闪避却还在贪输出] B -. 也统一拿 latest(v2) 回看 .-> Y[Task B 的改善 无法准确归因到 E1]结果:
本方案解决的问题
因此,memory versioning 的本质,不只是“保存历史”,而是为 memory / experience 提供一个时间维度上的 policy view。从能力定义上看,这本质上也是一种对 memory state 的 time travel capability。
这也是为什么本方案必须支持:
readsearchpolicy view最终目标是让经验系统能够做到:
已确认的设计约束
1. 版本参数
统一使用:
data_version2. 版本号形式
使用毫秒级时间戳。
约定:
data_versiondata_version因此,
data_version是一批 memory 变更的全局版本号,而不是单文件独立版本号。3. 检索语义
当指定:
语义为:
<= X的最近可用状态进行还原<= X的历史版本,则说明该文件在该版本视角下不存在可用状态,不应被召回4. 向量策略
采用简化方案:
该方案的特点:
5. 历史存储位置
6. 正文与版本链关系
7. diff 粒度
MEMORY_FIELDS8. 删除策略
VERSION_HISTORY.status = "deleted"标记当前状态9. checkpoint / compact
一期不做 checkpoint:
10. diff 算法
本方案区分两类 diff,用于不同目的:
10.1 存储层 diff(用于
VERSION_HISTORY)diff-match-patchVERSION_HISTORY10.2 合并层 diff(用于 LLM merge)
unified diffhead_version != src_version时,向 LLM 展示base / head / candidate之间的差异10.3 双 diff 策略
因此本方案采用:
diff-match-patchunified diff也就是说:
VERSION_HISTORY里的 diff,不直接面向 LLM;11. 历史读取范围
search和按版本read12. 历史版本保留策略
13. 并发与锁策略
memory_file时必须加锁14. 历史数据兼容性
VERSION_HISTORY的历史 memory filedata_version读取或检索时,当前文件版本的判断顺序为:VERSION_HISTORY.data_versionVERSION_HISTORY.updated_atdata_version时可正常返回当前内容data_version时,不参与历史判断,直接视为不存在可用版本VERSION_HISTORY总体思路
Patch 定义
本文中的 patch,特指:
diff-match-patch生成的文本 patchsrc_version对应的 memory file 内容语义上可表示为:
也就是说:
src_version,不能脱离 base 单独解释。Patch apply 规则
经验提炼完成后,系统会先基于
src_version恢复出 base file,再生成 candidate updated content,随后通过diff-match-patch生成 patch。apply 规则如下:
如果
head_version == src_version如果
head_version != src_version从设计上看,这个规则可以概括为:
核心原则
每个记忆文件只保留一份最新完整内容,历史通过文件内版本链回溯。
即:
当需要查看历史版本时:
data_version找出目标版本文件结构设计
建议把记忆文件拆成三部分:
MEMORY_FIELDS(普通业务元数据)VERSION_HISTORY(系统版本元数据 + 版本链)其中:
MEMORY_FIELDS参与 diffVERSION_HISTORY不参与 diff 基线这样可以避免“历史记录本身不断进入下一次 diff”的套娃问题。
建议文件格式
版本语义
1.
VERSION_HISTORY.data_version表示当前文件正文对应的最新版本。
2.
VERSION_HISTORY.status表示当前正文在当前版本下的状态。
建议值:
activedeleted3.
versions每个 version item 表示:
例如:
v3version 保存v3 -> v2的 reverse diffv2version 保存v2 -> v1的 reverse diff因此,版本链方向是:
写入流程设计
建议在现有 memory apply / updater 流程中改造。
Step 1:生成 batch 级
data_version一次 memory apply 开始时生成统一版本号:
目的:
Step 2:处理单文件写入
场景 A:新建文件
VERSION_HISTORY.data_version = 当前 batch data_versionVERSION_HISTORY.updated_at = 当前写入时间VERSION_HISTORY.status = "active"VERSION_HISTORY.versions追加:op = createreverse_diff = null场景 B:更新文件
流程:
MEMORY_FIELDS,不含VERSION_HISTORY)MEMORY_FIELDS,不含VERSION_HISTORY)MEMORY_FIELDSVERSION_HISTORY.data_version、VERSION_HISTORY.updated_at、VERSION_HISTORY.statusVERSION_HISTORY.versions追加:data_version = 当前 batch data_versionop = updatereverse_diff = ...场景 C:逻辑删除
流程:
VERSION_HISTORY.status = "deleted"VERSION_HISTORY.updated_at = nowVERSION_HISTORY.data_version = 当前 batch data_versionop = deletereverse_diff = 当前 status=deleted 状态 -> 删除前状态这样历史还原时即可恢复删除前内容。
还原流程设计
提供一个核心能力:
还原逻辑
不传
data_versionVERSION_HISTORY.status = "deleted",则默认视为不可见传入
data_version = X流程:
MEMORY_FIELDS与VERSION_HISTORYVERSION_HISTORY.data_version(当前正文版本)<= X的历史版本:VERSION_HISTORY的历史文件,应按“仅存在当前 head 单版本”处理VERSION_HISTORY.data_version <= X:VERSION_HISTORY.versionsentry.data_version > X的记录依次应用reverse_diffMEMORY_FIELDS与VERSION_HISTORYVERSION_HISTORY.status = "deleted",则该版本不可见;否则返回检索流程设计
接口形式
流程
Step 1:最新向量召回
从当前向量索引召回候选 memory file URI。
Step 2:历史还原
对每个候选 URI 调用:
Step 3:过滤删除态
data_version is None:VERSION_HISTORY.status = "deleted"的文件data_version:status = "deleted",则该文件不可见Step 4:返回还原内容
将还原后的文本作为最终检索结果返回,或用于后续 prompt 注入。
删除可见性规则
默认检索
不传
data_version时:历史检索
传
data_version时:存储实现细节建议
1.
VERSION_HISTORY不参与 diff 基线建议参与 diff 的“可还原业务文本”只包括:
MEMORY_FIELDS不包括:
VERSION_HISTORY否则历史链本身会反复被写入 diff,导致版本膨胀。
2. diff 算法建议
推荐使用成熟文本 diff / patch 方案,例如:
要求:
3. 后续 compact 预留
虽然一期不做 compact,但建议版本历史结构预留压缩能力,例如未来支持:
4. 版本历史上限
一期直接限制:
这意味着非常老的历史版本可能不可恢复。
5. 单文件写锁
写入 memory file 时必须加单文件排他锁:
MEMORY_FIELDS、更新VERSION_HISTORY建议 metadata 字段
MEMORY_FIELDS只保留业务元数据,例如:
{ "memory_type": "preferences" }VERSION_HISTORY建议结构:
{ "data_version": 1780000000123, "updated_at": "2026-05-27T15:10:23.456Z", "status": "active", "versions": [ { "data_version": 1780000000123, "op": "update", "reverse_diff": "..." }, { "data_version": 1779999999000, "op": "delete", "reverse_diff": "..." }, { "data_version": 1779999998000, "op": "create", "reverse_diff": null } ] }建议接口
1. 读取当前或历史版本
2. 检索当前或历史版本
3. 逻辑删除
说明:
结论
本方案采用:
data_version作为 batch 级全局版本号data_version还原目标状态这是一个偏工程实用的一期方案:
All reactions