# 内存系统

LibreFang 的内存系统提供持久化存储、语义搜索和知识图谱功能。

---

## 目录

- [概述](#overview)
- [架构](#architecture)
- [配置](#configuration)
- [会话管理](#session-management)
- [记忆操作](#memory-operations)
- [向量搜索](#vector-search)
- [知识图谱](#knowledge-graph)
- [会话压缩](#session-compaction)
- [后台整固（梦境模式）](#background-consolidation-auto-dream)
- [内存提供者插件 API](#memory-provider-plugin-api)
- [使用追踪](#usage-tracking)
- [API 端点](#api-endpoints)
- [最佳实践](#best-practices)
- [故障排除](#troubleshooting)

---

## 概述

LibreFang 内存系统包含：

- **SQLite 持久化** - 结构化 KV 存储
- **向量嵌入** - 语义搜索能力
- **知识图谱** - 实体和关系
- **会话管理** - 跨通道记忆
- **会话压缩** - 当单个会话体量过大时自动收缩
- **梦境模式（Auto-Dream）** - 可选的后台整固机制，让加入的 agent 定期反思并整理自己的长期记忆，详见 [后台整固（梦境模式）](#后台整固梦境模式)
- **使用追踪** - 成本和使用统计

<Note>
**提示**: 内存数据默认存储在 `~/.librefang/data/librefang.db`，可配置存储路径。
</Note>

---

## 架构

```
┌─────────────────────────────────────────┐
│              Agent Loop                  │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│           Memory Subsystem               │
├─────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ Session │  │ Vector  │  │Knowledge│  │
│  │ Store   │  │ Search  │  │ Graph   │  │
│  └─────────┘  └─────────┘  └─────────┘  │
├─────────────────────────────────────────┤
│           SQLite Database               │
└─────────────────────────────────────────┘
```

---

## 配置

### 基本配置

```toml
[memory]
decay_rate = 0.05
sqlite_path = "~/.librefang/data/librefang.db"
```

### 高级配置

```toml
[memory]
decay_rate = 0.05
sqlite_path = "~/.librefang/data/librefang.db"
vector_dimension = 1536
max_memory_items = 10000
auto_compact = true
```

| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `decay_rate` | Float | 0.05 | 记忆置信度衰减率 |
| `sqlite_path` | String | ~/.librefang/data/librefang.db | 数据库路径 |
| `vector_dimension` | Integer | 1536 | 向量维度 |
| `max_memory_items` | Integer | 10000 | 最大记忆条目 |
| `auto_compact` | Boolean | true | 自动压缩 |

---

## 会话管理

### 创建会话

```bash
# 创建新会话
librefang session create --name "research-project"
```

### 列出会话

```bash
# 列出所有会话
librefang session list
```

### 会话操作

```bash
# 查看会话详情
librefang session info <session-id>

# 删除会话
librefang session delete <session-id>

# 压缩会话
librefang session compact <session-id>

# 导出会话
librefang session export <session-id> --format json
```

---

## 记忆操作

### 存储记忆

```bash
# 存储简单键值（位置参数：agent key value）
librefang memory store <agent> "user:preference:theme" "dark"

# 示例：为 coder agent 存储版本信息
librefang memory store coder "project:version" "1.0"

# 等价形式：使用 set 子命令
librefang memory set coder "note:1" "Important note"
```

### 搜索记忆

```bash
# 关键词搜索
librefang memory search "project"

# 向量搜索 (语义搜索)
librefang memory search --vector "find information about AI agents"

# 带过滤的搜索
librefang memory search "meeting" --tags "work" --limit 10
```

### 记忆操作

```bash
# 读取记忆
librefang memory get <key>

# 更新记忆
librefang memory update <key> --value "new value"

# 删除记忆
librefang memory delete <key>

# 列出所有记忆
librefang memory list --prefix "project:"
```

---

## 向量搜索

### 语义搜索

LibreFang 支持向量嵌入的语义搜索：

```bash
# 语义搜索示例
librefang memory search --vector "machine learning techniques for text classification"
```

### 相似度阈值

```toml
[memory]
similarity_threshold = 0.75
```

### API 端点

```bash
# 语义搜索 API（GET 请求，使用查询参数）
curl "http://127.0.0.1:4545/api/memory/search?q=find+information+about+AI&limit=10&threshold=0.8"
```

---

## 知识图谱

### 实体管理

```bash
# 添加实体
librefang kg add-entity --type "person" --name "John Doe" --properties '{"role": "developer"}'

# 列出实体
librefang kg list-entities --type "person"

# 搜索实体
librefang kg search-entities "John"
```

### 关系管理

```bash
# 添加关系
librefang kg add-relation \
  --from "person:john" \
  --relation "works_at" \
  --to "company:acme"

# 列出关系
librefang kg list-relations --entity "person:john"

# 查询关系
librefang kg query --from "person:john" --relation "works_at"
```

### 图谱查询

```bash
# 查询路径
librefang kg path --from "person:alice" --to "company:acme"

# 查询子图
librefang kg subgraph --entity "person:bob" --depth 2
```

---

## 会话压缩

### 自动压缩

当会话消息数量达到阈值时自动压缩。压缩参数由 runtime 内部管理，不在 `config.toml` 中暴露。默认阈值为 80 条消息，保留最近 20 条原文，其余进行摘要压缩。

### 手动压缩

```bash
# 压缩会话
librefang session compact <session-id>

# 压缩所有会话
librefang session compact-all

# 查看压缩状态
librefang session compaction-status
```

### 压缩算法

1. **保留最近 N 条消息**
2. **提取关键信息**
3. **生成摘要**
4. **保留工具调用历史**

---

## 后台整固（梦境模式）

会话压缩只处理 **单个会话** 不溢出。梦境模式是更长时间尺度上的互补机制：定期唤醒已加入的 agent，把一份四阶段提示词（定位 / 收集 / 整固 / 修剪）交给它，让它自己跨会话地整理长期记忆 —— 通过 `memory_store` 写入持久见解、修剪过时条目。

<Note>
**默认关闭。** 梦境模式会按计划消耗真实 token，因此全局开关和每个 agent 的加入开关默认都是 `false`，必须显式开启。完整参考见 [`[auto_dream]`](/zh/configuration/core#auto_dream)。
</Note>

### 开启方式

两个开关必须同时为 `true`：

```toml
# ~/.librefang/config.toml
[auto_dream]
enabled = true
min_hours = 24         # 同一 agent 再次梦境的最短间隔
min_sessions = 5       # 上次梦境之后至少触及这么多会话才会触发
```

```toml
# agent manifest (.toml)
auto_dream_enabled = true

# 可选：用 per-agent 覆盖全局阈值
auto_dream_min_hours = 168      # 每周一次，适合低频 agent
auto_dream_min_sessions = 1     # 每个会话之后都触发，适合高频 agent
```

### 一次梦境的流程

1. 主触发路径是 `AgentLoopEnd` 钩子 —— agent 每完成一次 turn，kernel 立刻依次检查四道闸门：全局启用 → 时间间隔 → 会话活跃数 → 文件锁。任一不通过即跳过。另有稀疏的兜底调度器（默认 `check_interval_secs = 86400`，即每天一次）覆盖从不 turn 的 agent（例如等待外部触发的 channel bot）。
2. 闸门通过后，agent 以 **fork turn** 方式从 canonical session 派生调用（`kernel.run_forked_agent_streaming`）—— system prompt、tools、消息前缀和父 turn 字节级一致，命中 Anthropic prompt cache。Fork turn 不会把消息写回 canonical session，所以用户对话历史不会被梦境过程污染。工具白名单（`memory_store` / `memory_recall` / `memory_list`）在 execute 阶段强制，不在 request 构造阶段过滤 —— 这样请求 schema 仍然和父 turn 对齐，cache 才能命中。被 prompt 注入的梦境即使试图调用非 memory 工具，也只会拿到合成的错误结果。
3. 流式进度（phase、工具调用次数、涉及的记忆条目、最近一 turn 预览、token / 费用）存入每 agent 的进度注册表，通过状态端点对外暴露。
4. 成功后锁的 mtime 前进到 "现在"（作为时间闸门的基准）；失败或中止后会回滚 mtime，下次 tick 会重试。

### 使用入口

- **Web Dashboard** — 设置页 → 梦境模式卡片，每个 agent 一行：加入开关、运行中/完成/失败 badge、"立即梦境" / "中止" 按钮、运行中时的进度预览。
- **TUI** — `librefang` → Dashboard tab 顶部会出现 DREAMS 条，每 agent 一行状态字符；未发生过梦境时自动隐藏。
- **HTTP API** — `GET /api/auto-dream/status`、`POST /api/auto-dream/agents/{id}/trigger`、`POST /api/auto-dream/agents/{id}/abort`、`PUT /api/auto-dream/agents/{id}/enabled`。
- **审计日志** — 每次梦境都会发出一条 `DreamConsolidation` 审计事件，附带 phase、token 使用量和美元成本。

### 手动控制

- `POST /api/auto-dream/agents/{id}/trigger` 绕过时间和会话闸门（仍需满足文件锁和加入开关）。
- `POST /api/auto-dream/agents/{id}/abort` 中止手动触发的梦境，并回滚 mtime 让时间闸门重新打开。
- 调度器触发的梦境是顺序串行执行，不支持单独中止 —— 只能等它完成或命中 `timeout_secs`（默认 600 秒）。

完整字段、默认值、manifest 覆盖和 runtime 工具白名单的强制逻辑见 [`[auto_dream]`](/zh/configuration/core#auto_dream)。

---

## 内存提供者插件 API

`MemoryProvider` trait 允许把外部记忆后端（向量数据库、知识图谱、远程存储服务等）和 LibreFang 内置的 SQLite 内存并排接入。

### 架构

- 永远存在 **一个内置 provider**（默认的 SQLite 后端存储）。它不能被移除。
- 同时最多注册 **一个外部 provider**。外部 provider 只是补充内置存储，不替换它。
- 外部 provider 失败时，内置 provider 继续正常工作。外部 provider 的错误会被捕获、以 `WARN` 级别记录，不会传播到 agent loop。

### Trait 接口

```rust
#[async_trait]
pub trait MemoryProvider: Send + Sync {
    /// A short, unique name for this provider (e.g. "pinecone", "weaviate").
    fn name(&self) -> &str;

    /// Returns `true` for the built-in provider, `false` for all external providers.
    fn is_builtin(&self) -> bool;

    /// Returns a text block to inject into the agent system prompt at session start,
    /// or `None` if there is nothing to inject for this session.
    async fn system_prompt_block(&self, session_id: &str) -> Option<String>;

    /// Fetches relevant context for the given query string before an LLM call.
    /// The returned string is appended to the context window.
    async fn prefetch(&self, query: &str, session_id: &str) -> Result<String, MemoryError>;

    /// Called after each agent turn completes. Use this hook to index new content,
    /// synchronize state, or flush write buffers to the external backend.
    async fn on_turn_complete(&self, session_id: &str, turn_summary: &str) -> Result<(), MemoryError>;
}
```

### 注册外部 provider

在初始化阶段把你的实现传给 kernel 的内存子系统：

```rust
use librefang_memory::{MemoryManager, MemoryProvider};

// Build your custom provider
let my_provider: Arc<dyn MemoryProvider> = Arc::new(MyVectorDbProvider::new(config));

// Register it with the memory manager
memory_manager.set_external_provider(my_provider).await;
```

同时只能注册一个外部 provider。再次调用 `set_external_provider` 会替换之前注册的 provider。

### 用 `prefetch_all` 聚合上下文

内存管理器暴露 `prefetch_all(query, session_id)`，它会调用每个已注册 provider 的 `prefetch` 并把结果拼接：

```rust
// Called internally by the agent loop before each LLM request
let context = memory_manager.prefetch_all(&user_query, &session_id).await;
```

各 provider 的结果之间用空行分隔，并带上 provider 名作为标签前缀。任一 provider 返回错误时，该 provider 的结果会被跳过（同时打 `WARN` 日志），其余 provider 继续被查询。

### 错误隔离

外部 provider 的错误从不向 agent 或用户暴露：

1. `prefetch` 错误 → 打 `WARN` 日志，该 provider 的贡献用空字符串代替。
2. `on_turn_complete` 错误 → 打 `WARN` 日志，静默丢弃。
3. `system_prompt_block` 错误（panic）→ 在调用点捕获，返回 `None`。

这种设计确保配错的或暂时不可用的外部后端无法干扰 agent loop。

---

## 使用追踪

### 追踪操作

```bash
# 查看使用统计
librefang usage

# 查看 Agent 使用
librefang usage --agent <agent-id>

# 查看提供商使用
librefang usage --provider
```

### 成本追踪

```bash
# 查看成本
librefang cost

# 按时间范围查看成本
librefang cost --from 2025-01-01 --to 2025-01-31

# 导出报告
librefang cost export --format csv
```

---

## API 端点

### KV 内存操作

| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/memory/search` | GET | 搜索记忆（查询参数：`q`、`limit`、`threshold`） |
| `/api/memory` | POST | 存储记忆条目 |
| `/api/memory/{id}` | GET | 获取指定记忆条目 |
| `/api/memory/{id}` | DELETE | 删除记忆条目 |

### 会话操作

| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/memory/sessions` | GET | 列出会话 |
| `/api/memory/sessions/{id}` | GET | 获取会话详情 |
| `/api/memory/sessions/{id}/compact` | POST | 压缩会话 |

### 知识图谱

知识图谱是 **per-agent** 的,不是全局 REST 接口。用 per-agent 端点:

| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/memory/agents/{id}/relations` | GET | 查询该 agent 的知识图谱(实体 + 关系) |
| `/api/memory/agents/{id}/relations` | POST | 给该 agent 的图谱存新关系 |

Agent 也可以从循环里直接用 [`knowledge_add_entity` / `knowledge_add_relation` / `knowledge_query` 工具](/zh/agent/tools#knowledge-graph) 构建图谱。早期文档列的 `/api/memory/kg/*` 端点从未发布过 —— 调那些路径会 404。

### 主动记忆（Proactive Memory）

主动记忆让 agent 自主浮现、整合、召回长期知识,不用显式工具调用。每条记忆同时存三份 —— 语义存储(文本 + embedding)、结构化 KV(`memory:{id}`)、知识图谱(抽取的 entity / relation 三元组) —— 所以 agent 可以按相似度、按 ID、或从已知 entity 沿图谱遍历来召回。

#### 条目 schema

| 字段 | 类型 | 用途 |
|---|---|---|
| `id` | UUID | 唯一记忆 ID |
| `agent_id` | UUID | 所有 agent —— 条目**严格按 agent 隔离**,不会跨 agent 泄漏 |
| `content` | text | 记忆文本 / 事实 |
| `source` | enum | 来源:`auto_memorize`、`manual_add` 等 |
| `scope` | enum | 级别:`user_memory`、`session_memory`、`agent_memory` |
| `confidence` | f64(0.0–1.0) | 相关度分数;随时间衰减 |
| `metadata` | JSON | 自由 KV;含 `category`(见下) |
| `created_at` | RFC3339 时间戳 | 创建时间 |
| `accessed_at` | RFC3339 时间戳 | 最近访问 —— 驱动衰减 |
| `access_count` | int | 被召回的次数 |
| `deleted` | bool | 软删除标志 |
| `embedding` | vector(独立表) | 余弦相似度搜索用 |

#### 作用域(Scope)

| 作用域 | 生命期 | 用途 |
|---|---|---|
| `user_memory` | 跨 session 持久化 | 用户级事实和偏好(可在该用户的多个 agent 间共享)。 |
| `session_memory` | `session_ttl_hours`(默认 `24`)后自动删除 | 当前对话的工作笔记。 |
| `agent_memory` | per-agent 持久化 | Agent 学到的行为和技能,严格隔离。 |

#### 类别(Category)

类别住在 `metadata.category`。`auto_memorize` 默认抽取的有:`communication_style`、`preference`、`expertise`、`work_style`、`project_context`、`personal_detail`、`frustration`。类别列表可配置。

#### 自动整合

每个 agent 每 **10** 次 `auto_memorize` 自动触发一次(无需显式 cron / 用户操作)。整合器:

1. 用分级相似度梯队找重复或近似条目:
   - **子串包含**(完全 / 超集 / 子集)
   - **向量余弦**(存了 embedding 时)
   - **Jaccard 词重叠**(回退)
2. 每个重复簇里**保留最近创建的**那条。
3. 软删除其余,记日志方便审计。

整合器只扫最近 **100** 条,以让 dedup 循环在热路径上保持 O(n²)-安全。要扫完整历史,手动调 `POST /api/memory/agents/{id}/consolidate`。

#### 衰减

未访问超过 1 天的记忆:

```
decayed_conf = original_conf × e^(-decay_rate × days_since_access)
final_conf   = min(decayed_conf × (1 + log2(access_count)), 1.0)
```

衰减扫描**每小时最多一次**(在 `auto_retrieve` 顶部检查)。默认值:`confidence_decay_rate = 0.01`(很慢 —— 大约 70 天减半)、`session_ttl_hours = 24`。一条记忆"过期"的条件是:(a)超过一天没访问,**或** (b)scope 是 `session_memory` 且 TTL 到了。

#### per-agent vs 跨 agent

除 `user_memory` 外,所有条目严格按 agent 隔离。Search、consolidate、eviction 都按 `agent_id` 过滤 —— 一个 agent 没有路径读到另一个 agent 的 `agent_memory` 或 `session_memory`。Per-agent 上限默认 **1000** 条;超过时按"最旧 / 最低 confidence"逐出。

#### API 端点

主动记忆端点分两类 —— 全局和 per-agent。(KV memory 在 `/api/memory/agents/{id}/kv/*`,是另一个子系统,不在此列。)

**全局 / 跨 agent:**

| 方法 | 路径 | 用途 |
|---|---|---|
| GET | `/api/memory` | 列出所有主动记忆(分页,可选 `?category=`) |
| POST | `/api/memory` | 新增记忆条目 |
| GET | `/api/memory/search?q=&limit=` | 跨所有条目语义搜索 |
| GET | `/api/memory/stats` | 全局统计 |
| GET | `/api/memory/config` | 获取记忆配置 |
| PATCH | `/api/memory/config` | 更新记忆配置 |
| POST | `/api/memory/cleanup` | 手动跑 session-TTL 清理 |
| POST | `/api/memory/decay` | 手动跑 confidence 衰减 |
| POST | `/api/memory/bulk-delete` | 批量删除 |
| PUT | `/api/memory/items/{id}` | 更新单条记忆 |
| DELETE | `/api/memory/items/{id}` | 删除单条记忆 |
| GET | `/api/memory/items/{id}/history` | 记忆编辑历史 |
| GET | `/api/memory/user/{user_id}` | 该用户跨所有 agent 的 user-level 记忆 |

**Per-agent:**

| 方法 | 路径 | 用途 |
|---|---|---|
| GET | `/api/memory/agents/{id}` | 列出该 agent 的记忆 |
| DELETE | `/api/memory/agents/{id}` | 重置(清空)该 agent 的全部记忆 |
| GET | `/api/memory/agents/{id}/search?q=` | 限制在此 agent 范围的语义搜索 |
| GET | `/api/memory/agents/{id}/stats` | 该 agent 的统计 |
| DELETE | `/api/memory/agents/{id}/level/{level}` | 按 scope 清(`session`、`agent`、`user`) |
| GET | `/api/memory/agents/{id}/duplicates` | 找近似条目但不删 |
| POST | `/api/memory/agents/{id}/consolidate` | 对完整历史触发整合 |
| GET | `/api/memory/agents/{id}/count` | 记忆数量 |
| GET | `/api/memory/agents/{id}/relations` | 查询该 agent 的知识图谱 |
| POST | `/api/memory/agents/{id}/relations` | 存关系 |
| GET | `/api/memory/agents/{id}/export` | 导出该 agent 的记忆(JSON) |
| POST | `/api/memory/agents/{id}/import` | 导入记忆(JSON) |

之前的 `/api/memory/proactive/*` 命名空间是一个从未发布的草稿端点 —— 用上面这套。如果客户端还在调旧路径,会得到 404。

---

## 最佳实践

1. **定期压缩** - 防止会话过大
2. **使用标签** - 便于组织和搜索
3. **设置衰减率** - 控制记忆置信度
4. **监控使用** - 跟踪成本和使用量

---

## 故障排除

### 内存搜索慢

```bash
# 重建向量索引
librefang memory reindex

# 检查索引状态
librefang memory index-status
```

### 数据库膨胀

```bash
# 清理旧数据
librefang memory cleanup --older-than 30d

# 压缩数据库
librefang memory vacuum
```

### 记忆丢失

```bash
# 检查数据库完整性
librefang doctor

# 恢复备份
librefang memory restore --backup <path>
```
