# 01 - 记忆分层设计

## 概述

天工 Memory 采用 7 层分级模型，每层有明确的数据特征、生命周期和访问模式。

## 分层定义

### 1. Profile Memory（用户画像）

**职责**：存储用户长期稳定的偏好和习惯。

**数据特征**：
- 变化频率：极低（月/季度级）
- 体积：小（< 2KB）
- 生命周期：永久

**内容示例**：
- 语言偏好（简体中文）
- 常用技术栈（Rust、Vue）
- 输出风格偏好
- 常用模型/平台

**存储路径**：`~/.tiangong/memory/profile/`

**数据结构**：

```rust
/// Profile 记忆条目
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileEntry {
    pub key: String,
    pub value: String,
    pub updated_at: String,
}
```

**写入触发**：
- 用户在设置中显式修改
- Rumination 层从对话历史中提炼稳定偏好

---

### 2. Workspace Memory（工作区记忆）

**职责**：存储与特定项目/工作区绑定的背景信息。

**数据特征**：
- 变化频率：低（周/月级）
- 体积：中（2-10KB）
- 生命周期：与工作区绑定

**内容示例**：
- 项目目标和架构约束
- 目录约定和部署环境
- 当前开发阶段重点
- 已知技术债和风险

**存储路径**：`~/.tiangong/memory/workspaces/<workspace_id>/workspace.json`

**workspace_id 生成规则**：
- 取工作区根目录绝对路径的 SHA-256 前 16 位
- 写入 `workspace.json` 时同时记录原始路径，便于反查

```rust
pub fn workspace_id_from_path(path: &Path) -> String {
    use sha2::{Sha256, Digest};
    let digest = Sha256::digest(path.to_string_lossy().as_bytes());
    hex::encode(&digest[..8]) // 16 字符
}
```

---

### 3. Episodic Memory（事件记忆）

**职责**：记录任务执行过程中的关键事件和结果。

**数据特征**：
- 变化频率：高（每次任务执行后）
- 体积：每条 0.5-2KB，总量持续增长
- 生命周期：长期保留，低活跃条目定期归档

**内容示例**：
- 任务目标与最终结果
- 执行步骤摘要
- 成功/失败原因
- 关键产物引用

**存储路径**：`~/.tiangong/memory/workspaces/<workspace_id>/episodes/`

**数据结构**：

```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Episode {
    pub id: String,                    // scru128
    pub session_id: String,
    pub title: String,
    pub summary: String,
    pub outcome: EpisodeOutcome,
    pub keywords: Vec<String>,
    pub tool_calls: Vec<String>,       // 涉及的工具名
    pub importance: f32,               // 0.0-1.0
    pub created_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EpisodeOutcome {
    Success,
    PartialSuccess,
    Failed,
    Abandoned,
}
```

**写入触发条件**：
- RuntimeEngine 的事件循环 turn 正常完成时
- 仅在实际执行了工具调用的 turn 中触发（纯对话不产生 Episode）
- 写入主体：`memory/writer.rs` 中的 `EpisodeWriter`

**写入流程**：
1. turn 完成 → RuntimeEngine 发出 `TurnCompleted` 事件
2. `EpisodeWriter` 接收事件，调用独立 Memory LLM 提取摘要；未配置 Memory LLM 时使用规则提取
3. 摘要写入 `episodes/<id>.json`

**质量控制**：
- Phase B 阶段采用"只写不读"策略，写入后不参与 Recall
- 提取 prompt 固定模板，避免 hallucination
- 每条 Episode 附带 `importance` 评分，由专用 Memory LLM 在提取时评估；规则 fallback 使用保守启发式评分

---

### 4. Entity Memory（实体记忆）

**职责**：维护项目中的关键实体及其属性。

**数据特征**：
- 变化频率：中
- 体积：每条 0.5-1KB
- 生命周期：与实体存在周期一致

**实体类型**：

```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EntityType {
    Project,
    Repository,
    Server,
    Skill,
    Provider,
    Document,
    Module,
}
```

**存储路径**：`~/.tiangong/memory/workspaces/<workspace_id>/entities/<entity_id>.json`

**实体识别策略**（Phase C+ 实现）：
- 从 Episode 写入时同步提取涉及的实体
- 基于工具调用参数自动识别（如 `read_file` 的路径参数 → 文件实体）
- 不做独立的 NER，依赖 LLM 在 Episode 提取时一并输出

