news 2026/10/18 13:03:09

给Claude装上记忆外挂:跨会话上下文持久化设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
给Claude装上记忆外挂:跨会话上下文持久化设计与实现

如果你经常用 Claude 写代码、梳理方案或者整理知识,大概率遇到过同一个让人抓狂的场景:上一个会话里刚说完的背景、偏好、技术选型,换一个新窗口再打开,它就像失忆了一样,什么都得从零开始讲。

这个痛点持续了大半年之后,我做了一个叫claude-mem的小工具。它本质上是一套跨会话的“记忆外挂”:在对话结束时自动抽取关键信息,保存到本地文件;下一次新会话启动时,再把这份记忆作为上下文的一部分自动注入。这样一来,AI 会表现出“还记得你”的状态,之前敲定的方案、你习惯的表达风格、项目当前进行到哪一步,都被保留了下来。

这个东西挺适合这么几类人:连续几天盯着同一个项目、需要让 AI 维持固定输出风格、以及经常在多个主题之间切换的用户。这篇博文就把我的设计思路、核心实现、踩过的坑全部摊开讲,代码和配置可以直接照着抄。

1. 为什么需要给 AI 接上记忆

1.1 会话断裂让长线工作变得低效

对话式 AI 默认是无状态的,这句话有两层意思。第一层是技术层面:每次请求都是独立的上下文,服务器不保留你上一个请求说了什么。第二层是体验层面:所有需要“前后一致”的任务,都会因为这种断裂而变得非常别扭。

我举个例子。之前我在做一个内部工具的项目,需要 AI 帮忙写某个模块的接口设计。第一个会话里花了大概二十分钟对齐了数据库表结构、鉴权方案和接口命名规范,AI 给了很具体的建议,我也都确认了。第二天继续弄的时候,我开了个新会话,想把前一天的结论作为基础继续往下推进,结果它直接给我设计了一套完全不同的表结构,名称、字段全对不上,等于前一天白干了。

这种场景在写代码、写文档、做方案设计这些场景里太常见了。对话轮次一多,上下文窗口很快就被撑满,人们习惯性地新开一个会话,然后重新喂一轮背景。沟通成本被白白浪费在这些重复劳动里。

1.2 人靠笔记,AI 也靠“笔记”

人是怎么解决这个问题的?靠笔记。项目经理不可能把整个项目的所有细节都记在脑子里,他会写文档、写周报、更新进度表。换了一个人接手,先看文档,再问几个关键问题,就能快速上手。

AI 也是一样的逻辑。它本身没有记忆,但我可以给它造一个“笔记系统”。让它在对话结束时,把当前会话里出现的关键决策、用户偏好、项目状态沉淀成结构化记录;下次对话开始前,再把这些记录翻出来,作为上下文的一部分喂回去。

这个思路听起来简单,难点在于几个细节:记录什么、存成什么格式、如何避免记忆膨胀、怎么处理冲突。我会在后面的部分逐一说明。

2. 整体方案设计:五步闭环

2.1 从“对话-遗忘”到“对话-沉淀-回忆”

我的claude-mem在设计上围绕一条链路展开:采集、抽取、存储、注入、更新。五个环节形成闭环,每次会话都会让记忆变得更完整。

第一步,采集。在每次对话的自然结尾,我通过提示词引导 AI 输出一段简短的结构化总结,包含用户偏好、项目状态、决策记录、下一步计划这几类信息。

第二步,抽取。我需要从对话里过滤出真正值得长期记住的内容。比如用户说“以后接口返回尽量用统一的错误码格式”,这属于永久性偏好,应该沉淀下来;而“今天把登录接口写完”这类一次性信息,属于短期任务,记录一下即可。

第三步,存储。抽取出来的信息被写入本地文件。这里我选择的是 Markdown 加 JSON 混合的方案:Markdown 方便人眼阅读,JSON 方便脚本做后续的合并和更新。

第四步,注入。新对话开始前,运行一个预处理命令,把记忆文件渲染成一段固定格式的上下文,作为系统提示词的一部分发送给 AI。

