# Palimpsest

> Local-first, battle-tested, memory that never disappears. Hybrid vector search + knowledge graph + full-text retrieval for AI agents.

- **Type:** MCP server
- **Install:** `agentstack add mcp-jiay-77-palimpsest`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [JiaY-77](https://agentstack.voostack.com/s/jiay-77)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [JiaY-77](https://github.com/JiaY-77)
- **Source:** https://github.com/JiaY-77/Palimpsest

## Install

```sh
agentstack add mcp-jiay-77-palimpsest
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Palimpsest

**本地优先的长期记忆系统 · AI 助手的跨会话记忆底座**

**Local-first, battle-tested, memory that never disappears.**

> _Palimpsest_：拉丁语，原指「重写的羊皮纸」——旧字迹被覆写抹去，却又在岁月里重新透出。
>
> 我们把这个意象搬进记忆里：**新的事实覆盖旧的事实，但旧迹永不真正丢失**——每一次改写都通过一条有迹可循的 **版本链**（`REVISED_BY`）连接，新旧记忆可查可溯。

[](/)
[](/)
[](LICENSE)
[](/)
[](/)
[](https://github.com/JiaY-77/Palimpsest/actions/workflows/ci.yml)

**中文** | [English](./README_EN.md)

---

## 一句话介绍

Palimpsest 是一个 **本地优先的嵌入式长期记忆系统**，将 **语义向量检索（Vector Search）、加权知识图谱（Knowledge Graph）与全文检索（Full-Text Retrieval）** 三合一，把 AI 助手的跨会话记忆统一存放、管理、演化在一座本地数据库里。

我们的目标是成为 **AI 助手的「记忆底座」** —— 让每一次对话的收获都不再随会话关闭而烟消云散，而是**可检索、可关联、可演进**：

- 🗃️ **混合检索** —— 语义向量（cosine）与 FTS5 全文索引（`trigram` 分词，支持中文子串匹配）通过 RRF（Reciprocal Rank Fusion）或级联方式融合，每条命中都标注来源 `fts_hit` / `sem_hit`
- 🔗 **图谱扩散召回** —— 节点由 **加权边**（`RELATED_TO` / `REVISED_BY` / `CAUSES` / `REFERS_TO`）相连，BFS 沿边扩散召回；扩散按最强边截断、弱边过滤、可按域「块」隔离，防止跨域污染
- 🕸️ **社区发现** —— 内置 Leiden 聚类，一键把记忆库分成主题簇（如项目簇、人物关系簇），回答「记忆库里都有哪些圈子」
- 🔄 **冲突检测与版本链** —— 写入时与相似旧记忆比对：高相似度（score > 0.75）判为同一事实被取代，旧版标记 `outdated` 并通过 `REVISED_BY` 链向新版；中相似度只记 `related_ids` 提示相关不误标；type / domain 双隔离防跨类误标
- 🛡️ **写入前敏感扫描** —— 存储前按 **10 条正则规则**扫描：**强规则**（API Key、令牌、私钥、SSH Key、Bearer Token 等 8 条）命中即**拒绝写入**并报告命中的规则；**弱规则**（身份证、手机号 2 条）命中仅**放行并打 `secret_hint` 标记**供审计——命中原文仍会入库，弱规则是审计线索而非脱敏（详见 [`SECURITY.md`](SECURITY.md)）
- 🧹 **容量合并与记忆盘点** —— `mem_consolidate` 把近似重复节点合并（相似度 ≥ 0.85、保护高价值记忆）；`mem_stats` 统一盘点库内分布（类型/域/重要度/时间/图谱/热点/弱敏感标记数/**tier 分层分节**），回答「库里有什么」
- ⏫ **高频记忆自动升级** —— 检索命中自动计数（`hit_count`），`promote` 把反复被用到的记忆浮出水面：升权 + 打标（dry-run 预览、幂等可逆），为人工升级知识库提供依据
- ⏳ **记忆生命周期** —— 时间衰减加权（`MEMORY_DECAY_FACTOR`，默认 0.95/月）在排序中淡化陈旧记忆而不动存储；`kb_chunk` 知识切片豁免衰减；`outdated` 旧版默认不再参与普通检索（可显式追溯）
- 📁 **任务自动归档** —— 完成任务自动移出热库，写成 markdown 归档至知识库归档目录后删除——先 `dry-run` 预览，`apply` 提交
- ✅ **部署体检** —— `doctor` 一键体检关键文件 / 存储 / FTS / 依赖 / Embedding 可达性与**向量维度一致性**（实测 vs 库），每个失败项直接给出修复命令（`--json` 机器可读）；`startup-check` 为其轻量子集
- ✂️ **省 token 设计** —— 检索默认只返回 **150 字摘要 + 元数据**，而非全文；完整内容按需二次拉取
- 🗂️ **记忆分层（`tier`）** —— 检索侧的轻量视图，不迁数据、不改存储：默认只取**事实层**（`memory` / `correction` / `decision` / `plan` / `task` 等），把日志层（`record` / `event` / `git_commit`，约占活跃节点四成）从默认检索与注入池中摘出；`tier="logs"` 只取日志层，`tier=""` 显式回到全量（历史追溯通道）。同一套分层视图同步覆盖 `mem_review` 的 `recent_ingests` 与 `mem_stats` 的 `tiers` 分节。层清单由 `TIER_FACTS` / `TIER_LOGS` 配置，未登记的 type 保守归事实层
- 🔐 **可选 API Key 鉴权** —— 默认关闭（localhost 本机直连）；设置 `PALIMPSEST_API_KEY` 后 REST 层要求 Bearer / X-API-Key 头，适合局域网受信部署
- 🎯 **三接口、一核心** —— MCP（stdio）、FastAPI REST、完整 CLI 三套接入共用同一套底层工具，行为永不割裂
- 🧠 **Hermes 双插件换脑** —— 把 Hermes 的记忆层整体换成 Palimpsest：Memory Provider（语义召回 + 自动沉淀）+ Context Engine（压缩前图谱提炼），一行命令激活，记忆跨会话不丢

---

## 为什么需要 Palimpsest？

### 当前 AI 助手的三类「记忆困境」

绝大多数 AI 应用同时面临三类数据能力的割裂：

| 场景 | 传统做法 | 问题 |
|---|---|---|
| 跨会话记忆 | 每次会话从零开始 | 历史经验与事实随会话关闭而丢失 |
| 知识库语义化 | 简单关键词匹配 | 无法理解语义，无法在概念间关联 |
| 记忆治理 | 无序堆积/手动清理 | 重复、过期、矛盾的信息越来越多 |

Palimpsest 用 **一个本地内核** 同时解决「检索、关联、演进」三件事，避免在向量库、文档库、图谱库之间搬运与同步。

### 「记忆不丢」的一个例子

> 你告诉助手「服务监听 8090 端口」。后来设计变更，又说「端口改为 8095」。
>
> 旧记忆并不会被粗暴覆盖——它被标记为 `outdated`，通过 `REVISED_BY` 指向新版本。任何时候版本链查询都能展开这条链，看清这个事实**如何一步步演变成今天的样子**。这就是 Palimpsest：覆而不失，改写可溯。

---

### 使用场景

#### 场景 1 · 长期陪伴 / 个人助理 agent 的跨会话记忆（Hermes 等）

把 Palimpsest 接入 agent 后，它就是你的「记忆底座」：每轮对话自动召回相关历史、把强信号记忆自动沉淀，会话结束时再提炼本轮要点；上下文压缩之前，图谱还会先提炼一次，把散落的片段织成可检索的网络。会话关闭也没关系——下次见面它依然记得住、想得起。

#### 场景 2 · 知识库语义化（Obsidian 用户）

把积累了多年的 Obsidian Vault 变成可语义检索的资产：`build_kb_index.py` 扫描全部 `.md`，按 Markdown 标题切片、向量化入库，`[[双链]]` 上下文原样保留。搜索不再是「关键词碰运气」，而是「语义相关、附带图谱邻居」。

#### 场景 3 · 创作设定库（小说 / 世界观作者）

`build_novel_index.py` 把本地的创作 Vault（角色卡、世界观、人物关系文档）整文件入库为 `domain=novel` 节点；`link_novel_relations.py` 按关系清单批量建边（师徒/血缘/阵营等）；配合社区发现与图谱查询，设定之间的关系一目了然。创作数据留在本地，不入公网。

#### 场景 4 · 记忆治理（防污染 / 防膨胀 / 可追溯）

记忆库不会越用越乱：写入前敏感扫描拦下密钥，冲突检测 + 版本链让每次改写都有迹可循，容量合并把近似重复收缩成一条，时间衰减淡化陈旧记忆，盘点与 promote 让高频记忆浮出。记忆是资产，不是垃圾场。

---

### 给 Hermes 用户：把它变成你的记忆插件

Hermes 预留了 memory provider / context engine 插槽，Palimpsest 为此提供**双插件**：**Memory Provider**（记忆读写）+ **Context Engine**（上下文压缩前提炼）。插件源码在仓库 [`hermes-plugin/`](./hermes-plugin/README.md)，含 `plugin.yaml`（`kind=standalone`）与两个 hooks：`on_session_end`（会话结束提炼要点）与 `on_pre_compress`（压缩前图谱提炼）。

部署（把插件复制到 Hermes 插件目录，然后一行一件激活）：

```bash
# 1. 复制插件到 Hermes 插件目录（默认 ~/.hermes/plugins/）
mkdir -p ~/.hermes/plugins/palimpsest
cp hermes-plugin/* ~/.hermes/plugins/palimpsest/

# 2. 激活（一行一件）
hermes plugins enable palimpsest
hermes config set memory.provider palimpsest
hermes config set context.engine palimpsest-graph
```

激活后，每轮对话都会自动发生这些事：

- **自动召回** —— 每轮经 REST `:8090` 检索相关历史（`memory.provider=palimpsest`）。
- **强信号自动沉淀** —— 高信号的事实自动写入记忆（启发式判断，不依赖 LLM）。
- **会话结束提炼** —— `on_session_end` 把本轮要点沉淀为结构化记忆。
- **压缩前图谱提炼** —— `on_pre_compress` 用 `context.engine=palimpsest-graph` 提炼图谱要点，喂给压缩阶段。
- **记忆工具集** —— `palimpsest_search` / `palimpsest_ingest` / `palimpsest_link` / `palimpsest_graph` 等，供 agent 主动调用。

两点注意：

- REST 服务（`:8090`）需**常驻运行**（如 `scripts/start_rest.vbs` 开机自启）。
- 自动沉淀是**启发式**判断（相似度、重要度阈值），不是 LLM 判断——它求「快、稳、不花钱」，而非「聪明」。

---

### Obsidian 用户：我们的读取思路（即使不用 Palimpsest）

> 这一节讲的是「思路」，不是广告——就算你完全不用 Palimpsest，也能照此用任何工具链复刻。

我们不把 Vault 当「文件」看待，而是当作**知识源**。读取分五步：

1. **Vault 目录即知识源** —— 递归扫描 `KNOWLEDGE_DIR` 下的全部 `.md`（自动跳过 `.obsidian` 等配置目录），每个笔记就是一个待处理文档。
2. **按 Markdown 标题智能切片** —— 以 `##` / `###` 为边界切成 300~800 字符的块，块内**原样保留 `[[双链]]`**，让「哪篇关联哪篇」的上下文不丢。
3. **向量化入库** —— 每个切片经 embedding 编码，作为 `kb_chunk` 节点（`domain=kb`）写入存储，构成可语义检索的知识资产。

**想自己实现？** 这套流程的骨架很简单：一个向量库（sqlite-vec / chroma 皆可）+ 一个 embedding 服务就能复刻。真正的设计点有两个：

- **切片粒度** —— 太粗检索不准、太碎丢上下文。
- **双链保留** —— 让 `[[A]]⇄[[B]]` 的关系进入检索结果，而不是只在正文里躺着。

本思路的现成实现即 `scripts/build_kb_index.py`（全量 `--full` / 增量默认，增量按 `mtime` 对比只重建变化文件）。

---

## 快速上手

### 安装（通用）

```bash
# 1. 需要 Python 3.10+
python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate

# 2. 安装依赖
pip install -r requirements.txt
```

接下来按你的情况选一条路径——

### 路径 A：云端 key，三行起跑（适合没装 Ollama、想最快跑起来）

```bash
# 1. 复制配置模板
cp .env.example .env

# 2. 编辑 .env：填入云端向量 API Key + LLM Key
#    EMBEDDING_API_KEY=你的云端key      # 留空或删除该行 → 自动走本地 Ollama
#    EMBEDDING_BASE_URL=https://api.voyageai.com/v1  (按服务商填写)
#    EMBEDDING_MODEL=voyage-3                      (按服务商填写)
#    EMBEDDING_DIM=1024                            (按服务商填写)
#    DEEPSEEK_API_KEY=你的LLMkey       (LLM_BACKEND=deepseek 时必填)
```

> **不设置 `EMBEDDING_PROVIDER` 即可**——系统自动探测：检测到有效 `EMBEDDING_API_KEY` → 走云端。
> 如需强制指定，可显式写 `EMBEDDING_PROVIDER=openai` 或 `EMBEDDING_PROVIDER=ollama`。

### 路径 B：本地 Ollama（隐私优先，数据不出本机）

```bash
# 1. 安装并启动 Ollama（https://ollama.com）
# 2. 拉取向量模型
ollama pull qwen3-embedding:0.6b

# 3. 复制配置模板
cp .env.example .env

# 4. 编辑 .env：填入 LLM Key（向量后端无需额外配置，默认本地 Ollama）
#    DEEPSEEK_API_KEY=你的LLMkey       (LLM_BACKEND=deepseek 时必填)
#    或 LLM_BACKEND=ollama              (全部走本地，无需任何 API Key)
```

> **两条路径通用说明：**
> - 换 provider = 换向量空间，**必须重建知识库索引**——详见 [更换向量模型 / 重嵌全库](#更换向量模型--重嵌全库)。
> - `EMBEDDING_PROVIDER` 留空 = 自动探测（推荐）；显式写 `ollama` 或 `openai` 可强制指定。

### 启动

```bash
# 推荐：部署体检（每个失败项都会打印对应的修复命令）
python scripts/palimpsest_cli.py doctor

# 轻量自检（doctor 的子集）
python scripts/palimpsest_cli.py startup-check
```

> **首次运行体检**：`doctor` 会检查关键文件 / 存储 / FTS / 依赖 / Embedding 服务可达性 / 向量维度一致性（实测 vs 库），任一失败项都会给出可执行的修复命令。
> 若 Embedding 项失败：本地 Ollama 请先启动并 `ollama pull qwen3-embedding:0.6b`；
> 若使用云端，请确认 `.env` 已配置 `EMBEDDING_API_KEY`。

```bash
# REST 服务 (:8090)
python -m uvicorn main:app --host 127.0.0.1 --port 8090

# MCP 服务（stdio —— 接入任意 MCP 客户端）
python mcp_server.py

# CLI（示例）
python scripts/palimpsest_cli.py search "架构最近发生了什么变化？"

# 监控面板 (:8010)
python scripts/dashboard.py

# 索引知识库（KNOWLEDGE_DIR 下的 Obsidian .md 文件）
python scripts/build_kb_index.py
```

> **单进程写入约束（重要）**：库文件由 triviumdb 以**独占写模式**打开——第二个写连接（同进程或跨进程）会在
> 构造 `TriviumDB` 时直接失败并报 `Database locked`；节点 ID 由应用层按「当前已提交最大 id + 1」分配。
> 因此：
> - REST 服务**禁止多 worker / 多实例**并发写同一库（不要用 `uvicorn --workers N`，保持上面这条单进程命令）；
> - MCP 服务、CLI、dashboard 与 REST 同时指向同一个 `DB_PATH` 时，写操作互斥失败——需要并行写请各自指向不同 `DB_PATH`；
> - 该约束是 fail-fast 的：不会静默产生重复 ID 或损坏数据，而是把冲突的写请求直接报错。

Windows 下 `scripts/start_rest.vbs` 可以隐藏窗口启动 REST 服务（如开机自启），日志写入 `scripts/start_rest.log`。

**MCP 客户端接入**（通用 MCP servers 配置）：

```json
{
  "mcpServers": {
    "palimpsest": {
      "command": "python",
      "args": ["mcp_server.py"],
      "cwd": "/path/to/Palimpsest"
    }
  }
}
```

---

## 配置

所有配置均从环境变量读取（`.env` 文件由 `python-dotenv` 自动加载），完整带注释模板见 `.env.example`。

| 变量 | 默认值 | 说明 | 生效前提（Precondition） |
|---|---|---|---|
| `REST_PORT` | `8090` | FastAPI REST 服务端口 | 启动 REST 服务时 |
| `DASHBOARD_PORT` | `8010` | 监控面板服务端口 | 启动 dashboard 时 |
| `DB_PATH` | `data/mh_memory.db` | 嵌入式 TriviumDB 数据库路径 | — |
| `PALIMPSEST_API_KEY` | *（空 = 关闭）* | 可选 REST 鉴权；设置后除 `/` 外所有请求须带 Bearer / X-API-Key | 启用 REST 鉴权时 |
| `LLM_BACKEND` | `deepseek` | LLM 后端：`deepseek` 或 `ollama` | 需要 LLM 调用时 |
| `DEEPSEEK_API_KEY` | *（空）* | DeepSeek API 密钥 | `LLM_BACKEND=deepseek` |
| `DEEPSEEK_BASE_URL` | `https://api.deepseek.com` | DeepSeek API 基础地址 | `LLM_BACKEND=deepseek` |
| `DEEPSEEK_MODEL` | `deepseek-v4-flash` | DeepSeek 模型标识 | `LLM_BACKEND=deepseek` |
| `OLLAMA_BASE_URL` | `http://localhost:11434/v1` | Ollama OpenAI 兼容基础地址 | `LLM_BACKEND=ollama` |
| `OLLAMA_MODEL` | `deepseek-r1:7b` | 作为 LLM 的 Ollama 对话模型 | `LLM_BACKEND=ollama` |
| `EMBEDDING_PROVIDER` | *（空 = 自动探测）* | 向量后端：留空自动探测（有云端 key → `openai`，否则 → `ollama`）；显式写 `ollama`（本地、私有）或 `openai`（OpenAI 兼容云端，如 Voyage/硅基流动） | — |
| `OLLAMA_EMBEDDING_MODEL` | `qwen3-embedding:0.6b` | 本地 Ollama 向量模型 | `EMBEDDING_PROVIDER=ollama` |
| `OLLAMA_EMBEDDING_BASE_URL` | `http://localhost:11434` | Ollama 原生 embedding API 根地址（与 LLM 的 /v1 解耦） | `EMBEDDING_PROVIDER=ollama` |
| `OLLAMA_EMBEDDING_DIM` | `1024` | 向量维度（本地后端） | `EMBEDDING_PROVIDER=ollama` |
| `EMBEDDING_API_KEY` | *（空）* | 云端向量端点的 API 密钥 | `EMBEDDING_PROVIDER=openai` |
| `EMBEDDING_BASE_URL` | `https://api.voyageai.com/v1` | 云端向量基础地址（任意 OpenAI 兼容端点） | `EMBEDDING_PROVIDER=openai` |
| `EMBEDDING_MODEL` | `voyage-3` | 云端向量模型 | `EMBEDDING_PROVIDER=openai` |
| `EMBEDDING_DIM` | `1024` | 向量维度（云端后端） | `EMBEDDING_PROVIDER=openai` |
| `MEMORY_DECAY_FACTOR` | `0.95` | 月度记忆衰减（排序用，`score × importance × factor^(天/30)`）；`1.0` 关闭衰减；`kb_chunk` 节点永不衰减 | soft 模式：仅进入 ε 微调项 `recency_norm`（ε 默认 0.02 → 排序影响 ≤0.02，一年内约 0.01 量级，近乎半死参数）；hard 模式：乘性硬加权 |
| `MEMORY_RERANK_MODE` | `soft` | 重排模式：`soft` = 语义分为主线 + ε 级元数据微调（默认）；`hard` = 旧版乘性硬加权（可回退） | — |
| `SOFT_RERANK_EPS` | `0.02` | `soft` 模式的 ε：落在余弦分差区间的 15%–40%，只做 tie-break | `MEMORY_RERANK_MODE=soft` |
| `DOMAIN_BOOST_EPS` | `0.10` | 域软加权加分（加性，作用在语义分上）：`domain_boost` 非空时对同域候选加此值 | `domain_boost` 参数非空 |
| `KB_SOFT_RERANK_MULT` | `1.5` | `kb_chunk`（知识块不老化）在 `soft` 模式下的 ε 加成倍率 | `MEMORY_RERANK_MODE=soft` |
| `DOMAIN_BIAS_WEIGHT` | `1.15` | 域偏置检索的额外权重 | `domain_bias` 参数非空 |
| `EXPAND_MAX_EDGES_PER_NODE` | `20` | 图谱扩散时每节点最多扩散的最强边数 | 图扩散启用（`RETRIEVAL_EXPAND_DEPTH≥1` 或检索附带邻居） |
| `EXPAND_MIN_EDGE_WEIGHT` | `0.0` | 图谱扩散弱边过滤阈值（0 关闭） | 图扩散启用（`RETRIEVAL_EXPAND_DEPTH≥1` 或检索附带邻居） |
| `RRF_K` | `60.0` | 混合检索 RRF 常数 k（单侧命中也计贡献） | `mem_hybrid_search` 且 `mode=rrf` |
| `RRF_SEM_WEIGHT` | `1.0` | 混合检索 RRF 语义侧权重 | `mem_hybrid_search` 且 `mode=rrf` |
| `RRF_FTS_WEIGHT` | `0.1` | 混合检索 RRF 精确（FTS）侧权重——语义主序干净后 FTS 小幅加成 | `mem_hybrid_search` 且 `mode=rrf` |
| `RETRIEVAL_EXPAND_DEPTH` | `0` | 语义主序的图扩散深度：`0` = 纯语义排序（默认）；`1` = 图邻居参与语义主序（可一键回退） | 检索启用图扩散时 |
| `TIER_FACTS` | `memory,correction,decision,plan,task,review,solution,inspiration,user_intent,character_state` | 归入事实层的记忆 type（逗号分隔）；未登记的 type 一律归事实层 | 检索与注入按 tier 过滤时 |
| `TIER_LOGS` | `record,event,git_commit` | 归入日志层的记忆 type（逗号分隔），默认不进检索与注入池 | 检索与注入按 tier 过滤时 |
| `DEFAULT_TIER` | `facts` | 检索与注入的默认分层；`logs` 只回日志层，空串 = 不过滤（全量历史通道） | 未显式指定 `tier` 时 |
| `MEM_INGEST_MAX_LENGTH` | `50000` | 单条记忆 content 最大字符数，超长拒绝写入 | `mem_ingest` 写入时 |
| `KNOWLEDGE_DIR` | *（可选）* | 知识库根目录（待索引的 Obsidian `.md` 文件） | 使用 `kb_index` / `build_kb_index.py` 时 |

---

## 更换向量模型 / 重嵌全库

### 为什么要重嵌？

不同的 embedding 模型产生不同的向量空间——**跨模型的向量不可混用**。如果切换了 `EMBEDDING_PROVIDER`、`OLLAMA_EMBEDDING_MODEL` 或 `EMBEDDING_MODEL`，必须对库中所有节点重新生成向量（重嵌），否则新旧向量空间互相排斥，检索质量会急剧下降。

### 推荐操作顺序

```bash
# 1. 体检：确认 provider / 模型 / 维度正确，embedding 服务可用
python scripts/palimpsest_cli.py reindex --check

# 2. 预览：查看将要重嵌哪些节点
python scripts/palimpsest_cli.py reindex --dry-run

# 3. 正式执行（默认断点续跑，Ctrl+C 中断后可自动续跑）
python scripts/palimpsest_cli.py reindex --yes

# 4. 验证：跑一次检索冒烟
python scripts/palimpsest_cli.py search "测试" --top-k 3
```

常用选项：

| 选项 | 说明 |
|---|---|
| `--only memory,record` | 只重嵌指定类型 |
| `--skip kb_chunk,novel_chunk` | 跳过指定类型 |
| `--batch 128` | 每 128 个节点打印进度 |
| `--restart` | 忽略断点，从头重嵌 |

### 换维度（新模型输出维度不同）

如果新模型的输出维度与当前库不一致（如从 1024 维换到 768 维），**不能直接重嵌**——必须新建库。流程如下：

```bash
# 1. 导出
python scripts/export_all_data.py

# 2. 重建（新库）
python scripts/rebuild_db.py

# 3. 修改 .env 中对应维度配置
# OLLAMA_EMBEDDING_DIM=768   或   EMBEDDING_DIM=768

# 4. 重建知识库索引
python scripts/build_kb_index.py --full

# 5. 如有小说设定库
python scripts/build_novel_index.py --source  --full
```

---

## 使用

### MCP 工具（15 个）— `mcp_tools/*`

| 工具 | 说明 |
|---|---|
| `mem_search` | 统一检索：记忆 / 知识库 / 两者；可选图谱邻居扩展、域偏置、**域软加权 `domain_boost`**、块级隔离、**记忆分层 `tier`**（默认 `facts` 只回事实层，不含 `record`/`event`/`git_commit`；`""` = 不过滤） |
| `mem_hybrid_search` | 混合检索：FTS5 + 向量；`mode=rrf`（k=60）或 `cascade`；同样支持 `domain_boost` 域软加权与 `tier` 分层；命中标注 `fts_hit` / `sem_hit` |
| `mem_retrieve` | 语义检索，返回 150 字摘要 + 元数据（绝不返回全文） |
| `mem_get_full` | 按 ID 拉取节点完整内容 |
| `mem_ingest` | 写入新记忆——含冲突检测、`REVISED_BY` 版本链、敏感扫描、长度护栏 |
| `mem_recent` | 最近的记忆（新的在前） |
| `mem_review` | 最近 N 天的周期性回顾 + 治理候选（高价值升级 / outdated 清理 / 低价值）；`tier`（默认 `facts`）作用于 `recent_ingests`：`logs` 只回日志层、`""` 不过滤 |
| `mem_stats` | 库级盘点：类型 / 域 / 重要度 / 时间 / 图谱分布 + 热点节点；`tiers` 分节按检索侧 tier 语义分组（facts / logs / unclassified）并输出实际生效的 `TIER_FACTS` / `TIER_LOGS` 清单 |
| `mem_version_history` | 沿 `REVISED_BY` 链展开，查看事实演化过程 |
| `mem_consolidate` | 近似重复检测；dry-run 预览或 apply 合并 |
| `mem_communities` | Leiden 社区发现：把记忆库聚成主题簇，回答「有哪些圈子」 |
| `kb_index` | 将知识库 `.md` 文件索引为 `kb_chunk` 节点（向量化） |
| `kb_search` | 对已索引知识切片的语义搜索 |
| `graph_neighbors` | 从某节点出发对知识图谱做 BFS（关系过滤、深度 1–3、弱边过滤） |
| `mem_link` | 手动创建图边（`RELATED_TO` / `CAUSES` / `REFERS_TO`；默认双向） |

### CLI 命令 — `scripts/palimpsest_cli.py`

| 命令 | 说明 |
|---|---|
| `search "QUERY"` | 统一检索（`--scope all\|memory\|kb`、`--neighbors`、`--block`） |
| `hybrid-search "QUERY"` | FTS5 + 向量混合检索（`--mode rrf\|cascade`） |
| `ingest "CONTENT"` | 写入新记忆（`--importance 0.5`、`--type memory`、`--domain`） |
| `link --source N --target N` | 创建图边（`--relation`、`--one-way`） |
| `index` | 扫描并索引知识库 |
| `graph --id N` | 某节点的图谱邻居（`--depth`、`--relation`、`--min-weight`） |
| `recent` | 最近的记忆（`--limit`、`--domain`） |
| `review` | 最近 N 天的周期回顾（`--tier facts\|logs\|''` 作用于 recent_ingests） |
| `stats` | 库级盘点统计（totals/域/重要度/时间/图谱） |
| `kb "QUERY"` | 知识切片的语义搜索 |
| `consolidate` | 合并预览；`--apply` 执行合并（`--threshold 0.85`、`--max-importance 0.8`） |
| `promote` | 高频记忆升级候选；`--apply` 升权打标（`--days`、`--min-hits`） |
| `ingest-git` | 将近期 git 提交索引为 `git_commit` 节点（幂等） |
| `fts-rebuild` | 重建完整 FTS5 索引 |
| `fts-search "QUERY"` | 原始 FTS5 搜索（trigram 子串） |
| `doctor` | 部署体检：关键文件 / 存储 / FTS / 依赖 / Embedding / 向量维度一致性，失败项给出修复命令（`--json` 机器可读） |
| `startup-check` | 运行启动自检（`doctor` 的轻量子集，失败时退出码 1） |
| `task-archive` | 归档已完成任务；`--apply` 写入 markdown 并删除节点 |
| `reindex` | 全库向量重嵌入（换 embedding 模型后使用；`--check` 体检、`--dry-run` 预览） |

示例：

```bash
python scripts/palimpsest_cli.py ingest "服务监听 8090 端口" --domain work --importance 0.6
python scripts/palimpsest_cli.py search "8090 端口" --neighbors
python scripts/palimpsest_cli.py stats
python scripts/palimpsest_cli.py promote            # 预览高频记忆候选
python scripts/palimpsest_cli.py consolidate        # 预览合并候选
python scripts/palimpsest_cli.py consolidate --apply # 合并
```

### 区块（Blocks）

`block` 是「域分组」概念：图谱按区块隔离，扩散检索只沿同区块的边，防止跨域污染。出厂内置通用区块：`task`（任务）、`kb`（知识库）、`hermes`（助手自身记忆）、`novel`（小说创作设定）、`general`（未分类兜底）。你也可以把自己的 `domain` 当作区块使用（如 `--block myproject`）。`--block` 留空则按全量模式检索。

节点归属统一由 `payload.domain` 字段表达。写入记忆时通过 `--domain X` 或 `mem_ingest(domain=...)` 指定区块；`kb` 类型节点由知识库索引自动设置为 `kb`。

### REST API — `main.py`，端口 8090

|

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [JiaY-77](https://github.com/JiaY-77)
- **Source:** [JiaY-77/Palimpsest](https://github.com/JiaY-77/Palimpsest)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-jiay-77-palimpsest
- Seller: https://agentstack.voostack.com/s/jiay-77
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
