☰
claude-mem 记忆机制设计:从抽取、存储到召回的全链路实践
2026/10/9 9:14:22 网站建设 项目流程

1. 项目概述与核心定位

1.1 这个项目到底在解决什么问题

第一次看到claude-mem这个名字,我的直觉是:这应该是一个给 Claude 系列模型做“记忆管理”的工具。事实也确实如此。它要解决的核心痛点非常明确——大语言模型在长对话、多轮任务、跨会话协作中,上下文窗口有限、历史信息容易丢失、重复交代背景的成本极高。

你可以把它理解成给 Claude 配了一个“外置大脑”。平时你跟 Claude 聊天,聊完就散了,下次再聊它完全不记得你是谁、之前做过什么。claude-mem做的事情,就是把这些对话中的关键信息抽取出来,存到一个结构化的记忆库里,下次需要的时候再按需召回,塞回上下文里。

这个项目适合谁?我梳理了三类人:

  • 重度 Claude 用户:每天要跟 Claude 来回几十轮,做代码审查、文档撰写、方案设计的人。
  • AI 应用开发者:想在自己的产品里给 Claude 加上长期记忆能力,但不想从零造轮子。
  • AI Agent 研究者:关注记忆机制、上下文压缩、检索增强生成(RAG)在对话场景落地的技术人。

1.2 为什么“记忆”这件事值得单独做一个项目

很多人会问:Claude 本身不是支持 200K 甚至更长的上下文吗?直接全塞进去不就行了?

这个问题我踩过坑。上下文窗口大,不代表你应该把所有东西都塞进去。原因有三个:

第一,成本。上下文越长,token 消耗越大,API 调用费用是线性甚至超线性增长的。你每次对话都带 10 万 token 的历史,钱包扛不住。

第二,注意力稀释。模型在超长上下文里,对关键信息的注意力会被大量无关内容稀释。我实测过,同样一个问题,带 5 万 token 冗余历史 vs 带 2000 token 精准记忆,后者的回答质量明显更稳。

第三,跨会话持久化。上下文窗口再大,也是单次会话内的。你关掉窗口,一切归零。而真实的工作场景是跨天、跨周、跨项目的,需要的是持久化记忆。

claude-mem的价值就在于:它不追求“记住所有”,而是追求“记住该记的,忘掉该忘的,需要时能精准找回来”。

1.3 核心能力全景

我把它的核心能力拆成四个层次,方便你建立整体认知:

能力层次具体功能解决的核心问题
记忆抽取从对话中识别关键信息什么值得记
记忆存储结构化持久化到本地或数据库存在哪、怎么存
记忆召回按相关性检索并注入上下文怎么找回来
记忆管理去重、更新、过期、压缩怎么保持记忆库健康

这四层是递进关系。抽取做不好,存的就是垃圾;召回做不好,存了也白存;管理做不好,记忆库会越来越臃肿,最后拖垮整个系统。

提示:很多人一上来就关注“怎么存”,其实最难的、最影响效果的是“抽取”和“召回”。存储层用 SQLite 还是向量库,反而是最容易替换的部分。

2. 记忆机制的设计思路与方案选型

2.1 为什么不用纯向量检索

一提到“记忆”,很多人的第一反应是上向量数据库,把所有对话切片、embedding、存进去,需要时做相似度检索。这个方案能用,但claude-mem这类项目通常不会只用向量检索,原因值得说清楚。

纯向量检索有三个硬伤:

  • 语义漂移:用户说“上次那个方案”,向量检索可能召回一堆“方案”相关的片段,但未必是“上次”那个。时间、指代、上下文关系,向量表达不好。
  • 无法精确匹配:用户问“我上周三提到的那个 API key 叫什么”,向量检索对精确的实体、时间、数字不敏感。
  • 缺乏结构:记忆之间是有关系的,比如“项目 A 用了技术 B,技术 B 有个坑 C”。向量检索是扁平的,表达不了这种图结构。

所以成熟的做法是混合检索:向量检索负责语义相似,关键词检索(BM25 之类)负责精确匹配,再加一层结构化过滤(时间、类型、来源)。三者加权融合,效果比单一方案稳得多。

2.2 记忆的分层设计

我在实际项目里总结出一个好用的分层模型,claude-mem的设计思路也基本吻合:

第一层:工作记忆(Working Memory)就是当前会话的上下文,容量小、更新快、随会话结束而清空。这一层不需要持久化,交给模型的原生上下文就行。

第二层:短期记忆(Short-term Memory)最近几次会话的关键信息,比如最近三天的对话摘要、当前正在进行的任务状态。这一层需要持久化,但可以设置过期时间。

