大模型记忆实战
Day07|大模型记忆实战——给你的 AI 装上"三层记忆"
前言:世界上最聪明的失忆症患者
大模型就像一个智商 180 但只有 5 秒记忆的人。
你跟他聊了一下午,讲了你的项目叫"星火计划"、你团队有 8 个人、你的技术栈是 Python + FastAPI。他听得很认真,回答得很专业。你心想:这个 AI 真懂我。
然后你关掉窗口,第二天重新打开。
"你好,我叫什么?""我的项目是什么?"
它一脸无辜:"抱歉,我不知道你在说什么。"
这不是段子。这是 2026 年大模型的现状——GPT-4o 能写代码、能做数学证明,但它记不住你昨天说过的一句话。
上一篇 Day06 讲了 RAG,解决了"模型不知道你的私有数据"。但 RAG 管的是外部知识——公司文档、产品手册。有一个问题它管不了:模型记不住你说过什么。
没有记忆的 AI 助手,永远是个聪明的陌生人——每次对话都要从头自我介绍,用户没有归属感,产品没有粘性。
这篇文章干四件事:
- 讲透三层记忆架构——短期、中期、长期各管什么
- 讲 FIFO 机制怎么保证记忆实时性,不被过时信息干扰
- 给你一个可运行的三层记忆系统 demo(Python,直接跑)
- 工程落地的实战经验——踩过什么坑,怎么避
读完你会发现,给大模型装记忆,不是"把所有对话存下来"那么简单——它需要像人脑一样,知道什么该记、什么该忘、什么该更新。
PART 01:三层记忆架构——短期、中期、长期
先想一个问题:人是怎么记忆的?
你跟同事聊了 30 分钟项目方案。聊完你记住了什么?
- 刚刚说的几句话——精确到原文("他说 API 限流改成 200 次/分钟")
- 今天讨论的几个要点——大意记得,原文忘了("他提了个缓存方案,大致是 Redis + TTL")
- 这个同事是谁——结构化事实("他叫老王,负责后端,偏好 Go")
这三种记忆,对应大模型记忆系统的三层:
短期记忆:最近 N 轮对话,原样保留
定义:当前会话中,最近 K 轮对话的原始内容。
这就是模型的"工作台"——正在聊什么,一目了然。实现最简单,就是把最近的消息原封不动塞进 prompt。
容量:由上下文窗口决定。GPT-4o 是 128K tokens,大约能装 128~256 轮对话。但实际项目中你不会用满——token 越多,API 成本越高,响应也越慢。
实际设定:一般保留最近 5~10 轮(10~20 条消息),大约 2K~5K tokens。够模型理解"当前在聊什么"就行。
核心价值:精确。原文一字不差,不存在信息损失。
中期记忆:对话摘要,压缩存储
定义:超出短期窗口的历史对话,被 LLM 压缩为摘要后存储。
当对话越来越长,早期的消息被"挤出"短期窗口。直接丢掉太可惜——里面可能有重要信息。所以用一个折中方案:让 LLM 把旧对话总结成一段摘要,存起来。
触发时机:短期记忆满 K 轮时自动触发。把被挤出的对话交给 LLM 做摘要,摘要结果存入中期记忆。
核心价值:省 token。一段 10 轮对话可能 5K tokens,压缩成摘要只要 200~500 tokens。信息密度提升 10 倍以上。
代价:有细节损失。摘要保留了"主旨",但可能丢掉具体数字、精确措辞。这是可接受的折中——大多数场景下,知道"他提了个缓存方案"就够了,不需要记住原文。
长期记忆:用户画像,持久存储
定义:从对话中提取的结构化事实——用户是谁、做什么、偏好什么。
这是最"高级"的记忆。它不是存对话原文,也不是存摘要,而是提取出关于用户的关键信息,像"用户画像"一样存着。
提取方式:每次对话结束后,用 LLM 从对话中抽取关键事实:
- 姓名、职业、公司
- 项目名、技术栈
- 偏好、习惯("喜欢用 Python"、"不喜欢 Java")
- 重要决策("决定用 Redis 做缓存")
核心价值:跨会话、不衰减。关掉窗口再打开,模型依然"认识你"。这是让 AI 从"工具"变成"伙伴"的关键。
三层对比总结:
| 维度 | 短期记忆 | 中期记忆 | 长期记忆 |
|---|---|---|---|
| 存什么 | 最近 N 轮原文 | 对话摘要 | 用户特征/偏好 |
| 容量 | 5~10 轮 | 数十条摘要 | 数十条事实 |
| 精确度 | 100%(原文) | 80%(主旨) | 95%(结构化) |
| 生命周期 | 会话内 | 跨会话,可更新 | 跨会话,持久 |
| token 消耗 | 高(原文) | 中(摘要) | 低(JSON) |
PART 02:FIFO 机制——保证记忆的实时性
三层记忆搭好了,但有个问题:记忆不是越多越好。
想象一下:用户三个月前说"我在做电商项目",今天说"我转做短视频了"。如果你的长期记忆里还存着"做电商",模型会给出电商相关的建议——完全过时了。
过时的记忆不是资产,是噪音。
这就是 FIFO(First In, First Out)的价值——让记忆系统保持"新鲜",及时淘汰过时信息。
短期记忆:严格 FIFO,满了就丢
短期记忆最简单——滑动窗口,新消息进来,旧消息挤出。
消息流:[msg1, msg2, msg3, ..., msg10, msg11, msg12] 窗口 K=5(保留最近 5 轮 = 10 条消息): → [msg3, msg4, ..., msg12] # msg1, msg2 被挤出被挤出的消息不是直接丢掉——它们会被送入中期记忆做摘要压缩。这样既控制了短期窗口的大小,又不丢失历史信息。
中期记忆:合并 + LRU,旧摘要被新摘要替代
中期记忆不是简单堆叠。同一个话题的多次摘要,应该合并而不是追加。
举个例子:
- 第 1 次摘要:"讨论了 API 限流方案,决定用令牌桶算法"
- 第 2 次摘要(同一个话题):"讨论了 API 限流方案的具体实现,确定限流阈值 200 次/分钟"
这两条应该合并为:"API 限流方案:令牌桶算法,阈值 200 次/分钟。"
淘汰策略:LRU(Least Recently Used)。如果一条摘要长期没被引用(用户再也没提过相关话题),它的优先级降低。当摘要数量超过上限时,低优先级的先被清理。
长期记忆:Update,不是 Append
这是最容易踩坑的地方。长期记忆不是堆积信息,而是更新画像。
用户说"我在做电商"→ 长期记忆存{"project": "电商"}。三个月后用户说"我转做短视频了"→ 长期记忆应该更新为{"project": "短视频"},而不是追加成{"project": "电商, 短视频"}。
关键区别:
- Append(追加):所有历史事实堆在一起 → 过时信息干扰判断
- Update(更新):同一实体的最新状态覆盖旧状态 → 永远是最新画像
# 错误做法:追加 long_term_memory.append({"key": "project", "value": "短视频"}) # 结果:[{"key": "project", "value": "电商"}, {"key": "project", "value": "短视频"}] # 正确做法:更新 long_term_memory.update("project", "短视频") # 结果:[{"key": "project", "value": "短视频"}]三层淘汰策略对比:
| 层 | 策略 | 触发条件 | 处理方式 |
|---|---|---|---|
| 短期 | 严格 FIFO | 超出 K 轮 | 挤出 → 送入中期做摘要 |
| 中期 | LRU + 合并 | 同话题重复 / 超上限 | 合并同类摘要,清理低频 |
| 长期 | Update 覆盖 | 新事实与旧事实冲突 | 用新值覆盖旧值 |
PART 03:一个可运行的三层记忆系统 demo
理论讲完了,下面给一个完整的、可以直接跑的三层记忆系统。
""" Day07 三层记忆系统完整 demo 短期记忆(FIFO滑动窗口)+ 中期记忆(对话摘要)+ 长期记忆(用户画像) 依赖:pip install openai 环境变量:OPENAI_API_KEY """ import os import json import time from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) # ========== 短期记忆:FIFO 滑动窗口 ========== class ShortTermMemory: """短期记忆:保留最近 K 轮对话原文""" def __init__(self, max_rounds: int = 5): self.messages: list[dict] = [] self.max_messages = max_rounds * 2 def add(self, role: str, content: str) -> list[dict]: """添加消息,返回被挤出的消息""" self.messages.append({"role": role, "content": content}) evicted = [] while len(self.messages) > self.max_messages: evicted.append(self.messages.pop(0)) return evicted def get_context(self) -> str: if not self.messages: return "" return "\n".join(f"{m['role']}: {m['content']}" for m in self.messages) # ========== 中期记忆:对话摘要存储 ========== class MidTermMemory: """中期记忆:将被挤出的对话压缩为摘要""" def __init__(self, max_summaries: int = 20): self.summaries: list[dict] = [] self.max_summaries = max_summaries self.buffer: list[dict] = [] def add_evicted(self, messages: list[dict]): self.buffer.extend(messages) if len(self.buffer) >= 4: self._summarize() def _summarize(self): if not self.buffer: return conversation = "\n".join(f"{m['role']}: {m['content']}" for m in self.buffer) prompt = f"请将以下对话总结为简洁摘要(200字以内),保留主题、关键结论、具体数字和名称:\n{conversation}\n请输出摘要:" resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], temperature=0.3, ) self.summaries.append({ "content": resp.choices[0].message.content.strip(), "created_at": time.time(), "access_count": 0, }) self.buffer = [] if len(self.summaries) > self.max_summaries: self.summaries.sort(key=lambda s: s["access_count"]) self.summaries = self.summaries[-self.max_summaries:] def get_context(self) -> str: if not self.summaries: return "" lines = [] for s in self.summaries: s["access_count"] += 1 lines.append(f"- {s['content']}") return "[对话历史摘要]\n" + "\n".join(lines) # ========== 长期记忆:用户画像 ========== class LongTermMemory: """长期记忆:提取并维护用户画像""" def __init__(self): self.facts: dict[str, str] = {} def extract_and_update(self, user_message: str): current_profile = json.dumps(self.facts, ensure_ascii=False) if self.facts else "(空)" prompt = f"""从以下用户消息中提取需要长期记住的关键事实。 如果新事实与已有画像冲突,输出 update 操作。如果没有新事实,输出空 JSON。 当前画像:{current_profile} 用户消息:"{user_message}" 输出 JSON:{{"facts": {{"key": "value"}}, "updates": {{"key": "new_value"}}, "deletes": ["key"]}} 只输出 JSON:""" resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}], temperature=0, ) try: result = json.loads(resp.choices[0].message.content.strip()) for k, v in result.get("facts", {}).items(): self.facts[k] = v for k, v in result.get("updates", {}).items(): self.facts[k] = v for k in result.get("deletes", []): self.facts.pop(k, None) except (json.JSONDecodeError, KeyError): pass def get_context(self) -> str: if not self.facts: return "" return f"[用户画像]\n{json.dumps(self.facts, ensure_ascii=False, indent=2)}" # ========== 记忆管理器 ========== class MemoryManager: def __init__(self, short_rounds: int = 5): self.short_term = ShortTermMemory(max_rounds=short_rounds) self.mid_term = MidTermMemory() self.long_term = LongTermMemory() def process(self, role: str, content: str): if role == "user": self.long_term.extract_and_update(content) evicted = self.short_term.add(role, content) if evicted: self.mid_term.add_evicted(evicted) def build_prompt(self, new_message: str) -> str: parts = [] long_ctx = self.long_term.get_context() if long_ctx: parts.append(long_ctx) mid_ctx = self.mid_term.get_context() if mid_ctx: parts.append(mid_ctx) short_ctx = self.short_term.get_context() if short_ctx: parts.append(f"[最近对话]\n{short_ctx}") parts.append(f"user: {new_message}") return "\n\n".join(parts)跑完你会看到这样的流转过程:
| 阶段 | 短期记忆 | 中期记忆 | 长期记忆 |
|---|---|---|---|
| 第 1 轮后 | 1 轮原文 | 空 | {"name": "张明", "project": "星火计划"} |
| 填充 10 轮后 | 最近 3 轮 | 2~3 条摘要 | 不变 |
| 第 12 轮(回忆) | 命中 ✅ | 摘要可参考 | name=张明, project=星火计划 |
| 第 13 轮(更新) | 最新 | 新增摘要 | project 更新为 短视频 |
核心要点:三层记忆各司其职——短期保证"正在聊什么"、中期保证"之前聊过什么"、长期保证"用户是谁"。FIFO 贯穿三层,保证信息不过时。
PART 04:工程落地——记忆系统的实战经验
demo 能跑通,但真正上线会遇到很多坑。分享几个我在实战中踩过的。
坑一:事实提取不准确——把闲聊也存进了长期记忆
最常见的问题。用户开玩笑说"我是个外星人",提取器就存了{"identity": "外星人"}。
解法:在提取 prompt 中明确约束:
EXTRACT_PROMPT = """从用户消息中提取需要长期记住的事实。 规则: 1. 只提取明确的、严肃的个人信息(姓名、职业、项目、技术偏好) 2. 忽略:闲聊、玩笑、一次性问题、测试内容 3. 如果不确定是否值得记住,不提取(宁可漏掉,不可错记) 4. 如果新事实与旧事实冲突,标记为 update"""关键是第三条——宁可漏掉,不可错记。记忆污染比记忆缺失更影响体验。
坑二:摘要丢细节——关键数字被省略
LLM 做摘要时经常省略具体数字。比如原文"API 限流改成 200 次/分钟",摘要变成"讨论了 API 限流调整"——关键数字丢了。
解法:在摘要 prompt 中强制要求保留实体——必须保留具体数字、人名、项目名、技术名称。
坑三:长期记忆更新冲突
用户说"我不喜欢 Python 了,转 Go 了"。你希望长期记忆从{"language": "Python"}变成{"language": "Go"}。
解法:在提取 prompt 中明确区分update(覆盖旧值)和append(新增属性),以及delete + append(否定旧信息时)。
与 RAG 整合:统一检索架构
Day06 讲的 RAG 是检索外部文档。长期记忆本质上也可以存入向量库——检索"用户是谁"和检索"公司退货政策"用的是同一套技术。RAG 和 Memory 共用一套向量基础设施,运维成本更低。
性能考量:控制额外 LLM 调用
三层记忆系统每条消息可能触发:事实提取(1 次 gpt-4o-mini)+ 摘要压缩(溢出时触发)+ 回答生成(主模型)。额外成本大约主调用的 20%~30%。
- 事实提取和摘要都用gpt-4o-mini(便宜、快)
- 摘要不要每轮都触发,积累到阈值再压缩
- 长期记忆提取可以异步执行——不阻塞用户响应
结尾:记忆是 AI 从"工具"变"伙伴"的分水岭
回头看这篇文章的核心:
- 三层记忆:短期(最近 N 轮原文)→ 中期(对话摘要)→ 长期(用户画像)
- FIFO 流转:短期满了挤出去做摘要,摘要中提取事实更新画像——信息逐层沉淀,不是堆积
- 淘汰策略:短期严格 FIFO、中期合并 + LRU、长期 Update 覆盖——保持记忆"新鲜"
为什么这套架构重要?因为记忆是 AI 从"工具"变成"伙伴"的分水岭。
一个没有记忆的 AI,每次对话都像跟失忆的天才聊天——它很聪明,但它不认识你。
一个有三层记忆的 AI,它知道你是谁、之前聊过什么、现在在讨论什么——这种感觉,才叫"我的 AI 助手"。
大模型最缺的不是算力,是记忆——一个没有记忆的 AI,永远是个聪明的陌生人。
互动时间:你的 AI 应用里,记忆系统是怎么搭的?三层都上了,还是只用了短期?踩过什么坑?欢迎评论区聊聊。
下一篇 Day08 预告:大模型 MCP 实战——让大模型不只生成文本,还能调用你的工具和服务。关注小刘檀木,不错过每一篇。
我是小刘檀木,一个从二本摸爬滚打到上市公司 AI 负责人的普通人。关注我,把 AI 学进简历。
— END —
小刘檀木 · 帮普通人把 AI 学进简历