第五步,更新。每次对话结束后,重新执行采集和抽取,把新信息合并进存储文件。合并过程需要处理“旧记忆和新记忆冲突”的情况。

这五步里面最容易做砸的是“存储格式”和“合并策略”,如果设计得不好,记忆文件很快就会变成一堆互相矛盾、难以解析的乱码。我在 2.2 和 2.3 里具体讲我踩过的坑和最后的方案。

2.2 存储格式:Markdown 与 JSON 的取舍

存储格式的选择上,我经历过好几个版本的迭代。

最开始我用的是纯文本,一个memory.txt把所有内容堆在一起。好处是简单,坏处是完全没有结构。过了两个星期,文件里同时存在“用户偏好写中文注释”和“代码注释尽量统一用英文”这两条内容,AI 每次都人格分裂。

后来我改成纯 JSON,结构是有了,但有个问题:人看起来非常不友好。我想在开会时快速翻一下 AI 到底记得我什么偏好,打开 JSON 文件看到一堆花括号,阅读效率极低。

最终我采用的方案是 MD + JSON 双轨制。

  • Markdown 文件按主题组织,比如project_state.md、user_preferences.md、decisions.md,人类直接阅读,修改也很方便。
  • 每个 Markdown 文件顶部带一个 YAML 小字段块,维护元信息如updated_at、version,脚本也能快速解析。
  • 核心决策类的关键条目同时以 JSON 行(.jsonl)的形式追加到独立文件里,便于做统计、搜索、回滚。

这套方案兼顾了两个诉求:人能读懂、机器能处理。启动注入时,脚本同时读取 Markdown 和 JSON,合并成一段统一的上下文块;需要人工介入纠偏时,直接编辑 Markdown 就行。

2.3 目录结构与文件组织

项目隔离是我很早就定下来的原则。不同项目之间不应该共享记忆,否则做 A 项目时会莫名其妙带出 B 项目的上下文。我的记忆目录结构大概是这样的:

~/.claude-mem/ projects/ project-alpha/ profile.md # 用户偏好、角色设定、常用术语 project_state.md # 当前进展、待办、下一步 decisions.md # 重大决策及理由 archives/ 2025-01-01.md # 按时间归档的历史摘要 project-beta/ ... global/ global_preferences.md # 不区分项目的通用偏好 cache/ memory_block.md # 最近一次构建的注入块

global目录我用来存一些跨项目的偏好,比如“回复时先给结论再给理由”“代码示例偏好 TypeScript 风格”。archives用来做版本归档,每个会话结束生成一份当日快照,方便回滚。cache里的memory_block.md是每次预处理生成的“最终注入产物”,新对话直接把整个文件塞进上下文即可。

3. 核心实现拆解

3.1 会话结束时的记忆采集

采集这步是整个系统的输入源头,做不好后面全都白搭。我的做法是在每轮对话的末尾,追加一句约定好的“收尾指令”,让 AI 把这段对话中的重要信息整理成结构化内容。

使用的提示词大致如下:

会话即将结束。请基于本次对话生成一段记忆记录,只保留值得长期记住的内容。 要求: 1. 用 JSON 格式输出,字段固定为: {"timestamp": "...", "preferences": [...], "project_state": "...", "decisions": [...], "next_actions": [...]} 2. preferences 只放用户表达的稳定偏好,临时性指令不放进去。 3. decisions 必须包含决策内容和决策理由。 4. 如果本次对话没有值得记录的内容,直接输出空数组。 5. 不要输出 JSON 之外的任何文字。

这一步看似简单,实际有个很大的坑:AI 有时候会把“过渡性内容”当成“长期偏好”。比如用户随口说“这次用 pandas 处理数据”,它可能就把“用户偏爱 pandas”记进偏好里了。所以我在提示词里反复强调“临时指令不记录”,同时在保存端的脚本里添加一层过滤规则。

我还在脚本里加了一个过滤关键词列表,凡是包含“这次”“临时”“暂时”“先用着”这类字眼的条目会被二次筛选,要么丢弃,要么降级为短期任务而非长期偏好。

3.2 记忆写入脚本

采集到 JSON 之后,写入逻辑我用 Python 实现,核心代码不长,关键的合并逻辑就在这里。