第三层:长期记忆(Long-term Memory)稳定的用户偏好、项目背景、领域知识、重要决策记录。这一层长期保留,需要定期整理和压缩。

分层的意义在于:不同层用不同的存储策略、不同的召回优先级、不同的过期规则。如果所有记忆一锅炖,要么召回不准,要么存储爆炸。

2.3 抽取策略:什么该记,什么该忘

这是整个系统里最考验设计功力的地方。我的经验是,抽取要基于“信息熵”和“复用价值”两个维度判断。

高复用价值 + 高信息熵的内容,比如:

  • 用户的明确偏好(“我习惯用 TypeScript,不要给我 JavaScript 示例”)
  • 项目的关键约束(“这个系统不能停机,所有改动要支持热更新”)
  • 重要的决策和原因(“选 PostgreSQL 是因为需要 JSONB 和全文检索”)
  • 具体的实体和参数(API 地址、字段名、配置项)

低价值、该过滤掉的内容:

  • 寒暄和确认(“好的”“明白了”“谢谢”)
  • 重复信息(同一件事说了三遍)
  • 临时性、一次性的内容(“帮我把这句话翻译一下”)

实操中,抽取通常用一个轻量 prompt 让模型做判断,输出结构化的 JSON。这里有个技巧:不要只让模型判断“是否重要”,而是让它输出记忆的类型、内容、置信度和建议的过期时间。这样后续管理才有依据。

2.4 存储选型:SQLite 还是向量库

这是被问得最多的问题。我的建议是:起步阶段用 SQLite + 向量扩展,规模上来后再考虑专用向量库。

理由如下:

  • SQLite 零运维、单文件、事务可靠,配合sqlite-vec或sqlite-vss扩展就能做向量检索。
  • 记忆数据量在个人使用场景下,通常几千到几万条,SQLite 完全扛得住。
  • 结构化字段(时间、类型、来源)和向量字段放一起,查询方便,不用跨系统 join。
  • 迁移成本低。真到了需要专用向量库的时候,数据导出再导入就行。

如果你一上来就上分布式向量库,运维复杂度会吃掉你大量精力,而收益在早期几乎为零。这是我踩过的坑,分享给你避雷。

3. 核心实现细节与实操要点

3.1 记忆抽取的 Prompt 设计

抽取质量直接决定整个系统的上限。我用的 prompt 结构大致是这样的(这是基于常见实践的合理补全):

EXTRACT_PROMPT = """ 你是一个记忆抽取器。从下面的对话中,识别出值得长期记住的信息。 判断标准: 1. 用户偏好、习惯、明确要求 2. 项目背景、技术约束、关键决策 3. 具体实体:人名、项目名、API、配置项、参数 4. 未完成的任务和待办事项 不要抽取:寒暄、确认、重复内容、临时性请求。 对每条记忆,输出: - type: preference | fact | decision | task - content: 简洁的陈述句 - confidence: 0-1 - ttl_days: 建议保留天数,0 表示永久 对话内容: {dialogue} 以 JSON 数组输出。 """

这里有几个关键点:

第一,类型划分要克制。类型太多,模型判断容易乱;类型太少,后续召回没法做结构化过滤。我试过 4 到 6 个类型是比较舒服的区间。

第二,让模型输出置信度。后续召回时可以按置信度加权,低置信度的记忆不参与召回,或者只在没有高置信度结果时才用。

第三,TTL 让模型建议,但最终由系统决定。模型对时间的判断不一定准,但它的建议可以作为参考。系统层面再根据类型设置默认 TTL,比如 preference 永久,task 默认 30 天。

3.2 记忆去重与合并

不做去重,记忆库会迅速膨胀。用户每次说“我用 TypeScript”,都存一条,一个月后就有几十条重复。

去重的策略分两步:

第一步:精确去重。对记忆内容做归一化(去空格、统一大小写、同义词替换),然后做哈希,哈希相同直接丢弃。

第二步:语义去重。对归一化后的内容做 embedding,计算余弦相似度。相似度超过阈值(我一般设 0.92)的,判定为重复或高度相似。

对于高度相似但不完全相同的记忆,做合并而不是简单丢弃。合并规则:

  • 同类型、同主体的记忆,保留信息量更大的那条。
  • 如果新记忆包含旧记忆没有的细节,用新记忆替换旧记忆,但保留旧记忆的创建时间。
  • 冲突的记忆(比如用户先说“用 MySQL”,后说“改用 PostgreSQL”),保留新的,旧的标记为 superseded,不删除但不再召回。

注意:冲突记忆不要直接删。用户可能改主意改回来,保留历史能避免“记忆丢失”的尴尬。标记状态比物理删除更稳妥。

3.3 召回策略:怎么找得准

