Palimpsest

mcp
Security Audit
Warn
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.

SUMMARY

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

README.md

Palimpsest

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

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

Palimpsest:拉丁语,原指「重写的羊皮纸」——旧字迹被覆写抹去,却又在岁月里重新透出。

我们把这个意象搬进记忆里:新的事实覆盖旧的事实,但旧迹永不真正丢失——每一次改写都通过一条有迹可循的 版本链REVISED_BY)连接,新旧记忆可查可溯。

Version
Python
License
Storage
LLM
CI

中文 | 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.yamlkind=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_compresscontext.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 对比只重建变化文件)。


快速上手

安装(通用)

# 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=openaiEMBEDDING_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 留空 = 自动探测(推荐);显式写 ollamaopenai 可强制指定。

启动

# 推荐:部署体检(每个失败项都会打印对应的修复命令)
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 后端:deepseekollama 需要 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_searchmode=rrf
RRF_SEM_WEIGHT 1.0 混合检索 RRF 语义侧权重 mem_hybrid_searchmode=rrf
RRF_FTS_WEIGHT 0.1 混合检索 RRF 精确(FTS)侧权重——语义主序干净后 FTS 小幅加成 mem_hybrid_searchmode=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_PROVIDEROLLAMA_EMBEDDING_MODELEMBEDDING_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 Xmem_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)

No results found