给 Claude Code 装上“长期记忆“:本地部署双重记忆引擎实战

写在前面

用 Claude Code 做项目开发有个痛点------它没有长期记忆。

每次 /clear 或开新会话,之前讨论过的技术决策、踩过的坑、约定好的规范,全部归零。虽然 Claude Code 自带了 Auto Memory(会往 MEMORY.md 里写一些东西),但说实话,这个机制的召回精度和容量都很有限。你在 A 项目里踩过的坑,切到 B 项目就完全不记得了。

这篇文章记录我实际跑通的一套方案:在本地搭建双重记忆引擎,让 Claude Code 拥有跨会话、跨项目的持久记忆能力。零云端依赖,全部跑在本地。


一、整体思路:三层记忆架构

先说结论,最终落地的是一个三层结构:

复制代码
┌─────────────────────────────────────────────┐
│           Claude Code 用户交互层              │
│     CLAUDE.md / Auto Memory / Skills         │
└────────┬────────────────────┬───────────────┘
         │ MCP 工具调用        │ ov CLI
┌────────▼──────────┐  ┌──────▼──────────────┐
│ memory-engine     │  │ OpenViking Server   │
│ :17823 (SSE)      │  │ :1933               │
│                   │  │                     │
│ 会话记忆           │  │ 知识库管理           │
│ LanceDB 向量存储   │  │ 目录递归检索         │
│ Weibull 衰减      │  │ L0/L1/L2 分层加载    │
│ 混合检索           │  │ 结构化记忆           │
└────────┬──────────┘  └──────┬──────────────┘
         └──────┬─────────────┘
         ┌──────▼──────┐
         │ Ollama      │
         │ bge-m3      │  ← Embedding 模型(两个系统共享)
         │ qwen2.5:14b │  ← VLM(按需加载)
         └─────────────┘

每一层干什么:

层级 系统 存什么 怎么找
L1 即时层 Claude Code 原生 Auto Memory MEMORY.md 里的项目速览 每次会话全量注入上下文
L2 会话层 memory-engine(自建 MCP Server) 偏好、决策、踩坑、规范 向量+BM25 混合检索 + 时间衰减
L3 知识层 OpenViking 文档、代码库、深度上下文 语义搜索 + 分层加载

为什么要分三层?因为 Claude Code 的上下文窗口是有限的。L1 每次全量加载(几百 token),L2 按需召回(省 token),L3 只在需要深度知识时才拉取。三层各有分工,不互相抢上下文。


二、memory-engine:核心会话记忆

这是整套方案里最有价值的部分。简单说就是一个本地跑的 MCP Server,底层用 LanceDB 做向量存储,提供四个工具:

  • memory_store --- 存记忆
  • memory_recall --- 搜记忆
  • memory_forget --- 删记忆
  • memory_stats --- 看统计

2.1 为什么不直接用 Auto Memory

Auto Memory 有三个硬伤:

  1. 容量限制MEMORY.md 超过 200 行就会被截断
  2. 无衰减机制:三个月前存的过时信息和昨天存的新决策权重一样
  3. 无项目隔离:切到不同项目,记忆是混在一起的

memory-engine 解决了这三个问题。

2.2 混合检索 + Weibull 衰减

记忆不是存了就完事,关键是怎么找回来

检索分两路并行:

  • 向量检索:把查询语句 embedding 成向量,在 LanceDB 里做 ANN 搜索
  • BM25 全文检索:传统关键词匹配,处理精确术语命中

两路结果融合后,再叠加 Weibull 衰减

javascript 复制代码
// 核心衰减公式
function weibullScore(ageDays, tier) {
  const halfLife = { core: 90, working: 30, peripheral: 7 }[tier];
  const beta = { core: 1.2, working: 1.0, peripheral: 0.8 }[tier];
  const lambda = halfLife / Math.pow(Math.LN2, 1 / beta);
  return Math.exp(-Math.pow(ageDays / lambda, beta));
}

什么意思呢?记忆按重要性分三档:

  • 核心记忆(importance ≥ 0.8):半衰期 90 天,比如"项目用 PostgreSQL 15"
  • 工作记忆(importance 0.5~0.8):半衰期 30 天,比如"上次部署时改了 nginx 配置"
  • 外围记忆(importance < 0.5):半衰期 7 天,比如"今天 debug 了一个 CSS 对齐问题"

最终得分还会乘以访问频次------被多次召回的记忆会越来越"牢固",类似间隔重复学习。

2.3 项目隔离

通过 scope 参数实现:

复制代码
memory_store({content: "偏好 dark mode", scope: "global"})
  → 跨项目共享

memory_store({content: "本项目用 4 空格缩进", scope: "project:my-api"})
  → 仅 my-api 项目可见

memory_recall({query: "缩进偏好", scope: "project:my-api"})
  → 搜 project:my-api + global 两个范围