import json import os from datetime import datetime MEMORY_ROOT = os.path.expanduser("~/.claude-mem/projects") def load_jsonl(path): if not os.path.exists(path): return [] with open(path, "r", encoding="utf-8") as f: return [json.loads(line) for line in f if line.strip()] def append_jsonl(path, record): os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n") def merge_record(project, record): base = os.path.join(MEMORY_ROOT, project) # preferences 合并,去重,保留稳定条目 prefs_path = os.path.join(base, "profile.md") existing_prefs = read_md_section(prefs_path, "preferences") new_prefs = record.get("preferences", []) merged = merge_dedupe(existing_prefs, new_prefs) write_md_section(prefs_path, "preferences", merged, updated_at=datetime.now()) # decisions 做追加归档 append_jsonl(os.path.join(base, "decisions.jsonl"), record.get("decisions", []))

这里我必须解释一下merge_dedupe的逻辑:它不只是简单去重,还会做语义相似度判断。比如旧条目是“代码注释用英文”,新条目是“注释统一用英文”,两条字符串不一样,但意思一样。我引入了一个轻量级的相似度检查,基于公共词比例计算,超阈值则用新的覆盖旧的。

这个覆盖策略很重要,因为记忆不应该无限累积。如果一条偏好被用户反反复复用不同方式表达,说明它是真实偏好的概率很高,应当被保留为最新版本,但旧版本需要让位。

3.3 启动时注入记忆块

新会话启动时,我会先运行一个准备命令,渲染记忆块:

claude-mem build --project project-alpha

核心逻辑是读取该项目下所有相关文件,拼接成一段固定格式的上下文字块。

def build_memory_block(project): base = os.path.join(MEMORY_ROOT, project) sections = [] for fname in ["profile.md", "project_state.md", "decisions.md"]: path = os.path.join(base, fname) if os.path.exists(path): sections.append(open(path, encoding="utf-8").read()) block = "以下是此前的跨会话记忆,是已经和用户确认过的信息,请默认全部已知:\n\n" block += "\n\n".join(sections) # 控制长度 tokens_estimate = len(block) / 1.5 # 中文粗略估算 if tokens_estimate > 1800: block = compress_block(block, max_tokens=1800) return block

然后我把它保存在cache/memory_block.md,在真正和 AI 对话时,把这个文件的内容复制到系统提示词里。如果你用的是 API,更优雅的做法是直接在 system prompt 中动态拼接:

你正在继续一个跨天进行的项目。以下是你此前的记忆,这些信息默认成立,不要重复向用户确认: {{memory_block}}

这个注入方式的效果非常明显。之前需要用户重新讲述的上下文,现在 AI 会在第一句话里就表现出“心里有数”。比如你问它“之前我们设计接口的时候,鉴权方案确定用哪种了吗”,它能直接答出你上次的结论,而不是一脸茫然反问你“什么鉴权方案”。

3.4 记忆压缩与清理

记忆无限增长是必然遇到的硬问题。上下文窗口就那么大,如果记忆块占掉一半,留给真实对话的空间就小了。

我采用的压缩策略分三层。

第一层,按 token 预算硬截断。我设定一个上限,比如 1800 token。超出部分按照“重要度权重”排序截断。权重规则是:用户偏好 > 决策记录 > 项目状态 > 历史归档。

第二层,定期做记忆摘要。每周跑一次脚本,对过去七天的决策归档自动生成一份“周总结”,压缩成几条高度凝练的结论,替代那些零散的记录。这个操作利用 AI 本身做,准确率基本够用。

第三层,主动遗忘。项目完成后,把project_state.md里的内容归档到archives/,然后把当前状态清零。这一步很多人忽略,导致旧项目的记忆一直残留,新项目启动时被过时信息干扰。我现在的习惯是每个项目收尾时自动清理,只保留 profile 级别的长期偏好,其他全部归档。

4. 实际使用效果与参数调优

4.1 使用前 vs 使用后的差异

我截取一个实际工作中的对比,大家感受会更直观。

使用claude-mem前:

