你有没有遇到过这种情况:昨天刚和 Claude 敲定的技术方案、代码风格和部署环境,今天新开一个会话,它像失忆了一样全忘了,你得从头再交代一遍。如果只是个人聊天倒还能忍,但你要是拿它做项目助手、客服机器人、或者企业内部的问答系统,这种“每次对话都要重讲背景”的体验基本就是灾难。claude-mem 这个名字听起来像一个小工具,实际上代表的是一整套解决 AI 无状态对话问题的思路:在模型和你的业务之间加一层长期记忆中间件,让它把历史对话里的关键信息沉淀下来,下次需要的时候自动捞回来。这篇文章不打算甩给你一个现成仓库就完事,我想把这类记忆增强工具从设计思路到落地实现完整拆一遍,顺便给出一份能直接抄作业的最小代码。适合正在做对话类应用的开发者,也适合被反复要求重复交代上下文折腾到崩溃的深度 AI 用户。
后面我会覆盖:为什么需要记忆层、存储结构怎么设计、检索策略怎么做、代码怎么接进现有对话流程,以及我在实际搭建中踩过的一些坑。整体偏工程向,但我会尽量把概念讲得直白,保证你照着做完能跑起来。
1. claude-mem 是什么:给 AI 对话装上长期记忆的中间件
1.1 无状态会话的痛点:鱼只有七秒记忆
Claude 这类大语言模型本身是无状态的。每次你发送一轮消息、拿到一个回复,这个交互就结束了。模型不会在服务端保存你的聊天记录,更不会在下次请求时自动读取上次聊了什么。服务器能看到的,只有你当前这次请求里携带的那几段 messages。这就像一条只有七秒记忆的鱼,每次新会话都是“初次见面”。
这种设计不是 bug,而是大模型服务的合理形态。模型推理只负责“根据当前输入生成输出”,维护用户状态是应用层该干的事。但问题也随之而来:你昨天跟它确认过的项目背景、用户偏好、技术约束,今天都必须重新讲一遍。对个人用户来说,这顶多是麻烦;对开发者来说,这意味着你要在业务代码里手工维护一堆会话状态、背景资料、历史摘要。更麻烦的是,当对话变长,你得想方设法把之前的内容压缩、裁剪,才能塞进有限的上下文窗口。我见过不少团队为了“让 AI 记住上下文”,写了各种花式拼接逻辑,最后维护成本比业务代码还高。
claude-mem 要解决的核心问题,就是让对话服务具备“跨会话记忆能力”。它不是模型本身的能力,而是夹在模型和业务之间的一层胶水:负责把对话中值得沉淀的信息提取出来、存储起来,并在下一次对话开始时把相关内容检索出来,注入到模型的输入里。这样一来,模型看起来就像真的记住了你。
1.2 claude-mem 能解决什么问题
从实际使用场景来看,加了这层记忆中间件之后,最直观的改变有三个。
第一,跨会话记住用户偏好。比如用户说过“回复尽量用列表”“我不喜欢太官方的语气”“项目部署在端口 8080”,这些信息可以自动沉淀,下次对话直接生效,用户不用反复强调。
第二,保存项目级决策记录。拿代码助手场景来说,今天确定用某个框架、定下某个目录结构,明天继续聊时能自动带出这些背景,上下文一致性会好很多。
第三,降低重复问答带来的 token 浪费。我见过一个客服机器人,用户每次问问题都要先自我介绍“我是某某会员,上次反馈过订单问题”。如果系统能记住这些,不仅体验更好,还省下大量重复 token。
那什么人适合用这套东西?主要是这几类:做对话类产品、需要把用户画像和偏好传给模型的开发者;做 Agent 编排,希望 Agent 在多轮任务中不丢失关键信息的工程师;以及重度使用 AI 助手做个人知识管理的用户。反过来,如果你的业务合规要求极严、不允许存任何明文对话信息,或者你的对话场景本身就是一次性的短问答,那 claude-mem 就不太合适,得先做脱敏或直接放弃。
1.3 和向量数据库、聊天记录备份不是一回事
很多人一听“记忆增强”,第一反应就是“你要上向量数据库了吧”。这个理解有点偏差。claude-mem 的本质是一套记忆管理系统,向量数据库只是它可选的一种检索工具。它更像大脑皮层,负责完成“提取—存储—检索—遗忘”这一整条链路;而聊天记录备份只是把原始对话原封不动存起来,根本不算记忆。
举个例子。你和模型聊了十分钟,讨论服务部署方案,中间提到“服务器内存 4G,Python 版本 3.10,端口用 8080”。如果只是备份聊天记录,这段信息淹没在一大堆寒暄和废话里,下次检索出来也是一大坨,模型还是要自己找重点。而真正有意义的记忆应该是高度浓缩的事实:“部署环境:Python 3.10,端口 8080,内存 4G”。claude-mem 做的事就是这个——它要知道什么时候该记、记什么、怎么组织,然后在需要的时候把最相关的那几条捞回来,而不是把所有日志一股脑塞给模型。
所以判断一个记忆系统好不好的关键,恰恰不是存储容量,而是“记什么、忘什么、怎么在正确时机取出来”。
2. 核心设计思路:记忆到底怎么存、怎么取、怎么注入
2.1 先分清三类记忆:会话级、用户级、项目级
在设计记忆层之前,最好先把记忆分成不同粒度,不然很容易做成一个大杂烩。我一般分成三层。
会话级记忆是短期记忆,只服务当前这一轮对话。它用来记录“刚刚聊到哪了”“上一条回复里提到的临时变量叫什么”。这类记忆通常不落库,放在内存里,对话结束就可以丢。用户级记忆是长期记忆,跨会话保留。比如用户的称呼、偏好、语言风格、常用工具链。这类记忆适合存成结构化字段,每次对话都值得参考。项目级记忆则是围绕某个主题、某个项目展开的关键上下文,比如“这个项目的代码仓库路径”“技术栈是前后端分离”“上周确定用 PostgreSQL 作为主库”。
实际落地时,我会在每条记忆上打一个 level 标签,同时在查询时区分优先级:项目级的硬约束最高,用户级的偏好其次,临时信息最低。这样注入 prompt 时,可以先保证硬约束不丢,再考虑补充软性的偏好信息。如果一开始就不分层,后面做记忆裁剪会非常痛苦。
2.2 存储选型:从 JSON 到向量数据库怎么选
记忆的存储方案没有标准答案,但选型逻辑很清晰:根据数据量、并发量、检索复杂度来决定。
| 存储方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| JSON 文件 | 简单直观,改起来快 | 并发差,查询靠遍历 | 个人工具、原型验证、几百条以内 |
| SQLite | 支持事务,可靠性高,零部署成本 | 语义检索要自己实现 | 中小项目,单机部署,万级以内 |
| PostgreSQL + pgvector | 功能全,支持 SQL 和向量索引 | 运维成本高,需单独维护 | 正式 SaaS 服务、多租户、需要复杂查询 |
| 专业向量数据库 | 百万级向量检索性能好 | 组件重,需要网络部署和额外监控 | 大规模 RAG、海量非结构化记忆 |
我给大多数人的建议是:起步直接用 SQLite。理由很简单,记忆条数一般不会上百万。一个重度用户哪怕每天沉淀 50 条记忆,一年也就一万多条,SQLite 完全扛得住。不要为了“显得高级”一开始就上重组件,部署复杂度会吃掉你的开发效率。
真正需要认真花心思的是检索字段。我会在 SQLite 里存原始文本、关键词数组、主题分类、时间戳、访问次数,另外可选存一个 embedding 向量。检索时先用关键词做粗筛,缩小候选集,再在候选集里算向量相似度排序。这个“先粗后精”的思路,比单靠其中任意一种都稳。
2.3 检索策略:多路召回、分数合并、时间衰减
记忆检索的目标是在用户提问之后,快速找到“对这个场景最有用的几条记忆”。只靠关键词匹配不够,因为用户表达方式千变万化;只靠向量相似度也不够,因为精确 ID、版本号这类信息,embedding 特别容易翻车。所以要采用多路召回。
第一路是向量相似度。把用户当前问题转成 embedding,和记忆库里的 embedding 算余弦相似度,召回 Top N。这一路对“意思相近但用词不同”的查询很有效。第二路是关键词/全文检索。把问题里的名词、ID、版本号抽取出来,精确匹配记忆的 keywords 字段或原文。这一路能精确命中“CLAUDE-01 环境”“端口号 8080”这类冷信息。第三路是规则补充。比如用户明确说了“记住”“以后都”,对应的记忆必须无条件召回。
召回之后就是排序。我给每条记忆算一个综合分,大致公式是:
score = 0.6 × 语义相似度 + 0.2 × 新近度 + 0.2 × 访问权重
语义相似度就是 embedding 余弦值;新近度会随时间衰减,比如 30 天前的记忆权重降到接近 0;访问权重则反映常用程度,一条被反复参考的记忆应该更重要。这三项加权之后,取最高的 5 到 10 条注入上下文。
这里有个容易翻车的细节:时间衰减权重不能给太高。我试过把新近度权重提到 0.4,结果系统只记得最近几天的内容,把一个月前的重要项目约束全忘了。后来改成 0.2,才算平衡。
2.4 记忆注入与 token 预算
查到了记忆,接下来要解决的是怎么注入。常规做法是拼进 system prompt,因为它对模型有全局约束效果,比塞进用户消息里更稳定。我会把记忆块放在 system prompt 的靠前位置,用明显的分隔符包起来,例如“以下是该用户的长期记忆,请优先参考”。
但注入不是把检索到的所有记忆全部塞进去。上下文窗口有限,记忆占多了,真正要处理的任务内容就没空间了。我一般会设一个 token 预算,比如最大注入 800 token。先注入“硬记忆”——用户明确要求记住的偏好、项目肯定不变的约束,这类必须进;再按综合分从高到低填充“软记忆”,填到预算为止。预算建议控制在请求总 token 的 10% 到 20% 之间。比如一个 8000 token 窗口的对话场景,记忆最多占 1200 token,超过就要压缩。
还有一个细节:如果检索返回的是非常长的原始文本,记得先做压缩,只保留核心事实,再注入。我最开始图省事,直接把命中记录原文拼进去,结果一条 500 字的旧记录就挤掉了几千 token 的有效空间,后来改成“让模型把旧记录浓缩成三句话摘要再入库”,问题就解决了。
3. 实操构建:从零搭建一个最小可用的 claude-mem 记忆服务
3.1 环境准备与依赖选型
我下面要给的这套最小实现,不依赖任何厂商 SDK,只用 Python 3.10 及以上版本自带的标准库,加一个 requests 作为网络请求库。SQLite 是 Python 自带的,不需要额外安装。这样你可以把代码复制下来就跑,不受某个服务商 SDK 升级影响。
依赖安装命令:
pip install requests numpy python-dotenvnumpy 用来计算向量相似度,如果暂时不打算接 embedding 接口,也可以去掉。dotenv 用来管理环境变量,比如 API Key。初始化项目目录:
mkdir claude-mem && cd claude-mem touch memory.db claude_mem.py .env我的目标不是给一个完整成品,而是把记忆写入、检索、注入、这几个核心环节串起来。生产环境用的话,你可以在它的基础上扩展异步、重试和鉴权。
3.2 先搭记忆表:字段设计决定上层能力
数据库表设计是记忆系统里最容易被低估的环节。字段如果设计得太简单,比如只存一个 text 字段,后面做查询、做分类、做审计都会很痛苦。我建议至少包含这些字段:
CREATE TABLE IF NOT EXISTS memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, namespace TEXT DEFAULT 'default', content TEXT NOT NULL, topic TEXT DEFAULT '', keywords TEXT DEFAULT '[]', source TEXT DEFAULT 'user_input', level TEXT DEFAULT 'normal', created_at REAL NOT NULL, updated_at REAL NOT NULL, access_count INTEGER DEFAULT 0 ); CREATE INDEX IF NOT EXISTS idx_user ON memories(user_id); CREATE INDEX IF NOT EXISTS idx_topic ON memories(topic);user_id 是记忆归属,实现多用户隔离;content 是核心内容;keywords 用 JSON 数组存,方便做关键词匹配;source 记录这条记忆来自哪里,是用户主动要求记住、还是系统自动抽取,这个字段在排查问题时特别有用;level 就是前面说的硬记忆、软记忆分层;access_count 用于排序时的热门加权。
初始化数据库的函数:
import sqlite3, json, time from pathlib import Path DB_PATH = Path(__file__).parent / "memory.db" def get_conn(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): with get_conn() as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, namespace TEXT DEFAULT 'default', content TEXT NOT NULL, topic TEXT DEFAULT '', keywords TEXT DEFAULT '[]', source TEXT DEFAULT 'user_input', level TEXT DEFAULT 'normal', created_at REAL NOT NULL, updated_at REAL NOT NULL, access_count INTEGER DEFAULT 0 ) """) conn.execute("CREATE INDEX IF NOT EXISTS idx_user ON memories(user_id)") conn.execute("CREATE INDEX IF NOT EXISTS idx_topic ON memories(topic)")3.3 记忆写入:哪些内容值得记
写入记忆这件事,不能来一条记一条,必须区分“什么值得进长期记忆”。我总结了两个触发源。
一是规则触发。用户明确说了“记住”“以后都这样”“我的习惯是”这类表达时,把后面的话提取出来写入。另外,业务方也可以用 API 显式写入,比如客服工单结束后,自动记录“该用户偏好电话回访”。这种硬性触发的记忆,level 标记成 hard,下次对话必须注入。
二是模型摘要触发。在长对话结束后,把这段对话交给模型做一次轻量总结,让它输出三到五条“值得长期记住的事实”。比如“用户是前端开发工程师”“项目部署环境是 Python 3.10”“暂定每周五上线”。这种自动抽取的记忆在写入前要经过关键词、置信度检查,避免把闲聊内容当事实存下来。
写入函数长这样:
def add_memory(user_id, content, topic="", keywords=None, source="user_input", level="normal", namespace="default"): with get_conn() as conn: conn.execute( """INSERT INTO memories (user_id, namespace, content, topic, keywords, source, level, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", (user_id, namespace, content, topic, json.dumps(keywords or []), source, level, time.time(), time.time()) )这里有个经验:写入前先做一次去重。同一事实刚写过,就不要写第二遍。最简单的做法是查一下同 user_id 下有没有内容相似度超过 0.9 的记录,有就跳过。否则用户每说一次“记住”,库里就会出现三条一模一样的记录,检索时反而干扰排序。
3.4 记忆检索与注入:核心代码串联
检索函数是整套系统的关键。我先做一个轻量版,用关键词打分加时间衰减:
def search_memories(user_id, query, top_k=5, namespace="default"): with get_conn() as conn: rows = conn.execute( "SELECT * FROM memories WHERE user_id = ? AND namespace = ? ORDER BY updated_at DESC LIMIT 200", (user_id, namespace) ).fetchall() query_set = set(query.lower().split()) query_terms = [t for t in query_set if len(t) > 1] scored = [] for row in rows: score = 0.0 ks = set(json.loads(row["keywords"] or "[]")) overlap = len(query_terms & ks) if overlap > 0: score += overlap * 1.0 if row["topic"] and row["topic"] in query: score += 0.5 if row["level"] == "hard": score += 0.8 age_days = (time.time() - row["updated_at"]) / 86400 recency = max(0.0, 1.0 - age_days / 30.0) score += recency * 0.2 if score > 0: scored.append((score, row)) scored.sort(key=lambda x: x[0], reverse=True) return [dict(row) for _, row in scored[:top_k]]注意,这里用了最简单的空格分词。中文场景这样会吃亏,因为中文没有天然空格。你可以把 split 换成 jieba 分词,或者直接用子串匹配判断关键词是否出现在 query 里。比如:
if any(k in query for k in ks): score += 1.0这个对中文更友好,建议直接用。
有了检索能力,接下来把它接进对话请求。我会写一个函数,先根据用户问题检索记忆,再组装 system prompt,最后调用模型接口:
import requests def build_system_prompt(user_id, query, base_prompt="你是一个可靠的助手。"): memories = search_memories(user_id, query, top_k=5) if not memories: return base_prompt mem_lines = "\n".join(f"- {m['content']}" for m in memories) return base_prompt + "\n\n以下是该用户/项目相关的长期记忆,请优先参考:\n" + mem_lines def chat_with_memory(user_id, user_message, api_url, api_key): system_prompt = build_system_prompt(user_id, user_message) resp = requests.post( api_url, headers={"Authorization": f"Bearer {api_key}"}, json={ "model": "your-model-name", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ] }, timeout=60 ) resp.raise_for_status() reply = resp.json()["choices"][0]["message"]["content"] if "记住" in user_message or "以后都" in user_message: add_memory(user_id, user_message.split("记住", 1)[-1].strip(), source="explicit") return reply流程就是:用户提问进来,先检索记忆,把记忆拼进 system prompt,再请求模型接口。回复返回后,如果检测到用户有明确的“记住”指令,就把对应内容写入库。这个链路是最小闭环,你可以直接照着跑。
3.5 提供命令行管理工具
记忆系统一定要有管理入口。不能让记忆只进不出,否则时间长了库里全是垃圾。我一般会给它配一个简单的命令行工具,至少支持查、删、清三个动作。
python claude_mem.py list --user user_001 python claude_mem.py search --user user_001 --query "部署环境" python claude_mem.py delete --id 42 python claude_mem.py clear --user user_001用 argparse 实现就行,核心是连数据库,把对应记录打印出来或删掉。比如 list 命令:
import argparse def cmd_list(args): with get_conn() as conn: rows = conn.execute( "SELECT id, content, topic, level, updated_at FROM memories WHERE user_id = ? ORDER BY updated_at DESC LIMIT 20", (args.user,) ).fetchall() for r in rows: print(f"#{r['id']} [{r['level']}] {r['content']} (updated: {r['updated_at']})") if __name__ == "__main__": init_db() parser = argparse.ArgumentParser(prog="claude-mem") sub = parser.add_subparsers() p_list = sub.add_parser("list") p_list.add_argument("--user") p_list.set_defaults(func=cmd_list) args = parser.parse_args() args.func(args)一定要保留手动删除能力。自动记忆再好,也有抽风的时候。没有删除入口的记忆系统,就像没有橡皮擦的草稿纸,迟早写满错误。
4. 常见问题与排查技巧实录
4.1 为什么检索不到记忆
这是使用过程中出现频率最高的问题。明明库里有记录,但对话时就是不生效。我总结过几类排查思路。
先看库里有没有数据。很多人把记忆写成功了,但实际查询时 user_id 对不上,白白折腾半天。查一下SELECT COUNT(*) FROM memories WHERE user_id = ?就能确认。
再看关键词匹配。中文场景如果按空格分词,基本就是废的,必须改成包含匹配或加上中文分词。第三看相似度阈值。有的人直接上了向量检索,阈值定在 0.8,结果很多真正有用的记忆因为表述差异被过滤掉了。向量阈值的经验是:0.6 以下太松会噪声大,0.8 以上太严会漏召回,从 0.7 开始调比较合适。
还有个容易被忽略的问题:记忆查询时不带 namespace。如果系统默认走 default,而写入时用了 project_a,那当然查不到。加一行日志,打印当前查询的 user_id、namespace、命中的数据量,能省下一堆排查时间。
4.2 上下文被记忆淹没怎么办
记忆注入太多,会让模型过于关注历史背景,忽略当前用户真正问的问题。我遇过一个场景:system prompt 里有 2000 多字的历史记忆,用户问一句“帮我确认下今天的天气”,模型居然先回了一段“根据你之前的部署计划……”,完全跑偏。这就是记忆淹没。
解法有三个。第一是硬性 token 预算,每次注入的记忆总长度控制在 500 token 以内。第二是只注入最相关的前几条,不要贪心。第三是给硬记忆、软记忆分级,硬记忆就算长一点也保留,软记忆按相关性挤占空间。我实际用下来,5 条记忆、总长约 400 token,是信息量和污染量之间的平衡点。
4.3 记忆污染与语义漂移
记忆系统最大的风险是污染。模型自动抽取记忆时,可能把一次闲聊里的抱怨当成长期偏好;可能把临时方案当成永久约束;也可能因为上下文太复杂,抽出来的结论本身就是错的。这些错误记忆一旦写进去,会在后续对话里持续影响模型输出,而且你很难察觉。
我的应对方法是“写前验证 + 写后审计”。写前验证就是抽取出记忆后,先做置信度判断,比如只有对话轮次超过 5 轮、且关键信息重复出现的记录才写入。写后审计就是定期查看记忆库,删除过时信息。我们部门把这个当成周常任务,每周花十分钟清理一次。一套系统跑了几个月后,我最大的感受是:宁可少记,不可乱记。
4.4 隐私与安全边界
记忆系统本质上是在持久化用户信息,所以安全边界必须提前想好。我的原则有三条:一是不记录敏感原文,只保存提炼后的事实;二是不记录密码、密钥、联系方式等字段;三是对记忆库文件做权限控制,普通用户账号不该有直接读写权限。
如果做多租户系统,user_id 隔离是底线,还要考虑加一层 namespace 隔离。更严格的话,可以对 content 字段做加密存储,解密只在注入 prompt 前进行。虽然会增加一点延迟,但对合规敏感的业务来说值得。
4.5 冷启动:新用户没有任何记忆怎么办
新用户第一次进入对话时,记忆库是空的。这个时候最忌讳的事情,是为了让系统“显得有记忆”而硬塞一堆通用模板,这会让所有用户收到一模一样的默认偏好,体验反而更怪。
我习惯做一个冷启动引导。首次对话时,提示用户可以告诉我固定的偏好,比如“请记住我偏好简洁回复”。这句话会被系统解析成硬记忆写入。另外,空记忆时 system prompt 里就不要放“以下是长期记忆”这段了,避免模型产生困惑。记忆是加分项,没有记忆时把普通对话质量做好才是底线。
5. 进阶玩法与部署建议
5.1 多用户与多项目隔离
如果要把 claude-mem 做成真正的服务,多租户隔离是绕不开的。最简单的方式是给每条记忆加 user_id 和 namespace 两个维度。user_id 区分不同用户,namespace 区分同一用户的不同项目。比如某位用户同时维护“后台管理系统”和“数据分析平台”两个项目,各自的记忆应该互相隔离,否则一个项目的技术方案会污染另一个项目。
查询时强制带上这两个维度:
SELECT * FROM memories WHERE user_id = ? AND namespace = ?索引建议建复合索引:(user_id, namespace, updated_at)。这样 limit 查询不会再走全表扫描。
5.2 记忆审计与可视化
记忆系统一定要能回溯。我加了一张 memory_logs 表,记录每次插入、删除、修改的时间、操作类型和来源。这张表平时不参与对话,只在排查问题时查。有一次用户反馈“AI 突然变得很官方”,我翻 log 发现是两天前自动摘要把“用户偏好轻松语气”误记成了“用户偏好书面报告风格”。有了日志,10 分钟就定位了。
可视化不用做得很复杂,定期统计一下不同 topic 的数量、记忆总量增长趋势、访问次数最高的记忆即可。这些统计能帮你判断记忆库是不是正在劣化。比如“每天新增 50 条但 Top 20 记忆大量重复”,说明去重逻辑失效了。
5.3 性能优化与容量预估
SQLite 的并发能力有限,但单机场景够用。几个优化点:开启 WAL 模式减少读写锁冲突,把记忆写入放进异步队列,批量计算 embedding 并缓存结果。实测下来,先关键词粗筛再向量排序的方式,在一万条记录以内,单次检索延迟可以做到 50 毫秒以下,完全不用上中间件。
容量预估可以这样算:一条记忆平均 50 token,一万条才 50 万 token。即便你对每个请求注入 500 token 的记忆,也只影响 1% 的 token 消耗。所以对大多数场景来说,瓶颈根本不在容量,而在记忆质量和注入策略。
5.4 接入 Agent 或 RAG 系统
记忆层可以当做一个工具交给 Agent 自己调用,而不是每次请求都强制注入。比如让 Agent 在觉得缺少背景信息时,主动调用search_memories(query, user_id)这个函数获取记忆。这样能进一步节省 token,同时让记忆的使用时机由任务驱动,更符合真实业务逻辑。如果你的系统已经接了 RAG,可以把 claude-mem 作为“对话记忆源”和 RAG 的“文档知识源”并列,一个管个人/项目上下文,一个管外部文档答案,两者各司其职。
我实际把这套东西在本地跑了几个月,最大的体会是:记忆系统的难点不在存储,而在遗忘和克制。代码逻辑谁都能写,但真正好用的记忆服务要敢删、敢不记。最后分享一个小技巧:每条记忆一定要带 created_at 和 source 字段,排查问题的时候它能帮你省一整天。这个字段没有成本,却能救急,别偷懒省掉。