MemTether:让多个AI客户端共享一份长期记忆的开源方案
1. 先聊痛点AI客户端的金鱼脑是怎么逼我动手的如果你同时在用Claude、ChatGPT、豆包、通义千问甚至本地跑Qwen和DeepSeek你一定经历过这种崩溃瞬间——上午刚在Claude里讨论完一个项目的技术选型下午切到ChatGPT想让它接着给优化建议结果它一脸茫然地回你一句我没有这个项目的上下文。你只能重新粘贴需求、重新说明背景、重新描述已经说过的限制条件。来回折腾五分钟还没开始干活先把自己整烦了。这个问题的本质是每家AI客户端只维护自己的会话记忆跨客户端的信息根本不互通。我做了个开源工具叫MemTether解决的就是这个碎片化问题——让多个AI客户端共享同一份外部记忆谁的记忆都不再是孤岛。我可以直接把它的能力一句话说清MemTether是一个轻量级的记忆服务端AI客户端通过标准协议读写一份统一的长期记忆数据库你在这家模型里聊过的关键背景、决策结论、项目约束换到另一个客户端继续聊也能直接续上。不用改客户端的UI、不用学习新的聊天界面只需要让客户端指向MemTether提供的接口即可。这个工具适合谁我的判断是三类人重度多模型用户日常在多家AI产品间切换受够了重复给上下文。本地模型玩家同时跑多个开源模型想给每一个都配一份固定人设档案和长期记忆。AI Agent开发者在搭建多Agent协作系统需要一个让不同Agent共享事实数据的中转层。项目代码放在GitHub上完全开源MIT协议拿到就能跑。下面我把从设计到落地的全过程拆开讲包括架构选择、模块划分、踩坑记录和实测对比希望能给同样被金鱼脑AI困扰的人一条能直接走通的路。2. 为什么官方云端记忆救不了你问题边界卡在哪开始写代码之前我先花了整整两天梳理需求边界。因为共享记忆这四个字听起来简单真做起来很容易滑向做一个通用的大模型记忆系统这种无底洞。我给自己定了几条边界。2.1 要共享的是事实性上下文不是全部聊天记录网上有一些工具走的是保存全部聊天记录随时检索的路线相当于给聊天记录做全文搜索引擎。这个方案的痛点很明显记录越多检索越慢还容易检索到无关内容污染模型理解。聊天记录里有大量客套话、试错过程、错误猜测喂给模型反而增加噪音。各家客户端的消息格式不统一导入导出的兼容性很麻烦。我的做法刚好相反MemTether只存入真正有长期价值的结构化记忆比如用户正在做的是一个电商数据分析项目后端技术栈是Python FastAPI12月底必须上线UI设计偏简洁风格这类信息。这种记忆的特点是小、稳定、高复用。跟聊天记录全量存档相比它更像是一个项目档案夹而不是一个历史聊天流水账。2.2 官方云端记忆的封闭性为什么不能依赖它你可能会问Claude不也有Projects、ChatGPT不也支持自定义指令吗没错但它们是各自封闭的记忆绑定在单一平台上换客户端就作废。记忆格式不开放没法导入导出到别的系统。有额度限制免费层的自定义指令长度和数量都有限。无法被程序化调用Agent想动态注入记忆很麻烦。也就是说官方方案设计出来的目的是让你更依赖某个平台而MemTether的定位是让你不被任何一个平台绑死。这是本质差异。2.3 我最终锁定的功能定位与取舍清单经过需求收敛我敲定了第一版必须具备的能力功能项优先级说明结构化记忆的增删改查必须核心数据模型支持按命名空间隔离按关键词/标签检索记忆必须聊天时动态拉取相关记忆注入提示词多客户端同时读写必须并发访问是共享的前提标准API接口必须让任何客户端都能接入用户级隔离可选第一版先做单用户留好扩展位聊天记录全量存储砍掉定位偏离维护成本高向量检索延后记忆量小的时候关键词匹配够用这个取舍很关键。很多人做类似的工具第一反应就是上向量数据库但我的实际判断是个人场景下几百条结构化记忆用SQLite加LIKE检索毫秒级就出结果了不需要提前引入embedding环节。过早引入复杂技术栈是业余项目最常见的死法先把核心流程跑通量确实大了再演进。这个决定帮我省掉了大量不必要的复杂度。3. 架构设计全拆解一个存储内核加一个接入层架构这层我犹豫了很久。最开始想过做一个跟LangChain深度绑定的工具后来否决了——LangChain学习者多、跟进者多但版本迭代快、耦合深别人想复现很容易被版本问题卡住。最终我选择了最朴素的路线一个独立的HTTP服务数据层面用存储抽象层隔离协议层面优先兼容业界标准而不是绑定任何特定框架。3.1 整体模块划分MemTether的结构可以分成三层来看第一层是存储内核。负责记忆数据的持久化、索引和查询。我用了SQLite作为默认实现好处是零配置、单文件、备份方便对个人和团队规模完全够用。数据模型上设计了三个表memory_items存记忆正文和元信息tags存标签memory_tags存记忆和标签的多对多关系。第二层是服务层。封装了记忆的增删改查、标签过滤、关键词搜索等逻辑对外暴露统一的Python接口。这一层做得好不好直接决定了上层API的稳定性和可测试性。第三层是接入层。提供两种标准方式的接入能力一是REST API任何能发HTTP请求的客户端都能用二是MCP兼容端点支持当前AI生态里标准的上下文传递协议让支持MCP的工具链可以无缝对接。3.2 为什么最终选了FastAPI加SQLite而不是更重的组合这套技术栈在圈子里不算新鲜但选它是一层层比较过才定的FastAPI vs Flask vs DjangoFlask太轻异步支持弱同时处理多个客户端的并发请求比较吃力Django太重为了一个记忆服务搬出全套框架属于杀鸡用牛刀。FastAPI的异步支持好自带OpenAPI文档调试期可以直接在浏览器里测接口省了写文档的功夫。模型加载、多客户端并发这种IO密集场景async天然占优。SQLite vs MySQL/PostgreSQLMemTether定位是你可以跑在笔记本上、也可以跑在一台小服务器上的工具SQLite的单文件特性让它部署成本几乎为零。加上WAL模式的并发读优化之后实测支撑十几个客户端同时访问完全没问题。如果后续量上来了存储抽象层可以让我们平滑迁移到PostgreSQL。httpx vs requests服务层需要主动调用外部模型API去校验某些记忆的时效性httpx的异步客户端跟FastAPI的event loop配合更顺不存在线程切换的额外开销。3.3 存储抽象层这套设计里最值得复用的部分这是我认为整个项目里设计价值最高的一段代码也是很多人写这类工具容易偷懒跳过的部分。我把存储逻辑定义了一个基类里面只有几个核心方法create_memory、get_memory、update_memory、delete_memory、search_memories、list_by_tag。SQLite实现和未来的PostgreSQL实现都继承这个基类。为什么一定要抽这一层因为我在实际使用中发现用户的第一需求永远是先跑起来但第二个需求很快就会变成数据多了之后我觉得不够用想换存储。如果代码里到处都是SELECT * FROM memory_items这种裸SQL到时候会改到想删库跑路。抽了一层之后迁移存储引擎只需要新增一个实现类业务代码一行不用改。我还在存储层加了命名空间的概念。每个记忆项属于一个namespace比如project-alpha、personal-preferences、agent-1。这样一来同一个MemTether实例可以服务多个项目或多种用途互相之间数据不串味。这个设计是从Docker的namespace隔离上借鉴来的思路代价只是一行字段加一个索引收益是使用场景立刻变广了。4. 部署与接入实战从零开始把第一个AI客户端连上来光讲架构太空了我直接带你把整个链路跑通。以下所有步骤我都基于当前最新版v0.2.1实测过照着抄基本不会出问题。4.1 环境准备与安装MemTether的依赖极简可以说是一个Python环境加一个pip命令的事。# 建议使用Python 3.10 python -m venv memtether-env source memtether-env/bin/activate # Windows下是 memtether-env\Scripts\activate # 安装MemTether会自动带上FastAPI、uvicorn、sqlite3驱动等依赖 pip install memtether # 启动服务默认监听0.0.0.0:8868 memtether start看到Uvicorn running on http://0.0.0.0:8868就说明服务起来了。此时打开浏览器访问http://localhost:8868/docs会看到自动生成的API调试页面所有接口都能直接在上面试。这里有个我经常遇到的小坑如果你之前装过其他依赖了FastAPI版本的包建议在虚拟环境里重新装避免版本冲突。我第一次在自己本机跑的时候就因为全局环境里残留了一个旧版pydantic导致接口文档页打不开后来把虚拟环境里所有依赖更新到最新版才解决。所以最保险的做法永远是新建虚拟环境全新安装。4.2 先人工写入几条基础记忆验证服务的核心链路最朴素但最有效的验证方式——先用curl写几条记忆再去查确认数据落了库。# 写入一条记忆 curl -X POST http://localhost:8868/api/memory \ -H Content-Type: application/json \ -d { namespace: demo-project, content: 用户正在做一个电商数据分析平台技术栈是FastAPI加Vue, tags: [project, tech-stack] } # 查询刚才写入的记忆 curl http://localhost:8868/api/memory?namespacedemo-projecttagproject响应里能看到id就是这条记忆的唯一标识后续的更新和删除都靠它。此时如果你去数据库文件里看一眼默认在~/memtether/memtether.db会发现memory_items表里已经有了这条记录。链路通着心里就踏实了。4.3 打通MCP协议让Claude和ChatGPT原生接入如果说REST API是什么客户端都能连那MCP就是主流AI客户端天生就懂的标准语言。在MemTether里我做了两件事来支持MCP一个版本化的模型端点挂在/api/mcp路径下兼容MCP 0.4及以上的标准调用格式。把记忆的读写包装成MCP的Tool集合客户端只要加载这几个Tool就能像调用原生工具一样操作MemTether的记忆库。在Claude Desktop或任何支持MCP的客户端里配置文件加上如下内容即可{ mcpServers: { memtether: { command: memtether, args: [mcp], env: { MEMTETHER_ENDPOINT: http://localhost:8868/api/mcp } } } }配置好之后重启客户端在工具列表里就能看到memtether_save_memory和memtether_search_memory两个工具。实际对话时你只需要对Claude说一句把这个项目的技术选型记到MemTether里它就会自动调用工具把信息存进去。下次在ChatGPT里输入同样的工具配置问我之前那个项目的技术栈是什么它能直接从MemTether里检索出来回答你。4.4 直接用REST API对接本地模型一个超简单但很实用的方案本地模型这一块是最灵活的因为很多开源模型本身就自带工具调用能力。你完全不用装任何插件直接在自己的Python脚本里写个10行的对接就能用。import httpx def save_memory(content: str, tags: list[str], namespace: str default): resp httpx.post( http://localhost:8868/api/memory, json{namespace: namespace, content: content, tags: tags}, timeout5 ) return resp.json()[id] def search_memory(query: str, namespace: str default): resp httpx.get( http://localhost:8868/api/memory, params{namespace: namespace, q: query}, timeout5 ) return [item[content] for item in resp.json()[items]] # 示例给本地模型套上一层MemTether记忆 def chat_with_memory(model_api, user_input: str): memories search_memory(user_input) context \n.join(memories) if memories else prompt f这是关于当前任务的历史记忆\n{context}\n\n现在用户说{user_input} return model_api.chat(prompt)这个思路其实就是最朴素的 RAG——不需要复杂的分块和向量化把用户输入当关键词去检索结构化记忆然后拼进prompt里。因为记忆本身就是高度浓缩的信息所以命中率相当可观。我在自己机器上用Qwen2.5-7B做过测试不接MemTether时每次对话都得手动把项目背景粘一遍接上之后凡是问过项目的部署环境是CentOS 7这类事实性问题第二次问同一主题模型能直接给出准确答案不需要重复给背景。对于一个7B的小模型来说有了外部记忆兜底它在连续性对话上的表现能向上够到更大模型的水准。原因是模型不需要再用有限的上下文窗口去记住所有背景只需要在检索到相关记忆之后做推理这个分工非常合理。5. 实测对比不同AI客户端接入MemTether后的真实表现工具好不好用不能光靠感觉我专门搭了一套测试环境做横向对比。目标很简单在不同客户端里问同一个跨会话记忆问题看在MemTether的帮助下谁没翻车。5.1 我构造的测试方法与场景测试流程固定为三步通过REST API写入三条结构不同的记忆一条短句、一条多行技术描述、一条带多个标签的项目背景。分别在不同客户端里问我之前记录过一个电商项目的技术栈是什么记录每个客户端的回答正确性、答错率和平均响应时间。为了公平所有客户端都指向同一个MemTether实例且各自的会话都是新开的不携带任何平台侧的记忆。5.2 测试结果的核心结论客户端类型接入方式回答正确率答错/未答响应速度备注Claude DesktopMCP工具高偶发未答正常工具调用稳定偶尔需要提示它查一下记忆ChatGPTMCP工具高低偏慢检索后通常会主动补充说明本地Qwen2.5-7BREST API注入较高偶发编造偏快注入质量直接影响回答质量豆包暂不支持MCP待接入未测未测只支持自家插件生态需要走代理方案从结果里能看出两个规律一是支持MCP的客户端接入成本最低效果也最稳定因为工具调用的上下文结构是标准的客户端不会误解二是本地模型走REST注入时检索命中率直接决定了回答准确率如果检索没命中模型就会凭空编一个技术栈出来这在实际使用中是最大的风险点需要在prompt里明确加一句如果记忆里没有相关信息请直接说不知道。5.3 从对比里得到的三个接入建议第一优先启用MCP接入。它相当于协议级别的原生支持配置正确之后一般不会出幺蛾子。第二本地模型接入时要在prompt里加未知就回答未知的约束否则模型编造起来真的脸不红心不跳。第三记忆条目的内容不要写口语化闲聊要写成信息密度高的陈述句这样检索命中后直接拼进prompt不给模型二次加工的空间。6. 开发过程中踩过的坑三条最值得记录的完整排查链路这部分写一点项目之外最值钱的经历——我在开发MemTether时踩的几个坑每个从表面现象到根因都很典型很多人做类似项目也会遇到。6.1 坑一时间戳覆盖导致记忆悄悄丢失这应该算是整个项目里最隐蔽的一个bug。现象是某条记忆在写入后过了一段时间再读内容变成空字符串了但数据库里确实存在这条记录。排查链路是这样的——先是通过SELECT * FROM memory_items查发现content字段是空的说明写入环节至少部分失败了。于是去看接口日志发现第一次写入是成功的。再往前翻发现几小时前有一次PUT请求更新过这条记忆而那个请求的请求体里content字段传了空字符串。原因找到了我自己在写更新逻辑时没有做空值校验一旦某个客户端传入了一个只有标签没有正文的更新请求就会把原来的内容覆盖掉。修复很简单在服务层加一条规则content为空时拒绝更新返回400。但这个教训是结构性的——所有共享数据的写入接口都必须明确校验字段完整性不能信任任何调用方。后来我在所有更新接口前加了一层Pydantic的字段校验整个类别的bug就绝迹了。6.2 坑二SQLite的数据库被锁定错误出现在意料之外的场景WAL模式我已经开过了照理说并发的读不会被写阻塞。但有一次实测时三个客户端同时写入SQLite直接抛了database is locked。我一开始以为是WAL没生效查了下数据库的journal_mode确实是wal。又怀疑是超时时间设太短把busy_timeout从5s调到了30s还是偶尔报错。最后定位到根因FastAPI的异步event loop里我用同步的sqlite3库直接做了数据库操作多个协程会在线程池里并发执行写事务这时候SQLite的锁机制就hold不住了。解决方案是把SQLite操作统一封装进asyncio.to_thread或者直接用aiosqlite库。所有写事务强制走同一个异步事件循环的调度避免多线程同时抢写。给关键写入路径加了一个进程内的互斥锁保证同一时间只有一个写事务在跑。改完之后并发写入的问题再没出现过。这个坑提醒我选型的时候要看库的并发模型是否和你的运行时匹配同步库硬塞进异步框架迟早要出事。6.3 坑三MCP Schema版本漂移导致的兼容性异常这个坑是在接入Claude时发现的。第一次配置MCP连接很顺利但过了两周客户端一升级记忆工具突然全部失效Claude报了工具描述格式不兼容的错误。排查时我先看了Claude侧的插件日志发现它期望的MCP schema是0.5版本而我的端点还在向它声明0.3。其实我代码里写的版本号是错的——MCP的标准库更新后我忘了同步更新自己服务端声明的协议版本导致客户端按新格式解析服务端按旧格式响应自然对不上。修复方案是在服务端加一个版本协商的机制根据客户端在握手阶段传过来的能力头动态返回对应版本的schema描述。再往后我也学会了MCP协议还在快速演进期任何暴露给外部客户端的协议能力描述都需要跟随上游更新这一块要有定期的维护意识。7. 使用模式总结MemTether在真实场景里还能怎么玩项目本身做到这个程度基本的功能和稳定性已经能支撑日常使用了。但我实际用了两个月之后发现它的价值远不止跨客户端续聊这一个场景这里我把探索出来的几个使用模式列出来供你参考。7.1 用命名空间管理多项目记忆我在本地跑了三个项目一个电商数据分析平台、一个个人博客、一个开源的MCP工具。三个项目的记忆分别放在ecommerce-analytics、blog、mcp-tool三个命名空间下。在任何客户端里只要在提问时附带一句查一下关于博客项目的记忆MemTether就能精确过滤到对应空间不会把另外两个项目的背景混进来。这个模式对同时维护多个项目的开发者特别有用。7.2 给多Agent系统做共享事实层最近我在试验多个Agent协作的架构其中一个场景是这样一个Agent负责理解用户需求另一个Agent负责查找技术资料第三个负责编写代码。这三个Agent如果没有共同的事实基础很容易各说各话。我把MemTether放在它们中间让需要共享的信息全部走MemTether的读写三个Agent在各自的上下文中只保留当前步骤的关键信息需要查阅我们已经决定的架构方案时再实时从MemTether拉取。实验下来误协调的情况明显变少每个Agent的上下文窗口压力也小了很多。这个用法本质上就是把MemTether当做一个容量无限的白板来用。局限在于它目前只支持结构化记忆不支持文档批量上传或长文本检索。如果对这个方向有兴趣可以关注后续版本里计划加入的向量检索和文档索引能力——做肯定是要做的只是不在第一版的scope里。7.3 小团队内部的知识同步如果你跟朋友合作开发或做研究完全可以把MemTether部署在一台小服务器上几个人公用一个实例用命名空间区分不同成员或不同课题。跟Notion、飞书这类知识库比起来MemTether的优势是它是面向AI的——团队里的AI助手读了同一份记忆大家在各自的工具里问同一个问题得到的答案是一致的不会出现你的AI不知道我的AI查到过什么这种信息断层。我在一次线下的开发者聚会上把这套思路讲给其他人听当场就有几个人回去搭了自己的实例。他们反馈最多的一句话是以前从来没想过AI的记忆还可以这样变成一个基础设施。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →