把电脑轨迹变成活的工作记忆:Work Memory Engine 设计记

一个 local-first 的工作记忆引擎:待办自己长出来、记忆可以提问、每天一页晨报。附 14 条真实踩坑换来的设计决策。

代码开源在 GitHub:AlexZWANG1/work-memory-engine。引擎 + HTTP 后端 + Web UI + 机制测试,零重量依赖,离线可跑。这篇讲它的设计,和 14 条踩坑换来的决策。

它解决什么

一个知识工作者每天在电脑上产生大量上下文:和 AI 的对话、IM 聊天、看过的文档、屏幕上的操作。这些上下文当天就开始死去——三天后你已经说不清”那件事当时到哪一步了”,要花半小时重新拼装。

这个引擎的目标只有一句话:让”之前那件事怎么样了”永远有一个权威、可溯源的答案。

三个入口:

  • 待办:每条工作线现在到哪、下一步是什么,自动长出来,不用手记
  • 提问:自然语言问自己的记忆(“我昨天做了什么""X 项目卡在哪”),答案带可点开的证据
  • 晨报:每天凌晨自动整理全天,睡醒看一页纸

一图流

采集(纯泵,零 LLM):屏幕理解 / IM 消息 / AI 对话 / 云文档变更
        ↓  Raw:append-only 原始流
合流窗:10 分钟一桶 + 分诊

Event Writer(agent 会话) → Events:可验证的事实点
        ↓  30 分钟一轮
Thread Writer → Threads:一条条工作线
        ↓  每天 04:00
梦·舰队(唯一有权做结构手术的环节) → 晨报

待办面板 · 自然语言提问 · 晨报

四个设计立场

  1. 代码校验,模型解释。 语义判断(这段轨迹意味着什么)全部交给 agent 会话;代码只做机械校验:引用是否存在、是否在白名单、有没有静默回退。不用规则引擎模拟理解。
  2. 一切可溯源。 每个事实句必须带事件级引用,点开能看到原始证据;没证据就写 missing,不许编。
  3. 分层节律。 事件 10 分钟一窗(快、碎),主线 30 分钟一轮(整合),梦每天一次(结构手术)——越往上越慢、越有全局观,权限也越大:只有梦可以合并、拆分、重命名工作线。
  4. 自愈优先于完美。 所有后台环节都假设会坏:锁会泄漏、模型会断供、队列会被占死。每一类死法都有机制级的闸,外加一只 15 分钟自检的看门狗,把”坏了”在分钟级喊出来。

14 条设计决策,每条都是真实踩坑换来的

写在前面的原则:机制优先于嘱咐,注释里的条件必须逐字落进代码。

1. 吸收账只增不覆

工作线维护一份”吸收过哪些事件”的账。曾经的实现是每轮覆写成”本轮引用的事件”——一条老线的账被覆写到只剩 1 个 id,时间轴和去重全失忆。定案:账本只增不减;条目级的引用才按本轮正文洗。账本是历史,引用是现场,两者语义不同。

2. 结构手术只属于低频环节

白天写手(30 分钟一轮)禁止合并/拆分/重命名工作线。频率越高的环节越容易在局部视野下做草率的全局决定。结构权力集中给一天一次、读全库、有快照回滚的梦。

3. 舰队梦:多 agent 分工 + 代码装配

单会话的梦(一个 agent 通读全库一次性输出)永远超时。定案:主编 agent 带子 agent 舰队并行——逐线读档 → 主编定收敛方案 → 逐战线撰写 → 产物由代码机械装配。模型负责想,代码负责拼,拼不齐整晚作废不落盘。

配套的防吞线闸:主编必须写”退役意图清单”,代码交叉核对——任何线”既没写成战线又没被宣布退役”(等于某个子 agent 把文件写坏了),整晚作废。部分成功比全部失败更危险,因为它静默。

4. 多 agent 事件流必须按 agentId 过滤

嵌入 agent 内核跑舰队时,子 agent 的 turn 结束事件和主编的进同一条事件流。首跑 37 秒暴毙:第一个子 agent 说”我做完了”,编排器误以为主编收工,把全舰队掐死。任何”等 XX 完成”的信号,先问一句:这条流里还有谁会发同名事件。

