☰
给Claude加持久记忆:跨会话上下文连续性的架构设计与实操
2026/10/8 5:01:46 网站建设 项目流程

1. 从"聊完就忘"说起:claude-mem 到底想解决什么

如果你用 Claude 这类对话式 AI 做过稍微长一点的项目,大概率遇到过这种尴尬:昨天聊了三个小时,把需求、约束、命名规范、踩过的坑都对齐了,今天开个新会话,它一脸无辜地问你"请问你想做什么"。你只能把昨天的上下文再喂一遍,喂到一半发现 token 快满了,于是又得删删减减,最后干脆放弃,回到"每次只问一个小问题"的原始状态。

这不是模型不行,而是对话式 AI 的默认记忆模型是"会话级"的——一次会话就是一个封闭的上下文窗口,窗口一关,记忆清零。claude-mem这个项目,从名字就能看出来,它瞄准的正是这个痛点:给 Claude 加一层跨会话的持久记忆。你可以把它理解成给 AI 配了一个"外部笔记本",每次对话结束后,把值得留下的东西写进笔记本;下次开新会话时,先把笔记本里相关的部分翻出来,塞进上下文,再开始聊。

它解决的问题很具体:上下文窗口有限、会话之间不共享状态、重复交代背景成本高。适合谁来参考?三类人最受益。第一类是长期用 Claude 做开发、写作、研究的知识工作者,项目周期动辄几周几个月,上下文连续性直接决定效率;第二类是想自己动手搭一套"AI 记忆系统"的工程师,claude-mem 是一个很好的参考实现,能看清记忆的写入、检索、注入这条完整链路;第三类是对 RAG、向量检索、上下文工程感兴趣但还没上手的人,这个项目把抽象概念落到了具体代码上。

需要先说明一点:claude-mem这个名字在社区里对应的实现不止一个,有基于 MCP 协议的、有基于本地文件加检索的、也有做成 CLI 工具的。本文不绑定某一个具体仓库,而是围绕"给 Claude 做持久记忆"这个核心命题,把这类项目通用的架构、关键决策、实操步骤和踩坑经验讲透。你拿到的可能是一个开源仓库,也可能要自己动手拼,思路是通用的。

2. 记忆系统的三层结构:写入、存储、召回

在动手之前,得先把"记忆"这件事拆开看。很多人一上来就想"我要让 AI 记住所有东西",结果做出来一个又慢又贵又不好用的东西。真正可用的记忆系统,一定是分层的。

2.1 为什么不能把所有对话都存下来

最朴素的想法是:把每次对话的完整记录都存进数据库,下次全量塞回上下文。这个方案在 demo 阶段能跑通,但一上真实场景就崩。原因有三个。

第一是成本。上下文是按 token 计费的,你把过去一个月的对话全塞进去,一次请求可能就几万 token,钱烧得飞快,而且大部分内容是无关的。第二是信噪比。上下文窗口里塞的东西越多,模型越容易"分心",真正重要的信息反而被淹没,回答质量下降。第三是时效性。三个月前的一个临时决定,可能早就被推翻了,你把它塞进去,模型会拿旧信息当事实,产生误导。

所以记忆系统的第一原则是:存的时候要筛选,取的时候要排序。不是"记住一切",而是"记住该记的,忘掉该忘的,需要时能找回来"。

2.2 写入层:什么内容值得被记住

写入层要回答的问题是:一次对话结束后,哪些内容应该被持久化?我的经验是分成四类,优先级从高到低。

  • 事实性信息:项目名称、技术栈、目录结构、命名规范、API 约定。这类信息一旦确定,长期有效,必须记。
  • 决策与理由:为什么选 A 方案不选 B,为什么放弃某个库。这类信息价值极高,因为它能防止后续重复讨论同一个问题。
  • 待办与状态:当前进行到哪一步、下一步要做什么、有哪些阻塞。这是"工作记忆",时效性强,需要带时间戳。
  • 偏好与风格:用户喜欢简洁还是详细、代码风格、文档格式。这类信息积累起来,能显著提升体验。

