# ai-memory-core — 持久化 AI 记忆系统

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

一个轻量级 Rust 库，为 AI 应用提供长期记忆能力 — 持久化、注入和提取跨会话的知识。

## 特性

| 能力 | 说明 |
|---|---|
| **空间隐喻** | Wing/Hall/Room 三层路径分类，声明式宏 (`declare_wings!` / `declare_halls!`) 驱动 |
| **纯 Markdown 存储** | `memories.md` 文件即数据库，人类可读，可直接用编辑器查看修改 |
| **元数据追踪** | MD5 内容指纹 + 跨会话访问统计，自动识别高价值记忆 |
| **Token 预算注入** | 2000 token 预算，按优先级排序后注入 system prompt |
| **LLM 记忆提取** | 会话结束后自动总结提取重要记忆（支持 Anthropic / OpenAI / DeepSeek） |
| **零重依赖** | 核心仅依赖 `serde`、`serde_json`、`chrono`、`dirs`、`md-5` |

## 设计理念

### 热记忆 vs 冷记忆

```
┌─────────────────────────────────────────────────────────┐
│                      对话上下文                           │
│  ┌─────────────┐                                        │
│  │  热记忆注入   │ ← 每次对话前自动注入（优先级排序）         │
│  │  (2000 tokens)│                                        │
│  └─────────────┘                                        │
├─────────────────────────────────────────────────────────┤
│                      磁盘存储                             │
│  ┌──────────────────────────────────────────┐            │
│  │  冷记忆 (memories.md)                     │            │
│  │  ● 全部持久化历史                           │            │
│  │  ● Markdown 格式，人类可读                  │            │
│  │  ● 自动去重                                │            │
│  └──────────────────────────────────────────┘            │
│  ┌──────────────────────────────────────────┐            │
│  │  元数据 (memories.meta.json)              │            │
│  │  ● MD5 内容指纹                            │            │
│  │  ● 访问次数统计                            │            │
│  │  ● 高价值标识 (access_count >= 5)          │            │
│  └──────────────────────────────────────────┘            │
└─────────────────────────────────────────────────────────┘
```

### 空间隐喻（三层分类）

```
Wing (维度)       Hall (主分类)            Room (子分类)
────────────────────────────────────────────────────
project ────┬─── Preferences ───────┬─── programming-languages
            │                       ├─── editor-config
            │                       └─── communication-style
            │
            ├─── Project Knowledge ─┬─── frontend-stack
            │                       └─── database-schema
            │
            ├─── Decisions ─────────┬─── architecture
            │                       └─── technology-choice
            │
            └─── Workflow Patterns ─┬─── code-review
                                    └─── deployment

user ───────┬─── Preferences ───────┬─── communication-style
            └─── Project Knowledge ─┬─── personal-tools
```

## 快速开始

```toml
[dependencies]
ai-memory-core = "0.1"
```

### 基础用法

```rust
use std::path::PathBuf;
use ai_memory_core::{
    init, handle_memory_save, load_memories,
    inject_memories_into_system_prompt
};

fn main() {
    // 1. 初始化数据目录（不调用则默认 ~/.ai-memory/）
    init(PathBuf::from("/path/to/data"));

    // 2. 保存记忆（空间路径 + 内容）
    handle_memory_save(
        "Preferences/programming-languages",
        "偏好使用 TypeScript 和 Rust"
    ).unwrap();

    handle_memory_save(
        "Decisions/database-choice",
        "采用 PostgreSQL 作为主数据库"
    ).unwrap();

    // 3. 加载全部记忆
    if let Some(memories) = load_memories() {
        println!("{}", memories);
    }

    // 4. 注入到 system prompt（自动按优先级排序，控制在 2000 tokens 内）
    let prompt = "You are a helpful assistant.";
    let enriched = inject_memories_into_system_prompt(prompt);
    // 输出: "You are a helpful assistant.\n\n[USER_MEMORY]\n\n偏好使用...\n\n[/USER_MEMORY]"
}
```

### LLM 记忆提取（需启用 `llm-extraction` feature）

```toml
[dependencies]
ai-memory-core = { version = "0.1", features = ["llm-extraction"] }
```

```rust
use ai_memory_core::{extract_memories_with_llm, build_extraction_prompt};

async fn 会话结束后自动提取(api_key: &str, 对话内容: &str) {
    // 1. 生成提取提示词
    let prompt = build_extraction_prompt(对话内容);

    // 2. 调用 LLM 提取并自动保存
    let 本次提取数量 = extract_memories_with_llm(
        api_key,
        对话内容,
        "anthropic"  // 或 "openai" / "deepseek"
    ).await.unwrap();

    println!("从本次会话中提取了 {} 条记忆", 本次提取数量);
}
```

### 自定义分类（通过宏扩展）

