如何为 AI Agent 构建一套真正能用的记忆系统
一份从零到生产的实战指南,基于 5 个 Agent 协作团队 30 天的实际运行经验。
第一部分:为什么 Agent 需要记忆系统
1.1 Context Window 不是记忆
每个 LLM Agent 都有一个致命弱点:上下文窗口(Context Window)不等于记忆。
上下文窗口是临时的。当对话过长,系统会自动压缩(Compaction),丢弃早期内容。当 session 结束,一切归零。Agent 醒来时,对之前发生的事情一无所知 -- 除非它写进了文件。
Session 1: 做了重要决策 A → 没写进文件 → Compaction
Session 2: Agent 完全不知道决策 A 的存在 → 重复讨论 → 浪费时间这不是理论问题。在实际运行中,我们观察到:
- Agent 在不同 session 中重复提出相同建议
- 跨频道(Telegram、Discord)的信息完全隔离
- Compaction 后 Agent 丢失了关键上下文
1.2 设计哲学
我们的核心原则只有一条:
文件 = 事实来源。你不写进文件的东西 = 你从来不知道的东西。
Agent 的记忆不在它的"脑子"里,而在磁盘上。Context window 是工作台,文件才是仓库。
这意味着:
- 所有重要信息必须实时写入文件
- Agent 每次启动都从文件读取状态
- 不依赖"记得去检查",而是靠系统触发(cron、heartbeat)
1.3 本方案的定位
我们选择了 Markdown 文件 作为记忆的主要载体,而不是纯数据库方案。
TIP
我们的答案是混合方案:Markdown 作为事实来源(可读层),QMD 向量数据库作为检索加速层。写入 Markdown,索引自动同步。
第二部分:三层架构
2.1 短期层:NOW.md
用途: Agent 的"工作台",记录当前状态、优先级、阻塞项。
特点:
- 每次 heartbeat 覆写(Write),不追加
- 只保留当天的完成项
- 是 Compaction 的"救生筏" -- Agent 压缩上下文后首先读这个文件
示例结构:
NOW.md - Workbench
> Mission: 你的核心目标
## Today (MM/DD)
- [x] 已完成的重要事项
- [!] 需要关注的问题
## P0 Priorities
| # | Item | Status | Owner |
|---|------|--------|-------|
| P0 | 最重要的事 | 进行中 | Agent A |
| P1 | 次重要的事 | 待开始 | Agent B |
## Agent Status
| Agent | Focus |
|-------|-------|
| Agent A | 当前任务 |
| Agent B | 当前任务 |
---
Updated: YYYY-MM-DD HH:MMWARNING
NOW.md 是唯一允许覆写的记忆文件。其他记忆文件只追加,不覆写。
2.2 中期层:每日日志
用途: 事件流水,记录当天发生的一切。是记忆系统的"原始数据"。
文件名格式: memory/YYYY-MM-DD.md
写入格式:
## 14:30 — 完成了数据库迁移
迁移 PostgreSQL 从 v14 到 v16,耗时 45 分钟。
关键步骤:先备份 → 升级 → 验证数据完整性。
无数据丢失。
### 15:15 — Agent B 提交了周报
Agent B 在 Discord #reports 频道提交了本周业务报告。
要点:用户增长 12%,留存率稳定在 65%。写入工具: 推荐用一个带自动时间戳的脚本(下文详述),避免手动编码时间。
关键设计:
- 追加式(append-only),永不覆写
- 不同 session 看不到彼此的对话,所以重要信息必须立刻写进日志
- 格式统一为
### HH:MM — Title,便于扫描和检索
2.3 长期层:INDEX.md → 结构化子目录
用途: 经过提炼的、结构化的知识库。不是原始事件,而是从事件中抽取的可复用知识。
设计: INDEX.md 是导航枢纽,指向各个子目录。Agent 启动时扫描 INDEX.md,按需加载具体文件。
Memory Vault Index
> Agent 启动时先扫这个文件,按需加载具体内容。
## Lessons
| 文件 | 优先级 | 状态 | 最后验证 | 说明 |
|------|--------|------|----------|------|
| [[cron-discipline]] | P0 | active | 2026-02-26 | 自动化调度经验 |
| [[infrastructure]] | P1 | active | 2026-02-20 | 基础设施运维 |
| [[api-integration]] | P2 | stale | 2026-01-15 | API 集成(可能过时) |
## Decisions
| 文件 | 说明 |
|------|------|
| [[2026-02-14-architecture-v2]] | 架构升级决策 |
## People
| 文件 | 优先级 | 状态 | 说明 |
|------|--------|------|------|
| [[user-profile]] | P0 | active | 用户画像和偏好 |关键设计:
- INDEX.md 包含健康度指标(优先级、状态、最后验证日期),Agent 扫描时能立即判断哪些知识可信
- P0 优先级的文件 = 核心知识,永不归档
stale标记 = 超过 30 天未验证,信息可能过时
2.4 三层之间的信息流动
┌──────────────┐ 实时写入 ┌──────────────────┐
│ 对话/事件 │ ──────────────→ │ 每日日志 (中期) │
└──────────────┘ └────────┬─────────┘
│
每晚 23:45 反思
(提炼 + CRUD)
│
▼
┌──────────────┐ 每次 heartbeat ┌──────────────────┐
│ NOW.md (短期) │ ◄──── 覆写 ───── │ 知识库 (长期) │
└──────────────┘ │ lessons/ │
│ decisions/ │
│ people/ │
└──────────────────┘
│
每周日 GC 归档
│
▼
┌──────────────────┐
│ .archive/ (冷存储) │
└──────────────────┘信息从上到下逐层提炼:
- 原始对话 → 写入日志(保留细节)
- 日志 → 提炼到知识库(抽取可复用的教训/决策/画像)
- 过期数据 → 归档到冷存储(释放检索空间)
第三部分:目录结构与文件规范
3.1 完整目录树
workspace/
├── NOW.md # 短期:状态仪表盘(覆写式)
├── MEMORY.md # 可选:指向 INDEX.md 的指针
├── AGENTS.md # Agent 操作手册
├── HEARTBEAT.md # Heartbeat 执行流程
│
└── memory/
├── INDEX.md # 知识导航(启动时必读)
├── YYYY-MM-DD.md # 每日日志(追加式)
│
├── decisions/ # 战略决策记录
│ └── YYYY-MM-DD-slug.md
├── lessons/ # 可复用教训(按主题)
│ └── TOPIC.md
├── people/ # 人物/Agent 画像
│ └── NAME.md
├── projects/ # 项目状态追踪
│ └── PROJECT.md
├── preferences/ # 用户偏好与边界
│ └── user-preferences.md
├── reflections/ # 每日自省记录
│ └── YYYY-MM-DD.md
├── actions/ # 任务生命周期
│ ├── open/
│ ├── in-progress/
│ └── done/ # >14 天自动归档
│
└── .archive/ # 冷数据(以 . 开头,搜索引擎不索引)
├── YYYY-MM-DD.md # 归档的旧日志
└── reflections/ # 归档的旧反思3.2 知识文件 Frontmatter 规范
所有 lessons/、people/、decisions/ 下的文件必须带 YAML frontmatter:
title: "Cron 调度纪律"
date: 2026-02-13 # 创建日期
category: lessons # lessons | person | decision
priority: P0 # P0 核心 | P1 重要 | P2 参考
status: active # active | superseded | conflict
last_verified: 2026-02-26 # 最后确认内容仍然正确的日期
tags: [cron, automation, reliability]状态流转:
active ──→ superseded (被更新版本取代)
active ──→ conflict (发现矛盾信息,待人工裁决)
conflict ──→ active (人工裁决后恢复)3.3 .archive/ 冷存储设计
为什么用 . 开头的目录?
我们使用 QMD 作为语义搜索引擎。QMD 用 Bun.Glob 扫描文件时,硬编码跳过以 . 开头的目录。这意味着:
| 路径 | QMD 行为 |
|---|---|
memory/2026-01-15.md | 会索引 |
memory/.archive/2026-01-15.md | 自动跳过 |
memory/archive/2026-01-15.md | 仍会索引(没有点号前缀) |
这是一个零配置的隔离方案 -- 不需要修改搜索引擎的配置文件或排除规则,只靠目录命名约定就实现了冷热分离。
TIP
归档的文件不会被删除,仍可通过文件系统直接访问。如果使用 Obsidian,可以在设置中开启"显示隐藏文件"来浏览归档内容。
第四部分:写入机制
4.1 写入工具:memlog.sh
#!/usr/bin/env bash
# memlog.sh — 自动时间戳的日志追加工具
# 用法: memlog.sh "Title" "Content body"
set -euo pipefail
MEMORY_DIR="${MEMORY_DIR:-/path/to/workspace/memory}"
TODAY=$(TZ=Asia/Shanghai date +%Y-%m-%d)
NOW=$(TZ=Asia/Shanghai date +%H:%M)
FILE="$MEMORY_DIR/$TODAY.md"
TITLE="${1:?Usage: memlog.sh \"Title\" \"Body\"}"
BODY="${2:-}"
# 如果文件不存在,创建带日期标题的文件
if [[ ! -f "$FILE" ]]; then
printf "# %s\n" "$TODAY" > "$FILE"
fi
# 追加带时间戳的条目
{
printf "\n### %s — %s\n" "$NOW" "$TITLE"
[[ -n "$BODY" ]] && printf "\n%s\n" "$BODY"
} >> "$FILE"
echo "Logged to $TODAY.md at $NOW"关键设计决策:
- 时间戳从系统时间自动获取,不需要 Agent 自己编码(避免幻觉)
- 追加式写入,永远不会覆盖已有内容
- 使用
set -euo pipefail确保错误不会静默失败
4.2 路由规则
当 Agent 获得新信息时,需要判断这条信息应该写到哪里:
新信息到来
│
├─ 是一个重大决策? → decisions/YYYY-MM-DD-slug.md (新建)
│
├─ 是一条可复用的经验? → lessons/TOPIC.md (追加)
│
├─ 是关于某个人的新信息? → people/NAME.md (追加)
│
├─ 以上都不是,但值得记录? → memory/YYYY-MM-DD.md (日志)
│
└─ 无实质内容 → 不写 (NOOP)TIP
经验法则:日志可以随便写(宁多勿少),但知识文件要谨慎写(先读再写)。日志是临时的便签本,知识文件是蒸馏后的精华。
4.3 CRUD 验证
写入知识文件(lessons/、people/、decisions/)时,必须遵循 "先读再写" 原则:
准备写入 lessons/cron-discipline.md
│
├─ Step 1: 读取目标文件当前内容
│
├─ Step 2: 比较新知识与已有内容
│ │
│ ├─ 已有内容完全覆盖了新知识 → NOOP (跳过)
│ │
│ ├─ 新知识是对已有内容的更新 → UPDATE
│ │ 旧版标记: > [Superseded 2026-02-26]
│ │ 追加新版本
│ │
│ ├─ 新知识与已有内容矛盾 → CONFLICT
│ │ 两版都保留
│ │ 加标记: > CONFLICT (2026-02-26): 与上方内容矛盾
│ │
│ └─ 全新的知识 → ADD (追加新段落)
│
└─ Step 3: 更新 frontmatter 中的 last_verified 日期为什么需要 CRUD 验证?
不做验证的后果是记忆幻觉 -- Agent 写入了错误的、重复的、或矛盾的信息,然后在未来的检索中把这些错误信息当作事实使用。学术界称之为 HaluMem(Memory Hallucination)。
CRUD 验证是防线:每次写入前强制阅读已有内容,把冲突暴露出来而不是埋进去。
实施时机:
- 日间 heartbeat: 做轻量级去重(检查目标文件末尾有没有相同内容)
- 夜间反思(23:45): 做深度 CRUD 验证(完整比对 + 分类 + 标记)
第五部分:检索机制
5.1 三级检索策略
查询到来
│
├─ L1: 扫 INDEX.md → 定位目标文件 (0 cost, 纯文件读取)
│ 适用:知道要找什么类别的信息
│
├─ L2: 直接读取目标文件 (0 cost, 纯文件读取)
│ 适用:已知具体文件路径
│
└─ L3: QMD 语义搜索 (有延迟, 适合模糊查询)
适用:不确定信息在哪个文件TIP
优先走 L1/L2,L3 作为兜底。
5.2 QMD 混合检索
QMD 是一个本地混合搜索引擎,结合了两种检索方式。
配置示例:
{
"memory": {
"backend": "qmd",
"qmd": {
"searchMode": "query",
"update": {
"interval": "5m",
"onBoot": true
},
"limits": {
"timeoutMs": 15000
}
}
}
}QMD 每 5 分钟自动重新扫描文件系统,新增/修改/删除的文件会自动更新索引。
5.3 中文搜索的已知限制
WARNING
QMD 的 FTS5 使用 unicode61 tokenizer,不支持中文分词。
Workaround:
- 用
query模式(推荐): 跳过 FTS,走向量语义搜索,对中文有效但较慢 - 写入时加空格: 在中文文档中有意识地用空格分隔关键词
- 未来方向: 等 QMD 支持 trigram tokenizer 或 ICU 中文分词
5.4 INDEX.md 健康度标记
INDEX.md 不仅是导航表,还是知识库的健康度仪表盘。
第六部分:记忆生命周期
6.1 日间:实时写入
用户对话 ──→ 做了决策/完成任务/获得新信息
│
▼
memlog.sh 写入日志
│
▼
路由判断:是否需要写入知识库?
│
┌──────┴──────┐
│ 是 │ 否
▼ ▼
先读再写 完成
(轻量级去重)6.2 每晚 23:30:日志同步
日志同步(daily-log-sync)是当天的最后一道防线,确保没有信息遗漏。
6.3 每晚 23:45:夜间反思
夜间反思(nightly-reflection)是记忆系统最核心的整合环节,相当于人脑睡眠时的记忆整合(Consolidation)。
执行流程:
Step 1: 读取上下文
Step 2: 写反思 (memory/reflections/YYYY-MM-DD.md)
Step 3: 清理日志
Step 4: CRUD 回写知识库 ← 核心步骤
Step 5: 过时扫描6.4 每周日 00:00:冷数据归档
每周一次的 GC(Garbage Collection)负责把冷数据归档到 .archive/。
6.5 生命周期总览
时间轴(一天)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
07:00 Agent 启动 → 读 NOW.md → INDEX.md → 今日日志
23:30 日志同步 — 全天 session 补漏
23:45 夜间反思 — 提炼 + CRUD + 过时扫描
00:00 (周日) GC — 冷数据归档第七部分:遗忘与衰减
7.1 为什么需要主动遗忘
一个只增不减的记忆系统最终会被自己淹没。人脑的艾宾浩斯遗忘曲线告诉我们:遗忘不是 bug,是 feature。
7.2 温度模型
Temperature(file) = w_age x age_score + w_ref x ref_score + w_pri x priority_score
age_score = exp(-0.03 x days_since_creation) # 半衰期约 23 天
ref_score = min(recent_references / 3, 1.0) # 近 7 天被引用次数
priority_score = { P0: 1.0, P1: 0.5, P2: 0.0 }
权重: w_age=0.5, w_ref=0.3, w_pri=0.2第八部分:可靠性保障
8.1 记忆幻觉问题
Agent 的记忆系统面临四种"幻觉"风险(来源:HaluMem 研究)。
8.2 写入时验证:CRUD 四分类
详见第四部分 4.3 节。
8.3 过时检测
通过 last_verified 日期和温度模型自动标记过时内容。
8.4 冲突处理流程
WARNING
设计原则:宁可暴露冲突,不可静默覆盖。
8.5 INDEX.md 作为健康度仪表盘
INDEX.md 不只是目录 -- 它是整个知识库的健康状态一览表。
第九部分:多 Agent 协作中的记忆
9.1 每个 Agent 独立 memory/ 目录
每个 agent 拥有自己的 workspace 和 memory 目录,互不干扰。
9.2 主 Agent 如何聚合子 Agent 产出
主 Agent 可以读取子 Agent 的 memory 文件(需要绝对路径),聚合跨 agent 的信息。
9.3 跨 Session 信息同步原则
TIP
解决方案:文件是唯一的跨 session 通道。
第十部分:快速上手
10.1 最小可用配置
最少需要 3 个文件:NOW.md、AGENTS.md、memory/YYYY-MM-DD.md
10.2 渐进式搭建路径
| 阶段 | 时间 | 目标 |
|---|---|---|
| 阶段 0 | 立刻 | 基本的跨 session 记忆 |
| 阶段 1 | 第 1 周 | 结构化知识积累 |
| 阶段 2 | 第 2 周 | 知识质量保障 |
| 阶段 3 | 第 3 周 | 检索效率 + 主动遗忘 |
| 阶段 4 | 第 4 周+ | 完整的生产级记忆系统 |
TIP
建议:不要一次性搭全。从阶段 0 开始,每周加一层。
10.3 Obsidian 集成
我们的记忆系统天然兼容 Obsidian,因为它就是一堆 Markdown 文件。直接用 Obsidian 打开 workspace 目录即可浏览和编辑所有记忆文件。
附录:设计灵感
这套系统从一个只有 3 个文件的最小配置起步,经过 30 天的实际运行迭代到了当前状态。它不是一次性设计出来的,而是在解决真实问题的过程中逐步演进的。
如果你也在给自己的 Agent 搭记忆系统,从阶段 0 开始。遇到问题时再加层。