反过来,不该记的也很明确:寒暄、重复确认、已经被推翻的中间结论、大段的原始代码(除非是核心片段)。判断标准很简单:这条信息在两周后的新会话里,还有参考价值吗?没有就别存。

2.3 存储层:文件、数据库还是向量库

存储层的选型直接决定了系统的复杂度和能力上限。常见的有三种路线,各有取舍。

存储方案优点缺点适用场景
本地 Markdown 文件零依赖、可读、可版本控制检索靠关键词,语义能力弱个人使用、记忆量小
SQLite + 全文索引单文件、查询快、支持结构化字段语义检索需额外扩展中等规模、需要元数据过滤
向量数据库语义检索强、模糊匹配好依赖嵌入模型、有运维成本记忆量大、检索质量要求高

我的建议是从 Markdown 起步,需要时再升级。原因很实际:早期你根本不知道自己的记忆会长成什么样,用文件能随时打开看、随时改,调试成本最低。等记忆条目超过几百条、关键词检索开始频繁失效时,再引入向量检索也不迟。很多项目一上来就上向量库,结果调 embedding 模型的时间比写业务逻辑还多,得不偿失。

2.4 召回层:怎么把相关记忆找出来

召回层是整个系统里最考验功力的部分。它要解决的是:新会话开始时,用户说了一句话,我怎么从几百条记忆里挑出最相关的几条塞进上下文?

纯关键词匹配的问题是"词不达意"。用户说"那个登录的问题",记忆里存的是"认证模块的 token 刷新逻辑",字面上一个词都对不上,但语义上高度相关。纯向量检索的问题是"过度联想",有时候会把八竿子打不着的东西也捞出来。实践中比较稳的做法是混合检索:先用元数据(项目名、时间范围、类型)做粗筛,再用关键词和向量做精排,最后按分数截断,只取 top-K 条。

K 取多少?这取决于你的上下文预算。一般来说,记忆部分占整个上下文的 20% 到 30% 比较合理,剩下的留给当前对话。如果单条记忆平均 100 token,那 K 大概在 10 到 20 之间。这个数字不是拍脑袋,是要根据你实际用的模型窗口大小反推的。

3. 把记忆接进 Claude:几种集成路径的取舍

搞清楚记忆系统的内部结构后,下一个问题是:怎么让它和 Claude 协同工作?这里有几条技术路线,复杂度、灵活性、维护成本差别很大。

3.1 MCP 协议路线:标准化但需要理解协议

MCP(Model Context Protocol)是目前把外部能力接进 Claude 的主流方式。它的思路是:你写一个 MCP Server,暴露几个工具(比如save_memory、search_memory),Claude 在对话过程中可以主动调用这些工具来读写记忆。

这条路线的好处是标准化、可复用。你写好的 Server,理论上能被任何支持 MCP 的客户端使用,不绑定某一个产品。而且 Claude 是"主动"调用工具的,它可以根据当前对话判断"这条信息值得记",比被动全量存储聪明得多。

代价是你得理解 MCP 的基本概念:Server 怎么注册、工具怎么定义 schema、请求和响应怎么序列化。如果你之前没接触过,建议先跑通官方的最小示例,再往记忆逻辑上套。别一上来就写完整的记忆系统,那样调试起来会很痛苦。

3.2 本地文件加脚本路线:最土但最可控

如果你不想引入协议层的复杂度,最直接的办法是:写一个脚本,在会话开始和结束时手动触发。会话结束时,把对话导出,跑一个脚本让 Claude 自己总结出"值得记的条目",追加到 Markdown 文件;会话开始时,跑另一个脚本,根据当前任务描述检索相关条目,拼成一段"背景提示",粘贴到新会话开头。

