为什么 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 个文件:

  1. NOW.md — 每次对话开始时读取,了解当前状态
  2. AGENTS.md — 告诉 Agent 如何操作记忆(写入规则、文件路径、格式要求)
  3. 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 记忆系统最重要的设计原则。