5. 快进可以跳窗,但必须能补账

管线吞吐追不上实时时,“快进到最近 90 分钟”是对的(不做不可能追平的考古),但曾经注释声称”跳过的由夜里的梦从 raw 补”——梦根本不读 raw,这句是一厢情愿,真实后果是某天下午 7 小时 1492 条原始记录从未被事件化。修法:防重台账 + 游标可拨回 + 回看上限可配——补账变成”拨回游标重启”一个动作。跨组件的兜底承诺,必须核对对方真的读什么。

6. 排空逻辑的活锁:注释和代码差一个词,系统瘫一天

写手排空的注释写”吃满一批(40)还有剩才连跑”,代码写成”剩任何 1 个就连跑”。写手连轴占死全局串行队列,事件窗被饿到 2 小时一个,新事件一滴一滴进来又给写手续命——完美的活锁,而且只在写手”健康”时发作(它生病超时的日子反而没事)。教训:吞吐敏感的循环条件,值得为”注释=代码”写一个单测。

7. 检索截断必须新鲜优先

记忆检索有”最多扫 N 个文件”的保护上限。曾经按目录枚举顺序截断——事件库过了 N 之后,最新的事件永远进不了搜索范围,而且症状是漂移的(今天还搜得到前天,明天就搜不到昨天),极难被发现。定案:超上限按修改时间新鲜优先截断。“最近的记忆一定搜得到”是记忆系统的天理,用单测锁死。

8. 搜索语义要匹配使用者的心智

检索实现是整串子串匹配,但 agent 天然把 query 当搜索引擎用(空格分隔多个关键词)——多词查询永远 0 命中,agent 得出”没有记录”的错误结论。定案:按词命中 + 命中词数排序 + 整串命中加权。工具的实际语义和调用方的心智模型不一致,比工具坏了更危险——坏了会报错,不一致只会安静地给错误答案。

9. 给 agent 的日期,不要带对冲话术

召回 prompt 注入”今天是 X 日”时,顺手加了句”(本地时区可能晚数小时)“——模型据此自作聪明把日期倒推一天,“昨天”全错。删掉对冲、精确注入本地日期加星期后一次通过。给模型的锚点要么精确,要么别给;模糊限定会被当成推理素材。

10. 锁要有硬 TTL,兜底要能自愈

写锁曾经允许不带过期时间(等于永久锁),一次事故里一个脚本拿 6 小时锁把整条生产线闷死 5 小时。定案三层:锁 TTL 硬上限 / 僵尸锁清道夫(持有者进程死了就清)/ 危险工具入口装”生产管线活跃即拒跑”的机械判据。

11. 沉默故障比故障更可怕:看门狗

模型配额耗尽让整理链路沉默瘫了 36 小时,是人先发现的。定案:15 分钟自检三类沉默故障(整理会话连败 / 机器活跃但事件停产 / 管线滞后过大),命中直接发系统通知。自愈机制修得再多,也要有一只不参与干活、只负责喊疼的眼睛。

12. 对话即记忆

用户和记忆系统的每次问答,本身落回原始流(与 AI 对话同构),被下一个整理周期消化。你问过什么、系统怎么答的,成为可召回的历史——“我上周问过你什么”也应该有答案。

13. 观测留底是产品功能

每次 agent 会话的完整输入(系统 prompt + 任务书)和输出原样存档,面板可回放。用户对一个自动整理系统的信任,来自”我随时能看到它拿什么想、想出了什么”——黑箱的记忆没人敢依赖。

14. 时间类问题的读法要显式教

“昨天做了什么”的正确检索是按日期捞当天事件,而不是按主题词猜。agent 默认按主题搜(它不知道事件正文带时间戳),需要在 prompt 里显式写出这条读法。凡是数据有特殊结构(日期键、账本、白名单),都值得一行 prompt 把结构讲给模型听。

更完整的资料

仓库里有全套设计文档:ARCHITECTURE.md(全链路架构)、DECISIONS.md(本文 14 条的原文)、modules/(8 个模块的独立设计文档)。

相关的方法论在这两篇:人应该干什么:和一群 agent 一起工作的心法我的 AGENTS.md:给 AI 的工作规则全文