为什么 Agent 需要独立的记忆系统
每个 LLM Agent 都有一个致命弱点:上下文窗口(Context Window)不等于记忆。
上下文窗口是临时的——对话过长时系统会自动压缩(Compaction),丢弃早期内容;session 结束后一切归零。Agent 醒来时,对之前发生的事一无所知,除非它把信息写进了文件。
在实际运行中,这会导致三个典型问题:
- Agent 在不同 session 中重复提出相同建议
- 跨频道(Telegram、Discord)的信息完全隔离
- Compaction 后 Agent 丢失关键上下文
核心设计哲学只有一条:文件 = 事实来源。你不写进文件的东西 = 你从来不知道的东西。 Context window 是工作台,文件才是仓库。
三层记忆架构
整套记忆系统分为短期、中期、长期三层,信息从上到下逐层提炼。
短期层:NOW.md
NOW.md 是 Agent 的"工作台",记录当前状态、优先级和阻塞项。
关键特性:
- 每次 heartbeat 覆写(Write),不追加
- 只保留当天的完成项
- 是 Compaction 的"救生筏"——Agent 压缩上下文后首先读这个文件
- 唯一允许覆写的记忆文件,其他记忆文件只追加不覆写
中期层:每日日志
用途是记录事件流水,充当记忆系统的"原始数据"。
文件名格式为 memory/YYYY-MM-DD.md,写入格式统一为 ### HH:MM — Title,便于扫描和检索。
关键设计:
- 追加式(append-only),永不覆写
- 不同 session 看不到彼此的对话,所以重要信息必须立刻写进日志
- 推荐用带自动时间戳的脚本写入,避免 Agent 自己编码时间(防止幻觉)
长期层:INDEX.md 与结构化子目录
长期层存放的不是原始事件,而是从事件中抽取的可复用知识。INDEX.md 是导航枢纽,指向各个子目录。Agent 启动时扫描 INDEX.md,按需加载具体文件。
关键设计:
- INDEX.md 包含健康度指标(优先级、状态、最后验证日期),Agent 扫描时能立即判断哪些知识可信
- 高优先级文件 = 核心知识,永不归档
[⚠️ stale]标记 = 超过 30 天未验证,信息可能过时
三层之间的信息流动
原始对话 → 写入日志(保留细节)
日志 → 提炼到知识库(抽取可复用的教训/决策/画像)
过期数据 → 归档到冷存储(释放检索空间)
目录结构与文件规范
知识文件 Frontmatter 规范
所有 lessons/、people/、decisions/ 下的文件必须带 YAML frontmatter,包含 last_verified(最后验证日期)等字段,用于支撑过时检测和健康度管理。
冷存储设计:.archive/ 目录
使用 . 开头的目录作为冷存储,原因很巧妙——QMD 搜索引擎用 Bun.Glob 扫描文件时,硬编码跳过以 . 开头的目录。这是一个零配置的隔离方案,只靠目录命名约定就实现了冷热分离。归档文件不会被删除,仍可通过文件系统直接访问。
写入机制
日志写入脚本 memlog.sh
关键设计决策:
- 时间戳从系统时间自动获取,不需要 Agent 自己编码(避免幻觉)
- 追加式写入,永远不覆盖已有内容
- 使用
set -euo pipefail确保错误不会静默失败
路由规则
当 Agent 获得新信息时,需要判断写入位置。经验法则:日志可以随便写(宁多勿少),知识文件要谨慎写(先读再写)。 日志是临时便签本,知识文件是蒸馏后的精华。
CRUD 验证
写入知识文件时,必须遵循"先读再写"原则,防止记忆幻觉(HaluMem)——即 Agent 写入错误、重复或矛盾的信息,然后在未来把这些错误信息当作事实使用。
实施节奏:
- 日间 heartbeat:做轻量级去重(检查目标文件末尾有没有相同内容)
- 夜间反思(23:45):做深度 CRUD 验证(完整比对 + 分类 + 标记)
检索机制
三级检索策略
- L1:直接文件路径——已知目标时直接读取
- L2:INDEX.md 导航——通过索引表定位
- L3:QMD 语义搜索——不确定目标时的兜底方案
优先走 L1/L2,L3 作为兜底。大多数检索靠 INDEX.md 导航就能解决。
QMD 混合检索
QMD 是本地混合搜索引擎,结合了全文搜索(FTS5)和向量语义搜索两种方式。query 模式会同时执行两种搜索,然后用 LLM 做重排序(reranking),返回综合最优结果。QMD 每 5 分钟自动重新扫描文件系统,新增/修改/删除的文件会自动更新索引。
中文搜索的已知限制
QMD 的 FTS5 使用 unicode61 tokenizer,不支持中文分词。中文没有空格分隔词语,unicode61 会把连续汉字视为一个 token。
临时方案:
- 用
query模式走向量语义搜索(对中文有效但较慢) - 写入时有意识地用空格分隔关键词(如"盘前 简报"而非"盘前简报")
- 未来方向:等 QMD 支持 trigram tokenizer 或 ICU 中文分词
记忆生命周期
日间:实时写入
Agent 通过 heartbeat(定时心跳,典型配置 30 分钟到 1 小时)和用户对话两个途径写入记忆。Heartbeat 的核心工作包括扫描活跃 session、提取信息写入日志、路由到知识文件、刷新 NOW.md、检查任务生命周期。
每晚 23:30:日志同步
当天最后一道防线,确保没有信息遗漏。
每晚 23:45:夜间反思
记忆系统最核心的整合环节,相当于人脑睡眠时的记忆整合(Consolidation)。执行深度 CRUD 验证、知识提炼、过时检测。
为什么把 CRUD 放在夜间而不是每次写入?
- 日间写入追求速度(memlog.sh < 0.01s),加 CRUD 会变成 12 秒(语义搜索延迟)
- 日志是事件流,重复是正常的(同一事件从不同 session 写入)
- 知识库的更新频率低,夜间有完整一天数据,判断更准确
每周日 00:00:冷数据归档
每周一次的 GC(垃圾回收)把冷数据归档到 .archive/。归档后 QMD 在下次自动重扫(≤5 分钟)时自动移除索引。
遗忘与衰减:为什么需要主动遗忘
一个只增不减的记忆系统最终会被自己淹没。30 天运行积累了 114 个 Markdown 文件,不做遗忘会导致:
- 检索噪音增大:向量搜索返回大量不相关旧信息
- 启动变慢:INDEX.md 越来越长,扫描成本增加
- 存储膨胀:搜索索引持续增长
正如艾宾浩斯遗忘曲线告诉我们的:遗忘不是 bug,是 feature。
温度模型
用"温度"衡量记忆的活跃程度:高温 = 近期频繁引用,低温 = 长期未被访问。温度随时间自然衰减,被引用时升温。
归档前提炼
GC 不是简单地扔掉旧文件。夜间反思(23:45)已经从日志中提取了可复用知识写入 lessons/,GC(00:00)只归档已被反思处理过的日志。如果发现某个日志没有对应的反思记录,标记为 [UNREFLECTED] 暂不归档。先提炼再遗忘,知识不会因为归档而丢失。
关键设计还包括引用检查——如果一个旧日志仍被其他文件引用(比如某个 lesson 引用了 [[2026-01-30]]),它不会被归档,确保知识图谱完整性。
可靠性保障:对抗记忆幻觉
Agent 记忆系统面临四种幻觉风险(来源:HaluMem 研究),需要多层防线:
写入时验证:CRUD 四分类
每次写入知识文件前,强制阅读已有内容,将新信息分类为 Create(新建)、Read(已存在且一致)、Update(需更新)、Delete(需删除),把冲突暴露出来而不是埋进去。
过时检测
夜间反思时扫描所有文件的 last_verified 字段,超过阈值的标记为 stale。这不是自动删除,而是降低置信度——Agent 仍可使用,但会意识到需要重新验证。
冲突处理原则
宁可暴露冲突,不可静默覆盖。 静默覆盖是记忆幻觉最危险的形式——Agent 以为自己"知道"的东西其实是错的。
多 Agent 协作中的记忆
独立目录,聚合共享
每个 Agent 有自己的工作空间和 memory/ 目录,只读写自己的记忆。跨 Agent 信息共享通过主 Agent 的 heartbeat 聚合——扫描各子 Agent 的近期产出文件,提取关键信息写入自己的日志,跨 Agent 洞察路由到自己的 lessons/。
跨 Session 信息同步
不同 session 之间完全隔离(Telegram 上的对话和 Discord 上的对话互不可见),文件是唯一的跨 session 通道。任何在一个 session 中获得的重要信息,必须立刻写入文件。
快速上手:从三个文件开始
最小可用配置只需 3 个文件:
- NOW.md — 每次对话开始时读取,了解当前状态
- AGENTS.md — 告诉 Agent 如何操作记忆(写入规则、文件路径、格式要求)
- memory/YYYY-MM-DD.md — 日志文件,追加式写入
这就是最小可用的记忆系统。Agent 启动时知道当前状态,对话中的重要信息会被记录,下次启动时能恢复上下文。
渐进式搭建路径
- 阶段 0:NOW.md + 日志(1 天搞定)
- 阶段 1:加入 INDEX.md + 知识子目录(1 周)
- 阶段 2:加入 heartbeat + 夜间反思(2 周)
- 阶段 3:加入 QMD 检索 + 归档 GC(3-4 周)
建议:不要一次性搭全。 从阶段 0 开始,每周加一层。每个阶段都可独立运行。
Obsidian 集成
这套记忆系统天然兼容 Obsidian——本质就是一堆 Markdown 文件。将 memory/ 目录设为 Obsidian vault,启用 Graph View 可视化 decisions/、lessons/、people/ 之间的引用关系,发现潜在的知识关联。
这套系统从 3 个文件起步,经过 30 天迭代到当前状态,不是一次性设计出来的,而是在解决真实问题(Compaction 丢信息、搜索找不到、知识过时、记忆冲突)的过程中逐步演进的。如果你也在给自己的 Agent 搭记忆系统,从阶段 0 开始,遇到问题时再加层——这本身就是 Agent 记忆系统最重要的设计原则。