ADR-0001: 日记原语 — 事件流与状态层分离
- 状态:Accepted(2026-07-21 提出,随设计 PR #14 评审定稿)
- 日期:2026-07-21
- 实施:✅ 已落地 —— #16 #17 #19(跟踪 issue #15)
- 关联:ADR-0002(时间检索建立在本原语上)、XnneHangLab ADR-0001(创始设计与硬约束来源)
背景
wikimem 目前只有一种存储原语:category/item 的 wiki(状态层)——内容是主轴,条目可改写。ts 随条目存储,但从未参与检索。
陪伴/桌宠场景需要回答"前天晚上我吃了什么"这类问题,这要求一种以时间为主轴的情景记忆(事件流),当前缺失——ADR-0002 的时间门控没有可以门的对象。
上游两个反面教训:
- MoeChat:LTM(按天日记)与 CoreMemory(核心事实)用"重要/永久程度"划界。重要性是连续的、会变的,切不出干净边界,两个存储的职责长期模糊。
- memU(早期):六种 memory type,每种 type 各跑一遍 LLM,memorize 过重。
另外,情绪需要归处:只有事实、没有情景,角色就没有灵魂。但"情绪"混进状态层会加剧边界模糊。
决策
1. 新增第二存储原语:diary(日记,事件流)
与现有 wiki(状态层)并列:
- 按天一个 markdown 文件:
diary/YYYY-MM-DD.md,每条## HH:MM一个条目,正文后接现有的 HTML 注释 metadata(复用owner / source_conv / ts格式)。 - append-only:框架只提供追加与读取 API,不提供改写既有条目的 API(人当然可以直接改文件——文件即真相,硬约束 3 不变)。
- 条目内容是宿主 LLM 写的生动短段落——场景、情绪、事实在一起。框架不生成内容、不做质量判断(写什么、何时写、什么文风,是宿主的 memorize 策略,见 XnneHangLab ADR-0004)。
- 条目内容可含 wiki-links,与状态层互链。
journal.jsonl照记 diary 写入,与 wiki 写入同一套操作日志。
2. 边界规则(一条就够)
"发生过的事"进日记,"现在为真的事"进 wiki。
判据是离散的:一条记忆有没有"它发生在何时"这个属性。同一件事可以两侧成对出现——日记记事件("7 月 21 日,他说他换了工作,语气很兴奋"),wiki 的 work 条目被更新(状态),互不打架。
3. 不引入 type 分类学
memU 的六种 memory type 属于状态层的内容策略,归宿主的 extraction prompt 管(wikimem 的 category 本来就是自由的)。框架只认两种原语:diary 和 wiki。
4. 当下的情绪状态不进框架
效价/唤醒度这类每轮变化的 runtime 状态(参照 MoeChat 的 emotion_state)不是记忆、不被检索,留在应用运行时。事件中的情绪以日记文本的形式进入记忆——"状态即时,情绪入事"。
5. 文件名即时间索引
日期范围 → 文件集合,O(天数),零索引结构、零依赖。这是 ADR-0002 时间门控的地基;MoeChat 用按天 jsonl 验证过这条路,我们只是把载体换成符合硬约束 3 的 markdown。
理由
- "事件 vs 状态"的切分是离散判据,修复了"重要程度"连续谱带来的边界模糊。
- "jsonl 事实碎片 vs 生动 markdown 日记"不二选一:灵魂在日记,检索精度在 wiki,两层各司其职。
- 按天 markdown 同时满足:人可读可编辑(硬约束 3)、文件名可当时间索引、Obsidian/任意编辑器即免费浏览器。
后果
正面
- 时间检索有了对象(ADR-0002 得以成立)。
- 情绪有了归处,且不污染状态层。
- memorize 不加重:宿主单次抽取调用可同时产出 wiki 条目与日记段落(结构化输出一次带回,见 XnneHangLab ADR-0004),仍满足"至多 1 次 LLM 调用"。
负面 / 代价
- 存储 API 面积扩大(Diary 的追加/按日读取/按窗口列举;挂在
MemoryStore还是独立类,实现期定)。 - 日记条目的检索粒度(整条 vs 段落)、同分钟多条目的 heading 去重、wiki-link 能否指向日记条目——实现期需要敲定的开放问题,不在本 ADR 展开。
- BM25 需要覆盖日记文件:窗口内即时构建(天级文件量可忽略,见 ADR-0002)。
实施
建议作为下一个里程碑(M5)拆 issue:Diary 存储 API → ADR-0002 的窗口检索 → 宿主插件接入。