基于MCP协议与本地向量数据库,为AI编程助手构建持久化记忆中枢
1. 项目概述为AI助手构建一个本地记忆中枢如果你和我一样每天都要和Claude、Cursor这类AI编程助手打交道肯定会遇到一个头疼的问题对话上下文太短了。刚在昨天的对话里定好了项目的架构决策今天打开新会话AI助手又得从头问起仿佛得了“健忘症”。更麻烦的是那些在调试过程中积累的宝贵经验——比如某个API的特定调用方式、某个依赖库的版本兼容性陷阱——往往散落在不同的聊天记录里难以系统化地复用。这就是我动手搭建Local Memory MCP Server的初衷。它本质上是一个运行在你本地的、专为AI助手设计的“记忆外挂”。通过一套标准化的工具接口MCP协议你的AI助手可以像访问本地文件一样对过往的对话“记忆”进行存储、检索和更新。所有数据包括文本和用于语义搜索的向量都完全存储在本地无需连接任何外部数据库服务。我选择的技术栈非常轻量用Bun作为运行时用Zvec作为内置的向量数据库用Ollama本地运行的embeddinggemma模型来生成文本向量。整个方案的核心目标就一个让AI助手在跨会话、跨项目的协作中拥有持续、可靠且私密的长期记忆能力。2. 核心设计思路与架构解析2.1 为什么选择MCP协议作为桥梁MCPModel Context Protocol是Anthropic推出的一套开放协议旨在为AI模型提供一种标准化的方式来访问外部工具、数据和功能。你可以把它想象成AI世界的“USB接口”标准。在开发这个记忆服务器时我选择基于MCP协议来实现主要基于以下几点考量首先是生态兼容性。目前主流的AI编程环境如VS Code的GitHub Copilot Chat和Claude Code都已原生支持MCP。这意味着我开发的服务器无需为每个平台做大量适配工作只要遵循协议标准就能被这些环境识别和调用。这极大地降低了集成成本也保证了项目的可持续性。其次是协议设计的优雅性。MCP定义了一套清晰的请求-响应模型和工具Tools抽象。对于记忆服务来说save、search、delete这些操作天然地对应着MCP的Tool概念。这种映射关系非常直观使得服务器的逻辑结构可以设计得非常清晰一个工具对应一个核心记忆操作。最后是未来的可扩展性。采用标准协议意味着如果未来有新的AI助手或平台支持MCP我的记忆服务器可以几乎无缝地接入。这避免了被单一平台锁定的风险。基于这些原因虽然初期需要学习MCP的规范但长远来看这是构建一个通用、可移植记忆层的最佳路径。2.2 技术栈选型背后的逻辑一个本地记忆服务器的核心无非是两件事存数据、找数据。存数据要可靠、高效找数据要精准、快速尤其是要能理解语义而不是简单的关键词匹配。我的技术选型就是围绕这两个核心需求展开的。1. 运行时与语言Bun TypeScript我放弃了传统的Node.js选择了Bun。原因很简单性能和对现代Web标准的原生支持。Bun的启动速度极快这对于一个需要常驻、随时响应AI助手请求的服务器来说至关重要。此外Bun内置了SQLite、打包工具和测试运行器这让项目依赖更简洁部署也更轻便。配合TypeScript可以在开发阶段就捕获大量的类型错误对于处理像记忆元数据id, type, summary等这类结构化的数据非常友好能极大提升代码的健壮性和可维护性。2. 向量数据库Zvec向量数据库是语义搜索的引擎。市面上选择很多比如Chroma、Qdrant但我最终选择了Zvec一个新兴的嵌入式向量库。我做这个决定的关键因素是“零运维”和“轻量级”。Zvec是一个纯JavaScript/TypeScript库可以直接通过npm安装无需单独部署一个数据库服务进程。它直接将向量索引存储在本地文件如.zvec中。这意味着部署简单bun install就完成了数据库“部署”。资源占用低没有额外的守护进程内存占用少。数据隐私绝对可控所有数据都在一个本地文件里安全感十足。 虽然它在处理超大规模向量集上亿条时可能不如专业分布式向量数据库但对于个人或团队级别的项目记忆存储几千到几十万条它的性能完全绰绰有余且KNN最近邻搜索查询接口非常易用。3. 嵌入模型Ollama embeddinggemma文本要转换成向量才能进行语义搜索这就需要嵌入模型。我选择了通过Ollama在本地运行embeddinggemma模型。为什么不用OpenAI的API核心就两个字隐私和成本。隐私项目决策、代码片段、调试日志可能包含敏感信息。将这些数据发送到云端存在潜在风险。本地化处理从根本上杜绝了数据泄露的可能。成本与可控性使用本地模型没有API调用费用且网络延迟为零。Ollama管理模型非常方便ollama pull embeddinggemma就能准备好模型。embeddinggemma是一个在通用文本上表现良好的轻量级嵌入模型生成的768维向量在精度和存储效率上取得了很好的平衡。可靠性服务不依赖外部网络即使断网你的AI助手记忆功能依然完好。注意embeddinggemma模型生成的向量维度是768。在配置环境变量EMBEDDING_DIM时必须设置为768否则Zvec在创建向量集合时会因维度不匹配而报错。这是整合过程中最常见的配置错误之一。2.3 数据模型与工作流设计记忆不是简单的文本堆砌。为了有效管理和利用我设计了以下核心数据结构和交互流程记忆条目Memory Item结构每个记忆单元都包含以下字段这些字段既作为元数据用于过滤其文本内容summary,text又用于生成向量。id: 自增主键唯一标识。workspaceKey:项目的核心隔离维度。通常设置为项目文件夹名如my-nextjs-app确保不同项目的记忆互不干扰。type: 记忆类型如decision架构决策、preference偏好设置、gotcha踩坑记录、apiAPI约定等。方便分类检索。summary: 记忆的简短摘要是语义搜索的主要依据之一。text: 记忆的详细内容。tags: 字符串数组用于打标签辅助检索。importance: 一个0到1的浮点数表示记忆的重要性权重。可以在搜索排序时加以考虑。embedding: 由summary text生成的768维浮点数向量。supersededById: 如果该记忆被更新这个字段会指向新记忆的id。被取代的记忆将不会出现在搜索结果中实现了信息的版本管理。核心工作流这个设计模拟了人类的知识积累过程获取信息、关联旧知、更新认知。提问与检索当你在聊天中向AI助手提出一个问题时助手会先调用memory.search工具。这个工具会将你的问题文本通过Ollama转换成向量然后在Zvec数据库中针对当前workspaceKey下的所有记忆向量进行相似度搜索如余弦相似度返回最相关的几条历史记忆。回答与增强AI助手将检索到的相关记忆作为上下文结合当前代码库的情况给出更精准、更有连续性的回答。沉淀与更新如果本次对话产生了值得保留的新知识比如解决了一个复杂bug的步骤AI助手或你可以手动触发memory.save将其存储下来。如果发现某条旧记忆已经过时或不准确可以使用memory.supersede标记它已被新的记忆条目取代或者直接用memory.delete删除它。这个闭环使得AI助手不再是“金鱼”而是一个能够随着项目推进不断学习和积累的伙伴。3. 环境准备与项目初始化实操3.1 基础依赖安装与验证在开始之前我们需要确保两个核心依赖就位Bun 和 Ollama。安装BunBun的安装非常快捷。打开你的终端执行以下命令。我推荐使用官方安装脚本它通常能处理大多数Linux/macOS环境。curl -fsSL https://bun.sh/install | bash安装完成后重启你的终端然后运行bun --version来验证安装是否成功。你应该能看到类似1.1.17的版本号输出。安装与运行OllamaOllama的安装同样简单。访问 ollama.com 下载对应你操作系统macOS, Linux, Windows的安装包。安装后Ollama服务通常会自动启动。你可以在终端里通过ollama --version检查并通过ollama list查看已拉取的模型初始为空。接下来是关键一步拉取嵌入模型。在终端中运行ollama pull embeddinggemma这个命令会从Ollama的模型库中下载embeddinggemma模型。下载速度取决于你的网络模型大小约为几个GB。完成后再次运行ollama list你应该能看到embeddinggemma出现在列表中。实操心得在拉取模型后我强烈建议运行一个简单的测试确认Ollama的嵌入接口工作正常。你可以使用curl命令curl http://localhost:11434/api/embed -d {model: embeddinggemma, input: Hello, world}如果返回一个包含大量浮点数的JSON数组说明Ollama服务及嵌入模型都已准备就绪。如果遇到连接错误请检查Ollama服务是否正在运行例如在macOS上查看Ollama应用状态。3.2 获取与配置Local Memory MCP项目环境准备好后我们来部署记忆服务器本身。克隆项目仓库git clone https://github.com/basst85/local-memory-mcp.git cd local-memory-mcp进入项目目录后你会看到典型的TypeScript项目结构包括src/源代码、tests/测试、以及配置文件tsconfig.json和package.json。安装项目依赖项目使用Bun作为包管理器运行以下命令安装所有必要的npm包包括zvec、以及各种开发依赖如测试框架。bun install这个过程会下载Zvec等核心库。如果一切顺利你会在node_modules目录下看到它们。理解环境变量配置在运行前有必要了解如何配置这个服务器。所有配置都通过环境变量完成提供了极大的灵活性。核心变量如下MEMORY_DB_PATH: 指定Zvec数据库文件的存储路径。默认是./data/memory.zvec。如果你想在多个项目间共享一个记忆库这里需要设置为一个绝对路径。OLLAMA_BASE_URL: Ollama服务的地址。默认是http://localhost:11434。如果你的Ollama运行在其他机器或端口上需要修改此变量。OLLAMA_EMBED_MODEL: 使用的嵌入模型名称。默认是embeddinggemma。如果你拉取并想使用其他模型如nomic-embed-text在此处更改。EMBEDDING_DIM: 嵌入向量的维度。必须与你使用的模型输出维度严格一致。对于embeddinggemma就是768。WORKSPACE_KEY: 工作空间键。这是隔离不同项目记忆的标识符。默认是default。在实际使用中我们通常会通过脚本或集成环境动态设置它例如设为当前项目文件夹名。你可以直接在启动命令前设置这些变量例如MEMORY_DB_PATH/absolute/path/to/memory.db OLLAMA_BASE_URLhttp://localhost:11434 bun run start但更常见的做法是在后续的VS Code或Claude Code配置文件中进行设置。4. 核心功能工具详解与使用范例服务器通过五个MCP工具对外提供服务。理解每个工具的输入输出是有效利用它的关键。下面我将结合具体JSON示例深入解析每个工具。4.1memory.search语义检索记忆这是最常用的工具用于在对话中为AI助手提供相关背景。{ tool: memory.search, arguments: { query: 我们项目里关于错误处理中间件的设计决策是什么, topK: 5, workspaceKey: my-express-api, typeFilter: decision } }query(字符串必需)要搜索的查询文本。AI助手会将用户当前的问题或对话上下文浓缩成一句查询。技巧让AI助手将复杂问题提炼成一句陈述句或疑问句搜索效果通常比直接扔一大段代码更好。topK(整数可选)返回最相似记忆的数量默认可能是10。根据上下文窗口大小合理设置避免返回过多无关信息。workspaceKey(字符串可选)指定在哪个工作空间内搜索。如果AI助手能感知当前项目路径它应该自动传入此参数。如果省略服务器可能会使用默认值或从上下文中推断。typeFilter(字符串可选)按记忆类型过滤。例如当你只想查找“决策”类记忆时非常有用。也可以结合tags进行更复杂的过滤具体取决于服务器实现。服务器内部发生了什么服务器收到请求提取query文本。调用本地Ollama服务的/api/embed接口将query转换为一个768维的向量。在Zvec数据库中对指定workspaceKey和可能的typeFilter下的所有记忆条目的embedding字段执行KNN查询。计算查询向量与每个记忆向量的相似度分数如余弦相似度。按分数降序排列返回前topK条记忆的完整信息包括id, summary, text等。4.2memory.save存储新记忆当有值得保留的新知识产生时调用此工具。{ tool: memory.save, arguments: { workspaceKey: my-express-api, type: gotcha, summary: 在Express中异步错误必须在中间件内捕获否则会崩溃。, text: 具体细节使用app.use((err, req, res, next) {...})定义错误处理中间件时如果路由处理函数是async的其内部抛出的错误不会被自动捕获。必须用try...catch包裹或在函数内部调用next(err)。否则进程会退出。解决方案使用express-async-errors包或在每个async函数中显式处理。, tags: [express, error-handling, async, middleware], importance: 0.9 } }summary和text这是核心。summary应简洁是搜索时的主要匹配目标。text可以详细。两者都会用于生成嵌入向量。type和tags良好的分类和标签是未来高效检索的基础。建议和团队约定几种固定的type如decision,api,gotcha,reftags则可以根据技术栈、功能模块自由添加。importance一个主观权重。你可以让AI助手在保存时根据信息的关键程度如“核心架构决策” vs “临时调试笔记”赋予一个值。未来在搜索排序时可以结合相似度和重要性进行加权。4.3memory.supersede与memory.delete记忆管理知识会过时需要维护。memory.supersede用于知识更新。例如之前决定使用“方案A”后来改用了“方案B”。你可以保存新的“方案B”记忆然后调用supersede将旧记忆的id和新记忆的id关联起来。这样搜索时旧记忆会被自动过滤掉但历史记录得以保留便于追溯变更。{ tool: memory.supersede, arguments: { workspaceKey: my-project, oldId: 15, newId: 28 } }memory.delete用于删除无用或错误的记忆。操作简单直接根据id删除。{ tool: memory.delete, arguments: { workspaceKey: my-project, id: 7 } }注意事项supersede是一种“软删除”保留了信息链路。delete是“硬删除”数据不可恢复。对于重要的架构决策记录建议使用supersede对于明显的临时性、测试性记录可以使用delete。4.4memory.ping健康检查这是一个简单的工具用于测试服务器是否正常运行通常返回服务器版本或状态信息。在配置集成时可以用来验证连接。{ tool: memory.ping }5. 与开发环境深度集成记忆服务器的价值在于被AI助手无缝调用。下面详细介绍如何将其集成到VS Code和Claude Code中。5.1 集成到VS Code Copilot ChatVS Code通过mcp.json配置文件来管理MCP服务器。项目根目录下已经提供了一个.vscode/mcp.json示例。项目级集成推荐这种方式将服务器配置保存在项目内便于团队共享。确保你的VS Code已经安装了GitHub Copilot Chat扩展。打开你正在开发的项目比如~/projects/my-app而不是记忆服务器项目本身。在你的项目根目录下创建或修改.vscode/mcp.json文件。填入类似以下的配置。关键是要将args中的--cwd路径指向你本地克隆的local-memory-mcp仓库目录并将MEMORY_DB_PATH也设置为一个绝对路径以实现跨项目共享记忆库。{ servers: { local-memory-mcp: { type: stdio, command: bun, args: [--cwd, /Users/yourname/code/local-memory-mcp, run, start], env: { MEMORY_DB_PATH: /Users/yourname/code/local-memory-mcp/data/memory.zvec, OLLAMA_BASE_URL: http://localhost:11434, OLLAMA_EMBED_MODEL: embeddinggemma, EMBEDDING_DIM: 768, WORKSPACE_KEY: ${workspaceFolderBasename} } } } }配置解析command和args告诉VS Code如何启动我们的Bun服务器。env设置了环境变量。特别注意WORKSPACE_KEY${workspaceFolderBasename}是VS Code的变量会自动替换为当前打开的项目文件夹名称如my-app。这完美实现了记忆按项目自动隔离。设置MEMORY_DB_PATH为绝对路径确保无论从哪个项目启动记忆都存到同一个数据库文件。保存后重启VS Code或重新加载窗口。打开Copilot Chat面板如果配置正确你应该能在聊天界面看到可用的工具列表里出现memory.*系列工具。用户级集成如果你希望在所有VS Code项目中都可用这个记忆服务器可以将其添加到用户全局配置。在VS Code中按下CmdShiftP(Mac) 或CtrlShiftP(Windows/Linux)输入并选择“MCP: Open User Configuration”。这会在全局位置打开一个settings.json文件。在其中添加上述servers配置块即可。5.2 集成到Claude CodeClaude Code是Anthropic官方的IDE扩展也深度支持MCP。集成方式类似但使用其命令行工具claude。项目级集成在需要共享记忆的项目根目录下运行以下命令。这会修改当前项目的.mcp.json文件。cd /path/to/your/project claude mcp add --transport stdio --scope project \ --env MEMORY_DB_PATH./data/memory.zvec \ --env OLLAMA_BASE_URLhttp://localhost:11434 \ --env OLLAMA_EMBED_MODELembeddinggemma \ --env EMBEDDING_DIM768 \ --env WORKSPACE_KEY${PWD##*/} \ local-memory-mcp -- bun run start注意命令中的----之前是claude mcp add命令的参数--之后是启动MCP服务器的命令。${PWD##*/}是Shell参数展开会获取当前目录名作为WORKSPACE_KEY。用户级集成如果你希望在任何地方使用Claude Code都能调用这个记忆将其添加到用户全局配置。claude mcp add --transport stdio --scope user \ --env MEMORY_DB_PATH/absolute/path/to/local-memory-mcp/data/memory.zvec \ --env OLLAMA_BASE_URLhttp://localhost:11434 \ --env OLLAMA_EMBED_MODELembeddinggemma \ --env EMBEDDING_DIM768 \ --env WORKSPACE_KEYdefault \ local-memory-mcp -- bun --cwd /absolute/path/to/local-memory-mcp run start这里使用了绝对路径并设置了一个默认的WORKSPACE_KEY。在实际对话中你可能需要手动指定或在不同的项目中使用不同的键。管理已配置的服务器claude mcp list列出所有已配置的MCP服务器。claude mcp get local-memory-mcp查看特定服务器的配置详情。claude mcp remove local-memory-mcp移除服务器配置。6. 高级配置、优化与问题排查6.1 性能调优与数据维护随着记忆条目的增多比如超过1万条一些优化可以考虑1. Zvec索引优化Zvec在后台会自动构建索引以加速搜索。但对于写入频繁的场景大量save操作你可以考虑在非高峰时段手动触发索引优化或者查阅Zvec文档看是否有针对读写比例的配置参数可以调整。默认配置对于中小规模数据已经足够高效。2. 嵌入批处理当前实现是每次搜索或保存时实时调用Ollama生成嵌入向量。如果遇到性能瓶颈可以考虑实现一个简单的批处理队列或缓存层。例如对于save操作可以先存储文本内容标记为“待嵌入”然后由一个后台进程定期批量处理这些待嵌入项。但这会增加系统复杂性除非确实需要否则不建议过早优化。3. 记忆清理策略定期清理无用记忆很重要。可以定期检查importance值很低的记忆条目。对于被supersede取代的旧记忆可以考虑在保留一段时间后归档或删除。编写一个简单的脚本定期连接数据库根据时间戳、类型、最后访问频率等维度进行清理。Zvec的数据库是单个文件备份起来也很方便。6.2 常见问题与解决方案速查表在实际部署和使用中你可能会遇到以下问题。这里我总结了一份排查清单问题现象可能原因解决方案VS Code/Claude Code中看不到memory.*工具1. MCP服务器配置错误或路径不对。2. 服务器进程启动失败。3. 扩展未正确加载MCP配置。1. 检查.vscode/mcp.json或.mcp.json中的command和args路径是否正确。2. 在终端手动进入服务器目录运行bun run start看是否有错误输出如Bun未安装、依赖缺失。3. 重启IDE或重新加载窗口。调用memory.search返回空或无关结果1.WORKSPACE_KEY不匹配在错误的空间搜索。2. Ollama嵌入模型未运行或返回错误。3. 数据库中没有当前工作空间的记忆。1. 确认调用工具时传入的workspaceKey与保存记忆时使用的一致。2. 检查Ollama服务状态(ollama list)并测试嵌入API(curl命令)。3. 先用memory.save存一条测试记忆再搜索试试。memory.save失败或报错1. 环境变量EMBEDDING_DIM与模型实际维度不匹配。2. Zvec数据库文件权限问题。3. Ollama生成嵌入失败。1.确保EMBEDDING_DIM设置为768对于embeddinggemma。2. 检查MEMORY_DB_PATH指向的目录是否有写入权限。3. 查看服务器进程的日志输出确认Ollama调用是否返回了有效的向量。服务器启动时报“Cannot find module”项目依赖未安装。在local-memory-mcp项目根目录下执行bun install。搜索或保存操作速度慢1. 首次调用需要启动Ollama模型冷启动。2. 记忆条目数量极大10万。3. 硬件资源CPU/内存不足。1. 冷启动属正常现象后续调用会快很多。2. 考虑对记忆进行分库或按时间分区。3. 确保机器有足够内存供Ollama模型加载。跨项目记忆混淆WORKSPACE_KEY设置成了固定值如default或者配置中未使用动态变量。在集成配置中务必使用${workspaceFolderBasename}VS Code或${PWD##*/}Shell来动态设置WORKSPACE_KEY实现自动隔离。6.3 扩展可能性这个项目是一个坚实的基础你可以根据需求进行扩展1. 支持更多嵌入模型修改src/embed.ts文件中的getEmbedding函数。除了Ollama你可以集成其他本地库如xenova/transformers或支持其他本地API如LocalAI。只需确保函数返回正确维度的Float32Array。2. 增加记忆关联性目前的搜索是基于向量相似度的“扁平”搜索。可以扩展数据模型增加记忆之间的“链接”字段如relatedMemoryIds实现简单的图谱功能让AI助手能进行关联性回忆。3. 实现记忆自动摘要与提炼可以定期运行一个后台任务让AI助手通过本地LLM对同一workspaceKey下的记忆进行回顾、去重和摘要生成更精炼的“项目知识图谱”并保存提升检索质量。4. 添加基于时间的衰减权重在搜索排序算法中引入时间衰减因子让较新的记忆在相似度接近时排名更靠前这更符合软件开发中近期决策更相关的特点。配置完成后最直接的测试方法就是直接向你的AI助手提问。你可以尝试问一些关于当前项目架构、之前解决过的特定错误的问题。如果配置正确AI助手会在后台调用memory.search并在其回复中提及或直接利用它找到的记忆。你也可以指示AI助手保存一条当前对话中的重要结论然后在新会话中验证是否能被检索到。这个过程能让你最直观地感受到这个本地记忆中枢如何打破会话间的壁垒让AI助手真正成为你项目里拥有长期记忆的协作伙伴。