---

### 5. Decision Memory（决策记忆）

**职责**：记录重要的技术/方案选择及其理由。

**数据特征**：
- 变化频率：低
- 体积：每条 1-3KB
- 生命周期：长期

**存储路径**：`~/.tiangong/memory/workspaces/<workspace_id>/decisions/<id>.json`

**数据结构**：

```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Decision {
    pub id: String,
    pub title: String,
    pub context: String,          // 决策背景
    pub alternatives: Vec<String>, // 备选方案
    pub chosen: String,           // 最终选择
    pub reasons: Vec<String>,     // 选择原因
    pub episode_ids: Vec<String>, // 关联的 Episode
    pub created_at: String,
}
```

**写入触发条件**：
- Episode 提取时，如果 LLM 识别到"方案对比与选择"语义，同步生成 Decision
- 用户显式要求记录决策时

---

### 6. Evidence Memory（证据记忆）

**职责**：保存原始证据，仅在需要回溯时加载。

**数据特征**：
- 变化频率：高
- 体积：大（可达数十 KB/条）
- 生命周期：按策略归档/清理

**内容**：
- 工具调用的完整输出日志
- diff/patch 原始内容
- 原始对话片段

**存储路径**：`~/.tiangong/memory/workspaces/<workspace_id>/evidence/`

**设计原则**：
- Evidence 不参与 Recall 的粗召回阶段
- 仅在 Recall 的"证据回放"步骤中按需加载
- 定期归档：超过 30 天且 `importance < 0.3` 的 Evidence 归档到 `.archive/`

---

### 7. Injection Memory（注入记忆）

**职责**：为不同运行主体维护压缩后的稳定注入上下文。

详见 [02-注入层设计.md](02-注入层设计.md)。

---

## 各层关系图

```
Profile ──────────────────── 稳定不变，常驻注入
  │
Workspace ─────────────────── 项目绑定，周期更新
  │
  ├── Episodic ────────────── 任务驱动，高频写入
  │     │
  │     ├── Entity ────────── 从 Episode 中提取
  │     └── Decision ─────── 从 Episode 中提取
  │
  └── Evidence ────────────── 原始数据，按需回放
        │
Injection ─────────────────── 各层精华的压缩注入
```

## MemoryNode 统一抽象

所有层的记忆条目共享统一的元数据抽象：

```rust
/// 记忆节点（统一索引用）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryNode {
    pub id: String,              // scru128
    pub kind: MemoryKind,
    pub scope: MemoryScope,
    pub title: String,           // → SQLite + Tantivy
    pub summary: String,         // → SQLite + Tantivy + Qdrant(embedding)
    pub keywords: Vec<String>,   // → SQLite + Tantivy
    pub importance: f32,         // → SQLite + Qdrant(payload)
    pub confidence: f32,         // → SQLite
    pub status: MemoryStatus,    // → SQLite + Qdrant(payload)
    pub created_at: String,      // → SQLite + Tantivy
    pub updated_at: String,      // → SQLite
}
```

> **存储分布说明**：MemoryNode 的元数据存储在 SQLite，文本字段同步到 Tantivy 全文索引，
> summary 的 embedding 向量存储到 Qdrant。详见 [07-持久化格式与技术选型.md](07-持久化格式与技术选型.md)。

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MemoryKind {
    Profile,
    Workspace,
    Episode,
    Entity,
    Decision,
    Evidence,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MemoryScope {
    Global,
    Workspace { workspace_id: String },
    Session { session_id: String },
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MemoryStatus {
    Active,
    Archived,
    Outdated,
    Superseded,
}
```

## 分级内容模型

每条记忆支持三级深度展开，逐级加载：

| 深度 | 内容 | 典型大小 | 加载时机 |
|------|------|---------|---------|
| depth-1 | 一句话摘要（`summary`） | 50-100 字符 | 粗召回阶段 |
| depth-2 | 结构化视图（完整字段） | 200-500 字符 | 定向展开阶段 |
| depth-3 | 原始证据/全文 | 1-10KB | 证据回放阶段 |

```rust
pub struct MemoryDepthContent {
    pub depth1: String,                    // summary
    pub depth2: Option<serde_json::Value>, // 完整结构化数据
    pub depth3_ref: Option<String>,        // Evidence 文件路径引用
}
```
