关于 tether
tether 是面向个人 Agent 的本地优先 MCP 记忆服务,使用 SQLite 保存持久笔记。它支持项目感知检索和可选的跨设备同步,并可在扩展功能不可用时退回关键词搜索。
主要功能
- 01记忆、检索、关联和遗忘笔记
- 02SQLite 存储与可选设备同步
- 03关键词与语义混合搜索
- 04基于使用图的关联检索
- 05项目感知的启动索引
适用场景
- 01在 Agent 会话间延续个人偏好
- 02通过 libSQL 或 Turso 跨设备共享记忆
- 03编码时检索相关项目笔记
MCP 相关
它向兼容 MCP 的 Agent 提供记忆操作:记忆、检索、关联和遗忘。自动加载的启动索引也会呈现简明记忆上下文。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx tether-memoryTETHER_DBPath to the SQLite memory file. Defaults to the platform data dir (e.g. ~/.local/share/tether/memory.db).TETHER_SEMANTICSet to 0 to force keyword-only recall. Semantic recall needs the [semantic] extra and is on by default when available.TETHER_SYNC_URLOptional libSQL/Turso primary URL to sync memory across devices. Leave unset for local-only.TETHER_SYNC_TOKEN密钥Auth token for TETHER_SYNC_URL.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
4收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| body | string | 是 | |
| tags | string | 否 | |
| type | string | 是 | |
| links | any | 否 | |
| title | string | 是 | |
| crystallizes | any | 否 |
详细介绍
tether
一个跨设备的个人 Agent 共享记忆层。 tether 是一个由本地 SQLite 文件提供支持的 MCP 服务。任何兼容 MCP 的 Agent 都可以 remember、recall、link 和 forget 持久化笔记——关于你、你的项目和偏好的事实——让上下文伴随你,而不是随着每次会话结束而消失。
它仅在本地运行,无需任何配置。将它指向托管的 libSQL/Turso 主库后,同一个文件就会成为嵌入式副本,近乎实时地在所有设备间同步你的记忆。
为什么
不久的将来,个人 Agent 将运行在许多设备上——笔记本电脑、台式机和手机。要让它们感觉像是同一个助手,而不是几个健忘的助手,记忆就必须成为伴随你的底层基础:可从每台设备和任何 Agent 读取和写入,而不是被隔离在单个工具中。
tether 就是这样的基础。它有意设计成一个便利层——存在时能让 Agent 更有用;出现故障时也绝不会妨碍 Agent 的工作。
状态
v0.6.1。 核心功能(四种记忆操作 + 启动索引 + FTS5)已在 v0.1 发布;此后,recall 增加了语义检索功能、整合功能、关联使用图、自组织存储和可选的结晶功能——每项都是增量式功能,且都能平稳退化为纯关键词检索。以下所有功能均已实现。
设计和原理始于
docs/superpowers/specs/2026-07-03-tether-design.md;
关联核心、种子主导检索、自组织存储(Tier B1)和结晶(Tier B2)各有独立的设计文档,位于
docs/superpowers/specs/,对应的计划则位于
docs/superpowers/plans/。
设计概览
- 四种记忆操作:
remember·recall·link·forget。(启用结晶功能会增加一个反思控制工具dismiss_cluster——它不属于记忆操作。) - 写入时执行 upsert,避免存储中逐渐充斥近似重复项。
- 丰富的检索结果(id、type、title、body、tags、
updated_at,以及说明每条结果为何出现的via凭据),让 Agent 能判断信息是否过时,并引用要更新的内容。body是以查询为中心的摘录,而非完整记忆——参见摘录。 - 自动加载的启动索引——每条记忆占一行的精简列表,会在每次会话中呈现给 Agent,因此即使 Agent 没想到要搜索,记忆也能发挥作用。
- 本地优先,同步可选——未配置后端时,本地路径不受影响;功能退化时绝不抛出异常。
- 混合搜索,叠加关联能力——融合 FTS5 关键词结果和本地静态嵌入,再由使用图(显式链接、学习到的共同检索关系、语义邻居)补充相关记忆。每一层都是增量式的,且都能退化为纯关键词检索。
- 安全支持并行工具调用——Agent 同时发起多个
remember/recall调用时,结果仍然原子且报告准确;参见性能与持久性。
安装
需要在 Linux、macOS 或 Windows 上使用 Python ≥3.10。
使用 uv 将其注册到 Claude Code:
claude mcp add tether -- uvx tether-memory
……也可以先安装:
pip install tether-memory
claude mcp add tether -- tether-memory
(该软件包在 PyPI 上的名称是 tether-memory——tether 作为常见品牌名称已被占用。claude mcp add tether -- ... 中的 tether 只是 Claude Code 用来指代此服务的标签,无需与已安装的命令匹配。)
也可以将其添加为 Claude Code 插件(此仓库同时也是一个仅含一个插件的市场;该插件会运行 uvx --from 'tether-memory[semantic]',因此也会附带语义回忆功能):
/plugin marketplace add sidyellur/tether
/plugin install tether@tether
它也已在 MCP 注册表中列出,mcp-name 为 io.github.sidyellur/tether。
默认情况下,记忆存储在本地 SQLite 文件中:~/.local/share/tether/memory.db;在 Windows 上为 %LOCALAPPDATA%\tether\memory.db(可通过 TETHER_DB 覆盖任一路径,或设置 XDG_DATA_HOME,该变量在所有平台上均有效)。无需账户,无需联网——这就是一款供单台机器使用的完整工具。
项目感知
tether 知道自己正在为哪个项目提供服务:Claude Code 会在每个 MCP 服务的环境中设置 CLAUDE_PROJECT_DIR,tether 会将该目录的名称作为项目名称(可通过 TETHER_PROJECT 覆盖或禁用)。这会自动实现以下三项功能,无需配置:
- 启动索引优先显示当前项目。 自动加载的索引会先显示
# This project (<name>)部分,然后是# Everything else,因此 Agent 开始会话时就能看到当前所在仓库的决策和注意事项,而不是你最近在任何地方处理的内容。 - 工作记忆会自动添加标签。
project、feedback和reference记忆会获得proj:<name>标签,除非 Agent 自行传入proj:标签。user记忆与你本人有关,而非工作内容,因此保持全局可用。该标签是普通标签:recall(tags="proj:<name>")可确定性地列出某个项目的记忆。 - 相近结果中优先显示当前项目的内容。 带有当前项目标签的匹配项,会比同等质量的其他项目匹配项靠前几位;但绝不会超过明显更好的匹配项,而且未加标签的记忆既不会加分,也不会扣分。
| 变量 | 默认值 | 作用 |
|---|---|---|
TETHER_PROJECT | CLAUDE_PROJECT_DIR 的基本名称 | 显式指定项目名称;off 可禁用项目感知 |
不会回退到工作目录:在 Claude Code 之外(或设置 TETHER_PROJECT=off 时),tether 的行为与之前完全相同。
跨设备同步(可选)
将 tether 指向一个 Turso / libSQL 数据库,本地 文件就会成为嵌入式副本——读取速度如同本地,写入会传播到你的其他设备。安装额外依赖并设置两个环境变量:
pip install 'tether-memory[sync]'
export TETHER_SYNC_URL='libsql://<your-db>.turso.io'
export TETHER_SYNC_TOKEN='<your-auth-token>'
如果无法连接后端,tether 会记录 sync offline,并继续使用本地文件工作。
同步采用单主设计:每次写入都会发送到托管主库,副本只负责拉取,因此曾离线的设备会在下次拉取时追上进度(包括 forget 产生的墓碑),且永远不会让其他设备已忘记的记忆复活。唯一的缺口是:降级期间的写入只会进入该设备的本地文件,之后不会合并回去(#99 正在跟进协调机制)。
写入会立即推送。读取也会拉取数据,但经过防抖处理,最多每隔
TETHER_SYNC_READ_INTERVAL 秒一次(默认值为 30)——因此,即使设备只是查询内容,也能看到其他设备写入的内容,而不会一直停留在启动时的状态,直到它恰好进行写入。读取路径的拉取限制比写入路径严格得多:如果后端速度较慢,检索会返回本地数据,拉取结果留待下次读取,而不会让你等待。
| 变量 | 默认值 | 作用 |
|---|---|---|
TETHER_SYNC_READ_INTERVAL | 30 | 读取路径拉取之间的秒数;0 = 仅在写入时同步 |
TETHER_DEVICE_ID | 主机名 | 记录在每条记忆上的设备 ID(也是默认的 TETHER_AUTHOR) |
关于副本,有一点需要了解:libSQL 会将每次写入转发到托管主库;启用关联图(默认开启)时,recall
也会写入——它会记录哪些内容是在一起检索的,让记忆随时间建立关联。在副本上,这会让每次检索除了本地搜索外,还多出几次网络往返。如果你认为学到的关联不如其他因素重要,可以设置 TETHER_ASSOC=0,让 recall 恢复为只读。
关键词搜索
关键词分支使用 SQLite FTS5 对标题、正文和标签进行全文搜索,并按 bm25 排序。可以用自然语言提问:只要记忆中包含查询词的一部分,就会命中;包含的查询词越多,排名就越高。因此,“我们如何运行集成测试?”可以找到写着“pytest 运行测试”的笔记(常见功能词会被忽略)。索引会对英文单词进行词干提取,因此 tests
能匹配 test,deciding 能匹配 decided。词干提取仅适用于英语;对于其他语言的存储,可将其关闭,tether 会在下次启动时重建索引。
| 变量 | 默认值 | 作用 |
|---|---|---|
TETHER_FTS_STEMMING | 开启 | 设置为 0/false/off,按原样索引单词 |
在 LoCoMo 长对话基准测试中进行的测量(每个对话轮次对应一条记忆,共 1,536 个问题,评估“检索是否在前 10 条结果中返回了回答问题的轮次”)显示,单独使用关键词分支可为 61% 的问题找到证据,而对相同文本使用教科书式 BM25 的结果为 54%。该测试工具随仓库一同提供——请参阅基准测试。
语义搜索(可选)
默认情况下,recall 使用混合模式:关键词(FTS5)结果与语义(向量)结果融合,因此即使查询词与记忆中的确切用词不同,也能找到相关记忆(例如,搜索“automobile”会检索到一条关于你的“car”的笔记)。语义检索会在本地运行一个小型静态嵌入模型——不联网、不需要 API 密钥,也没有需要连接的服务。安装额外依赖:
pip install 'tether-memory[semantic]'
如果没有安装额外依赖(或设置了 TETHER_SEMANTIC=0),tether 会仅运行关键词 FTS5——语义搜索纯属附加功能,并非必需。首次运行时会为现有记忆生成一次嵌入(一次性回填);之后则采用增量方式。
环境变量:
| 变量 | 默认值 | 作用 |
|---|---|---|
TETHER_SEMANTIC | 开启 | 设置为 0/false/off,强制仅使用关键词检索 |
TETHER_EMBEDDING_MODEL | minishlab/potion-base-8M | 指定本地静态模型 |
整合(可选)
tether 会保留被取代的事实,而不是直接覆盖:当一条记忆被替换时,旧记忆会标记为不再有效(保留以供追溯),并从 recall 和启动索引中排除。Recall 也会适度优先显示较新的事实。两种可选行为可进一步增强这一机制:
| 变量 | 默认值 | 效果 |
|---|---|---|
TETHER_CONSOLIDATE | 关闭 | 开启(1/true)后,写入时会合并近似重复项——取代旧事实,而不是让存储变得零散(需要 [semantic] 附加项) |
TETHER_DEDUP_THRESHOLD | 0.92 | 将两条事实视为重复项所需的余弦相似度 |
TETHER_DECAY_HALF_LIFE_DAYS | 关闭 | 设为正数后,在 recall 中按指数规律降低较旧事实的排名 |
TETHER_AUTHOR | 设备 ID | 记录在每条记忆上的归属信息 |
整合不会删除记忆——forget 会软删除(参见工具),只有管理员 CLI 的 purge 会永久删除。若没有 semantic 附加项,所有这些功能都会退化为纯关键词 recall。
关联式 recall(可选)
recall 不只会返回关键词/语义匹配结果,还会沿着使用图找到相关记忆,因此询问某件事时也会显示与之关联的上下文。图中的边来自三个本地、确定性的来源——不需要 LLM,也不需要网络:
- 语义 — 根据嵌入向量查找最近邻(需要
[semantic]附加项), - 显式 — 使用
link()动词, - 赫布式 — 随着时间推移,一起 recall 的记忆会相互连接。
每条命中结果都附带一份 via 凭据,说明它为何会出现(直接匹配,或是通过哪条边找到),此外还有两个可选的 recall 参数可调整行为:
| 参数/变量 | 默认值 | 效果 |
|---|---|---|
budget(每次调用) | TETHER_RECALL_BUDGET | 关联查找的范围;0 = 仅直接匹配 |
session(每次调用) | 按时间分桶 | 将相关的 recall 分组,使它们能相互激活 |
TETHER_ASSOC | 开启 | 设为 0/false/off,以使用纯关键词+语义 recall |
TETHER_RECALL_BUDGET | 8 | 默认关联范围 |
TETHER_PROTECT_HEAD | 8 | 在关联结果之前锁定的顶级直接命中数量 |
TETHER_SEED_FLOOR | 0.35 | 语义命中作为关联遍历起点所需的最低余弦相似度;低于此值的记忆只能通过边访问。设为 0 可禁用此下限 |
关联式 recall 以种子为主:顶级直接匹配会被锁定,关联结果只会填入其下方的空位——因此,开启关联功能绝不会降低关键词/语义搜索已排在前面的命中结果。
使用 TETHER_ASSOC=0(或 budget=0,或图为空)时,recall 的行为与之前完全相同——关联式 recall 只会增加能力,绝不会破坏查找。
自组织存储(可选)
随着存储不断增长,tether 会利用同一张使用图让内容保持清晰易读:
- 由枢纽精选的启动索引。 自动加载的记忆索引超过
TETHER_BOOT_INDEX_CAP(默认值为 50)后会受到限制——该上限始终生效,因此大型存储不会产生无界索引。存在图时,超过上限后会显示两个带标签的部分——承载性记忆(行为度最高:explicit链接 + 学到的共同回忆,而非单纯相似性)和最新的记忆——这样索引既小巧,又能展示真正重要的内容。没有图时(TETHER_ASSOC=0),则改为普通的最新 N 条列表,而不再按枢纽/近期内容拆分。低于上限时,无论哪种情况都会显示完整的最新优先列表。 - 断连式遗忘(选择启用,
TETHER_FORGET)。每写入TETHER_FORGET_INTERVAL次后,会执行一次有界扫描,并对同时满足时间久远(TETHER_FORGET_AGE_DAYS,默认值为 90)且行为上孤立(没有explicit/hebbian边——语义相似性不计入)的记忆进行软归档。归档后的记忆会从回忆结果和启动索引中移除,但仍会保留且可恢复(复用与整合相同的标记为无效机制;不会删除任何内容)。安全限制:没有实时行为图时、记忆数量低于2 × CAP时,或单次扫描归档数量超过TETHER_FORGET_MAX_PER_SWEEP(默认值为 10)时,均不会运行。
| 变量 | 默认值 | 作用 |
|---|---|---|
TETHER_BOOT_INDEX_CAP | 50 | 超过此大小后精选启动索引 |
TETHER_FORGET | 关闭 | 启用遗忘扫描 |
TETHER_FORGET_AGE_DAYS | 90 | 符合淡出条件的最小存续天数 |
TETHER_FORGET_INTERVAL | 20 | 两次扫描之间的写入次数 |
TETHER_FORGET_MAX_PER_SWEEP | 10 | 单次扫描最多归档数量 |
关闭 TETHER_FORGET(默认值)且存储规模正常时,回忆功能和启动索引的行为与之前完全相同。
结晶化(可选,默认关闭)
启用 TETHER_CRYSTALLIZE=1 后,tether 会进行反思:检测相关记忆的密集集群,并提供给你命名。在反思过程中读取 tether://crystallization(它只支持主动拉取,不会自动加载)即可获取候选集群;使用 remember(..., crystallizes=[source_ids]) 为真正的原则命名——这会写入该原则并将其链接到源记忆——或使用 dismiss_cluster(id_a, id_b) 舍弃候选项。集群由显式链接 + 使用情况播种(语义相似性用于补全成员),因此找到的是*"这些内容属于一组"*这样的结构,而不只是主题相似性。tether 找出结构;你的 Agent 负责措辞。
结晶化的原则会成为启动索引的枢纽,并可在回忆时从其源记忆访问。注意:这会使「已命名」成为与「已使用」和「已链接」并列的第三种重要性信号——这是刻意设计的,因为 Agent 认为某条内容值得成为原则,本身就是一个强烈信号。
工具
| 工具 | 功能 |
|---|---|
remember(type, title, body, tags?, links?, crystallizes?) | 保存一条记忆;按 type+title 执行插入或更新,让事实不断完善而非重复。crystallizes=[ids] 会将其写成基于这些来源的原则(需要 TETHER_CRYSTALLIZE) |
recall(query?, type?, limit?, budget?, session?, tags?, id?, full?) | 先进行关键词与语义混合搜索,再沿使用关系图查找相关记忆;返回 id/type/title/body/tags/updated_at 以及 via 凭据。body 是以查询为中心的摘录——参见摘录——使用 id=N 可获取一条完整记忆。tags 是精确匹配筛选条件(记忆必须包含列出的所有标签);可与 query 组合使用,也可省略 query,以确保完整查找符合标签的记忆 |
link(id_a, id_b) | 在两条记忆之间建立双向链接 |
forget(id) | 软删除一条记忆:通过与整合相同的可逆 valid_to 机制,将其标记为不再有效(不纳入回忆或启动索引),而不是删除该行。有关真正永久删除,请参见导出和永久删除 |
dismiss_cluster(id_a, id_b) | 反思控制(结晶):丢弃由峰值边 (id_a, id_b) 形成的候选集群,使其不再重新出现。这不是记忆操作;仅在启用 TETHER_CRYSTALLIZE 时相关 |
此外还有三个资源:自动加载的 tether://memory-index(每次会话中显示的紧凑索引,每条记忆占一行)、仅供读取的 tether://status(运行时配置:语义/同步状态、记忆和边的数量、数据库路径——用于调试当前实际启用的内容),以及启用 TETHER_CRYSTALLIZE 时仅供读取的 tether://crystallization(用于反思流程的候选集群)。
摘录
recall 返回每条记忆正文中以相关性为中心的摘录,而非全文——窗口以出现的第一个查询词为中心,因此你能看到记忆为何匹配,而不只是开头几行。被截断的命中还会附带 truncated: true 和 body_chars(正文的实际长度);你可以用 recall(id=N) 获取需要查看的那条完整记忆。
这就是搜索引擎的结果形式:结果列表是指针索引,包含足以判断相关性的文本,而不是文档载荷。这一点很重要,因为记忆可能很大——一条 44KB 的日志记忆,会让无关查询每次调用耗费约 57–67KB,而检索本身耗时不到一毫秒。臃肿的是响应,不是引擎:
| 查询 | 完整正文 | 摘录 |
|---|---|---|
seed dominance | 57.0KB | 1.4KB |
hebbian edges | 66.9KB | 2.0KB |
cold start latency | 66.9KB | 2.0KB |
短于摘录宽度的记忆会像以前一样完整返回,且不标记为截断。
| 变量/参数 | 默认值 | 作用 |
|---|---|---|
TETHER_EXCERPT_CHARS | 500 | 摘录宽度;设为 0 时返回完整正文 |
id(每次调用) | — | 仅获取这条记忆的全文 |
full(每次调用) | false | 返回每条命中的完整正文——会产生完整载荷的开销;优先使用 id= |
性能与持久性
tether 旨在 Agent 的循环中隐形运行,因此我们会测量关键路径的性能,并确保其开销不会随存储规模增长而明显增加。以下数据来自启用了语义层和关联层的本地 SQLite 存储,运行在单进程中:
| 记忆数量 | remember | recall(罕见词项) | recall(出现在大多数记忆中的词项) |
|---|---|---|---|
| 500 | 1.6 ms | 1.8 ms | 3.7 ms |
| 2,000 | 1.8 ms | 2.5 ms | 7.7 ms |
| 8,000 | 3.4 ms | 1.0 ms | 19 ms |
以下几点有助于实现这一效果:
- 写入开销不会随存储规模增长。 用于语义搜索和邻居连接的嵌入矩阵保存在内存中,每次写入时逐行更新,而不是从 SQLite 重新读取。只有在向量整体发生变化(例如更换模型、回填数据),或有其他进程写入文件时(例如执行 CLI
purge、启动第二个服务、同步拉取数据),才会重建矩阵;SQLite 的data_version计数器会检测这些变化。 - 并行工具调用会被串行处理。 MCP 会在各自的线程中运行每个工具调用,而 Agent 会并行发起调用。所有 Store 操作都使用同一把锁,因此同时到达的
recall和remember各自都是原子的:事务不会交错,不会出现只提交了一半的写入,而且action始终准确。 - 提交时不会执行 fsync。 本地连接使用 WAL 和
synchronous=NORMAL:仍可防止数据损坏,但如果机器在检查点之前断电,最后几笔事务可能会丢失(应用程序崩溃不会丢失任何数据)。每次remember都会提交;启用图时,每次recall也会提交,因此每次调用都能省下一次磁盘同步。 - 搜索开销很低。 向量搜索是对内存矩阵执行一次 numpy 矩阵乘法;即使有数千条记忆,也远低于 1 毫秒,因此无需安装向量索引扩展。关键词搜索的开销取决于 FTS5,与匹配查询的记忆数量成正比。
首次启动的开销:安装 [semantic] 附加组件后首次启动(或更换模型后),系统会为每条现有记忆生成嵌入并连接其邻居,每几千条记忆需要一两秒。启动索引和仅按标签查找都会在每次调用时扫描存储;在常见规模下,两者都很快(2,000 条记忆时低于 10 ms),也是接下来要缓存的项目。
基准测试
bench/ 中有两个测试工具,都无需 API 密钥即可运行:
python -m bench.locomo— 在 LoCoMo 上进行仅评估检索的测试。该数据集包含十段较长的多会话对话,每段约有 1,500 个问题,并标注了回答问题所需的对话轮次。每个对话轮次都会成为一条记忆;评分依据是recall(question)是否返回这些轮次(recall@k、MRR),并分别评估仅关键词、关键词 + 语义、完整关联路径三种条件,同时与教科书式 BM25 基线比较。不涉及 LLM,因此该分数衡量的是记忆层能够控制的一件事——它是否向 Agent 提供了正确的事实——不能与记忆服务商在同一数据集上发布的、由 LLM 评判的“准确率”数据相提并论。首次使用时会下载数据(约 1.5 MB)。安装[semantic]附加组件,即可使用真实模型测试语义和关联条件。python -m bench.run— tether 自带的关联式回忆评估:使用一组受控任务语料,其中的任务成员会被一起使用;在模拟使用后,测量使用图相较于关键词 + 语义搜索带来的增益。需要[semantic]附加组件。
导出和永久删除
forget 永远不会删除数据,而是像整合一样进行软删除。两项管理操作被刻意排除在 MCP 工具之外,防止 Agent 触发它们;它们仅在一个小型命令行工具中提供:
tether export # dump all current memories to JSON (stdout)
tether export -o backup.json # ...or to a file
tether import backup.json # merge an export back into the store
tether restore <id> # un-forget a soft-deleted memory
tether purge <id> --yes # permanently delete a memory (bypasses forget)
import 会通过常规写入路径重放记录,因此会像 remember 一样按 type+title 执行插入或更新——导入到非空存储时会合并,而不是重复创建。不会保留 ID(文件中的 ID 在此处可能映射为其他 ID);链接会相应重新映射,指向文件之外的链接则会被丢弃,而不是指向错误的记忆。报告会告知处理结果:{"created", "updated", "skipped", "linked", "dropped_links"}。
restore 会清除 valid_to,撤销 forget(或整合、遗忘清理)的操作。如果更新的记忆后来已占用相同的 type+title,它会拒绝恢复,并指出冲突项,而不是含糊地失败。
没有 --yes 时,purge 会拒绝运行。所有命令都使用与服务相同的 TETHER_DB/TETHER_SYNC_* 环境变量。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。