我:帮我看看之前说的那个用户统计接口,现在要实现按时间段筛选,改一下。 AI:好的,可以帮我提供一下接口的完整定义,以及现在筛选逻辑的具体位置吗? 我:……(开始翻之前的代码,然后把上下文重新描述一遍)

使用claude-mem后:

我:帮我看看之前说的那个用户统计接口,现在要实现按时间段筛选,改一下。 AI:好的,根据记忆里你之前的偏好,接口路径保持 /api/users/stats,支持日期范围参数。当前接口在 src/api/user.ts 第 80 行。你希望我直接改这个文件,还是先把改动方案列出来给你确认?

第二种体验的差别在于:AI 不再像第一次见面的陌生人,而像一个确实接手过这个项目、随时可以继续干活的同事。

4.2 关键参数配置参考

下面是我在config.yaml里沉淀下来的一组参数,经过几轮项目验证,目前比较稳定:

memory: max_token_budget: 1800 # 注入上下文的最大 token 预算 compress_priority: # 压缩时的保留优先级 - preferences - decisions - project_state - archives auto_collect: true # 会话结束是否自动触发采集 dedupe_threshold: 0.82 # 相似度阈值,超过则视为重复 archive_on_complete: true # 项目完成时自动归档

dedupe_threshold是个很有意思的参数。设太高,比如 0.95,几乎不会合并重复记忆,文件会膨胀;设太低,比如 0.5,两句不同含义的话会被误判成同一句话,导致重要的差异被覆盖掉。0.8 左右是我试下来比较平衡的区间。

max_token_budget也不是越大越好。曾经我把它调到了 4000,结果 AI 的回答风格变得非常啰嗦,高频记忆来回引用,挤占了本该用于推理的空间。调到 1800 之后,既能覆盖核心背景,又能留足上下文给当前任务。

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

5.1 记忆文件里出现互相矛盾的条目

这个是最先遇到的问题。AI 在某个会话里记录了一条偏好“代码注释尽量用中文”,过了两天另一个会话里又记录了一条“注释统一用英文”,两条同时存在,AI 就开始随机挑选一条执行。

根源在于我在 3.2 里说的语义相似度合并没有做好。两个不同会话产生的记录,如果没有在写入前做交叉比对,就会并存。

我现在采取的办法是:所有 preferences 的新增条目,都先和已有的全部条目做一次相似度计算。超过阈值就用新条目替换旧条目,同时把旧条目移入archives/以备回溯。这个方案运行之后,矛盾条目的出现频率大幅下降。

5.2 会话一长,AI 越来越“沉浸”在记忆里

有一段时间我发现,注入记忆之后 AI 的回签质量反而下降了。它会在回答中频繁引用记忆里的内容,甚至编造“记忆里提到过某细节”,而实际上那个细节根本不存在。

排查后发现,问题出在提示词措辞上。原来的写法是“以下记忆信息可能对你有帮助”,这种表达给 AI 留下了过度自由发挥的空间,它会把记忆当作素材进行脑补。

改法很简单,把提示词改成严格约束形式:

以下是真实记忆,只可引用,不可虚构。若记忆与用户本次提供的信息冲突,以用户本次提供的信息为准。

同时加上一条兜底提示:“若记忆中没有相关内容,明确告知用户缺少信息,不要编造。”实测下来,幻觉问题大幅缓解。

5.3 隐私与敏感信息的边界

记忆文件是纯文本落在本地磁盘的,如果里面存了不该存的密钥、客户隐私信息,风险就是实打实的。我的建议是在采集阶段做一层屏蔽。

在记忆采集提示词里加一句:

如果对话中出现密码、密钥、个人信息,不要写入记忆记录,用 [敏感信息已屏蔽] 代替。

同时我在脚本层面加了一个正则黑名单,对常见的 token、密钥格式做二次过滤。这样即使 AI 偶尔遗漏,脚本也能兜底拦截。

5.4 不同项目间记忆串味

这个问题的触发场景是:上午还在写 Python 数据分析项目,下午切到另一个 TypeScript 项目。如果记忆目录结构不隔离,AI 可能在下午的会话里参考上午的偏好,导致输出风格完全跑偏。