在 A 项目里存的决策,不会污染 B 项目的记忆空间。但全局偏好(比如"我习惯用 vim 键位")在所有项目里都能找到。

2.4 实际效果

部署完成后,在 Claude Code 里直接测试:

复制代码
> 调用 memory_stats 看看记忆统计

memory_stats 返回:
{
  "total": 47,
  "by_scope": {
    "global": 12,
    "project:blue-erp": 28,
    "project:ai-tools": 7
  },
  "by_category": {
    "decision": 15,
    "lesson": 11,
    "convention": 8,
    "preference": 6,
    "fact": 4,
    "issue": 3
  }
}

存一条记忆:

复制代码
> 记住:部署测试环境时,jar 包超过 100MB 必须分包上传,否则宝塔面板会截断文件

memory_store 返回:
{
  "id": "a3f7...",
  "status": "stored",
  "scope": "project:blue-erp"
}

过了几天,在新会话里检索:

复制代码
> 我要部署到测试环境,有什么注意事项吗?

memory_recall 命中:
[
  {
    "content": "部署测试环境时,jar 包超过 100MB 必须分包上传,否则宝塔面板会截断文件",
    "score": 0.847,
    "category": "lesson",
    "created_at": "2026-03-16"
  },
  {
    "content": "测试环境 SQL 变更记录在 sql/update/update.sql,追加式写入",
    "score": 0.623,
    "category": "convention"
  }
]

这就是"长期记忆"的实际体验------上次踩的坑,这次自动提醒你。


三、OpenViking:知识库层

OpenViking 解决的是另一个问题:项目文档和代码库的深度检索

比如你有一个 ERP 项目,设计文档有 20 多份,会议纪要 10 份,数据库设计 4 份。每次开新会话都让 Claude 重新读一遍?不现实。

OpenViking 的做法是把这些文档预处理后存进向量库,需要时语义搜索拉取。它有个 L0/L1/L2 分层机制------搜索时先返回摘要(L1),需要详情时再加载全文(L2),省 token。

bash 复制代码
# 添加项目文档到知识库
ov add-resource ~/projects/blue-erp/docs --preserve-structure --wait

# 语义搜索
ov find "借货归还的业务流程" --limit 3

# 输出
Results:
  1. [0.89] viking://resources/blue-erp/docs/数据库设计/成品借货流程.md
     "借货归还支持三种模式:全部归还、部分归还、借转销..."
  2. [0.76] viking://resources/blue-erp/docs/会议纪要/0208会议.md
     "借货消耗按借出数量计算,包含在价格里..."
  3. [0.71] viking://resources/blue-erp/docs/问题确认.txt
     "借货归还单需增加明细表..."

四、部署要点(踩坑记录)

整套系统的部署链路是:Ollama → OpenViking → memory-engine → Claude Code 集成。

4.1 Ollama 是基座

两个系统都依赖 Ollama 提供 Embedding。我选的是 bge-m3(1024 维,中英文混合场景最强),VLM 用 qwen2.5:14b

踩坑:Ollama 必须配置开机自启。memory-engine 的 recall 操作需要调 Ollama 做 embedding,如果 Ollama 没运行,recall 会静默返回空结果------不报错,就是搜不到东西。排查了很久才发现。

4.2 OpenViking 的 Embedding 配置

OpenViking 的 embedding provider 不支持直接写 ollama,要写 openai,然后通过 api_base 指向 Ollama 的 OpenAI 兼容接口:

json 复制代码
{
  "embedding": {
    "dense": {
      "provider": "openai",
      "model": "bge-m3",
      "api_base": "http://localhost:11434/v1",
      "api_key": "no-key",
      "dimension": 1024
    }
  }
}

VLM 配置同理,api_key"no-key" 就行。

4.3 memory-engine 的 SSE 多会话并发

这个是最大的坑。

MCP SDK 的 McpServer 只能绑定一个 transport。如果用 stdio 模式,每开一个 Claude Code 工作空间就 fork 一个进程,10 个窗口就 10 个进程,每个 80MB。

改用 SSE 模式后,只需一个守护进程。但第二个会话连上来时,server.connect() 直接抛 "Already connected to a transport" 把进程搞崩了。

最终的解决方案是工厂模式:每个 SSE 连接创建独立的 McpServer 实例,但共享全局的 LanceDB 连接和 OpenAI client。McpServer 实例本身极轻量(几 KB 内存),重资源全局单例共享。

javascript 复制代码
// 每个连接创建独立 McpServer
function createMcpServer() {
  const srv = new McpServer({ name: "memory-engine", version: "1.0.0" });
  // 注册工具(共享全局 DB)
  srv.tool("memory_store", ..., async (args) => memoryStore(args));
  srv.tool("memory_recall", ..., async (args) => memoryRecall(args));
  return srv;
}

