Palimpsest
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Local-first, battle-tested, memory that never disappears. Hybrid vector search + knowledge graph + full-text retrieval for AI agents.
Palimpsest
本地优先的长期记忆系统 · AI 助手的跨会话记忆底座
Local-first, battle-tested, memory that never disappears.
Palimpsest:拉丁语,原指「重写的羊皮纸」——旧字迹被覆写抹去,却又在岁月里重新透出。
我们把这个意象搬进记忆里:新的事实覆盖旧的事实,但旧迹永不真正丢失——每一次改写都通过一条有迹可循的 版本链(
REVISED_BY)连接,新旧记忆可查可溯。
中文 | English
一句话介绍
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) - 🧹 容量合并与记忆盘点 ——
mem_consolidate把近似重复节点合并(相似度 ≥ 0.85、保护高价值记忆);mem_stats统一盘点库内分布(类型/域/重要度/时间/图谱/热点/弱敏感标记数),回答「库里有什么」 - ⏫ 高频记忆自动升级 —— 检索命中自动计数(
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=""显式回到全量(历史追溯通道)。层清单由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/,含 plugin.yaml(kind=standalone)与两个 hooks:on_session_end(会话结束提炼要点)与 on_pre_compress(压缩前图谱提炼)。
部署(把插件复制到 Hermes 插件目录,然后一行一件激活):
# 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 当「文件」看待,而是当作知识源。读取分五步:
- Vault 目录即知识源 —— 递归扫描
KNOWLEDGE_DIR下的全部.md(自动跳过.obsidian等配置目录),每个笔记就是一个待处理文档。 - 按 Markdown 标题智能切片 —— 以
##/###为边界切成 300~800 字符的块,块内原样保留[[双链]],让「哪篇关联哪篇」的上下文不丢。 - 向量化入库 —— 每个切片经 embedding 编码,作为
kb_chunk节点(domain=kb)写入存储,构成可语义检索的知识资产。
想自己实现? 这套流程的骨架很简单:一个向量库(sqlite-vec / chroma 皆可)+ 一个 embedding 服务就能复刻。真正的设计点有两个:
- 切片粒度 —— 太粗检索不准、太碎丢上下文。
- 双链保留 —— 让
[[A]]⇄[[B]]的关系进入检索结果,而不是只在正文里躺着。
本思路的现成实现即 scripts/build_kb_index.py(全量 --full / 增量默认,增量按 mtime 对比只重建变化文件)。
快速上手
安装(通用)
# 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、想最快跑起来)
# 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(隐私优先,数据不出本机)
# 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可强制指定。
启动
# 推荐:部署体检(每个失败项都会打印对应的修复命令)
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。
# 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 配置):
{
"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,必须对库中所有节点重新生成向量(重嵌),否则新旧向量空间互相排斥,检索质量会急剧下降。
推荐操作顺序
# 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 维),不能直接重嵌——必须新建库。流程如下:
# 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 <vault路径> --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 清理 / 低价值) |
mem_stats |
库级盘点:类型 / 域 / 重要度 / 时间 / 图谱分布 + 热点节点 |
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 天的周期回顾 |
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 预览) |
示例:
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
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/ |
服务信息 + 版本 + 端点索引 |
GET |
/export |
导出记忆为分页 JSON 快照(默认每页 100 条,上限 500) |
GET |
/summary |
人类可读的记忆摘要(事件 / 角色状态 / 计划) |
GET |
/memory/{id} |
读取单节点完整 payload |
POST |
/report |
基于当前存储生成 LLM 分析报告(Prompt 面向小说创作 / 角色扮演场景,不是通用摘要) |
DELETE |
/memory/{id} |
删除记忆节点(FTS 索引同步) |
PUT |
/memory/{id} |
更新节点 payload(合并语义:只改传入字段,其余保留;自动同步 FTS) |
PATCH |
/memory/{id} |
部分更新节点 payload(与 PUT 同合并语义,REST 语义更精确) |
PATCH |
/memory/{id}/vector |
更新节点的向量(维度需一致) |
POST |
/mem/search |
统一检索 |
POST |
/mem/hybrid-search |
FTS5 + 向量混合检索 |
POST |
/mem/ingest |
写入新记忆(含冲突检测 + 敏感扫描) |
POST |
/mem/link |
创建图边 |
POST |
/mem/stats |
库级盘点统计 |
POST |
/graph/neighbors |
某节点的图谱邻居 |
POST |
/graph/communities |
Leiden 社区发现 |
若设置了
PALIMPSEST_API_KEY,除/外所有端点要求Authorization: Bearer <key>或X-API-Key: <key>。
示例:
curl -X POST http://127.0.0.1:8090/mem/search \
-H "Content-Type: application/json" \
-d '{"query": "架构", "scope": "all", "top_k": 5}'
测试
# 在仓库根目录执行
python -m pytest tests/ -v
测试套件覆盖核心闭环:写入 → mem_search 命中 → mem_get_full 全文往返;图谱建边 → graph_neighbors / mem_communities;敏感扫描拒绝含密钥内容;混合检索 FTS 侧命中标记;冲突检测 / 版本链与 outdated 检索语义;consolidate / promote 干跑与幂等;PUT/PATCH 部分更新保留字段;并发与失败路径(脏 payload、embedding 不可用等)。
tests/conftest.py 在一切导入之前将 DB_PATH 重定向到 临时数据库(并隔离知识库)—— 测试套件永远不碰生产数据库;使用确定性 fake embedder,无需在线 Ollama 即可全绿。
应用层压测
scripts/rest_stress.py 对 REST 服务跑端到端压测,覆盖 6 类真实使用场景:高频检索 / 批量写入 / 图谱建边与扩散 / 边界输入(空内容、超长、坏 JSON、负 top_k 等)/ 读写混合长压 / 写入后召回正确性。
# 1. 起一个测试实例(独立库 + 独立端口;脚本会写入数据,不要指向生产库)
DB_PATH=/tmp/stress.db python -m uvicorn main:app --port 8091
# 2. 压测(--quick 为快速档)
python scripts/rest_stress.py --base http://127.0.0.1:8091 --seeds 200 --out report.json
输出 JSON 报告:每个场景的 qps、p50 / p95 / p99 延迟与错误率;另含边界用例的 HTTP 状态与正确性抽查结果(唯一标记能否召回、重复写入是否触发冲突检测)。随机种子固定(42),同样入参可复现。
检索质量评测
eval/ 是一套离线检索质量评测框架:从库内真实节点反推生成题集,对四条检索路径(fts / vec / rrf / cascade)计算 Recall@K、MRR@K、nDCG@K,让「改检索」的收益与回归可量化,而不是只凭主观感受。
# 生成题集(需要 DEEPSEEK_API_KEY;--dry-run 只看分层分布,不调 API)
venv/Scripts/python.exe eval/gen_eval_set.py --dry-run
# 跑评测(默认 4 种模式、top-10;可选 --modes rrf,cascade / --limit 20)
venv/Scripts/python.exe eval/run_eval.py
只读保护:所有脚本启动时先把真实库连同 sidecar 文件复制到 eval/.tmp/,全部读写落在副本上,并在跑前跑后对真实库文件算 SHA256 写进报告自证。题集与指标定义见 eval/README.md。
配套工具:
scripts/retrieval_probe.py—— 检索体检探针:固化已验证查询,输出 top-1 命中率与延迟基线,用于跨版本 / 跨 embedding 模型快速对比scripts/prod_entrypoint_check.py—— 生产入口复测:直接调检索的真实实现,在两种配置下各跑一遍题集,证明配置改动确实在生产链路上生效(只读,真库 SHA256 前后校验)scripts/ab_snapshot_*.py—— 单变量 A/B:从同一份真库快照复制两份副本,只改其中一份的变量,逐题归因谁赢谁输、赢在哪一层
项目结构
Palimpsest/
├── README.md # 中文主版
├── README_EN.md # 英文版
├── CHANGELOG.md # 版本历史
├── CONTRIBUTING.md # 贡献指南
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── LICENSE
├── .env.example # 配置模板(带注释)
├── .gitignore
├── requirements.txt
├── requirements-dev.txt # 开发依赖(ruff / mypy / pytest-cov)
├── config.py # 环境变量驱动配置
├── main.py # FastAPI REST 入口 (:8090)
├── mcp_server.py # MCP stdio 入口 (FastMCP)
├── dashboard.html
├── docs/ # RELEASING.md(发版流程)/ HERMES_INTEGRATION.md / refactor_plan.md
├── core/ # 共享引擎,无框架依赖
│ ├── trivium_store.py # TriviumDB 封装(向量+图谱+文档)
│ ├── conflict.py # 冲突检测 / 版本链(三层防误标)
│ ├── consolidator.py # 近似重复记忆合并
│ ├── stats.py # 库级盘点统计(mem_stats 核心)
│ ├── promoter.py # 高频记忆自动升级(hit_count → promote)
│ ├── fts_index.py # FTS5 全文索引(trigram, fts.db)
│ ├── reporting.py # LLM 生成的记忆报告
│ ├── secret_scan.py # 写入前敏感扫描(10 条规则)
│ ├── startup_check.py # 启动自检
│ ├── task_archive.py # 完成任务自动归档
│ ├── utils.py
│ └── version.py # 版本号来自 git tag(兜底 dev)
├── mcp_tools/ # 15 个 MCP 工具(MCP/REST/CLI 共用)
│ ├── __init__.py
│ ├── _common.py # 共享 store / mcp / 序列化助手
│ ├── memory.py # mem_* 工具
│ ├── kb.py # kb_index / kb_search
│ ├── graph.py # graph_neighbors / mem_link / mem_communities
│ ├── consolidate_tool.py # mem_consolidate
│ └── stats_tool.py # mem_stats
├── scripts/ # 运维工具
│ ├── palimpsest_cli.py # CLI(search/ingest/…/stats/promote)
│ ├── dashboard.py # 监控面板 (:8010)
│ ├── build_kb_index.py # 知识库分块 & 向量化
│ ├── build_novel_index.py # 小说设定库整文件入库(--source 指定 vault)
│ ├── link_novel_relations.py # 小说人物关系批量建边(dry-run/--apply)
│ ├── check_fts_consistency.py # FTS 内容级对账
│ ├── export_all_data.py # 只读 JSON 备份导出
│ ├── graph_edges.py # 持久化知识图谱边
│ ├── migrate_domain.py # 历史字段迁移(character_name → domain)
│ ├── rebuild_db.py # 从导出快照重建数据库
│ ├── reindex.py # 全库向量重嵌入(换模型后一键重建)
│ ├── start_rest.vbs # Windows 隐藏窗口 REST 启动器
│ ├── rest_stress.py # REST 应用层压测(6 场景端到端)
│ ├── retrieval_probe.py # 检索体检探针(top-1 命中率 + 延迟基线)
│ ├── prod_entrypoint_check.py # 生产入口复测(真实实现 + 双配置对照)
│ ├── ab_snapshot_*.py # 单变量 A/B(库副本 + 逐题归因)
│ └── tdb_stress/ # TriviumDB 压力测试(存储层)
├── eval/ # 离线检索质量评测(题集 / 4 模式 / Recall·MRR·nDCG)
├── hermes-plugin/ # Hermes 双插件(Memory Provider + Context Engine)
├── tests/ # pytest(conftest 隔离 + fake embedder,无需联网)
└── data/ # 运行时数据库(gitignore)
├── mh_memory.db # 主 TriviumDB 存储
└── fts.db # FTS5 全文索引
开发指南
- 虚拟环境: 每个 checkout 单独建一个(
python -m venv venv)并pip install -r requirements.txt。 - 新增工具: 在
mcp_tools/内用共享的@mcp.tool()装饰器注册——它会立即同时出现在 MCP 服务、REST 层与 CLI 中。 - 新增核心模块: 保持
core/不引入 FastAPI/MCP;经由mcp_tools/与main.py消费。版本号由core/version.py从 git tag 获取。 - 改了 schema? 重建 FTS 索引(
fts-rebuild)与知识库索引(build_kb_index.py);导出 / 重建工具在scripts/。 - 测试: 保持隔离——绝不让测试指向生产数据库。
提交 PR 前请先过质量门禁(与 CI 的 lint / typecheck / test 三个 job 一一对应):
python -m pytest tests/ -q # 测试(CI 上跑 3.10 / 3.11 / 3.12)
ruff check . # 静态检查(规则集钉在 pyproject.toml 的 [tool.ruff])
mypy # 类型检查(当前覆盖 core/,非严格起步)
python -m pytest --cov=core --cov=mcp_tools -q # 覆盖率基线(暂不设门槛,用于定位缺口)
开发依赖用 pip install -r requirements-dev.txt 安装;ruff 版本与 CI 对齐,避免门禁含义随版本漂移。
文档与代码的一致性由 scripts/readme_check.py 检查(MCP 工具清单 / CLI 子命令 / REST 路由 / 配置项键名与默认值 / 文件引用 / 行内代码配对)。CI 的 docs job 会以 --strict 跑它,本地跑法相同:
python scripts/readme_check.py --strict
版本发布遵循 语义化版本,流程见 RELEASING.md,历史见 CHANGELOG.md。
License
MIT © JiaY-77
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found