召回是整个系统里最影响体验的环节。我的做法是三路召回 + 重排序:

第一路:向量召回。把查询和记忆都做 embedding,算余弦相似度,取 Top-K。

第二路:关键词召回。用 BM25 或简单的关键词匹配,处理实体、数字、专有名词。

第三路:结构化召回。按类型、时间、来源过滤。比如用户问“我上次说的那个偏好”,就优先召回 type=preference 且时间较近的记忆。

三路结果合并后,用一个重排序模型(或者简单的加权公式)打分。我的加权公式大致是:

final_score = 0.5 * vector_score + 0.3 * keyword_score + 0.2 * recency_score

权重不是固定的,要根据场景调。技术问答场景,关键词权重可以高一点;闲聊场景,向量权重高一点。

召回数量控制也很关键。我一般召回 5 到 10 条,总 token 控制在 1000 以内。召回太多,反而稀释注意力;召回太少,可能漏掉关键信息。

3.4 上下文注入的格式

召回的记忆怎么塞回 prompt,也有讲究。我试过几种格式,最后固定用这种:

[相关记忆] - (preference, 2024-01-15) 用户偏好使用 TypeScript,不喜欢 JavaScript 示例。 - (decision, 2024-01-10) 项目选用 PostgreSQL,原因是需要 JSONB 和全文检索。 - (task, 2024-01-20) 待办:完成用户认证模块的单元测试。 [记忆结束]

要点:

  • 带类型和时间,让模型知道这条记忆的性质和新鲜度。
  • 用列表不用段落,模型对结构化列表的解析更稳。
  • 明确边界标记,避免模型把记忆和当前对话混淆。
  • 按相关性排序,最相关的放最前面。

我实测下来,这种格式比纯文本拼接的召回准确率高不少,模型也更少出现“张冠李戴”的情况。

4. 完整实操流程与关键环节

4.1 环境准备与依赖安装

假设你用 Python 做实现,基础环境这样搭:

python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install anthropic sqlite-vec numpy

如果你要用本地 embedding 模型,再加:

pip install sentence-transformers

选sqlite-vec而不是chromadb或faiss,理由是:单文件、零服务、和 SQLite 原生集成,个人项目和小团队用起来最省心。等数据量真的上来了再换也不迟。

4.2 数据库表结构设计

我用的表结构大致如下,兼顾结构化和向量检索:

CREATE TABLE memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, content TEXT NOT NULL, content_hash TEXT UNIQUE, confidence REAL DEFAULT 1.0, source_session TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP, status TEXT DEFAULT 'active' ); CREATE VIRTUAL TABLE memory_vectors USING vec0( memory_id INTEGER PRIMARY KEY, embedding FLOAT[768] );

几个设计决策的解释:

  • content_hash做精确去重,加 UNIQUE 约束,插入时冲突直接忽略。
  • status字段管理记忆生命周期,active / superseded / expired 三态。
  • expires_at为空表示永久,有值则到期后由清理任务处理。
  • 向量单独一张虚拟表,通过 memory_id 关联,查询时 join。

4.3 记忆写入流程

完整的写入流程分五步:

  1. 对话结束触发:会话结束时,把整段对话传给抽取器。
  2. 抽取:调用抽取 prompt,拿到结构化记忆列表。
  3. 归一化与去重:对每条记忆做归一化,算哈希,查库判断是否已存在。
  4. 语义去重:对通过精确去重的记忆做 embedding,和库中同类型记忆比对相似度。
  5. 写入:新记忆插入,相似记忆按合并规则处理,冲突记忆标记旧记录。

这里有个实操技巧:写入不要同步做,用队列异步处理。抽取和 embedding 都要调模型,同步做会阻塞主流程。我一般用一个简单的后台任务队列,会话结束后丢进去,用户无感知。

4.4 记忆召回流程

召回流程分四步:

  1. 查询改写:把用户当前输入改写成适合检索的形式。比如“上次那个方案怎么样了”改写成“方案 进度 状态”。
  2. 三路召回:向量、关键词、结构化并行执行。
  3. 融合重排:按加权公式打分,取 Top-K。
  4. 注入上下文:按固定格式拼接到 system prompt 或用户消息前。

查询改写这一步很多人会忽略,但它对召回质量影响很大。用户的自然语言输入往往包含大量指代和省略,直接拿去检索效果很差。用一个轻量 prompt 做改写,成本很低,收益很高。

4.5 记忆清理与压缩

记忆库需要定期维护,否则会越来越臃肿。我设置三个清理任务:

过期清理:每天跑一次,把expires_at已过期的记忆标记为 expired。

低频清理:统计每条记忆的召回次数,超过 90 天没被召回过的,降权或标记为 archived。