// SSE 连接处理
if (req.url === "/sse") {
  const transport = new SSEServerTransport("/messages", res);
  const srv = createMcpServer();  // 每连接独立实例
  await srv.connect(transport);
}

4.4 Zod 版本兼容

另一个坑:npm install zod 默认装 v4,但 MCP SDK 内部用的 zod-to-json-schema 不兼容 v4,tools/list 直接报 "Cannot read properties of undefined (reading '_zod')"

解决:锁定 Zod v3:

bash 复制代码
npm install "zod@^3.25"

4.5 L2 距离归一化

memory-engine 早期版本的 minScore 设了 0.3,导致 recall 几乎总是空结果。原因是 LanceDB 默认用 L2 距离(范围 0~2),归一化向量的 L2 距离大多在 0.5~1.5 之间,直接用 1 - distance 会产生负数。

修复:

javascript 复制代码
// 错误:1 - distance 可能为负
score = 1 - r._distance;

// 正确:L2 归一化到 [0, 1]
score = Math.max(0, 1 - (r._distance || 0) / 2);

同时 minScore 从 0.3 降到 0.05。


五、自动记忆捕获

记忆系统最怕的不是存不进去,是用户忘了存

实际使用中,90% 的有价值信息不是用户说"记住这个"产生的,而是隐含在对话过程中------纠错、追问、约束嵌入。

解决方案是在 CLAUDE.md 里写规则,让 Claude 自己判断什么时候该存:

用户说了什么 Claude 的动作
"不是 X 而是 Y" 存纠正后的正确信息,importance: 0.9
"零 X 依赖"、"不需要 Y" 提取偏好约束,importance: 0.8
"要求..."、"必须..." 提取约束条件,importance: 0.8
技术决策、架构选择 原文存储,importance: 0.7~0.9

再配合 /save-session 命令做会话结束时的兜底提取,基本不会漏重要信息。


六、资源占用

在 24GB 内存的 Mac 上实测:

组件 常驻内存 说明
Ollama bge-m3 ~1.2 GB Embedding,两个系统共享
Ollama qwen2.5:14b ~9.0 GB VLM,按需加载,空闲自动卸载
OpenViking Server ~300 MB Python HTTP 服务
memory-engine MCP ~80 MB Node.js + LanceDB
日常实际占用 ~6 GB VLM 空闲时自动卸载

16GB 内存的机器也能跑,把 VLM 换成 qwen2.5:7b,Embedding 换成 nomic-embed-text,总占用降到 ~8GB。


七、健康检查

部署完写了个一键检查脚本,日常开机跑一下确认所有服务正常:

bash 复制代码
$ bash ~/check-memory-stack.sh

====== Memory Stack Health Check ======
[Ollama]        OK  models: bge-m3:latest, qwen2.5:14b
[Embedding]     OK  bge-m3 dim=1024
[OpenViking]    OK  :1933
[memory-engine] OK  SSE :17823  sessions=2
=======================================

四个绿就说明一切正常。


八、总结

跑了两周下来,这套方案最明显的收益是上下文连续性

以前开新会话,得花 5~10 分钟把项目背景重新说一遍。现在 Claude 自动从记忆里拉取之前的决策和规范,直接进入干活状态。

特别是跨项目切换的场景------从前端项目切到后端项目,每个项目的记忆是隔离的,不会串。但全局偏好(代码风格、工具选择)又是共享的。

适合的场景

  • 长期维护的中大型项目(记忆越积越有价值)
  • 多项目并行开发(项目隔离避免混淆)
  • 团队有固定的编码规范和业务规则(存一次,每次自动加载)

不太适合的场景

  • 一次性脚本、临时任务(用完即走,不需要记忆)
  • 对隐私极度敏感的场景(记忆存在本地磁盘,需要自行管理)

整套方案全部开源组件,零云端依赖,数据不出本机。如果你也在用 Claude Code 做日常开发,可以试试。

相关推荐
本旺20 小时前
【Openclaw 】完美解决 Codex 认证失败
ai·codex·openclaw·小龙虾·gpt5.4
好运的阿财21 小时前
process 工具与子agent管理机制详解
网络·人工智能·python·程序人生·ai编程
花燃柳卧21 小时前
AI 团队工作流工程化架构方案
人工智能·ai编程·ai工作流
张張40821 小时前
(域格)环境搭建和编译
c语言·开发语言·python·ai
HashTang21 小时前
用自然语言驱动的开源 3D 建筑设计编辑器-Aedifex
前端·github·ai编程
乐鑫科技 Espressif21 小时前
使用 MCP 服务器,把乐鑫文档接入 AI 工作流
人工智能·ai·esp32·乐鑫科技
语戚21 小时前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
俊哥V21 小时前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
张涛酱1074561 天前
ALTK-Evolve:让AI Agent告别"永恒实习生"困境的长期记忆系统
ai编程