```rust
// 在 categories.rs 中扩展分类
declare_wings! {
    Project : "project";
    User : "user";
    Team : "team";  // 新增：团队维度
}

declare_halls! {
    Preferences : "Preferences";
    ProjectKnowledge : "Project Knowledge";
    Decisions : "Decisions";
    WorkflowPatterns : "Workflow Patterns";
    CodingStandards : "Coding Standards";  // 新增：编码规范
}
```

## API 参考

### 配置模块 (`config`)

| 函数 | 说明 |
|---|---|
| `init(dir: PathBuf)` | 设置自定义数据目录 |
| `memories_file() -> PathBuf` | 获取记忆文件路径 |
| `metadata_file() -> PathBuf` | 获取元数据文件路径 |

### 空间分类 (`categories`)

| 类型/函数 | 说明 |
|---|---|
| `Wing` 枚举 | 项目/用户维度 |
| `MemoryHall` 枚举 | 偏好/项目知识/决策/工作流模式 |
| `MemoryPath` 结构体 | 解析后的空间路径（wing, hall, room） |
| `path_schema() -> Value` | 工具 Schema 定义 |

### 文件 IO (`io`)

| 函数 | 说明 |
|---|---|
| `load_memories() -> Option<String>` | 加载记忆文件（不存在返回 None） |
| `save_memories(content) -> Result` | 保存记忆文件 |
| `append_to_section(memories, section, entry) -> String` | 追加条目 + 自动去重 |
| `ensure_memories_file() -> Result<PathBuf>` | 确保文件存在（首次自动创建骨架） |
| `format_initial_memories(section, entry) -> String` | 格式化新记忆文档 |

### 元数据 (`meta`)

| 类型/函数 | 说明 |
|---|---|
| `MetadataStore::load()` | 从磁盘加载元数据（失败时降级返回空存储） |
| `MetadataStore::save()` | 持久化元数据到磁盘 |
| `MetadataStore::track_access(fingerprint)` | 记录一次记忆访问 |
| `MetadataStore::high_value_memories()` | 获取高价值记忆（access_count >= 5） |
| `content_fingerprint(content) -> String` | 计算 MD5 指纹（32 位十六进制） |

### 记忆保存工具 (`tool`)

| 函数 | 说明 |
|---|---|
| `handle_memory_save(path, content) -> Result<String>` | 解析路径、验证内容、保存到文件 |
| `memory_save_schema() -> Value` | JSON Schema（用于 LLM function calling） |

### 会话注入 (`session`)

| 函数 | 说明 |
|---|---|
| `load_memories_for_injection() -> String` | 加载并格式化记忆（优先级排序，控制在 2000 tokens 内） |
| `inject_memories_into_system_prompt(prompt) -> String` | 将记忆注入到 system prompt |

### 记忆提取 (`extractor`)

| 函数 | 说明 |
|---|---|
| `build_extraction_prompt(summary) -> String` | 生成 LLM 提取提示词（支持外部模板） |
| `parse_extraction_result(result) -> Vec<ExtractedMemory>` | 解析 LLM 输出为结构化记忆 |
| `save_extracted_memories(memories) -> Result<usize>` | 保存提取的记忆 |
| `extract_and_save_memories(result) -> Result<usize>` | 完整管线：解析 + 保存 |
| `extract_memories_simple(summary) -> Result<usize>` | 演示版提取器（打印摘要，不保存） |
| `extract_memories_with_llm(key, summary, provider) -> Result<usize>` | 完整 LLM 提取（需 feature） |
| `clear_prompt_cache()` | 清除缓存的提取提示词模板 |

## 记忆文件格式

```markdown
# User Memories
# Last updated: 2025-05-09

## Preferences
### programming-languages
- [2025-05-09] 偏好使用 TypeScript 和 Rust
- [2025-05-08] 使用 VS Code 编辑器

## Decisions
### database-choice
- [2025-05-09] 采用 PostgreSQL 作为主数据库
```

## 去重机制

保存时，系统提取条目内容（忽略日期前缀），与当前 section 下已有条目进行内容比对。如果发现重复内容，仅更新日期，不新增条目。

## 外部提示词模板

提取提示词可通过外部文件自定义。在 `data_dir/prompts/memory/extract.md` 放置模板文件：

```markdown
你是一个专业的记忆提取助手。请从对话中提取以下内容：

## 提取规则
1. 用户偏好：...
2. 重要决策：...

## 输出格式
**[类别]** 一句话记忆内容

## 对话摘要
{conversation_summary}
```

使用 `{conversation_summary}` 作为对话内容的占位符。修改模板后调用 `clear_prompt_cache()` 强制重载。

## 测试

```bash
cargo test
```

## 文档

- **[使用手册 (USAGE_zh.md)](docs/USAGE_zh.md)** — 集成示例、最佳实践、故障排查。

## Feature Flags

| Flag | 额外依赖 | 说明 |
|---|---|---|
| `default` | — | 全部核心功能 |
| `llm-extraction` | `reqwest` | 异步 LLM 记忆提取 |

## License

MIT