解决方式就是我 2.3 里设计的项目隔离。每个项目拥有独立子目录,claude-mem build时只读取当前项目。全局记忆只放跨项目通用偏好,并且设定最多 5 条的限制,避免全局文件变成大杂烩。

5.5 记忆整理脚本本身出 bug 怎么办

脚本也是代码,也会出 bug。最怕的是记忆文件被错误合并,数据覆盖后找不回原来的内容。

我的保护措施是:每次写入前,自动把当天修改前的原始文件备份到一个带时间戳的快照目录。这样即使写入逻辑写错,也能恢复到上一版。这个成本很低,收益却非常大。我强烈建议所有做类似工具的人都保留这层快照机制。

6. 我的一点实操心得

做这个工具的核心体会是:AI 的“记忆力”其实是个工程问题,而不是模型能力问题。你不需要训练模型,只需要做好信息管理——什么东西值得记、记在哪里、什么时候翻出来用、什么时候主动忘掉。把这四个问题想清楚,几十行脚本就能带来非常明显的体验提升。

如果你也想动手做一个类似的工具,我的建议是不要一上来就追求复杂。先用一个 Markdown 文件做记忆存储,手动复制到新会话里验证效果。等确认这套流程真的能帮到你,再逐步加入自动采集、语义去重、快照回滚这些机制。

另外,记忆工具长期用下来,你会发现自己越来越依赖它固定的输出结构和记录条目。我目前最满意的反而是它的“低存在感”——启动时静默注入,结束时静默记录,平时完全感觉不到它的存在。好的工具就应该是这样的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 23:51:09

AI辅助学术专著写作:从框架搭建到风格统一的完整方法论

学术专著写作,五个字,能让不少研究者熬掉几层头发。我最近陪某高校一位同事整理一部跨学科专著,前后改了十几轮框架,光目录就推翻三次,更别提逐章逐节把材料揉进正文时那种“明明什么都有,就是不知道从哪下…

作者头像 李华
网站建设 2026/10/16 1:47:15

基于SpringBoot+Vue的智慧农业专家远程指导系统构建实践

1. 项目拆解:智慧农业专家远程指导系统到底解决什么问题第一次看到"065智慧农业专家远程指导系统-springbootvue"这个标题,我脑子里蹦出来几个关键词:远程、专家、指导、农业。看似简单的组合,其实背后是一个非常典型的…

作者头像 李华
网站建设 2026/10/15 22:45:26

WebSocket实时聊天系统从零搭建与高可用实践

简介:这是一份面向计算机专业本科生的毕业设计/课程设计实战项目,基于Python与Vue.js实现轻量级实时在线聊天系统,解决传统HTTP轮询在即时通信场景下的高延迟与低效问题。资源包共31个文件,涵盖15个JavaScript逻辑文件&#xff08…

作者头像 李华
网站建设 2026/10/17 2:17:05

claude-mem:为Claude打造跨会话长期记忆的MCP服务实践

做 AI 编程助手的朋友,最近应该都遇到过同一个痛点:Claude 明明很强,但换个会话它就"失忆"了。上午刚对齐的技术方案、下午新开一个终端窗口,你就得把上下文重新喂一遍。我刚开始用 claude-mem 就是为了解决这个事&…

作者头像 李华
网站建设 2026/10/17 2:11:18

特价股票策略与去中心化自治投资:从规则设计到链上执行

我最近被问得最多的问题,不是“某个代币能不能买”,而是“你这套去中心化自治投资(DAI)的框架,能不能把我平时打折买股票那套逻辑也放进去”。问的人多了,我意识到很多人对“特价股票策略”有偏见&#xff…

作者头像 李华
网站建设 2026/10/16 15:14:14

基于Scikit-learn的可解释音乐推荐系统实战

简介:这是一套面向计算机专业本科生的高分毕业设计级音乐推荐系统源码,聚焦机器学习在个性化推荐中的工程落地,适用于课程设计、期末大作业及毕设参考,尤其适合缺乏项目经验但希望动手实践推荐算法的学习者。资源共1106个文件&…

作者头像 李华