这条路线的优点是完全可控、零黑盒。每一段记忆你都能看到、能改、能删。缺点是手动,需要你养成习惯。但对于个人使用来说,手动触发反而是一种保护——它逼你在每次会话结束时花两分钟想想"这次到底产出了什么",这个反思过程本身就有价值。

3.3 自动化钩子路线:体验最好但坑最多

最理想的是全自动:会话一结束自动写入,会话一开始自动召回,用户完全无感。实现方式通常是利用客户端的钩子(hook)机制或者包装一层代理。

这条路线体验最好,但坑也最多。首先是触发时机不好把握,会话"结束"的判定标准是什么?关窗口算吗?切换话题算吗?其次是写入质量,自动总结出来的记忆往往很水,因为模型不知道什么对你重要。最后是调试困难,出问题时你很难定位是钩子没触发、总结没做好,还是召回没匹配上。

我的建议是:先用手动路线跑通闭环,确认记忆质量达标后,再逐步自动化。跳过手动阶段直接上自动化,大概率会得到一个"存了一堆垃圾、召回全是噪音"的系统。

4. 实操:从零搭一个最小可用的记忆闭环

下面这部分是干货,我会把最小可用版本(MVP)的搭建过程拆成可复现的步骤。假设你选择的是"本地文件 + 脚本"这条最稳的路线。

4.1 目录结构与文件约定

先定一个清晰的目录结构,这决定了后续所有操作的一致性。

claude-mem/ ├── memories/ │ ├── facts.md # 事实性信息 │ ├── decisions.md # 决策与理由 │ ├── todos.md # 待办与状态 │ └── preferences.md # 偏好与风格 ├── index.json # 元数据索引 └── scripts/ ├── save.py # 写入脚本 └── recall.py # 召回脚本

为什么按类型分文件而不是全塞一个文件?因为召回时的过滤维度不同。事实和偏好是长期有效的,召回时几乎总要带上;待办是时效性的,超过一定时间就该忽略。分文件让过滤逻辑简单很多。

每条记忆的格式建议统一成带元数据的块:

## [2024-06-15] 项目认证方案选型 - 类型: decision - 项目: my-web-app - 标签: auth, jwt, security - 内容: 最终选择 JWT + refresh token 方案,放弃 session。 原因是前后端分离部署,session 需要额外的共享存储, 而 JWT 无状态,水平扩展更简单。refresh token 有效期 7 天, access token 15 分钟。

这个格式的关键是元数据齐全。日期用于时效过滤,类型用于分类召回,项目用于隔离不同项目的记忆,标签用于关键词匹配。内容部分要写清楚"是什么"和"为什么",后者往往比前者更重要。

4.2 写入脚本:让 Claude 帮你总结

写入脚本的核心逻辑是:把一段对话丢给 Claude,让它按上面的格式输出记忆条目。提示词的设计是关键,我试过很多版本,下面这个比较稳:

你是一个记忆整理助手。请从以下对话中提取值得长期记住的信息, 按指定格式输出。判断标准:这条信息在两周后的新会话里还有参考价值吗? 只提取以下四类: 1. 事实性信息(项目配置、技术栈、约定) 2. 决策与理由(选了什么、为什么) 3. 待办与状态(进行到哪、下一步) 4. 偏好与风格(用户习惯) 不要提取:寒暄、重复确认、被推翻的结论、大段原始代码。 输出格式: ## [日期] 标题 - 类型: fact/decision/todo/preference - 项目: 项目名 - 标签: 逗号分隔 - 内容: 具体内容,包含理由 对话内容: {conversation}

这个提示词里有两个细节值得说。第一是给了明确的判断标准("两周后还有价值吗"),比笼统说"提取重要信息"效果好得多。第二是明确列出了不要提取的内容,负向约束往往比正向约束更能提升输出质量。

