这次我们来看一个比较“极客”的实验项目:作者在 Hacker News 上用 Show HN 形式分享了自己针对“LLM 长期记忆(Long term memory)”做的架构实验。这类项目通常不是开箱即用的成熟产品,而更像是一组思路、代码和实验数据:它把不同记忆方案放到真实会话场景里对比,直接回答一个工程问题——上下文窗口之外,模型怎么记住更久之前的信息。
如果你正在做 LLM 应用、Agent 开发或 RAG 工程,这篇文章会比论文更贴近实战。我会从长期记忆问题的来源讲起,梳理目前主流的架构方案与边界,再给出一套可执行的实验评估流程,最后补充部署、批量任务和常见排查方式。适合正在设计 Agent 记忆模块、犹豫要不要上向量库、或者想给自己的 LLM 应用加“记忆层”的读者。
先说清楚一点:由于原始发布页面没有给出完整安装包、显存数据和基准分数,本文不会编造“实测占用 XX GB”或“支持 XX 系显卡”。我会按实验项目通用流程来拆解,所有具体参数以你本机跑出来的结果为准。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | LLM 长期记忆架构实验,偏研究和原型验证 |
| 核心主题 | 长期记忆、上下文增强、多轮会话记忆、记忆检索 |
| 主要探索方向 | 向量化记忆、摘要压缩、层次化记忆、图结构记忆、混合记忆架构 |
| 硬件要求 | 取决于你使用的 LLM 与 Embedding 模型;纯检索层实验可先用 CPU 验证 |
| 显存占用 | 不确定,需按实际 LLM 版本、向量库和推理参数测试 |
| 启动方式 | 命令行脚本 / API 服务,具体以后仓库 README 为准 |
| 是否支持 API | 实验代码通常可包装为本地 HTTP 接口 |
| 是否支持批量任务 | 可自行编写批量导入与批量检索脚本 |
| 适合场景 | 研究对比、Agent 记忆层设计、RAG 增强、多轮对话上下文管理 |
这个项目的价值不在“一键启动”,而在架构选择的对比。长期记忆不是一个模型参数能解决的问题,它涉及存储、检索、压缩和注入方式四层设计。
2. 长期记忆为什么难:上下文窗口的硬约束
先回到最基本的问题:为什么 LLM 会“失忆”?
第一,上下文窗口是硬边界。模型每次推理能看到的 token 数是有限的。即使窗口有 128K、256K,也不可能把用户过去一年所有对话都塞进去。第二,token 成本不是零。长上下文的计算和花销会随长度增长,生产环境不可能无限堆历史记录。第三,输入过长后,模型对中间信息的注意力会明显下降,这是一个普遍存在的工程问题,与具体模型无关。
更麻烦的是 Agent 场景。用户要求“先查资料,再写计划,最后执行”,中间状态如果没有被显式保存,模型在一个新请求里就不知道之前做了什么。这里需要的不是“聊天记录”,而是结构化的任务状态和事实记忆。
所以长期记忆架构要做的事情不是“存得更多”,而是“记得更准”。评估一个方案好不好,要看三点:
- 该记得的还记得吗?
- 不该记的有没有混进来?
- 检索结果放回上下文后,模型用起来是否正确?
接下来的几种架构方案,本质上都是在回答这三个问题。
3. 主流的 LLM 长期记忆架构思路
3.1 向量数据库加检索增强
这是目前最常用的方案。核心思路是:把历史对话或文档切成块,用 Embedding 模型转成向量存入向量库;新问题进来时,先做相似度检索,把最相关的记忆片段拼进 Prompt,再让 LLM 回答。
优点是好落地,生态成熟。缺点是检索质量依赖块切分、Embedding 模型和阈值设置,而且纯向量相似度不一定能表达“时间先后”“因果逻辑”这类关系。
适合的场景:个人知识库、客服问答、RAG 文档问答。
3.2 摘要压缩与层次化记忆
当对话足够长,先让 LLM 把旧对话概括成摘要,把摘要作为高层记忆;新对话仍保留细节,等到超过窗口再压缩成新一层摘要。这就是层次化记忆。
这个方案的优点是能大幅减少 token 开销,让模型用很小的空间记住很长的历史。缺点是压缩过程会丢失细节,如果摘要写得不好,关键事实可能直接消失。
适合的场景:长对话、Agent 长期任务、个人 AI 助手。
3.3 图结构记忆
把实体和关系抽取出来,存成图结构。例如“小明喜欢咖啡”“小明是产品经理”,在图中就是节点和边。回答问题时,先定位相关实体,再沿着边的路径取回相关事实。
图记忆适合处理多跳推理和关系类问题,能避免向量检索中“语义相似但无关”的噪声。但构建图需要额外做实体识别和关系抽取,工程复杂度高。
适合的场景:多轮对话中的用户画像、复杂关系查询、知识密集型 Agent。
3.4 记忆模块与可学习写入
一些实验会直接在模型结构上增加“长期记忆模块”,或者维护一个可学习的记忆 Bank,让模型在推理时决定“要不要写入记忆”“从记忆中读哪条”。
这种方案的实验属性很强,通常需要改模型、微调或接外部可训练组件,普通应用层项目不会直接使用。但它指向一个方向:长期记忆不只是外挂检索,也可以做成模型能力的一部分。
适合的场景:研究探索、开源模型微调、需要定制记忆策略的原型。
3.5 混合架构
实际生产里很少只用一种方案。常见组合是:向量库做语义检索,图结构存实体关系,摘要层压缩旧历史,短期对话保持原样。每次请求按“短期上下文 + 相关长期记忆 + 任务状态”三层拼接。
混合架构的收益是记忆质量更稳,代价是系统复杂度直线上升。对于实验项目,建议先分别测通各层,再做组合。
| 方案 | 记忆粒度 | 工程复杂度 | 主要风险 | 推荐场景 |
|---|---|---|---|---|
| 向量检索 | 片段级 | 低 | 检索噪声、时间信息缺失 | 知识库、RAG |
| 摘要压缩 | 事件级 | 中 | 细节丢失、摘要失真 | 长对话、Agent |
| 图记忆 | 实体关系级 | 高 | 抽取质量、更新成本 | 用户画像、复杂推理 |
| 可学习记忆模块 | 模型级 | 很高 | 训练成本、可控性 | 研究实验 |
| 混合架构 | 多级 | 高 | 模块间协同 | 生产级 Agent |
4. 实验评估:怎么量化“记得住”
判断一个长期记忆实验是否有效,不能只看演示效果。建议至少设计四组测试。
4.1 事实保持测试
在一轮对话里告诉模型若干事实,之后隔 5 轮、10 轮、20 轮再问这些事实。对照组使用无记忆系统,实验组使用长期记忆系统,记录正确回答率。
4.2 冲突信息测试
先告诉模型“A 项目的截止时间是 3 月 1 日”,过一段时间更新为“A 项目截止时间改为 4 月 15 日”。系统应正确回答新事实,而不是被旧记忆干扰。这个测试对图记忆和向量检索尤其重要,因为旧向量可能仍然被检索回来。
4.3 多会话重启测试
模拟真实使用:会话一聊客户需求,会话二聊技术方案,会话三直接要求“基于前两次的需求和方案写一个总结”。看系统是否能跨会话关联信息。
4.4 性能与成本测试
记录三个指标:平均响应延迟、Prompt 平均 token 数、检索命中率。长期记忆方案如果引入后延迟翻倍但准确率只提升 2%,在真实场景中可能不值得。
建议整理成一张评估表:
| 测试项 | 指标 | 对照组 | 实验组 | 结论 |
|---|---|---|---|---|
| 事实保持 | 正确率 | 低 | 高 | 记忆生效 |
| 冲突更新 | 最新事实正确率 | 低 | 中 | 需检查检索干扰 |
| 跨会话 | 总结准确率 | 低 | 较高 | 记忆可跨会话 |
| 成本 | Prompt token 量 | 少 | 多 | 需权衡 |
5. 环境准备与通用实验流程
因为原始项目属于实验性质,这里给出一套通用环境检查清单。具体依赖要以仓库 README 为准。
5.1 环境检查清单
- 操作系统:Linux / macOS / Windows(推荐 Linux)
- Python 版本:建议 3.10 以上,避免某些向量库和 Embedding 库版本兼容问题
- LLM 推理服务:可以是 OpenAI API、本地 vLLM、Ollama 或 Transformers 脚本
- Embedding 模型:常见可选 bge、m3e、text-embedding-ada 等,按实际项目支持为准
- 向量数据库:Chroma、FAISS、Milvus、Qdrant 或简单 JSON 存储
- 显存与内存:取决于所选 LLM;如果只是做检索层实验,CPU 也可以跑
- 磁盘空间:模型文件和向量数据需要预留,建议至少 10GB 以上可用空间
5.2 通用启动流程
如果你要把实验项目跑起来,一般步骤如下。
# 1. 克隆项目 git clone <仓库地址> cd <项目目录> # 2. 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate # 3. 安装依赖 pip install -r requirements.txt # 4. 按 README 配置模型和向量库 # 5. 运行数据导入脚本 python import_memory.py --input ./data/conversations.jsonl # 6. 启动测试脚本或 API 服务 python run_experiment.py --query "之前聊过的用户偏好是什么?"以上命令是通用模板,实际脚本名和参数需要替换成项目里真实的入口文件。
6. 功能测试与效果验证
6.1 基础检索测试
测试目的:确认记忆写入后可被检索到。
操作步骤:写入一条记忆,如“用户 W 喜欢摄影和户外运动”,然后查询“用户 W 的爱好”。
预期结果:返回包含摄影和户外运动的记忆片段。
判断标准:Top 5 结果里出现正确记忆,且相似度分数明显高于无关片段。
如果失败,优先检查 Embedding 模型是否加载正确、文本是否被正确切块、向量库是否有数据。
6.2 跨会话召回测试
测试目的:验证长期记忆能跨会话使用。
输入示例:
- 会话一:设定事实“项目 H 的技术栈是 Go 和 PostgreSQL”
- 会话二:开启新会话,提问“项目 H 用的什么数据库?”
预期结果:系统能基于长期记忆返回 PostgreSQL,而不是“我不知道”。
失败排查:确认新会话的消息没有直接拼入旧对话,而是走了记忆检索;如果检索了但没命中,需要降低相似度阈值或检查切块大小。
6.3 摘要压缩测试
测试目的:验证长历史被压缩成摘要后仍保留关键信息。
把一段 10 轮对话交给摘要模块整理,然后提问摘要中隐含的事实。例如“周会上定下来什么时候上线?”。
判断标准:摘要应保留时间、负责人和动作三要素。如果摘要缺失关键字段,要调整摘要 Prompt 或增加结构化输出格式。
6.4 端到端对比测试
建议写一个脚本,分别跑“无记忆”和“有记忆”两组,对比同一批问题集的准确率。
# 通用对比脚本示例,需要按实际项目接口调整 import json questions = [ {"question": "用户W喜欢什么运动?", "expect": "摄影和户外运动"}, {"question": "项目H用什么数据库?", "expect": "PostgreSQL"}, {"question": "上线时间定在什么时候?", "expect": "下周五"}, ] def ask_with_memory(q): # 1. 检索相关记忆 # 2. 构造 prompt # 3. 调用 LLM # 4. 返回答案 return "模拟结果" def ask_without_memory(q): return "模拟结果" for q in questions: a1 = ask_without_memory(q["question"]) a2 = ask_with_memory(q["question"]) print(q["question"]) print("无记忆:", a1) print("有记忆:", a2)这个脚本会把对比结果打印出来,方便人工判断。自动化评估可以再接入 LLM 裁判或规则匹配。
7. 接口 API 与批量任务封装
实验项目跑通后,下一步通常是封装成服务。下面提供一个通用 API 包装思路。
7.1 API 服务示例
如果你要把记忆查询包装成 HTTP 接口,可以使用 FastAPI 或 Flask。以下是伪代码模板,实际路由和逻辑需要对齐项目代码。
# app.py 伪代码示例,不可直接运行 from fastapi import FastAPI, Request app = FastAPI() def recall_memory(query: str, top_k: int = 5): # 1. 将 query 转成 embedding # 2. 在向量库中检索 top_k # 3. 返回记忆片段列表 return [] @app.post("/api/recall") async def recall(request: Request): body = await request.json() query = body["query"] top_k = body.get("top_k", 5) memories = recall_memory(query, top_k) return {"query": query, "memories": memories}启动方式:
uvicorn app:app --host 127.0.0.1 --port 8000这里建议只绑定 127.0.0.1,如果部署在服务器上再通过反向代理做访问控制。
7.2 批量记忆写入脚本
长期记忆不能只靠人工一条条录。批量导入是刚需。下面是一个通用批处理示例。
# batch_import.py 伪代码示例 import json def embed_text(text: str): return [] # 替换为实际 embedding 调用 def store_vector(vector, metadata: dict): pass # 替换为实际向量库写入 with open("conversations.jsonl", "r", encoding="utf-8") as f: for line in f: item = json.loads(line) text = item.get("text", "") meta = item.get("meta", {}) vec = embed_text(text) store_vector(vec, meta) print("批量导入完成")批量任务需要注意两点:一是失败重试,二是幂等写入。如果脚本中断,重新执行时不应产生重复记忆。
7.3 批量测试与结果记录
批量评估时建议把结果写入 CSV 或 SQLite,方便统计准确率。每次请求保存问题、答案、检索到的记忆片段、相似度分数和响应耗时。实验结论都要以这些数据为依据,不能只凭几次演示判断。
8. 资源占用与性能观察
长期记忆方案的资源开销集中在三个位置。
8.1 Embedding 计算
每次写入和查询都需要调用 Embedding 模型。小模型在 CPU 上也能跑,但批量导入会比较慢。观察指标是每秒处理文本条数。
8.2 检索链路
向量检索的延迟取决于向量库类型和数据量。小规模实验用内存型 FAISS 或 Chroma 即可,数据量到百万级再考虑 Milvus、Qdrant 这类独立服务。
8.3 LLM 推理与 token 开销
长期记忆最终要注入 Prompt,这会增加输入 token。如果每次注入 2000 字记忆,成本可能比无记忆方案高不少。
建议观察以下指标并记录:
- 一次请求总耗时
- Prompt 输入 token 数
- 检索命中片段数量
- 是否触发上下文截断
- 记忆更新频率
查看显存占用时,可以用以下命令:
nvidia-smi --query-gpu=name,memory.used,memory.total --format=csv要强调的是,长期记忆方案不一定比把全部历史塞进上下文更省显存。它省的是 token 和上下文长度,而不是模型推理本身的显存。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 检索不到记忆 | Embedding 模型未加载或向量库为空 | 检查导入日志、向量库记录数 | 重新执行导入脚本,确认写入成功 |
| 检索结果太乱 | 切块过大或相似度阈值过低 | 打印 Top N 结果和分数 | 缩小切块大小,调高阈值 |
| 回答用旧事实 | 新旧记忆同时被检索 | 查看注入 Prompt 的记忆排序 | 加入时间戳并优先返回最新记录 |
| 启动时缺依赖 | 依赖版本冲突 | 查看报错堆栈 | 按 README 锁定版本,重建虚拟环境 |
| API 调用超时 | LLM 推理慢或检索阻塞 | 先测检索单独耗时 | 对 LLM 调用加超时与重试 |
| 批量任务重复写入 | 脚本无幂等控制 | 检查向量库记录数 | 加入 doc_id 去重逻辑 |
| 长历史压缩后事实错误 | 摘要 Prompt 缺少结构化输出要求 | 单独测试摘要模块 | 改成 JSON 或 key-value 格式输出 |
| 显存不足 | LLM 模型体积与量化未配置 | 观察 nvidia-smi | 换小模型或使用量化版本 |
排查原则:先确认每一层单独可用,再排查层与层之间的数据传递。向量检索有问题,就先单独测试检索;摘要有问题,就先单独测试摘要生成。不要把问题堆在一起调试。
10. 最佳实践与合规提醒
第一,先跑通最小闭环。不要一开始就上完整混合架构。最小闭环是:写入一条记忆、检索出来、注入 Prompt、得到答案。四步通了,再扩展数量和类型。
第二,给记忆加元数据。每条记忆至少记录时间、来源会话、置信度或优先级。没有时间戳的记忆在事实更新场景下会变成干扰源。
第三,控制注入量。不要追求“把所有相关记忆都塞进去”。通常注入 3 到 8 条高相关片段就够了,超过之后答案质量反而可能下降。
第四,建立数据管理规范。记忆数据可能包含用户隐私。处理个人身份信息、聊天记录、文档内容时,必须确认来源合法、用途合规。如果要使用真实用户数据做实验,需要脱敏并获得授权。
第五,不能滥用记忆做身份冒充或伪造用户偏好。如果系统被恶意注入虚假记忆,会直接影响回答可信度。生产环境需要考虑“记忆写入权限”和“记忆内容过滤”。
第六,涉及人脸、声音、版权材料的场景,必须确认授权。LLM 长期记忆本身不涉及这些,但如果将它集成到音视频、数字人或自动化内容生成流程,就同样需要合规审查。
11. 总结与下一步
这个项目最值得尝试的点,是它把“长期记忆”从一个概念变成了一组可以对比的实验。你不用纠结理论模型,而是可以直接观察不同架构在检索、召回、冲突更新和 token 开销上的真实差异。
第一次跑通时,建议先验证三件事:记忆能不能写入、检索能不能召回、注入记忆后回答是否变准。最容易踩的坑多半是切块大小和相似度阈值设置不当,导致检索结果里混入大量无关信息。
后续可以往两个方向扩展:一是把长期记忆和 Agent 任务状态结合,实现跨会话任务恢复;二是把沉淀下来的记忆整理成个人知识库或 LLM Wiki 式的结构化资料,让记忆不仅服务于问答,还能被二次复用。
架构没有银弹。长期记忆的选型最终取决于你的场景:知识库优先向量检索,长对话优先摘要压缩,复杂关系优先图记忆,生产级系统则需要组合。先用实验数据说话,再决定把哪一层放进生产环境。