压缩合并:对同类型、同主题的多条记忆,用模型做一次摘要合并,把多条压成一条。比如用户分五次说了五个偏好,可以合并成一条“用户偏好:1... 2... 3...”。

压缩这一步要谨慎,合并后信息密度高了,但可能丢失细节。我的做法是:合并后的记忆保留原始记忆的 ID 列表,需要时可以回溯。

5. 常见问题与排查技巧实录

5.1 召回不准的排查思路

召回不准是最常见的问题,排查要按顺序来:

现象可能原因排查方法
完全召回不到记忆没写进去 / 索引没建直接查库看数据
召回了无关内容embedding 质量差 / 阈值太低看相似度分数分布
该召回的没召回查询改写有问题 / 权重不合理打印三路召回结果
召回内容过时TTL 没生效 / 冲突没处理检查 status 字段

我的经验是,80% 的召回问题出在查询改写和 embedding 质量上,而不是检索算法本身。先把这两块调好,再考虑换检索方案。

5.2 记忆冲突的处理

用户改主意是常态。处理冲突的原则是:新记忆优先,旧记忆保留但降权。

具体做法:写入新记忆时,检索同类型、同主体的旧记忆,如果语义冲突(比如一个说用 A,一个说用 B),把旧记忆 status 改为 superseded,新记忆正常写入。召回时只召回 active 状态的记忆。

但有个例外:如果旧记忆的置信度明显高于新记忆(比如旧的是用户明确强调的,新的是随口一提),可以保留旧记忆,把新记忆标记为 pending,等下次确认。

5.3 性能优化的几个关键点

记忆系统用久了会变慢,优化点主要在三个地方:

第一,embedding 缓存。同一条记忆不要重复算 embedding,算一次存起来。查询的 embedding 也可以做短期缓存。

第二,向量索引。SQLite 的向量检索在数据量超过几万条后会明显变慢。这时候要么加 IVF 索引,要么迁移到专用向量库。

第三,召回数量控制。不要为了“保险”召回几十条,token 成本和延迟都会上去。5 到 10 条是甜点区。

5.4 我踩过的几个坑

坑一:抽取太激进。一开始我把阈值设得很低,什么都往库里存,结果记忆库一周就上万条,召回全是噪音。后来把抽取标准收紧,只存高价值信息,效果反而好了。

坑二:忽略时间维度。早期召回只看语义相似度,不看时间。结果用户问“我最近在做什么”,召回的全是半年前的旧事。加上 recency 权重后,体验明显改善。

坑三:没有做记忆隔离。不同项目、不同场景的记忆混在一起,互相干扰。后来加了 source 字段,召回时按来源过滤,干净多了。

坑四:同步写入阻塞主流程。一开始写入是同步的,每次会话结束要等好几秒。改成异步队列后,用户完全无感知。

提示:记忆系统的调优是个持续过程,不要指望一次调好。建议加个日志,记录每次召回的结果和用户反馈,用数据驱动优化。

6. 扩展方向与个人体会

6.1 可以继续深挖的几个方向

claude-mem这类项目,基础版本跑通后,有几个方向值得继续投入:

记忆图谱化。把扁平的记忆变成图结构,记忆之间建立关联。比如“项目 A”关联“技术 B”,“技术 B”关联“坑 C”。召回时可以做图遍历,找到间接相关的记忆。

多模态记忆。现在主要处理文本,未来可以扩展到图片、文件、代码片段。比如用户上传的设计稿,也可以作为记忆存储和召回。

记忆共享与协作。团队场景下,多个人的记忆可以共享。比如 A 记录的决策,B 也能召回。这需要解决权限和冲突问题,但价值很大。

主动记忆。现在的记忆是被动召回,未来可以让模型主动判断“这个问题我需要查一下记忆”,然后自己去检索。这更接近人类的记忆机制。

6.2 我个人的使用体会

用了一段时间下来,我最大的感受是:记忆系统的价值不在于“记住多少”,而在于“忘得对不对”。

一个什么都记的系统,和一个什么都不记的系统,体验一样差。真正好用的记忆系统,是知道什么该记、什么该忘、什么时候该想起来。这背后是对场景的深刻理解,而不是堆技术。

另外,不要过度设计。我见过太多人一上来就搞分布式向量库、图数据库、多级缓存,结果基础功能都没跑通。先用 SQLite 把闭环跑通,验证价值,再逐步优化。这是我踩过坑之后最想分享的一条经验。

最后分享一个小技巧:给记忆加一个“手动标记”入口。用户觉得某条信息重要,可以手动标记为“永久记忆”,系统优先保留。这个功能实现简单,但用户感知很强,能显著提升信任感。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询