脚本跑完后,把输出追加到对应的 Markdown 文件,同时更新index.json,记录每条记忆的日期、类型、项目、标签,方便后续检索。

4.3 召回脚本:混合检索的具体实现

召回脚本要做三件事:粗筛、精排、拼装。粗筛用元数据,精排用关键词加语义,拼装成一段可以直接粘贴的背景提示。

粗筛的逻辑:根据当前任务描述里的项目名,过滤出同项目的记忆;根据类型,决定是否包含待办(比如超过 30 天的待办默认不召回);根据日期,给新记忆更高权重。

精排我建议先用简单的关键词加权,别急着上向量。具体做法是:把当前任务描述分词,和每条记忆的标签、标题、内容做匹配,标题命中权重 3,标签命中权重 2,内容命中权重 1,加总后排序。这个土办法在记忆量小于 500 条时,效果往往比你想的好。

拼装时要注意控制长度。我的做法是给每类记忆设一个 token 上限,比如事实 500、决策 800、待办 300、偏好 200,超了就截断。拼出来的提示大概长这样:

以下是你之前和用户协作时积累的背景信息,请在回答时参考: 【项目事实】 - my-web-app 使用 React + TypeScript,后端 Node.js + Express - 数据库 PostgreSQL,ORM 用 Prisma 【关键决策】 - 认证用 JWT + refresh token(理由:前后端分离,无状态易扩展) 【当前待办】 - 用户列表页的分页逻辑还没做 【用户偏好】 - 喜欢简洁回答,代码示例要带注释

这段提示粘到新会话开头,Claude 立刻就有了上下文,不用你再从头交代。

4.4 验证闭环是否真的跑通

搭完之后一定要做验证,别自己骗自己。验证方法是:开一个全新会话,只粘贴召回提示,然后问一个依赖历史信息的问题。比如"我们上次定的认证方案是什么,为什么这么选?"如果 Claude 能准确答出 JWT 和理由,说明闭环通了。如果答不出来或者答错,就回去查是写入没存对,还是召回没匹配上。

我建议至少准备五个这样的验证问题,覆盖四类记忆。每次改动脚本后都跑一遍,确保没有回归。这个习惯能帮你省下大量"以为能用其实不能用"的时间。

5. 那些文档不会告诉你的坑

前面讲的是"应该怎么做",这一节讲"实际做的时候会怎么翻车"。这些都是我在真实使用中踩出来的,网上教程基本不会提。

5.1 记忆污染:错误信息一旦写入就很难清除

最隐蔽的坑是记忆污染。某次对话里 Claude 理解错了你的意思,总结出一条错误的记忆,写进了文件。之后每次召回都带着这条错误信息,Claude 基于它继续推理,产生更多错误,形成恶性循环。等你发现时,可能已经污染了十几条相关记忆。

防范的办法有两个。第一是写入时人工过目。手动路线的好处在这里体现得淋漓尽致——每次写入前你扫一眼,明显不对的直接删掉。第二是给记忆加"置信度"字段,自动总结的标记为"待确认",你确认过的标记为"已确认",召回时优先用已确认的。这个字段看起来多余,但真出问题时能救命。

5.2 召回过度:塞太多背景反而让回答变差

新手容易犯的另一个错误是召回过度。觉得"多给点背景总没坏处",结果塞了 3000 token 的记忆进去,Claude 的回答反而变得啰嗦、跑题。原因是上下文里信息太多,模型分不清哪些是当前任务相关的,哪些是历史噪音。

判断是否召回过度的信号:Claude 开始主动提一些你没问的历史细节,或者回答里出现"根据之前的记录"这种话但内容并不相关。出现这种情况,就该收紧召回策略,减少 K 值或者提高匹配阈值。

5.3 时间衰减:三个月前的待办不该再出现

待办类记忆必须带时间衰减。我踩过的坑是:一个两个月前就完成的待办,因为没标记完成,一直被召回,Claude 每次都提醒我"你还有个任务没做",烦不胜烦。

