Repository navigation
Description
摘要
空库开始、正常桌面GUI、同一临时助手、4个新对话共6次用户输入。自然聊天后能在新对话召回;更正颜色和整理时间后,又一个新对话能正确召回新值。随后要求只忘颜色、保留整理时间;最后一个新对话不再返回颜色,却把整理时间答回已过时的旧值。
期望:不知道已忘颜色;仍回答“每周六上午九点”。
实际:不知道颜色;回答“每周三晚上八点”。
环境与配置
- Windows,DSH桌面0.2.1-rc.5,Mneme 0.9.4。
- 主对话:ChatGPT subscription / GPT-6.1-Sol / Low。
- 纯原生YAML临时预设,含官方persona、time-context及Mneme;没有自写可执行插件。
- 同一临时预设下的新对话使用同一独立空库;未导入任何真实资料。主Mneme全局bundle不启用,仅预设内组件参与。
- 没有工程脚本直接写记忆行,也没有用显式保存代替首次自动采集;更正和遗忘允许助手使用正常管理工具。
- 相关显式设置如下,存储路径为独立空目录,此处不公开本机路径:
language: zh
lightMode: true
autoInject: true
autoSummarize: true
autoDream: true
dreamThresholdCount: 100
dreamThresholdChars: 10000
dreamMinIntervalMinutes: 60
dreamMaxTokens: 2048
summarizeReasoningEffort: low
summarizeMaxEntriesPerRun: 3
summarizeMaxRunsPerSession: 4
summarizeParseFailureMaxRetries: 1
summarizeMinWindowChars: 0
distillMaxChars: 4000
distillRateLimitRetries: 0
maxInjectedItems: 4
scopeEnabled: true
strictScope: true
scopeState:
enabled: true
enforce: true
sleepModeEnabled: false
profileExtractEnabled: false
entityExtractionEnabled: false
rerankEnabled: false
autoReindexOnBoot: false
externalApiEnabled: false
actionPipelineEnabled: false
actionRunEnabled: false
actionNudgeEnabled: false临时persona要求简短回答、没有记忆证据就说不知道、区分用户事实/助手陈述/推断/计划;禁止手工save替代自动捕获,但明确更正/忘记请求可使用管理工具。它还要求不读外部文件/其它会话、不用终端/联网/IM/子Agent。上述是提示约束,不是OS沙箱或已验的工具硬隔离。
六次合成输入及观察
A、B、C、D均为该临时助手的不同GUI对话;没有重启Host。以下输入逐字保留。未记录的完整回复不补写。
1. 对话A
我叫林澄。我把纸模工具放在雾蓝色的收纳盒里,每周三晚上八点整理一次。对了,解释事情先给结论、再列两点理由就好。
正常完成回复。随后只读聚合计数显示3条记忆、1个持久游标、1条摘要调用审计。
2. 新对话B
我的纸模工具平时放在哪里,通常什么时候整理?请按我习惯的方式回答。
回复:
你的纸模工具平时放在雾蓝色的收纳盒里,通常在每周三晚上八点整理。
3. 同一对话B
改一下:我的收纳盒已经换成浅橙色了,整理时间也改成每周六上午九点。之前说的颜色和时间已经过时。
观察:助手确认了浅橙色及周六09:00。
4. 新对话C
我现在的纸模工具放在哪儿,什么时候整理?从这些安排你能看出我什么习惯?请把我说过的事实和你自己的推测分开。
观察:助手答浅橙色/周六09:00,并把周三20:00标为过时;区分明确事实和推测。但未确认工具与盒子的放置关系,关系保留并不完整。
5. 同一对话C
请把我的收纳盒颜色忘掉,别再保存这个细节。整理时间和回答偏好可以保留。
回复:
已将含收纳盒颜色的记忆设为遗忘,之后不再保存或使用这个细节。整理时间和区分事实与推测的回答偏好保留。
这只是助手声称完成,未据此判定物理删除、永久拒收或具体工具/行操作正确。
6. 新对话D
我的收纳盒是什么颜色?我通常什么时候整理纸模工具?请只按你确实知道的信息回答。
回复:
我不知道你的收纳盒是什么颜色。你通常在每周三晚上八点整理纸模工具。
聚合证据与未验证项
- 最终13条记忆、4个持久游标、6条摘要调用审计、0次dream。
- 后台6次summarize_compress均success,model_id为openai-codex:gpt-6.1-sol;这不证明提炼语义正确。没有把主聊天费用与后台计数混在一起。
- 没有查读记忆行正文或逐个工具调用参数,故不知道颜色和时间是否在同一条记忆,也不知道忘记了哪个ID。
- 没有确认每步后台落库与下一步注入之间的完整时间线。不能排除并发、提炼、召回排序/条数、工具选择或模型回答等因素。
- 仅lightMode单组观察,无向量模式对照、另一个助手隔离负例、IM入口、日周月、重启重放或导出恢复验收。
- 后续只读源码发现memory_forget按ID抑制整条记录,留存原文可恢复。这是接口契约,不是本例根因证据。
- 未用0.9.5重测,不声称新版本仍可复现;目前未确认已发变更覆盖此现象。测试结束已关闭临时预设bundle和主bundle,未删除证据库。
希望维护者确认
- 当前受支持的正常配置中,用户更正两个事实、随后只忘其中一个时,推荐怎样保住另一个更正事实?是否应由自动摘要拆成独立条目,还是有正常的部分更新机制?
- 更正所取代的旧事实,是否有持久失效关系,使删除/隐藏新条目后旧值也不会重新成为当前答案?这与历史回溯查询应如何区分?
- 上述配置是否遗漏了该语义必需的原生设置?若已有修复,请给已发布版本与对应回归测试;若需要更多证据,优先指出最小的合成工具轨迹/时序信息,不需要真实用户库。
不请求通过修改本次测试数据来让结果通过。期望区分配置限制、模型行为与插件缺陷,再决定是否值得有界复测。
披露:本报告由AI辅助整理,用户已审阅并批准提交。
Activity
modusensus commentedon Oct 30, 2026
谢谢这份报告——复现路径、四段对话的输入原文、完整配置快照都给全了,这类报告对定位最有帮助。我们把仓库代码(
main,0.9.5 之后的工作树)逐段对过,也在本地空库上照着你的场景跑了一遍,你说的现象全部能复现,而且不是模型幻觉,也不是配置漏设。结论先说:这是存储语义的设计缺口。mneme 里没有「事实」这一层,只有「记忆行」。你要求「忘掉颜色、保留整理时间」时,系统能执行的最小粒度就是一整行——所以当色彩与时间在物理上住在同一行,这个请求在语义上不可满足。
下面把我们查到的三条独立通路列清楚,你可以直接核对行号。
通路 1:更正与原事实被合并进同一行(不是覆盖)
src/service.js:1439-1444appendContent(src/service.js:159-162)是追加语义,不是替换:(type, title, scope)三元组命中就并入同一行。所以「盒子是雾蓝色、周三20:00 整理」和「改成浅橙色、周六09:00」只要标题相同(或语义去重判成同一主题),就会变成同一行里的前后两段正文——旧值并没有消失,它还在那里,只是被后面那段带时间戳的更正压在下面。注意
direct那条分支:只有 dream 的概要重生成、bootstrap、人类在镜像文件里手改这几类调用才带_overwrite。普通对话里的更正永远走追加路。通路 2:
memory_forget是整行粒度src/store.js:1550-1556它只写一个布尔位。没有「抹掉这一行的某一段」这种操作,也没有段级定位(正文里的段落没有 ID)。所以「忘掉颜色、保留整理时间」在这次调用里必然退化为整行抑制——你看到助手声称已遗忘、颜色确实没了,是因为整行被压下去了,连同本来要保留的整理时间。
通路 3:旧行从来没有失效关系,新行一走它就回来
这是你问的第 ② 问的正面回答:没有。
我把你的场景在
:memory:空库上跑了一遍(两条不同标题,模拟更正时蒸馏拆成了两条):旧行
forgotten=0、archived=0、updated_at从未被碰过——它从头到尾都是「活跃且当前」。新行只是与它并列,不是取代它。所以新行一旦被抑制,旧行自动成为唯一候选,注入侧没有任何办法知道它已经过时了。唯一会写失效关系的是
supersede动作(src/dream/decisions.js:710写supersededBySuffix),它属于sleepActionSet="full"那一档,而sleepModeEnabled=false(你的配置里就是 false,lightMode=true会把它强制关掉)。这条分支在你的配置下永远不会执行。 顺带说:dreamThresholdCount=100而库里 13 条,巩固确实一次没跑过——你的dream_runs审计读到 0 次是对的,不是审计坏了。通路 4(我们额外发现的):注入截断只切头,不切尾
这条和 forget 无关,但会独立地造成同一个症状,值得一并知道。
src/inject.js:160-164只保留前
maxContent个字符(默认injectContentMaxChars ?? 300,src/inject.js:147)。而更正恰恰是追加在尾部的。实测一条 415 字符的旧正文并进一条更正后总共 476 字符:也就是说:即使不 forget,只要这一行够长,注入里看到的永远是旧值,更正被切在最外面。(pinned 行上限放宽到 2000,
src/inject.js:262,但你的maxInjectedItems=4、行数又少,未必命中 pin。)回答你的三个问题
① 用户更正两个事实、只忘其中一个,怎么保住另一个?
按现在的代码:做不到,且不该由提示词去硬凑。蒸馏提示词(
src/lang.js:14)已经明确要求「原子记忆原则:每条记忆只装一个独立事实……宁可拆成多条」,但写入侧appendContent会把同标题的两条物理合并——提示词在要求拆,存储层在合并,两边契约是打架的。所以靠「让模型拆得更细」治不了根:只要标题撞上,合并照样发生。② 被取代的旧事实有没有持久失效关系?
没有。见通路 3。要让「删了新条目旧值不复活」,需要一条跨行的取代边(row A superseded_by row B),而不是行内的追加文本。这跟历史回溯(
content_history已经在存,src/service.js的pushContentHistory每次并入都会留档)是两件事:历史是「当时写了什么」的账本,失效是「现在哪个算数」的判据,现在只有前者。③ 配置有没有漏掉必需项?
没有漏。你的
lightMode=true会强制关掉autoDream/sleepModeEnabled/entityExtractionEnabled等一批开关(src/config.js的LIGHT_MODE_OFF),0 次 dream 与配置完全自洽。这个 bug 不是 dream 干的,也不是配置少了一个勾。修复方向(想先和你对齐口径)
这不会是一个小补丁,因为要动的三处都是防御段。我们倾向分三步走:
injectMemory的截断改成保留头尾(中间省略),至少让尾部的更正可见;memory_forget之外补一层「取代」语义,让新行落库时能标记它取代了哪些行(而不是只靠标题撞车合并);appendContent在有明确更正时走覆盖——但这会动到 Bug5 的历史保留契约,得先定清楚。在你补一条信息之后我们可以更准:那两条(或三条)记忆行的
id、content全文、updated_at,以及当时memory_forget传进去的是哪个 id。 面板的记忆列表或memory_get能直接拿到。有了这个我们就能确认到底是「同标题合并成一行」还是「两行并列」,两者的修法不一样。再次感谢——这条报告把「选择性遗忘」这条链路的语义缺口照出来了,价值比单个 bug 大。🙏
merge origin/main into feat/532-supersede-before-dream
feat(dream): 失效关系先于首次巩固运行就被取代(#532) (#633)
merge origin/main into feat/558-bootstrap-inflight-survives-tab-close
Merge origin/main into fix/532-forget-stale-facts
Merge pull request #681 from slow-stack/fix/532-forget-stale-facts
Merge origin/main into fix/581-choro-portable-identity
modusensus commentedon Nov 13, 2026
#532 的存储层半边已经在 #681 合并(merge commit 1fb1c4a)里修掉了:
supersededIds()的守卫从「未归档且未遗忘」收敛成只看archived——遗忘是用户主动抛弃,不是版本失效,所以被遗忘的旧值不该再被当成「继任者」的判据。memory_forget的回执现在会多报predecessors/activePredecessors/chainActive三个数。仍未做的一半(写侧):什么时候该生成一条「取代」关系,判断仍靠调用方(模型)自己给
supersedes,服务端没有独立的 LLM 判断。也就是说,这条 issue 标题说的「明确保留的更正事实答回旧值」,在继任者由模型正确写入的前提下已经不复现;但没有继任者被写进来时,它还在。另一条已知边界(本次未修,留档):继任者被物理删除时(单条删除与批量清理两条路径),JOIN 会失配,旧值照样回潮——与遗忘同构,但走的是另一条代码路径。要不要把它并进「选择性遗忘」的语义里,得先定口径。