# 05 - 反刍与反思层（Rumination）

## 概述

Rumination 是 Memory 系统的"离线整理"机制。类似人类在非活动状态下整理记忆：

- 将短期 Episode 提炼为长期 Entity/Decision
- 更新 Injection 文件
- 标记过时记忆
- 发现记忆间的关联

## 三级反刍

### Micro（微观反刍）—— 每轮 turn 结束后

**触发时机**：每个包含工具调用的 turn 完成后

**职责**：
- 写入 Episode（详见 [01-记忆分层设计.md](01-记忆分层设计.md)）
- 更新 Session Injection（如果当前目标或已完成事项变化）

**延迟预算**：< 500ms（异步执行，不阻塞响应）

```rust
pub struct MicroRumination;

impl MicroRumination {
    pub async fn process(
        &self,
        turn_result: &TurnResult,
        session_id: &str,
        workspace_id: Option<&str>,
    ) -> Result<()> {
        // 1. 写入 Episode（SQLite + Tantivy + Qdrant）
        if turn_result.has_tool_calls() {
            let episode = self.extract_episode(turn_result).await?;
            let embedding = self.embedder.embed(&episode.summary).await.ok();
            self.store.write_memory(&episode.to_node(), &episode.full_content, embedding).await?;
        }

        // 2. 更新 Session Injection（条件触发）
        if turn_result.goal_changed() || turn_result.has_decisions() {
            self.update_session_injection(session_id, turn_result)?;
        }

        Ok(())
    }
}
```

---

### Meso（中观反刍）—— 会话结束/压缩时

**触发时机**：
- 会话正常结束时
- ContextOrganizer 执行消息压缩时

**职责**：
- 从多个 Episode 中提炼 Entity 和 Decision
- 更新 Workspace Injection
- 合并重复 Episode

**数据结构**：

```rust
pub struct MesoRumination;

impl MesoRumination {
    pub async fn process(
        &self,
        session_id: &str,
        workspace_id: &str,
    ) -> Result<RuminationReport> {
        let episodes = self.store.list_recent_episodes(workspace_id, 20)?;

        // 1. 实体提取
        let entities = self.extract_entities(&episodes).await?;
        for entity in &entities {
            let embedding = self.embedder.embed(&entity.description).await.ok();
            self.store.write_memory(&entity.to_node(), &entity.full_content, embedding).await?;
        }

        // 2. 决策提取
        let decisions = self.extract_decisions(&episodes).await?;
        for decision in &decisions {
            let embedding = self.embedder.embed(&decision.context).await.ok();
            self.store.write_memory(&decision.to_node(), &decision.full_content, embedding).await?;
        }

        // 3. 更新 Workspace Injection
        self.update_workspace_injection(workspace_id).await?;

        Ok(RuminationReport {
            entities_created: entities.len(),
            decisions_created: decisions.len(),
            episodes_merged: 0, // 后续实现
        })
    }
}
```

---

### Meta（宏观反刍）—— 定期调度

**触发时机**：
- 应用启动时，如果距离上次 Meta 反刍超过 24 小时
- 手动触发

**职责**：
- 标记过时记忆（文件已删除、项目已归档）
- 计算记忆质量评分
- 归档低活跃 Evidence
- 更新 Profile Injection（检测偏好变化）

```rust
pub struct MetaRumination;

impl MetaRumination {
    pub async fn process(&self) -> Result<MetaReport> {
        let mut report = MetaReport::default();

        // 1. 检查过时记忆
        let all_nodes = self.store.list_all_active_nodes()?;
        for node in &all_nodes {
            if self.is_outdated(node)? {
                self.store.update_status(&node.id, MemoryStatus::Outdated)?;
                // 从检索索引中移除
                self.store.remove_from_tantivy(&node.id)?;
                self.store.remove_from_qdrant(&node.id).await?;
                report.outdated_count += 1;
            }
        }

        // 2. 归档低活跃 Evidence
        let old_evidence = self.store.list_old_evidence(30)?; // 30天以上
        for ev in &old_evidence {
            if ev.importance < 0.3 {
                self.store.archive_evidence(&ev.id)?;
                report.archived_count += 1;
            }
        }

        // 3. 更新 Profile（检测稳定偏好）
        report.profile_updated = self.update_profile_if_needed().await?;

        Ok(report)
    }
}
```

---

## 记忆质量模型

```rust
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct MemoryQuality {
    pub relevance: f32,     // 与当前上下文的相关性
    pub freshness: f32,     // 时间新鲜度
    pub confidence: f32,    // 信息可信度
    pub usage_count: u32,   // 被召回使用的次数
}

impl MemoryQuality {
    pub fn composite_score(&self) -> f32 {
        self.relevance * 0.35
        + self.freshness * 0.25
        + self.confidence * 0.25
        + (self.usage_count as f32).min(10.0) / 10.0 * 0.15
    }
}
```

---

## LLM 调用策略

| 反刍级别 | LLM 使用 | 模型选择 |
|---------|---------|---------|
| Micro | 是（Episode 提取） | 专用 Memory LLM，低成本 |
| Meso | 是（Entity/Decision 提取） | 专用 Memory LLM |
| Meta | 否（规则判定） | 无需 LLM |

**成本控制**：
- Micro 每次调用约 200-500 input tokens + 100-200 output tokens
- Meso 每次调用约 2000-5000 input tokens + 500-1000 output tokens
- Meta 纯规则，零 LLM 成本

**模型隔离**：
- Memory LLM 由 `~/.tiangong/memory/config.json` 中的独立 Memory 配置指定。
- 未配置 Memory LLM 时，Micro/Meso 使用规则 fallback，不得复用主 `chat` 模型。
- Memory LLM 的失败、超时和 JSON 解析错误只影响记忆质量，不得阻断主对话链路。

---

## 实现优先级

| 阶段 | 内容 |
|------|------|
| Phase A | 不实现 |
| Phase B | Micro（Episode 写入）|
| Phase C | Meso（Entity/Decision 提取）|
| Phase D | Meta（过时检测、归档、Profile 更新）|
