内容
# Synapse
AI 编程代理的自托管长期记忆。Synapse 捕获 Claude Code(和 Cursor)会话记录,对它们进行结构化处理,形成可检索的片段和知识图谱,并通过 MCP 将两者作为一个排名结果返回。
**它解决的问题:** Claude Code 会话在重启后没有记忆。每个新会话都是冷启动的。Synapse 索引了过去会话中发生的事情,以便新的会话可以回忆起几周前决定、构建和尝试过的内容。
您的记忆数据和整个服务堆栈都运行在您自己的硬件上 - 没有第三方服务保存您的历史记录。默认管道确实会调用付费 API:用于嵌入和重新排名的 Voyage,以及用于提取的 Anthropic(Claude 订阅令牌,或 `ANTHROPIC_API_KEY`,或任何 OpenAI 兼容的端点)。提取成本随着您摄入的内容而增加 - 补充几个月的历史记录是一笔较大的开支。捆绑的 `local-inference` 配置文件和本地 LLM 运行,外部账户为零;检索质量在 Voyage 堆栈上进行了调整,因此预计会有一定程度的下降。
## 演示
使用存储库中捆绑的示例记录([`docs/example-transcript.json`](docs/example-transcript.json))进行召回流程(以确保完全重现):一个新会话提出问题,Synapse 从早期会话中回答,将知识图谱事实与原始记录融合。

