------零依赖、纯 Markdown、无需数据库,开源 AI 编程助手 IfAI v0.4.7 记忆系统架构深度解析

你有没有遇到过这种场景:每次打开 AI 编程助手,都要重复告诉它「我用 TypeScript」「数据库用 PostgreSQL」「测试用 vitest」。说完一百遍之后你开始怀疑------这东西到底有没有在听?
我也受够了。所以我在开源项目 IfAI 里做了一个决定:让 CLI 自己记住一切。
不是简单的配置文件,不是硬编码的偏好列表,而是一套完整的跨会话持久化记忆系统------AI 在对话中主动识别、保存、提取你的偏好,下次打开自动加载,零配置、零干预。
今天我把这套系统的核心架构开源出来,聊聊技术选型、踩过的坑,以及为什么「纯 Markdown 文件」可能是本地 AI 记忆的最佳载体。
一、为什么现有方案都不够好?
主流 AI 助手的「记忆」方案大致分三种:
| 方案 | 代表 | 问题 |
|---|---|---|
| 系统提示词硬编码 | Cursor Rules | 每次改都要手动编辑,无法动态更新 |
| 向量数据库 + RAG | ChatGPT Memory | 重依赖、高延迟、本地部署复杂 |
| 对话历史回溯 | 绝大多数工具 | token 爆炸、成本失控、噪声太多 |
我的需求很明确:
- 零新依赖------不引入 SQLite、不引入向量数据库
- 人类可读------用户能用任何文本编辑器打开、修改
- 自动去重------AI 重复提取同一偏好时,更新日期而非新增行
- 性能极致------记忆注入延迟 < 1ms,不影响会话启动速度
- 编译时类型安全------路径拼写错误在编译期就报错,不是运行时爆炸
最终选型:纯 Markdown 文件 + Rust 编译时宏 + 两层记忆架构。
二、空间隐喻:用「建筑」组织记忆
这是我自认为最得意的设计。
传统的 key-value 记忆太扁平,无法表达层次关系。我借鉴了 MemGPT 的分层思想,但做了极大简化------不用数据库,用 Markdown 标题层级:
shell
~/.ifai/memories.md
## Preferences ← Hall(大厅)
### programming-languages ← Room(房间)
- [2026-05-10] 用户喜欢使用 Rust 编程
- [2026-05-10] 项目使用 TypeScript 和 Rust
## Decisions ← 另一个 Hall
### architecture
- [2026-05-10] 采用事件驱动架构
更进一步,我引入了 Wing(建筑) 的概念,支持 3 层路径:
bash
Project/Preferences/programming-languages
↑ ↑ ↑
Wing Hall Room
多项目、多用户的完整空间结构:
scss
project/ifai (Wing) user/alice (Wing)
├── Preferences (Hall) ├── Preferences (Hall)
│ ├── programming-languages │ ├── communication-style (Room)
│ └── ui-themes │ └── communication-channels
├── Project Knowledge (Hall) └── ...
├── Decisions (Hall)
└── Workflow Patterns (Hall)
project/website-a (Wing) user/bob (Wing)
├── Preferences (Hall) ├── Preferences (Hall)
└── ... └── ...
同一个分类(如 Preferences)可以区分为「项目级别」和「用户级别」,互不干扰。
伪代码------路径解析:
rust
// 输入: "Project/Preferences/programming-languages"
// 输出: section_title = "## Project\n### Preferences\n#### programming-languages"
fn parse_path(path: &str) -> MemoryPath {
let parts: Vec<&str> = path.split('/').collect();
match parts.len() {
2 => MemoryPath { wing: None, hall: parts[0], room: Some(parts[1]) },
3 => MemoryPath { wing: Some(parts[0]), hall: parts[1], room: Some(parts[2]) },
_ => Err("路径必须是 2 层或 3 层")
}
}
fn section_title(path: &MemoryPath) -> String {
match &path.wing {
Some(wing) => format!("## {}\n### {}\n#### {}", wing, path.hall, path.room),
None => format!("## {}\n### {}", path.hall, path.room),
}
}
关键点:用 macro_rules! 在编译时生成 enum 和 FromStr 实现 ,拼写错误在 cargo build 时就暴露:
rust
declare_halls! {
Preferences : "Preferences";
ProjectKnowledge : "Project Knowledge";
Decisions : "Decisions";
WorkflowPatterns : "Workflow Patterns";
}
// 编译时自动生成: enum MemoryHall { Preferences, ProjectKnowledge, ... }
// 编译时自动生成: impl FromStr for MemoryHall { ... }
// 传入 "Prefernces"(拼错)→ 编译失败 ✓
三、两层记忆架构
整个系统的数据流如下:
javascript
┌─────────────────────────────────────────────────────────────┐
│ 热记忆 (Core Memory) │
│ ~/.ifai/memories.md │
│ - 始终注入 system prompt │
│ - ≤ 2000 tokens (约 4KB) │
│ - AI 可通过 MemorySave 工具实时修改 │
│ - 4 个 section: Preferences / ProjectKnowledge / │
│ Decisions / WorkflowPatterns │
└─────────────────────────────────────────────────────────────┘
↓ 注入
┌─────────────────────────────────────────────────────────────┐
│ System Prompt + [USER_MEMORY] │
│ ┌───────────────────────────────────────────────┐ │
│ │ [USER_MEMORY] │ │
│ │ <memories.md 完整内容> │ │
│ │ [/USER_MEMORY] │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓ 归档
┌─────────────────────────────────────────────────────────────┐
│ 冷记忆 (Archival Storage) │
│ ~/.ifai/sessions/YYYY-MM-DD-{id}.md │
│ - 按会话归档,人类可浏览 │
│ - 不自动注入,避免 token 浪费 │
│ - 未来可加语义搜索,作为 RAG 数据源 │
└─────────────────────────────────────────────────────────────┘
TUI 和 GUI 共享同一套 memory 模块,通过统一的 API 读写:
scss
┌─────────────────┐ ┌─────────────────┐
│ TUI 入口 │ │ GUI 入口 │
│ (CLI / ratatui) │ │ (Tauri / React) │
└────────┬────────┘ └────────┬────────┘
│ │
├────────────┬───────────┘
↓ ↓
┌─────────────────────────┐
│ memory 模块 (lib) │
│ ├─ io.rs (读写) │
│ ├─ tool.rs (工具) │
│ └─ extractor.rs (提取) │
└─────────────────────────┘
↑ ↑
│ │
┌────┴────┐ ┌───┴────┐
│注入到 │ │注册到 │
│system │ │Tool │
│prompt │ │Registry│
└─────────┘ └────────┘
每次会话启动时,memories.md 被整体注入到 system prompt 末尾:
rust
fn inject_memories(system_prompt: &str) -> String {
let memories = load("~/.ifai/memories.md");
format!("{}\n\n[USER_MEMORY]\n{}\n[/USER_MEMORY]", system_prompt, memories)
}
性能实测:19 KB 文件注入耗时 18μs(微秒),远低于 100ms 目标。原因是纯字符串操作,没有任何序列化/反序列化开销。
冷记忆(Cold Memory)
存储位置:~/.ifai/sessions/YYYY-MM-DD-{id}.md
每次会话结束时,自动生成人类可读的会话摘要:
markdown
# Session: 2026-05-10-abc123
## Summary
用户请求实现记忆系统外部化提示词功能...
## Token Usage
- Input: 12,450
- Output: 3,200
## Memories Extracted
- Preferences: 用户喜欢使用 Rust
- Decisions: 采用纯 Markdown 作为记忆载体
冷记忆不自动注入 system prompt(避免 token 浪费),但未来可以支持语义搜索,作为 RAG 的数据源。
四、AI 主动保存:MemorySave 工具
这不是被动记忆------AI 在对话中主动识别你的偏好并调用工具保存:
bash
你:记住,我喜欢用 Rust 写后端
AI:✓ Saved to Preferences/programming-languages: 用户喜欢使用 Rust 编程
工具定义极简:
json
{
"name": "MemorySave",
"parameters": {
"path": "Preferences/programming-languages",
"content": "用户喜欢使用 Rust 编程"
}
}
核心写入逻辑------带去重的 section 追加:
rust
fn append_to_section(memories: &str, section: &str, entry: &str) -> String {
let content = extract_content_without_date(entry); // "用户喜欢使用 Rust 编程"
// 在 section 范围内查找重复
if section_contains(section, content) {
replace_entry_with_new_date(section, entry) // 更新日期,不新增
} else {
append_new_entry(section, entry) // 追加新行
}
}
去重规则:忽略日期,只比较内容。这样 AI 每次确认同一偏好时,只会更新时间戳,文件保持干净。
权限设计 :MemorySave 被标记为 riskLevel: low, requiresApproval: false,完全自动执行,不弹出确认框。理由很简单------写一个本地 Markdown 文件,风险极低,打断对话流程才是真正的成本。
五、会话后批量提取
对话结束时,自动触发记忆提取流程:
ini
对话结束 → 判断是否值得提取(≥3 轮对话 or 有工具调用)
→ 生成对话摘要
→ 调用 LLM(temperature=0)提取新记忆
→ 合并到 memories.md(带去重)
→ 保存会话摘要到 sessions/
提取 prompt 模板外部化 到 ~/.ifai/prompts/memory/extract.md,用户可以完全自定义提取规则:
markdown
你是一个专业的记忆提取助手。
## 提取规则
1. 用户偏好:编程语言、工具、框架
2. 重要决策:架构决策、技术选型
3. 领域知识:项目特定的业务逻辑
## 输出格式
**[Preferences]** 使用 TypeScript 而非 JavaScript
**[Decisions]** 采用 PostgreSQL 作为主数据库
优先级:外部文件 > 内置默认 > 不提取。
六、工程细节:为什么是 Rust + Markdown?
为什么不用 JSON/YAML/TOML?
- Markdown 人类可读性最好(任何编辑器都能打开)
##层级天然表达树形结构- 便于 AI 直接生成和修改(LLM 对 Markdown 的训练数据最多)
- 便于 git diff 和版本管理
为什么不用 SQLite?
- 零依赖原则:不增加编译时间和二进制大小
- 人类可读:
memories.mdvssqlite3 memories.db "SELECT * FROM memories" - 便于备份:直接复制文件即可
- 便于调试:出问题时
cat一下就知道发生了什么
为什么用 macro_rules! 而不是 proc-macro?
macro_rules!是 Rust 内置,零编译开销- proc-macro 需要单独的 crate,增加编译时间
- 适度元编程,不过度设计
性能数据
| 指标 | 数值 |
|---|---|
| 记忆注入延迟(19 KB) | 18μs |
| 记忆注入延迟(冷启动) | 45μs |
| 去重追加操作 | < 1μs |
| 核心代码量 | ~1230 行 |
| 测试代码量 | ~390 行 |
| 新增依赖 | 0 |
七、实际效果
部署后,最直观的变化是:再也不用重复说同样的话了。
bash
# 第一次会话
你:我喜欢用 Rust,项目用 Tauri
AI:✓ Saved to Preferences/programming-languages: 用户喜欢使用 Rust
✓ Saved to ProjectKnowledge/tech-stack: 项目使用 Tauri 框架
# 第二次会话(新开的)
你:帮我写个 API 接口
AI:好的,我用 Rust + Tauri 来实现... (自动使用记忆中的偏好)
记忆文件本身就是最好的文档------打开 ~/.ifai/memories.md,你的所有偏好、决策、知识一目了然。
八、与主流产品的记忆能力对比
2025-2026 年,持久化记忆正从「噱头」变为 AI 编程助手的标配能力。IfAI 的方案在架构层面有几个独到之处:
| 维度 | IfAI v0.4.7 | Trae (字节) | 通义灵码 (阿里) | Cursor | Windsurf |
|---|---|---|---|---|---|
| 存储方式 | 纯 Markdown 本地文件 | 本地(私有格式) | 云端(百炼平台) | .cursorrules 文本文件 |
本地(私有格式) |
| 人类可读可编辑 | ✅ 任何编辑器直接改 | ❌ 黑盒 | ❌ 云端管理 | ✅ 纯文本 | ❌ 黑盒 |
| 自动记忆提取 | ✅ AI 主动 + 会话后批量 | ✅ 自动捕捉 | ✅ 自动整理更新 | ❌ 需手动维护 | ✅ 自动 |
| AI 主动保存 | ✅ MemorySave 工具 | ✅ 自动捕捉 | ⚠️ 部分支持 | ❌ | ✅ Cascade |
| 跨会话共享 | ✅ 热记忆始终注入 | ✅ 全局 + 项目双层 | ✅ 云端同步 | ⚠️ 规则文件持久 | ✅ Memories |
| 空间结构 | ✅ Wing/Hall/Room 三层隐喻 | ✅ 全局/项目两层 | ✅ 个人/工程/问题分类 | ❌ 扁平 | ⚠️ 有限 |
| 自动去重 | ✅ 内容比对 + 日期更新 | ❌ 未知 | ⚠️ 自动整理 | ❌ | ❌ 未知 |
| TUI + GUI 共享 | ✅ 同一文件跨界面 | N/A(仅 IDE) | ⚠️ 插件 + IDE | N/A(仅 IDE) | N/A(仅 IDE) |
| 零新增依赖 | ✅ 纯标准库 | N/A | N/A | N/A | N/A |
| 注入延迟 | 18μs | 未知 | 未知 | ~0(文件读取) | 未知 |
| 开源透明 | ✅ 完整开源 | ❌ 闭源 | ❌ 闭源 | ❌ 闭源 | ❌ 闭源 |
IfAI 的三个差异化优势:
-
透明可控 :记忆就是 Markdown 文件,用户能用
cat查看、用vim编辑、用git追踪变更。竞品要么云端黑盒,要么私有格式不可读。 -
声明式空间隐喻 :Wing/Hall/Room 三层路径不是简单的标签分类,而是编译时类型安全的层级结构------拼错路径名,
cargo build直接报错。 -
CLI 原生 :IfAI 是目前唯一一个在终端 TUI 中实现完整持久化记忆的编程助手。其他产品都是 GUI IDE 形态,没有命令行体验。
竞品的优势:
- Trae 的产品化体验最成熟,UI 管理 > IfAI
- 通义灵码的云端同步适合企业多设备场景
- Cursor 的代码索引和短期上下文理解仍然最强
九、总结
这套系统的核心哲学是:用最简单的技术,解决最实际的问题。
- 没有向量数据库,但够用
- 没有复杂的嵌入模型,但够精准
- 没有花哨的 UI,但人类可读
- 零新依赖,1200 行核心代码,95% 测试覆盖率
有时候,最好的架构不是选最前沿的技术栈,而是选最不容易出错的那条路。
v0.4.7 版本中,这套记忆系统已完整集成到 GUI 端------TUI 和图形界面共享同一份 memories.md,AI 在任意界面保存的记忆,另一个界面立即可用。会话归档(冷记忆)也在两端同步生效。
如果你也在做 AI 工具,希望这个思路能给你一些启发。代码已开源,欢迎 star 和 PR。
项目地址 :github.com/peterfei/if... 版本 :v0.4.7 核心技术:Rust / Tauri / macro_rules! / 纯 Markdown / 零依赖
如果觉得有用,欢迎转发给你的技术团队。