ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从零实现MCP Memory:用SQLite FTS5+OKF给Agent加持久记忆

从零实现MCP Memory:用SQLite FTS5+OKF给Agent加持久记忆 如果你正在用 Claude、Codex、Cursor 这类 AI 编程工具或者自己在搭 Agent 应用大概率已经踩过同一个坑上一轮对话里交代过的项目背景、代码规范、数据库连接信息换一个新会话就全部归零。Agent 不是每次都在“思考”而是每次都在“失忆”整个开发链条被这个短板卡得很死。很多人第一反应是上向量数据库把记忆做完 embedding 再存进去查询时走一遍 RAG 检索。这个方案不是不能用但为了“记住几句话”要单独部署一个数据库服务要处理向量化、相似度阈值、索引更新、服务运维复杂度和成本都不低。最近 Show HN 上出现了一个更务实的思路用 MCP 把记忆封装成标准化工具用 SQLite 做本地单文件存储用 FTS5 做全文检索再用 Google 的 OKF 这类紧凑序列化格式降低体积和解析开销这就是 MCP Memory 项目。这篇文章会讲清楚三件事Agent Memory 的痛点到底在哪里SQLite FTS5 OKF 相比向量库方案为什么在特定场景下更值得选以及如何从零实现一个可落地的 MCP Memory 服务包括建表、FTS5 索引、MCP 工具注册和完整验证流程。1. 这篇文章真正要解决的问题1.1 Agent 的“会话级失忆”问题大模型本身的上下文窗口在变大但这不意味着 Agent 有长期记忆。每一次新会话开始模型拿到的只是系统提示词、工具定义和当前对话内容它对你项目的历史决策、用户偏好、之前修复过的 Bug 一概不知。实际开发中你会遇到这些场景你上午让 Agent 记录了“日志统一走 SLF4J业务异常定义在 exception 包”下午新会话里它又用回了 SOP 式 try-catch。Agent 在多轮对话中自己推断出了某个接口的字段含义但下一轮它又忘了反复问你同一个问题。团队里多个开发者共享同一个 Agent但每个人和 Agent 的上下文是隔离的团队积累的知识没有沉淀下来。这些问题的根源不是模型能力不足而是缺少一个“跨会话、可检索、可更新”的记忆存储层。1.2 现有方案为什么很重目前业界给 Agent 加记忆主要有几条路线方案核心思路主要成本向量数据库 Embedding把文本向量化用相似度检索部署向量库、维护 embedding 模型、调相似度阈值LangChain 的 Memory 组件在对话链里维护历史消息只解决单会话内短期记忆跨会话要自己持久化把记忆塞进 System Prompt把所有历史拼进提示词上下文爆炸成本高检索能力弱MCP Memory用工具协议标准化记忆读写本地 SQLite 存储轻量无外部服务前两条路线都不是“错”而是“重”。对一个独立开发者和中小团队来说为了给 Agent 加记忆就去维护一套向量数据库本身就在引入新的运维负担。MCP Memory 的价值在于它把记忆层降维成了“一个文件 一个标准协议”。1.3 什么样的读者最应该看这篇文章如果你符合下面任意一条这篇文章对你会有实际帮助你在用 Claude Desktop、Codex、Cursor 等 MCP 客户端想给 Agent 加长期记忆。你在自己开发 Agent想把“记忆”做成本地优先、可离线使用的模块。你想了解 SQLite FTS5 在做全文检索时的真实能力和边界。你想弄明白 MCP 工具和传统 RAG、Agent Skill 之间的区别。这篇文章不是要否定向量数据库而是给你一个更轻的选项并告诉你它适合什么、不适合什么。2. MCP 与 Agent Memory 核心概念2.1 MCP 到底是什么MCPModel Context Protocol模型上下文协议是 Anthropic 推出的开放协议核心目的是统一 AI 应用与外部工具、数据源之间的交互方式。它的设计思路可以类比成“AI 世界的 USB-C 接口”模型不需要知道每个工具的内部实现只需要通过标准化的接口去调用工具、读取资源、获取提示词。在这个协议里有几个关键角色MCP Server提供工具、资源、提示词的服务端比如一个文件读取工具、一个数据库查询服务。MCP Client连接 Server 的客户端通常是 Claude Desktop、Codex、Cursor 这类应用或者是你自己写的 Agent。ToolServer 暴露给模型的可调用函数模型根据用户需求决定是否调用。MCP 的标准化价值在于同一个 Memory Server既可以被 Claude Desktop 使用也可以被 Codex 使用不需要为每个客户端单独开发一套记忆接口。2.2 Agent Memory 在 MCP 体系里的定位Agent Memory 是 MCP 体系里一个典型的工具服务。它把记忆抽象成几个基础操作保存记忆把一段重要的上下文关联到某个实体或命名空间。检索记忆根据关键词或语义找到相关历史信息。遗忘记忆删除指定实体和键名下的记忆。清空记忆重置某个实体的全部记忆。这些操作通过 MCP 标准接口暴露给模型。模型在对话过程中判断“这段信息值得记住”就调用保存工具用户问到一个需要历史信息的问题模型就调用检索工具。记忆对模型来说变成了可主动读写的外部资源而不是每次都被动塞进提示词里的文本。2.3 MCP 与 Agent Skill 有什么区别最近社区里经常有人问“Agent Skill 和 MCP 有什么区别”。这两个概念经常被混淆但定位完全不同。维度Agent SkillMCP本质一组提示词 脚本/工具的打包教模型“怎么做一件事”一套客户端与服务端之间的通信协议作用对象模型的行为能力比如“如何用 Git 规范提交”模型与外部系统的交互通道组合方式Skill 内部可以调用 MCP 工具MCP 工具可以被 Skill 编排类比更像是给模型的一份“岗位说明书”更像是标准插座和插头简单说Skill 告诉模型“遇到某种请求时按照什么流程处理”MCP 则为这个流程提供具体的执行通道。记忆本身可以被封装成 MCP 工具同时也可以被一个 Skill 编排进“每次对话开始先检索记忆”的流程里。两者不是替代关系而是协作关系。2.4 MCP Memory 与 RAG 的边界很多人看到“检索记忆”就会联想到 RAG。两者确实有相似之处但目标不同RAG 通常是针对“外部知识库”的问答增强数据量大、需要分块、需要向量化侧重“回答没训练过的问题”。MCP Memory 是 Agent 运行过程中的“私人记忆”数据量小、结构简单、需要快速读写侧重“记住上下文和偏好”。MCP Memory 更像是一个轻量 KV 存储加全文检索引擎而不是一个知识库系统。理解这个边界你就不会用错工具给 Agent 加个人记忆用 MCP Memory 很合适但要做一个企业知识库问答还是应该走 RAG 架构。3. 为什么是 SQLite FTS5 OKF 而不是向量库3.1 SQLite 天然适合做 Agent 本地存储SQLite 是所有主流数据库里最“省心”的一个无独立服务进程数据保存在单个文件里标准库自带驱动几十行代码就能完成建表和增删改查。对 Agent Memory 这种“本机一个人用”的场景SQLite 几乎是理想选择零部署、零运维不增加系统复杂度。单文件备份和迁移方便拷走即迁移。读写性能足够Agent 记忆的数据量级远达不到数据库瓶颈。事务支持完整写入记忆时不会出现半截数据。3.2 FTS5 解决了 SQLite 的模糊搜索短板SQLite 原生LIKE %keyword%搜索有两个问题一是慢二是只能做子串匹配做不了分词。FTS5Full-Text Search version 5是 SQLite 内置的全文检索引擎它解决了这些问题。FTS5 的核心机制是倒排索引Inverted Index在插入数据时FTS5 会把文本拆分成词项并记录每个词项出现在哪些文档行里。搜索时直接查词项索引不需要全表扫描。FTS5 的几个关键概念Tokenizer分词器负责把文本拆成词项常见的有unicode61、porter、trigram。unicode61对中文支持有限但英文和代码标识符效果不错。MATCH 语法FTS5 的搜索语法支持短语、字段、布尔组合。bm25 排序FTS5 内置 BM25 排序算法可以按相关性给搜索结果排序。外部内容表可以把 FTS5 虚拟表关联到普通业务表实现结构化字段和全文检索的联合查询。用MATCH做全文搜索和LIKE的差别是本质性的。LIKE %python%每次都要扫全表FTS5 直接在索引里定位词项速度不在一个量级。对于 Agent 记忆这种高频读写场景这个差距会直接影响体验。3.3 OKF 在项目里的作用标题里特别提到了 Google 的 OKF。从项目定位看OKF 是一种面向结构化数据的紧凑对象键值编码格式核心目标是减少序列化数据体积、加快解析速度。如果你熟悉 Protocol Buffers 或 FlatBuffers可以把 OKF 理解成同类定位的方案用二进制结构替代 JSON 文本换取更小的存储占用和更快的反序列化。在 Agent Memory 场景里这一步解决的是“记忆体积”问题。当 Agent 需要把大量历史记忆读回上下文时如果数据是 JSON 文本体积大、解析慢、内存占用高改用 OKF 编码后记忆对象以紧凑二进制形式存储从 SQLite 里读出来再交给模型时I/O 和内存开销都会明显下降。需要说明OKF 的具体内部格式、字段约束和版本信息请以项目 README 和官方文档为准。本文的重点是理解它在这个架构中解决的性能和体积问题。3.4 这套架构的整体判断这个项目真正打动人的地方在于“降维”记忆服务不需要单独的数据库、单独的向量进程、单独的 embedding API一个 SQLite 文件、几个 MCP 工具就完成了。它的性能逻辑是OKF 解决体积和解析开销FTS5 解决检索效率SQLite 解决存储可靠性三者组合起来把一个原本需要微服务架构的“记忆系统”压缩成了一个本地模块。当然它也有边界。如果你的记忆检索要求是语义层面相似度召回比如“找一段意思相近但关键词完全不同的历史记录”纯 FTS5 的词汇匹配是不够的。这种场景还是要考虑 embedding。MCP Memory 做的是把 80% 的“关键词记忆”场景用最轻的方案解决掉。4. 环境准备与前置条件4.1 运行环境本文示例以 Python 为主推荐环境如下Python 3.10 或更高版本3.12 兼容性最好。SQLite 3.35 以上版本FTS5 需要较新的 SQLite 版本Python 3.10 自带 SQLite 通常已支持。pip 可用的虚拟环境。不同操作系统下Python 自带的sqlite3模块不一定启用了 FTS5。启动时要用下面命令确认。4.2 安装 MCP Python SDKMCP 官方提供了 Python SDK推荐安装到虚拟环境里。python -m venv .venv source .venv/bin/activate # Windows 下为 .venv\Scripts\activate pip install mcp安装完成后验证版本pip show mcp4.3 确认 FTS5 可用FTS5 是 SQLite 的编译期可选模块某些发行版的 SQLite 没有编译进去。运行这条命令确认import sqlite3 conn sqlite3.connect(:memory:) try: conn.execute(CREATE VIRTUAL TABLE ft_test USING fts5(content)) print(FTS5 OK) except Exception as e: print(FTS5 NOT AVAILABLE:, e)如果输出FTS5 NOT AVAILABLE需要安装带 FTS5 的 SQLite 版本或者换一个 Python 环境。5. 核心流程拆解5.1 第一步设计记忆数据模型MCP Memory 的数据模型不需要复杂核心是“实体 键 内容”三元组entity记忆归属实体可以是用户、项目名或会话 ID用来隔离不同维度的记忆。key记忆的键名相当于这条记忆的代码标识便于精确读取和删除。content记忆的正文是 FTS5 建立全文索引的字段。metadata可选 JSON 字段存放来源、优先级等附加信息。created_at / updated_at时间戳用于按时间排序和清理。为什么要用 entity 做隔离因为实际场景里你会同时有“用户偏好”“项目知识”“团队规范”几类记忆如果不隔离搜索时会出现严重串扰。比如搜索“日志规范”时你既想要项目相关的规范又不想把用户无关的个人偏好带出来。5.2 第二步初始化 SQLite 与 FTS5 索引建表时要注意把业务表memories和全文索引表memories_fts分开用外部内容表模式让 FTS5 索引关联到业务表。这个模式的好处是业务字段和全文索引同时存在可以做“实体过滤 全文匹配”的联合查询。为了保证两个表的数据一致需要用触发器在插入、删除、更新时同步维护 FTS5 索引。这一步做不好后面就会出现“数据在业务表里但搜不到”的诡异问题。5.3 第三步实现 MCP 工具MCP Server 需要暴露四个核心工具store写入一条记忆。search按实体和关键词检索记忆走 FTS5 的 MATCH。forget按实体和键名删除一条记忆。clear清空某个实体的全部记忆。工具名称和参数设计要尽量语义化因为模型的工具调用是基于工具名称和描述来决策的。描述写得模糊模型就不知道该在什么场景下调用。5.4 第四步接入 MCP 客户端以 Claude Desktop 为例在配置文件里声明一个 mcpServers 节点指向你的 Server 启动命令。启动后客户端会自动发现工具、加载工具定义模型在对话中就能调用记忆工具了。6. 完整示例代码实现6.1 SQLite 表结构与 FTS5 初始化先创建项目目录和数据库初始化 SQL。文件路径schema.sql-- 文件路径schema.sql CREATE TABLE IF NOT EXISTS memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, entity TEXT NOT NULL, key TEXT NOT NULL, content TEXT NOT NULL, metadata TEXT DEFAULT {}, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_memories_entity ON memories(entity); CREATE INDEX IF NOT EXISTS idx_memories_entity_key ON memories(entity, key); -- 外部内容表FTS5 索引只存 content 字段rowid 对应 memories.id CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5( content, contentmemories, content_rowidid, tokenizeunicode61 ); -- 插入时同步索引 CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content); END; -- 删除时同步索引 CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN INSERT INTO memories_fts(memories_fts, rowid, content) VALUES (delete, old.id, old.content); END; -- 更新时先删旧索引再插新索引 CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN INSERT INTO memories_fts(memories_fts, rowid, content) VALUES (delete, old.id, old.content); INSERT INTO memories_fts(rowid, content) VALUES (new.id, new.content); END;解释一下外部内容表的关键点contentmemories表示 FTS5 表的数据源是memories表的content字段。content_rowidid表示 FTS5 的 rowid 对应业务表的主键。触发器必须在业务表上建保证业务数据变更时索引同步。tokenizeunicode61是通用分词器对英文和代码标识符友好中文会被按单字处理。6.2 MCP Memory Server 主代码文件路径mcp_memory_server.py 一个基于 SQLite FTS5 MCP 的轻量 Agent Memory Server。 通过 MCP 协议暴露 store / search / forget / clear 四个工具。 import json import os import sqlite3 import time from contextlib import closing from pathlib import Path from mcp.server.fastmcp import FastMCP DB_PATH os.environ.get( MEMORY_DB_PATH, str(Path.home() / .mcp_memory / memory.db), ) SCHEMA_PATH Path(__file__).parent / schema.sql mcp FastMCP(memory) def get_conn() - sqlite3.Connection: 创建数据库连接设置行工厂为 dict 风格。 conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row return conn def init_db() - None: 初始化数据库建目录、执行 schema.sql。 Path(DB_PATH).parent.mkdir(parentsTrue, exist_okTrue) with closing(get_conn()) as conn: conn.executescript(SCHEMA_PATH.read_text(encodingutf-8)) conn.commit() def escape_fts(text: str) - str: 将用户输入转成 FTS5 短语查询。 去掉双引号避免破坏查询语法再整体作为短语匹配。 safe text.replace(, ).strip() return f{safe} mcp.tool() def store(entity: str, key: str, content: str, metadata: str {}) - dict: 保存一条记忆。entity 表示记忆归属用户/项目/会话key 是记忆键名 content 是记忆正文metadata 是可选 JSON 字符串。 now int(time.time()) with closing(get_conn()) as conn: conn.execute( INSERT INTO memories (entity, key, content, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?) , (entity, key, content, metadata, now, now), ) conn.commit() return {status: ok, entity: entity, key: key} mcp.tool() def search(entity: str, query: str, limit: int 10) - dict: 在指定实体下按关键词全文检索记忆使用 FTS5 MATCH bm25 排序。 fts_query escape_fts(query) with closing(get_conn()) as conn: rows conn.execute( SELECT m.id, m.entity, m.key, m.content, m.metadata, m.updated_at, bm25(memories_fts) AS rank FROM memories_fts fts JOIN memories m ON m.id fts.rowid WHERE memories_fts MATCH ? AND m.entity ? ORDER BY rank LIMIT ? , (fts_query, entity, limit), ).fetchall() return {query: query, entity: entity, results: [dict(r) for r in rows]} mcp.tool() def forget(entity: str, key: str) - dict: 按实体和键名删除一条记忆。 with closing(get_conn()) as conn: cur conn.execute( DELETE FROM memories WHERE entity ? AND key ?, (entity, key), ) conn.commit() return {status: ok, deleted: cur.rowcount} mcp.tool() def clear(entity: str) - dict: 清空某个实体的全部记忆。 with closing(get_conn()) as conn: cur conn.execute(DELETE FROM memories WHERE entity ?, (entity,)) conn.commit() return {status: ok, deleted: cur.rowcount} def main() - None: init_db() mcp.run() if __name__ __main__: main()代码里几个值得注意的点mcp.tool()装饰器是 FastMCP 提供的最简工具注册方式。函数签名和 docstring 会作为工具定义传给模型所以 docstring 写清楚参数含义很重要。escape_fts是容易被忽略的安全点。FTS5 的 MATCH 语法支持布尔运算符、短语、列过滤等高级语法如果直接把用户输入拼进去用户输入里的特殊字符可能导致语法错误甚至导致非预期的查询行为。这里用“整体短语化”做了一层收敛。bm25(memories_fts)返回相关性分数数值越低相关性越高所以用ORDER BY rank升序。forget和clear都是物理删除。生产环境建议改成软删除后面最佳实践会说。6.3 MCP 客户端配置示例如果你使用 Claude Desktop在配置文件claude_desktop_config.json中增加节点{ mcpServers: { memory: { command: python, args: [/absolute/path/to/mcp_memory_server.py], env: { MEMORY_DB_PATH: /absolute/path/to/memory.db } } } }配置完成后重启 Claude Desktop在对话里可以尝试说“记住本项目日志规范统一使用 SLF4J不允许 System.out.println”。模型会调用store工具。然后新开一个会话问“项目的日志规范是什么”模型会调用search工具并返回你存过的那条记忆。6.4 用 Python 客户端做自动化冒烟测试不依赖图形客户端用官方 MCP SDK 也能直接验证 Server 是否可用。文件路径test_client.pyimport asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main() - None: server_params StdioServerParameters( commandpython, args[/absolute/path/to/mcp_memory_server.py], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print(发现工具数量:, len(tools.tools)) for t in tools.tools: print( -, t.name) # 写入一条记忆 await session.call_tool( store, { entity: demo, key: log-rule, content: 项目日志统一使用 SLF4J禁止 System.out.println, }, ) # 检索记忆 result await session.call_tool( search, {entity: demo, query: 日志 SLF4J, limit: 5}, ) print(检索结果:, result) if __name__ __main__: asyncio.run(main())7. 运行结果与效果验证7.1 启动 Serversource .venv/bin/activate python mcp_memory_server.py正常启动时FastMCP 会进入 stdio 监听模式没有报错输出。如果schema.sql路径不对或者目录没有写权限会抛出异常。7.2 运行冒烟测试另开一个终端运行python test_client.py预期输出类似发现工具数量: 4 - store - search - forget - clear 检索结果: content项目日志统一使用 SLF4J禁止 System.out.println ...这里判断成功有两个标准客户端能列出 4 个工具说明 MCP 协议握手正常。search能返回刚才store写入的内容说明 SQLite 写入和 FTS5 索引同步链路是通的。7.3 直接查库验证如果不想走 MCP 链路可以直接用 sqlite3 命令行检查数据sqlite3 ~/.mcp_memory/memory.db \ SELECT id, entity, key, content FROM memories;再验证 FTS5 是否命中sqlite3 ~/.mcp_memory/memory.db \ SELECT rowid, content FROM memories_fts WHERE memories_fts MATCH \SLF4J\;7.4 失败时第一步看哪里如果search返回空结果但memories表里有数据优先顺序是检查memories_fts表是否有对应 rowid。在 sqlite3 里手工执行 MATCH排除 MCP 层问题。查看escape_fts是否把查询短语处理得太严格。确认触发器的memories_au更新逻辑是否误删了索引。8. 常见问题与排查思路问题现象可能原因排查方式解决方案启动报错no such module: fts5当前 SQLite 未编译 FTS5sqlite3模块里执行CREATE VIRTUAL TABLE ... USING fts5验证更换 Python 环境或安装自带 FTS5 的 SQLite数据写入了但search搜不到触发器没建成功或索引未同步直接查memories_fts是否有 rowid重新执行schema.sql检查触发器是否存在MATCH 查询报语法错误用户输入包含特殊字符打印escape_fts输出用escape_fts统一转义禁止裸拼用户输入MCP 客户端列不出工具Server 未启动或配置路径错误用test_client.py直接测检查mcpServers配置、Python 环境和绝对路径多个实体互相串数据搜索时未按 entity 过滤确认 SQL 里AND m.entity ?所有查询强制带 entity 条件更新记忆后旧内容仍能搜到更新触发器没有删除旧索引手工验证memories_au检查 FTS5 外部内容表的 delete 语法数据库文件越来越大无清理策略FTS5 索引碎片积累查看磁盘占用和表行数定期INSERT INTO memories_fts(memories_fts) VALUES(optimize)定期清理过期记忆最容易被忽视的问题是触发器。外部内容表模式下如果业务表直接插入数据而触发器没有生效FTS5 索引永远是空的但业务表里看起来一切正常。所以每次初始化完建议先插入一条测试数据再立刻搜索把索引链路验证放最前面。9. 最佳实践与工程建议9.1 实体命名规范entity 建议用“类型:名字”的格式比如user:zhangsan、project:demo-api、team:backend。这样同一个 Server 可以服务多个实体检索时可以精确过滤。如果只用一个全局命名空间记忆之间会互相污染检索结果越来越不相关。key 命名建议统一小写下划线例如log-rule、db-conn-str、deploy-command。模型生成的 key 如果不约束会出现风格完全不一致的情况后续精确删除会很麻烦。9.2 记忆生命周期管理无界增长是记忆系统最常见的性能隐患。建议写入时带时间戳超过 30 天或 90 天的记忆定期归档或删除。对store设置幂等语义相同 entity key 重复写入时更新而不是新增避免同一事实被存成多行。清理时用 FTS5 的 optimize 命令合并索引段减小文件体积。-- 清理 30 天前、且属于临时会话实体的记忆 DELETE FROM memories WHERE entity LIKE session:% AND updated_at strftime(%s, now, -30 days);9.3 安全边界MCP Memory Server 本质上是本地工具但也要注意几个安全点默认只监听 stdio不要改成网络监听否则任何本机进程都能读写记忆。不要接受来自模型的可信度无限的用户输入拼进 SQL。FTS5 的 MATCH 语法用escape_fts处理业务字段用参数化查询。如果记忆里包含密钥、Token、个人数据建议在store层做加密或脱敏至少不要在 content 里明文保存密码。数据库文件权限默认设置为当前用户可读写不要用 777。9.4 何时该升级到向量检索如果实际使用中出现以下信号说明 FTS5 的关键词检索已经不够用用户经常用“意思相同但说法完全不同”的方式提问关键词完全匹配不到。记忆数量达到几十万条且查询结果相关性明显变差。需要按语义聚类、去重、推荐。这时候可以引入 embedding 向量索引但仍然建议保留 SQLite 做元数据存储用混合检索策略向量召回粗筛 FTS5 关键词精排。不是所有问题都要用向量库兜底。9.5 生产环境的工程建议数据库文件要纳入备份策略memory.db单文件可以直接用 cron 或系统备份任务定时拷贝。MCP Server 启动时增加健康检查逻辑先执行init_db()再对 FTS5 做一次SELECT count(*) FROM memories_fts失败直接退出并输出明确错误。日志要记录每次工具调用的 entity、key、耗时便于回溯模型的行为。多个 Agent 共享一个记忆库时考虑按 entity 做多实例隔离避免并发写锁竞争。10. 总结与后续学习方向这篇内容的核心是把“给 Agent 加记忆”从一个复杂工程问题简化成了一个可以用 SQLite 单文件解决的问题。关键点有三层MCP 提供标准化的工具调用协议让记忆读写对任何 MCP 客户端通用SQLite FTS5 提供无需外部服务的全文检索能力用倒排索引替代低效的 LIKE 扫描OKF 这类紧凑序列化格式负责压缩记忆体积降低 I/O 和内存开销。下一步你可以做这几件事先把文中的 schema.sql 和 mcp_memory_server.py 跑通用 test_client.py 完成冒烟测试然后接入 Claude Desktop 或 Codex试一试用自然语言让模型存记忆和取记忆接着根据你自己的使用场景调整实体命名和清理策略。如果对 FTS5 的查询语法感兴趣可以重点研究 MATCH 的布尔操作、短语查询和列过滤这些都是提升检索准确率的关键工具。如果你正在被 Agent 的“失忆问题”困扰这个方案值得花一晚上搭起来试一试。记住一个判断轻量方案解决不了语义检索时再上向量库而不是一开始就把系统做重。
返回列表