## 基准测试
在 [LongMemEval-S](https://github.com/xiaowu0162/LongMemEval) 上达到 **83.6%**(500 个问题,官方 gpt-4o 读取器 + 评判器),使用部署的管道端到端 - 没有基准侧内容注入。最强类别:时间推理 82.0%,知识更新 83.3%,单会话召回 96-100%。作为参考,Zep/Graphiti 报告 71.2%,Mastra 在同一读取器类别上报告 84.2%。
## 工作原理
```
Claude Code 会话
│ 停止钩子发送有界记录尾部
▼
POST /ingest ──► 记录(Postgres)──► 滑动窗口块 ──► 知识图谱事实提取(Haiku)
│
会话询问 synapse:recall ──► MCP 服务器融合,同时进行: ▼
• 重新排名的记录腿(BM25 + 向量) 知识图谱
• 知识图谱事实腿(向量 + BM25 + 1 跳) (Postgres)
• 网络研究腿
• 双时间历史腿(“当时 vs 现在”)
▼
紧凑的排名结果
```
1. `停止` 钩子将每个会话记录的尾部推送到 `/ingest` - detached,永远不会阻塞该回合。
2. `/ingest` 通过稳定的每回合 ID 进行重复数据删除 - 并且跳过已经存储在项目中的字节相同的回放(重试/重新导入保护)- 然后将真正新的回合作为 **记录**。
3. 后台轮询器将记录分组为重叠的 **块**,并从中挖掘实体和双时间事实 - **知识图谱**,在 Postgres 中。
4. `recall()` 并行运行其分支(重新排名的记录,知识图谱事实,时间线事件,网络研究,首选项,事实历史)并返回一个紧凑的排名结果。
架构,设计决策和背后的测量:
[ARCHITECTURE.md](./ARCHITECTURE.md).
## 盒子里有什么
- **情景记忆** - 每个情景记忆由一个深度抓取(每个分支 100 个候选)+ 一个 Voyage 交叉编码器重新排名提供。适用于广泛和精确查询的检索工作马。
- **知识图谱** - 实体和 Postgres 中的双时间 `RELATES_TO` 事实边;关系/多跳专家。事实永远不会被删除:矛盾使旧边失效并写入新边,从而实现“变化”历史分支。
- **时间线** - 一个仅追加的已记录事件日志(“已发货 X”,“已决定 Y”),由 LLM 门户挖掘并由 git 提交提供,用于回答“何时/按什么顺序”的问题。重新讲述的发生确认合并到其现有行(LLM 读取两个源回合,两个呈现顺序必须同意)而不是复制;每个事件携带 `personal`/`technical` 域标签,因此个人范围查询排除工作噪音;并且在引用的第三方材料(某人的电子邮件,粘贴的记录,文章)内叙述的故事永远不会被记录为用户自己的。
- **网络研究捕获** - WebFetch/Exa/Firecrawl/search 结果被捕获和嵌入,以便过去的研究是可召回的。
- **MCP 服务器** - 流式 HTTP 的 FastMCP(工具列表 [如下](#mcp-工具))。
- **Claude Code 插件** - 摄入 + 回忆线路,以及每晚的 dream→skills 通道,用于挖掘您的记录以维护自我改进的技能库,并具有可选的双向技能同步(`SYNAPSE_SKILLS_SYNC=1`)。参见 [plugin/README.md](./plugin/README.md).
## 堆栈
- **存储:** PostgreSQL(ParadeDB 镜像)用于所有内容 - 记录,块,队列,网络存储和知识图谱。
- **向量搜索:** pgvector `halfvec` HNSW(默认 2048 维;宽度在首次启动时通过 `SYNAPSE_EMBED_DIMS` 配置文件提供)。
- **全文搜索:** ParadeDB `pg_search`(BM25),通过倒数排名融合与向量融合。
- **嵌入 + 重新排名:** 默认情况下使用 Voyage AI(`voyage-4-large`,2048 维,和 `rerank-2.5-lite`)。可插拔:任何 OpenAI 兼容的 `/embeddings` 端点 + 任何 TEI/Infinity/Cohere 形状的 `/rerank` 服务器,或捆绑的 `local-inference` 配置文件(参见 `.env.example`)。已发布的检索质量在 Voyage 堆栈上进行了测量,重新排名分支很重要 - `SYNAPSE_RERANK_PROVIDER=none` 将召回降级为仅融合排序。
- **提取 LLM:** 默认情况下使用 Claude Haiku 4.5,通过 `claude-agent-sdk` 使用 Claude 订阅令牌或 `ANTHROPIC_API_KEY`。将 `SYNAPSE_LLM_PROVIDER=openai` 设置为指向任何 OpenAI 兼容的 `/chat/completions` 端点(OpenRouter,本地模型等)。
- **MCP:** FastMCP,流式 HTTP,端口 8765。
- **语言:** Python 3.12,使用 `uv` 管理。
> 早期版本将图存储在 FalkorDB 中。现在图位于 Postgres 中(`ingestion/kg_client.py`);FalkorDB 已被停用。
## 快速入门
整个安装,端到端 - 克隆,配置,`compose up`,连接插件:

单个盒子,所有内容本地:
```bash
git clone <此仓库> synapse && cd synapse
cp .env.example .env # 然后填写下面所需的数值
docker compose up -d --build # 构建镜像,启动 Postgres + 轮询器 + MCP 服务器
```
镜像从公共基础本地构建(无需注册身份验证),并且模式在 Postgres 首次启动时自动应用。
`.env` 中所需的配置:
- `SYNAPSE_DB_PASSWORD` - 绑定 Postgres 的密码(必须与 DSN 中的密码匹配)。
- `SYNAPSE_DB_URL` - Postgres DSN;`.env.example` 默认值只需要填写密码。
- `VOYAGE_API_KEY` - Voyage AI 密钥(嵌入和重新排名)。如果您配置了替代后端(`.env.example` 中的 `SYNAPSE_EMBED_*` / `SYNAPSE_RERANK_*`),则不需要此密钥。
- `CLAUDE_CODE_OAUTH_TOKEN` **或** `ANTHROPIC_API_KEY` - 提取 LLM 的身份验证(订阅令牌优先,如果两者都设置了)。
可选旋钮(`SYNAPSE_INGEST_TAIL`,`POLL_INTERVAL_SECONDS`,召回服务调优等)列在
[ARCHITECTURE.md §13](./ARCHITECTURE.md#13-configuration) 中。如果默认端口被占用,请在 `.env` 中设置 `MCP_PORT` 和/或 `POSTGRES_HOST_PORT`。设置 `LOGFIRE_TOKEN` 以将跟踪流式传输到 [Pydantic Logfire](https://logfire.pydantic.dev);留空则完全关闭遥测。
然后在每个 Claude Code 机器上安装 [插件](./plugin/README.md),以便会话自动馈送和查询服务器。存储库自带其自己的市场清单,因此无需发布任何内容:
/plugin marketplace add kraft87/synapse
/plugin install synapse@synapse
安装提示您的 `SYNAPSE_URL`(本地快速启动为 `http://localhost:8765`)和可选令牌,然后运行 `/reload-plugins`。使用 `! synapse-import` 一次导入数月的过去会话(参见
[导入现有会话](#导入现有会话))。
### 验证您的安装
```bash
# 1. 将测试记录(≥4 回合)发送到服务器:
curl -sX POST localhost:8765/ingest -H 'Content-Type: application/json' \
-d @docs/example-transcript.json
# 2. 等待一个轮询周期(最多 ~5 分钟),然后请求它返回:
curl -sX POST localhost:8765/recall -H 'Content-Type: application/json' \
-d '{"query": "what caching layer did we pick for the demo app search service"}'
```
两个时间期望看起来像 bug 但实际上不是:**记录** 在摄入后几秒钟内出现,但 **知识图谱事实** 从 4 回合的滑动窗口中提取 - 会话中少于 4 个人类回合会产生记录和一个 *空图*。并且提取在一个轮询周期上运行(`POLL_INTERVAL_SECONDS`,默认为 300),因此第一个事实在摄入后几分钟内落地,而不是立即落地。
### 导入现有会话
新的安装不必从冷启动开始。如果您已经在使用 Claude Code,那么数月的记录已经位于 `~/.claude/projects` 中 - 导入它们一次,`recall()` 便知道您第一天的历史记录:
```
! synapse-import # 在 Claude Code 会话内(插件将其放在 PATH 上)
```
它提供了一个可选的日期范围(按每个文件的最后活动日期),打印一个总结(文件数量,大小,估计回合数),并在发送任何内容之前请求确认 - 导入运行配置的 LLM 的 KG 提取,对于每个新回合,消耗的订阅使用量或 API 积分大致与回合数成比例,因此限定首次导入的日期范围可以限制支出。服务器通过 `span_id` 去除重复记录,因此 Ctrl-C 和重新运行始终是安全的:中断的导入从停止的地方继续。
也可以导入 Cursor 历史记录,但目前仅作为服务器端开发路径 (`python -m ingestion.cursor_sqlite_backfill`)。
### 升级
首次启动初始化永远不会在现有数据卷上重新运行。要将现有数据库更新到最新状态,请在其中运行 [`scripts/apply_schema.sh`](./scripts/apply_schema.sh)(迁移顺序的唯一源)- 请参阅脚本的头部以了解注意事项。
每个服务在启动时都会验证数据库模式是否与代码匹配(脚本会标记应用的版本;不匹配会拒绝启动并提供说明而不是在请求中失败)。因此升级顺序是:拉取,运行 `apply_schema.sh`,重启。`SYNAPSE_SCHEMA_CHECK=0` 跳过检查。
版本有标签(`v0.8.1` 等)。`main` 始终可发布,但要检查已知的良好版本,请检出最新的标签;需要迁移或重命名环境变量的更改会附带说明。
## MCP 工具
五个工具,按服务器注册它们的顺序列出(刻意 - 工具列表位置影响模型选择工具)。该板故意不是工具:插件的 SessionStart 钩子通过 `GET /context` 注入它,而列出的板工具会邀请模型重复注入一个块(Hermes 模式 - 当注入涵盖读取时,发送读取工具)。
- `recall(query, project=None, session_focus=None, group_id="technical")` - 主要检索工具:重新排名的记录 + 知识图谱事实 + 时间线 + 网络 + 历史记录。提供的记录段落带有 `role` 标签(`user` / `assistant` / `mixed`),因此调用者可以将人为陈述的事实赋予比代理自己的输出更高的权重。
- `fetch(ids)` - 将 id 扩展为完整记录:从召回结果的记录 id(`e:N`)和笔记 id(`n:N`)- 会话启动板块的 `n:ID` 行通过此工具将 ID 解析为其完整笔记正文。混合列表是可以的;未知 id 以 `skipped` 返回,单个调用最多可扩展 20 个 id。
- `remember(content=None, hook=None, body=None, type="project", project=None)` - 编写一个精选的记忆。优先形式传递 `hook`(一个一句话索引条目,约 120 个字符)+ `body`(完整自包含的笔记)和 `type`(`user` / `feedback` / `project` / `reference`);遗留的仅 `content` 形式仍然有效,并从第一个句子派生钩子。两种形式还存档文本为记录,并排队知识图谱提取。笔记存储在一个专用存储中,该存储在写入时被协调:一个重述现有笔记的新笔记会更新它, 而一个与之相矛盾的笔记会取代旧笔记,同时保留其谱系 - 因此精选集保持小且当前,而不是积累重复项。
- `recall_timeline(query=None, since=None, until=None, group_id=None)` - 用于“何时/按什么顺序”的问题的已记录事件;`group_id="personal"` 将范围限定为生活事件,排除技术/工作噪音。
- `recall_episodes(query, project=None, limit=5)` - 原始记录钻取。
还存在一个隐藏的工具,但不显示在工具列表中:`issue_machine_token`,身份验证门控管道 `synapse login` 调用以获取机器承载令牌。它通过名称保持可调用(`tools/call`)- 它只是从未竞争模型的注意力。
## 身份验证
MCP 服务器同时支持两种身份验证模式,因此机器和 claude.ai 网络连接器可以共享一个服务器:
- **机器承载令牌** - 静态令牌,插件的钩子发送到 `/ingest`,`/recall` 和 `/mcp`。无头盒直接设置它。
- **GitHub OAuth** - 用于 claude.ai 网络连接器和插件的 `synapse-login`。登录默认使用设备流(RFC 8628):在任何设备上批准一个短代码 - 无需相同主机浏览器 - 然后机器令牌为您存储( `--browser` 保留旧的循环流动)。访问权限被限制为 GitHub 用户的允许列表(`ALLOWED_GITHUB_USERS`);GitHub OAuth 应用程序需要启用“启用设备流”。
如果没有配置令牌,服务器将开放运行,这对于纯本地实例来说是可以的。可以通过 Cloudflare 隧道将中心实例暴露给 claude.ai;MCP 服务器自己处理身份验证,因此不需要单独的代理。
## 状态
单用户,自我托管,并在 homelab 中积极使用。它尚未打包为一键式产品,但服务器、插件和安装路径都已端到端工作。