解决办法是给待办加状态字段(pending/done/dropped),召回时只取 pending 的,并且超过一定天数(比如 14 天)的 pending 自动降权,提示里标注"可能已过期,请确认"。这个细节很小,但直接影响使用体验。

5.4 多项目串味:不同项目的记忆互相干扰

如果你同时用 Claude 做多个项目,项目隔离是必须的。我一开始没做隔离,结果做 A 项目时召回出了 B 项目的技术栈,Claude 给出的建议完全跑偏。

隔离的实现很简单:每条记忆都带项目字段,召回时先按项目过滤。但要注意共享记忆的处理——有些偏好(比如"喜欢简洁回答")是跨项目的,这类记忆项目字段设为global,所有项目都召回。区分"项目专属"和"全局共享",是隔离设计的关键。

6. 从能用走向好用:几个进阶方向

MVP 跑通之后,如果你想让这套系统更聪明,有几个方向可以深入。这些不是必须的,但每一个都能带来明显的体验提升。

6.1 记忆的自动合并与去重

用久了你会发现,同一个事实被反复记录,只是措辞不同。比如"数据库用 PostgreSQL"可能存了五遍。这些冗余不仅浪费上下文,还会让召回结果显得杂乱。

解决办法是定期做记忆合并。写一个脚本,把同类型、同项目、标签重叠度高的记忆找出来,让 Claude 判断是否重复,重复的合并成一条,保留信息最全的版本。建议每个月跑一次,保持记忆库的整洁。

6.2 引入向量检索的时机与方式

什么时候该上向量检索?我的经验是:当你发现关键词检索开始频繁漏召回时。具体信号是,你明明记得存过某条信息,但用各种关键词都搜不出来。这时候说明语义鸿沟已经超过了关键词能覆盖的范围。

引入方式建议渐进:先加一个向量索引作为补充,召回时关键词和向量各取 top-K,合并去重。不要一上来就用向量完全替代关键词,两者互补的效果最好。嵌入模型的选择上,中文场景建议用专门优化过中文的模型,通用模型在中文短文本上的表现往往不理想。

6.3 让记忆系统自己"反思"

更进阶的做法是让记忆系统具备反思能力:定期回顾最近的记忆,发现矛盾(比如两条决策互相冲突)、发现过时(比如某个待办长期未动)、发现模式(比如用户反复提到某个痛点),主动生成"洞察"条目。

这个方向很有意思,但要注意别过度设计。反思产生的洞察如果质量不高,反而会污染记忆库。建议先小范围试,人工审核一段时间,确认产出有价值再放开。

7. 我实际用下来的几点体会

最后聊点实在的。这套东西我用了一段时间,最大的感受是:记忆系统的价值不在于"记住多少",而在于"该记的记对了,该忘的忘掉了"。一开始我追求大而全,恨不得把每次对话都存下来,结果系统又慢又乱。后来做减法,只存四类核心信息,反而好用得多。

另一个体会是手动阶段不能省。我见过太多人想一步到位做全自动,最后卡在调试上放弃。手动写入和召回虽然麻烦,但它让你对"什么值得记"有真实的体感,这个体感是设计自动化策略的基础。等你手动跑了一两个月,闭着眼睛都知道哪些信息该存、怎么存,再去写自动化脚本,成功率会高很多。

还有一点:别把记忆系统当成万能药。它解决的是"跨会话上下文连续性"这一个问题,解决不了模型本身的能力边界。有些任务就是需要长上下文窗口,记忆系统只是缓解,不是根治。认清它的定位,才不会对它有不切实际的期待。

如果你正准备动手,我的建议是从最小的闭环开始:一个 Markdown 文件、一个写入提示词、一个召回脚本,先跑起来。跑通之后再考虑分类型、加索引、上向量。这个领域没有标准答案,适合你工作流的,就是最好的。

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

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

立即咨询