内容
# 维勒姆记忆 — 通过MCP实现持久化AI记忆
> **维勒姆** — 古老羊皮纸,最原始的记忆介质。
> 一个基于MCP(模型上下文协议)构建的持久化AI记忆系统。
[](https://python.org)
[](LICENSE)
---
## 什么是维勒姆记忆
维勒姆记忆是一个MCP服务器,为AI助手提供**持久化、可搜索的记忆**,跨越多个对话 — 解决了每次对话都从头开始的根本限制。
| 功能 | 工作原理 |
|-----------|--------------|
| **人类记忆** | 存储对话摘要 + 标签 + 完整上下文;通过语义向量搜索 |
| **记忆分组** | 通过CPM(k=4,可配置)自动分组相关记忆 |
| **后台守护进程** | 定期TTL清理 + 可选的自动去重扫描 |
| **预合并向量** | 每个条目1个向量(而非6个),在数学上与多向量评分相同 |
**关键差异点:**
- **零外部服务** — 单个SQLite文件,本地设备模型。无向量数据库,无云API。
- **预合并向量设计** — 存储减少6倍,速度提升4倍,搜索质量在数学上相同。
- **MCP原生** — 连接到任何MCP主机(DeepChat,Claude Desktop,自定义应用)。
---
## 快速开始
### 要求
- Python 3.12+
### 安装
```bash
cd vellum
pip install -r requirements.txt
```
### 运行
```bash
python run.py
```
### 测试
```bash
pytest tests/ -v
```
---
## MCP工具参考
### 记忆写入
```
memory_write(data: str) -> str
```
- `summary`(必填,≤200字符)
- `tags`(必填,恰好5个)
- `context_text`(可选)
- `category`(必填:`conversation`,`knowledge`,`document`,`preference`,`other`)
- `is_time_sensitive`(可选)
### 记忆查询
```
memory_query(query, top_k=3, score_threshold=0.15) -> str
```
返回按余弦相似度排序的条目(实际分数0-1)。每个结果包括`create_timestamp`,`category`,`is_time_sensitive`,`group_ids`。
### 上下文管理
```
memory_get_context(timeline_id, offset=0, limit=1) -> str
memory_write_context(timeline_id, context_text) -> str
```
在自然边界处自动分段(标题,代码块,列表,段落),最大8K字符/分段。
### 记忆分组
```
memory_get_groups(entry_id) -> str
memory_list_groups() -> str
memory_get_group_members(group_id) -> str
memory_rebuild_groups(threshold=0.45) -> str
```
`memory_rebuild_groups`从配置(`group_k`)中读取`k`,阈值是可选覆盖。
### 状态
```
memory_init() -> str
memory_status() -> str
```
所有工具在失败时返回JSON错误(非原始回溯),感谢`@_tool`装饰器。
---
## 架构
```
┌──────────────────────────────────────────────┐
│ AI助手(主机) │
│ memory_write / memory_query / ... │
└─────────────────────┬────────────────────────┘
│ MCP(stdio)
┌─────────────────────▼────────────────────────┐
│ 维勒姆记忆MCP服务器 │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 9个MCP工具 │ │ 线程安全延迟初始化 │ │
│ │ @_tool │ │ (双重检查) │ │
│ └──────┬───────┘ └──────────────────────┘ │
│ │ │
│ ┌──────▼─────────────────────────────────┐ │
│ │ 存储 + 分组 + 向量适配器 │ │
│ │ │ │
│ │ human_timeline.py — CRUD + 分段 │ │
│ │ groups.py — CPM分组 │ │
│ │ vector/adapter.py — 预合并搜索 │ │
│ │ db.py — SQLite + 初始化 │ │
│ │ errors.py — 异常类型 │ │
│ └─────────────────────────────────────────┘ │
└──────────────────────┬─────────────────────────┘
│
┌────────▼────────┐
│ SQLite(1个文件)│
│ vellum.db │
└─────────────────┘
```
| 组件 | 文件 | 责任 |
|-----------|------|----------------|
| 服务器 | `server.py` | MCP入口,10个工具,`@_tool`装饰器 |
| 数据库 | `db.py` | SQLite包装器,模式初始化 |
| 人类时间线 | `stores/human_timeline.py` | 记忆CRUD + 上下文分段 |
| 分组管理器 | `groups.py` | CPM k=4 记忆分组(可配置) |
| 向量适配器 | `vector/adapter.py` | 句子转换器,预合并向量 |
| 异常 | `errors.py` | 维勒姆记忆异常层次结构 |
---
## 检索设计
### 预合并向量
每个条目存储**1个合并向量**,而不是6个单独的向量:
```
score = (q·s + q·t₀ + ... + q·t₄) / 6 = q · (s + t₀ + ... + t₄) / 6
```
**在数学上与多向量相同**(通过10,000次随机测试验证,最大误差1.06e-08)。
| 指标 | 朴素(6个向量) | 预合并 |
|--------|------------------|------------|
| 存储 | 6000行 / 1K条目 | **1000行** |
| 点积 | 6000 | **1000** |
| 查询时间(1K) | ~147ms | **~38ms** |
### 记忆分组(CPM k=4,可配置)
1. 成对余弦相似度 ≥ 阈值 → 边
2. 查找所有k-团(扩展自底向上:边 → 3-团 → ... → k-团)
3. 共享k-1个节点的k-团 → 相同社区
4. 连通组件 → 分组
**`k` 和 `threshold` 从配置(`group_k`,`group_threshold`)中读取。**
在启动时自动构建;可以通过`memory_rebuild_groups()`覆盖。
---
## 后台守护进程
维勒姆记忆在启动时启动一个后台守护进程线程(`_start_daemon()`),该线程定期执行:
| 任务 | 默认间隔 | 配置键 | 环境变量 |
|------|-----------------|-----------|---------|
| **TTL清理** | 30分钟 | `daemon_interval` | `VELLUM_DAEMON_INTERVAL` |
| **去重扫描** | 30分钟(相同间隔) | `dedup_enable` + `dedup_threshold` | `VELLUM_DEDUP_ENABLE` + `VELLUM_DEDUP_THRESHOLD` |
### 去重扫描
启用后,守护进程比较所有条目的**摘要向量**(余弦相似度 ≥ `dedup_threshold`,默认为0.9):
- 跳过已标记为`is_time_sensitive=true`的条目
- 对于每个重复对:将较新的条目标记为`is_time_sensitive=true`,默认TTL为3天
- 后台TTL清理自动删除标记的重复项
启用方法:
```bash
set VELLUM_DEDUP_ENABLE=true
# 或通过SQL:INSERT OR REPLACE INTO config (key, value) VALUES ('dedup_enable', 'true');
```
---
## 项目结构
```
vellum/
├── __init__.py # 版本
├── server.py # MCP入口 + 10个工具 + @_tool + 守护进程线程
├── db.py # SQLite + 初始化 + 迁移
├── errors.py # 异常层次结构
├── groups.py # CPM分组(可配置k)
├── stores/
│ ├── __init__.py
│ └── human_timeline.py # 记忆CRUD + 分段 + 去重助手
└── vector/
├── __init__.py
└── adapter.py # 转换器包装器 + 预合并向量 + 摘要向量
schemas/
└── schema.sql
tests/
├── __init__.py
├── test_errors.py # 6个测试
└── test_stores.py # 13个测试
```
---
## 环境变量
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `VELLUM_DB_PATH` | `vellum/vellum.db` | SQLite文件的绝对路径 |
| `VELLUM_TRANSFORMER_MODEL` | `BAAI/bge-small-zh-v1.5` | 句子转换器模型名称 |
| `VELLUM_DEDUP_ENABLE` | `false` | 启用后台去重扫描 |
| `VELLUM_DEDUP_THRESHOLD` | `0.9` | 去重的余弦相似度阈值 |
| `VELLUM_DAEMON_INTERVAL` | `1800` | 守护进程循环间隔(秒) |
| `VELLUM_DEFAULT_TTL_DAYS` | `3` | 时间敏感条目的默认TTL |
| `VELLUM_CHUNK_SIZE` | `1800` | 写入时每个上下文分段的最大字符数 |
| `VELLUM_CONTEXT_SEPARATORS` | `["\\n## ", "\\n```", "\\n\\n", "\\n", " "]` | 分段分割的自然分隔符。字符串的JSON数组,例如`["\\n## ", "\\n```", "\\n\\n", "\\n", " "]` |
---
## 许可证
MIT