
1. 从“金鱼记忆”说起AI 对话为什么总是聊完就忘如果你深度使用过任何一款 AI 对话工具大概率都经历过这种抓狂时刻花了半小时跟它对齐需求、梳理背景、敲定方案结果关掉窗口再开一个新会话它就像换了个人之前聊过的东西一概不知你得从头再讲一遍。这种“聊完即忘”的体验圈内人戏称为“AI 金鱼记忆”——七秒之后世界重启。这个问题的根源其实不复杂。绝大多数对话式 AI 的“记忆”本质上只是当前会话的上下文窗口也就是一次对话里它能“看见”的文字总量。窗口一关上下文清空模型本身并不会把这次对话的内容存进任何持久化的地方。它没有“昨天”也没有“上周”只有“现在这段文字”。所以你会看到同一个问题今天问和明天问答案可能完全不一样因为它压根不记得你昨天问过。“Claude-Mem”这个标题指向的正是围绕这类对话式 AI 构建持久化记忆层的一类思路和工具。它的核心目标很直接让 AI 在跨会话、跨时间的场景下依然能记住你是谁、你之前做过什么、你偏好什么风格、你正在推进哪个项目。说白了就是给金鱼装一个外挂硬盘让它不再只有七秒。这篇文章适合三类人看。第一类是重度 AI 对话用户天天跟模型打交道被重复交代背景折磨得够呛第二类是想自己动手搭一套记忆系统的开发者关心数据怎么存、怎么检索、怎么注入第三类是对“AI 记忆”这个概念好奇、想搞清楚它到底靠不靠谱的普通读者。我会从设计思路讲到实操细节再到踩坑经验尽量让不同基础的人都能拿走点东西。需要先说明一点下面涉及的具体实现方案是基于这类记忆系统常见的工程实践做的合理推演和补全不是对某个特定产品的逆向拆解。思路是通用的你可以套用到自己常用的工具上。2. 记忆系统的整体设计为什么不能只靠“塞进上下文”2.1 上下文窗口不是记忆是草稿纸很多人第一反应是既然模型能记住当前会话那我把它之前说过的话全塞进新会话不就行了理论上可行实际上很快撞墙。上下文窗口是有长度上限的你把历史对话一股脑塞进去几个问题就把窗口占满了留给当前任务的思考空间被严重挤压。而且模型对超长上下文的注意力是衰减的塞得越多它越容易抓不住重点甚至开始胡言乱语。打个比方上下文窗口就像一张草稿纸。你可以在上面写当前这道题的演算过程但你不能把过去一个月所有草稿都糊在同一张纸上那样这张纸就没法用来算新题了。记忆系统的价值就是把这堆草稿整理成一本有目录的笔记本需要哪页翻哪页而不是全摊在桌上。2.2 分层记忆短期、长期、工作记忆各司其职一套能用的记忆系统通常会把记忆分成几层各管各的事。这个分层思路借鉴了认知科学里对人类记忆的划分落到工程上非常实用。短期记忆对应当前会话的上下文负责维持这一轮对话的连贯性用完即弃。长期记忆是持久化存储的部分把值得留存的信息写进数据库或文件跨会话存活。工作记忆则是介于两者之间的一个临时区专门存放当前任务相关的、从长期记忆里临时调出来的片段。为什么要分这么细因为不同记忆的读写频率、生命周期、检索方式完全不同。短期记忆追求快长期记忆追求稳和可检索工作记忆追求准。混在一起做最后一定是又慢又乱。我见过一些早期尝试把所有东西都往一个向量库里塞结果检索出来的全是无关的碎片反而干扰了模型判断。2.3 写入、检索、注入记忆系统的三段式流水线不管具体实现怎么变记忆系统的核心动作就三个写入、检索、注入。写入是判断当前对话里哪些信息值得长期保存然后把它结构化地存下来。检索是在新一轮对话开始时根据当前话题去长期记忆里捞出相关片段。注入是把捞出来的片段以合适的形式拼进当前上下文让模型“想起来”。这三个环节里最容易做砸的是写入和检索。写入做不好存进去的全是噪音检索做不好该想起来的时候想不起来不该想起来的时候乱想起来。注入反而是相对机械的一步但格式设计不好也会让模型误解。后面我会逐个拆开讲。2.4 方案选型向量库、结构化存储还是混合存记忆用什么这是绕不开的选型问题。常见的有三条路。纯向量库方案把每段记忆转成向量存起来检索时按语义相似度找。优点是能处理模糊语义你问“上次那个配色方案”它能找到“之前讨论的视觉风格”。缺点是精确匹配差你要找“2024年3月5日那条记录”它可能给你一堆日期相近的。纯结构化存储方案用关系型数据库或键值对存字段清晰精确查询强。缺点是语义检索弱你得知道自己在找什么才能找到。混合方案是现在比较主流的做法结构化字段存元数据时间、类型、标签、来源向量存内容语义检索时先用结构化条件缩小范围再用向量做语义排序。这样既有精确性又有灵活性。我个人更推荐混合方案虽然搭起来麻烦点但长期用下来省心。3. 核心细节拆解记忆到底该怎么存、怎么找3.1 什么信息值得被记住不是所有对话内容都值得存。如果把每句话都存下来长期记忆很快会变成垃圾场。判断标准大致有这么几条。稳定性这条信息是长期有效的还是只对当前这一轮有用比如“我的项目用 Python 3.11”是稳定的“帮我把这段代码改成异步”是一次性的。前者值得存后者不必。复用性这条信息未来还会被用到吗你的技术栈、你的写作偏好、你正在推进的项目背景这些会反复用到。一次性的调试过程用完就完了。独特性这条信息是不是模型本来就知道的常识如果是通用知识没必要存。只有跟你个人、你的项目强相关的信息才值得占用记忆空间。实操中我一般会让模型在对话结束时做一次“记忆提取”输出一个结构化的候选列表然后由规则或人工确认哪些入库。全自动写入风险太大容易把模型的幻觉也一起存进去。3.2 记忆的颗粒度太碎和太整都不行颗粒度是个很微妙的参数。存得太碎比如一句话一条记忆检索时捞出来一堆零散句子拼不成完整背景。存得太整比如整个会话存成一大块检索时要么全中要么全不中精度差。比较舒服的颗粒度是**“一个事实一条记忆”**但这条事实要自带足够的上下文。比如不要存“用户喜欢深色模式”而要存“用户在 UI 偏好上倾向深色主题理由是长时间阅读眼睛更舒服”。后者信息完整单独拎出来也能用。我试过按“话题块”来切分一个话题下的相关事实打包成一条。效果也不错但切分逻辑要设计好否则话题边界模糊时会切得很乱。3.3 元数据设计让记忆可被精准召回元数据是记忆系统的骨架。没有元数据检索就只能靠语义相似度硬碰精度上不去。我一般会给每条记忆挂这几类字段。字段作用示例时间戳判断新旧、支持时间范围检索2024-06-12T14:30类型区分事实、偏好、任务、事件preference标签多维度归类支持组合筛选前端, 配色, UI来源追溯这条记忆从哪来会话A第3轮置信度标记信息可靠程度0.9有效期支持会过期的记忆2024-12-31置信度这个字段很多人会忽略但特别有用。模型提取的记忆不一定都准标个置信度检索时可以设阈值低置信度的要么不召回要么召回时附带提醒。有效期则解决了“临时信息永久占用空间”的问题比如“这周在赶一个 deadline”过了这周就该失效。3.4 检索策略语义、关键词、时间衰减怎么配比检索是记忆系统里最考验功力的地方。单一策略都不够用得组合。语义检索负责“意思相近”用向量相似度找。关键词检索负责“字面匹配”用倒排索引找。时间衰减负责“越新越重要”给近期记忆加权。三者按什么比例配取决于你的使用场景。如果是长期项目助手时间衰减权重可以低一点因为项目背景是稳定的。如果是日常助理时间衰减权重要高因为你的偏好和状态变化快。我一般会用一个可调的加权公式初始值设成语义 0.6、关键词 0.3、时间 0.1然后根据实际召回效果微调。提示检索返回的结果不要直接全塞给模型先做一次去重和排序取 top-N。N 太大反而稀释注意力一般 5 到 10 条比较合适。3.5 注入格式怎么让模型“自然地想起来”检索出来的记忆怎么拼进上下文直接影响模型的使用效果。直接甩一堆 JSON 过去模型可能理解得磕磕绊绊。比较自然的做法是转成一段简短的叙述性文字放在系统提示或对话开头。比如不要写{type:preference,content:深色模式}而是写“补充背景该用户偏好深色主题界面此前多次提到长时间阅读时眼睛容易疲劳。”这样模型读起来顺用起来也准。注入位置也有讲究。放在对话最前面模型会当成背景知识放在用户消息后面模型会当成当前任务的补充。一般背景类记忆放前面任务相关记忆放后面。4. 实操落地从零搭一套能用的记忆层4.1 环境与依赖准备假设你要自己搭一套最简可行方案需要这些东西一个向量库本地可以用轻量的嵌入式方案比如基于文件的向量索引、一个结构化存储SQLite 就够别一上来就上重型数据库、一个嵌入模型用来把文本转向量、以及一段胶水代码把三者串起来。嵌入模型的选择上如果追求本地化、不依赖外部服务可以选参数量小一点的句子嵌入模型几百兆那种跑在普通机器上没问题。如果追求效果可以用大一点的但推理成本会上去。我建议先用小的跑通流程效果不够再换。依赖装好后先写一个最小的“存一条、查一条”的测试确认向量库和嵌入模型能正常配合再往上叠功能。很多人一上来就设计复杂架构结果卡在环境问题上好几天很打击积极性。4.2 记忆写入的完整流程写入流程我一般拆成四步。第一步触发提取。在对话告一段落时比如用户说“先这样”或者会话空闲超过一定时间触发一次记忆提取。不要每轮都提取太频繁且噪音大。第二步模型抽取。给模型一段提示让它从最近的对话里抽出值得长期保存的事实按预设的 schema 输出。schema 里包含内容、类型、标签、置信度。第三步去重与合并。新提取的记忆跟已有记忆做比对语义高度相似的合并或更新避免同一件事存好几遍。这一步用向量相似度加阈值判断就行。第四步落库。结构化字段进 SQLite内容向量进向量库两边用同一个 ID 关联。# 伪代码示意非可直接运行 def write_memory(raw_text): candidates extract_by_model(raw_text) for c in candidates: if is_duplicate(c): merge_or_update(c) else: mem_id store_structured(c) store_vector(mem_id, embed(c.content))4.3 检索与注入的代码骨架检索流程对应地也分几步。先拿当前用户输入做嵌入去向量库捞一批候选再用元数据条件时间范围、类型、标签过滤然后按加权分数排序最后取 top-N 转成叙述文字注入。def retrieve(query, top_n8): q_vec embed(query) candidates vector_search(q_vec, limit50) filtered filter_by_meta(candidates) ranked rank_by_weight(filtered, query) return format_as_context(ranked[:top_n])注入时我会把记忆分成“稳定背景”和“近期相关”两组分别放在上下文的不同位置。稳定背景放最前近期相关放用户消息前。这样模型能分清哪些是长期设定哪些是这次要用的。4.4 参数调优相似度阈值、召回数量、衰减系数这几个参数没有万能值得根据你的实际数据调。但我可以给一组起步值帮你少走弯路。参数起步值调整方向相似度阈值0.75召回太杂就调高漏召回就调低召回数量8上下文吃紧就减效果不够就加时间衰减半衰期30天状态变化快就缩短项目稳定就拉长去重相似度0.9重复多就调低误合并就调高调参时一次只动一个记录每次改动后的召回质量别一次改一堆否则出了问题都不知道是哪个参数导致的。4.5 一个最小可跑通的示例场景假设你在做一个长期写作助手。第一次对话你告诉它你的写作风格偏好、常用结构、忌讳的表达。这些被提取成偏好类记忆存下来。第二次对话你让它写一段开头检索环节会把你之前的偏好捞出来注入它写出来的东西就贴合你的风格不用你再交代一遍。这个场景的好处是反馈直接记忆有没有生效看输出风格就知道。我建议新手都从这个场景入手比做复杂的任务型助手更容易看到效果也更容易定位问题。5. 常见问题与排查技巧实录5.1 记忆污染错误信息被反复召回这是最头疼的问题。模型提取时把幻觉或误解存了进去之后每次检索都把它捞出来错误被不断强化。排查方法是给记忆加“来源追溯”发现可疑记忆时能查到它是从哪轮对话提取的回看原始上下文判断真假。预防手段是写入时设置信度阈值低置信度的先存着但不参与召回观察一段时间再决定是否启用。5.2 检索不准该想起来的没想起来常见原因有三个。一是嵌入模型对领域词汇不敏感通用模型遇到专业术语会失真换一个在你领域数据上微调过的模型能改善。二是元数据过滤太严把相关记忆筛掉了放宽条件试试。三是记忆颗粒度不对存得太碎导致单条信息量不足检索时匹配不上需要重新切分。5.3 上下文超限记忆塞太多把窗口撑爆记忆注入不是越多越好。我踩过的坑是贪心觉得多召回几条总没坏处结果窗口被占满模型反而抓不住当前任务。解决办法是给记忆注入设一个 token 预算比如不超过总窗口的 20%超了就按分数砍。另外长记忆可以做摘要压缩把多条相关记忆合并成一段概述再注入。5.4 记忆冲突新旧信息打架用户改了偏好但旧记忆还在检索时新旧一起召回模型不知道该听谁的。处理办法是给记忆加“版本”或“取代”关系新记忆写入时标记它取代了哪条旧记忆检索时自动过滤被取代的。时间戳也能帮忙同类型记忆冲突时优先取新的但要小心不是所有新信息都比旧的可靠。5.5 性能问题检索变慢拖垮体验记忆量上去之后检索会变慢。优化方向有几个向量索引换成支持近似最近邻的方案别用暴力搜索元数据过滤尽量在向量检索之前做缩小候选集高频查询做缓存。我实测下来把过滤前置这一招效果最明显候选集小一个数量级检索速度立竿见影。5.6 常见问题速查表现象可能原因排查动作反复召回错误信息记忆污染查来源降置信度该记的没记住提取规则太严放宽触发条件想不起来相关背景检索策略单一加关键词或时间维度窗口被撑爆注入无预算设 token 上限新旧信息打架无版本管理加取代关系检索越来越慢索引未优化换 ANN前置过滤6. 我踩过的坑和几条实在建议先说一个最容易被低估的点记忆系统的难点不在存而在判断什么该存。我早期版本追求“全量记录”觉得存得多总比存得少好结果三个月后记忆库变成一团乱麻检索出来的东西一半是噪音。后来改成“宁缺毋滥”只存高置信度、高复用性的信息效果反而好得多。记忆系统的质量取决于你扔掉了多少而不是存了多少。第二个坑是过度自动化。一开始我想让系统全自动提取、自动写入、自动检索结果模型偶尔的幻觉被存进去还反复召回污染越来越严重。后来我在写入环节加了一道人工确认虽然麻烦点但记忆库干净多了。如果你也在搭类似系统强烈建议至少在写入环节留个人工闸门等系统稳定了再考虑放开。第三个体会是注入格式比想象中重要。同样的记忆内容用 JSON 注入和用自然语言注入模型的使用效果差很多。JSON 结构清晰但模型读起来费劲自然语言读起来顺但结构信息丢失。我最后用的是“自然语言为主、关键字段用括号标注”的混合格式兼顾两者。最后分享一个小技巧给记忆系统加一个“遗忘”机制。不是所有记忆都该永久保留过期的、被取代的、长期没被召回的应该定期清理或归档。我设的规则是超过 180 天没被任何一次检索命中的记忆自动转入冷存储不再参与常规召回。这一招让检索精度和速度都上了一个台阶。记忆这东西跟人脑一样会忘才会记。