封面

Day07|大模型记忆实战——给你的 AI 装上"三层记忆"

前言:世界上最聪明的失忆症患者

大模型就像一个智商 180 但只有 5 秒记忆的人。

你跟他聊了一下午,讲了你的项目叫"星火计划"、你团队有 8 个人、你的技术栈是 Python + FastAPI。他听得很认真,回答得很专业。你心想:这个 AI 真懂我。

然后你关掉窗口,第二天重新打开。

"你好,我叫什么?""我的项目是什么?"

它一脸无辜:"抱歉,我不知道你在说什么。"

这不是段子。这是 2026 年大模型的现状——GPT-4o 能写代码、能做数学证明,但它记不住你昨天说过的一句话。

上一篇 Day06 讲了 RAG,解决了"模型不知道你的私有数据"。但 RAG 管的是外部知识——公司文档、产品手册。有一个问题它管不了:模型记不住你说过什么。

没有记忆的 AI 助手,永远是个聪明的陌生人——每次对话都要从头自我介绍,用户没有归属感,产品没有粘性。

这篇文章干四件事:

  1. 讲透三层记忆架构——短期、中期、长期各管什么
  2. 讲 FIFO 机制怎么保证记忆实时性,不被过时信息干扰
  3. 给你一个可运行的三层记忆系统 demo(Python,直接跑)
  4. 工程落地的实战经验——踩过什么坑,怎么避

读完你会发现,给大模型装记忆,不是"把所有对话存下来"那么简单——它需要像人脑一样,知道什么该记、什么该忘、什么该更新。


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记忆流转图

短期记忆:严格 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

理论讲完了,下面给一个完整的、可以直接跑的三层记忆系统。

三层记忆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 学进简历

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