模型本身没有记忆,Agent 的记忆全是你外挂进去的

LLM 是无状态的。每次调 API 就是一次性的"读入全部上下文、吐出回答",两次调用之间模型脑子里什么都没留下。所以 Agent 所谓的"记忆",全是你外挂进去的——存在数据库、向量库或者文件里,每次调用时选一部分塞进 prompt。

搞清楚这一点,Agent 记忆系统的所有设计都变得顺理成章。这一篇讲清三种主要的记忆类型、它们各自的实现,以及一个常被忽略的问题——怎么忘记

三种记忆,各干各的

借用人类认知心理学的分类,Agent 的记忆可以分三类:

工作记忆(短期)——当前对话的 messages 列表。这是最朴素的记忆,LLM 的"上下文窗口"就是它的容器。问题:窗口会满,对话长了必须裁剪或压缩。

事实记忆(长期语义)——用户是谁、喜欢什么、过去说过什么关键事实。这些不是对话回放,而是提炼出来的结构化/半结构化事实。例如 "用户是一个偏好 Pythonic 风格的后端工程师"。

过程记忆(长期程序性)——"我之前尝试过这个方法,失败了"。这是 Reflexion 积累的那种经验,和事实记忆的差别在于它是关于怎么做的知识,不是关于是什么的知识。

生产级 Agent 通常这三种都有。ChatGPT 的 Memory 功能就是事实记忆;**Cursor 和 Claude Code 的"对话历史"**是工作记忆 + 选择性事实记忆;Reflexion 式的失败反思是过程记忆。

工作记忆:把短窗口用好

最朴素的办法是把最近 N 轮对话原样塞进 messages。但对话一长,token 消耗爆炸。几种实用策略:

Sliding Window——只保留最近 K 轮(例如 10 轮)的对话,更早的丢掉。简单粗暴,适合话题切换频繁、历史不重要的场景。

Summary Buffer——把超出窗口的早期对话让模型总结成一段话,作为 system 消息的一部分。Langchain 里的 ConversationSummaryBufferMemory 就是这个思路。

def compact_messages(messages, keep_recent=6):
    if len(messages) <= keep_recent + 2:  # 2 = system + 首条 user
        return messages
    to_summarize = messages[1:-keep_recent]
    summary = llm(f"把以下对话摘要成 150 字以内,保留关键决定、用户偏好、遗留问题:\n{to_summarize}")
    return [
        messages[0],
        {"role": "system", "content": f"[对话摘要] {summary}"},
        *messages[-keep_recent:]
    ]

选择性保留——不是简单按时间裁剪,而是用模型判断"哪几轮对话对接下来还重要"。贵但精准。Anthropic 内部 Prompt Cache 的上下文管理有这个思路。

Claude 和 Gemini 的 200K/1M 上下文窗口让很多人以为工作记忆问题消失了。没有。长上下文里的"lost in the middle" 现象是真的——模型对中间位置的信息关注度显著下降。有效上下文远小于标称上下文。所以就算窗口够大,该压缩还是要压缩。

事实记忆:存什么、怎么存、怎么取

事实记忆的核心问题是什么信息值得长期记住。不是所有对话内容都要进记忆库——记多了检索时噪音大,反而拖累。

经验上值得存的:

用户身份和偏好——"用户是 TypeScript 开发者"、"喜欢极简的回答"、"时区是 GMT+8" 用户的长期目标——"正在学 Rust"、"筹备一个博客" 显式的偏好声明——用户说过的"不要用表情符号"、"代码用 2 空格缩进" 关键事实——"项目仓库在 github.com/foo/bar"、"部署到 Cloudflare" 历史决定——"之前讨论过用 PostgreSQL 而不是 MongoDB,原因是..."

不该存的:

临时信息——本次任务的中间结果 可以重新获取的信息——能通过工具查到的,不如实时查 冗余信息——已经在其他地方记过的

存的结构有两种主流做法:

向量记忆——每条事实一个 embedding,检索时按当前 query 的向量相似度取 top-K。灵活、扩展性好。代表实现:Mem0、Zep、Chroma 自带的 collection。

结构化记忆——事实按 schema 存到关系数据库,按字段查询。精确、可审计。适合身份、偏好这类有明确结构的事实。

现代 Agent 倾向两种结合:结构化存身份/偏好/时区这些稳定属性,向量存"过去说过的具体内容"

一个简单的事实记忆提取:

def extract_facts(conversation: list[dict]) -> list[str]:
    prompt = f"""从以下对话中提取值得长期记住的事实。
只提取:用户身份、偏好、目标、关键决定。
不要提取:临时信息、中间结果、可查询的信息。
对话: {conversation}
返回 JSON 数组,每项是一条简短的陈述。例如 ["用户是 TypeScript 开发者", "偏好极简回答"]"""
    resp = llm(prompt, response_format={"type": "json_object"})
    return json.loads(resp)["facts"]

这段在每次对话结束后跑一次,把提取出的事实写入向量库。下次对话开始时,根据用户首条消息检索 top-5 相关事实注入 system prompt。

过程记忆:怎么做的知识

过程记忆的典型场景是让 Agent 积累"怎么做事"的经验。例如一个会写 SQL 的 Agent,第一次写某个表的 query 失败了,反思"哦这个表的 created_at 是字符串不是 timestamp",下次遇到类似情况就会先检查字段类型。

实现上和事实记忆类似(向量库),但内容格式不同:

事实记忆: "用户喜欢极简回答"
过程记忆: "遇到 'created_at' 字段时,先检查它是 string 还是 timestamp,不同库的类型不一样"

检索时机也不同:事实记忆按 user 检索,过程记忆按任务情境检索。过程记忆在任务开始时根据任务描述检索相关经验,注入为"过去的教训"。

遗忘:被低估的一半

写得越多越多的时候,记忆系统会变得越来越差。原因很直接:噪音越来越大,相关信息被淹没。所以一个真正能长期运行的记忆系统必须有遗忘机制

几种常见的遗忘策略:

基于时间衰减——每条记忆有一个 confidence 分数,随时间指数衰减。检索时按 similarity * confidence 排序,自然淘汰旧记忆。

基于冲突覆盖——新记忆和旧记忆冲突时,让模型判断"哪个更准",把旧的标记为过时。用户三个月前说"我住北京",现在说"我搬到上海了"——需要把前者 invalidate。

基于显式清理——定期跑一个"记忆管家",扫描所有记忆,让模型判断"哪些还有用、哪些可以合并、哪些该删"。这是 Mem0 和 Letta(前 MemGPT)的核心做法。

def consolidate_memory(all_facts: list[dict]):
    prompt = f"""以下是 Agent 的长期记忆条目(JSON 列表)。
请:
1. 合并重复或相似的条目
2. 删除过时或明显错误的
3. 把过长的条目精简
返回整理后的 JSON 列表。
输入: {all_facts}"""
    return llm(prompt, response_format={"type": "json_object"})

跑的频率不用高——每天/每周一次就够。但不跑的话,半年后的记忆库就是一个垃圾场。

Mem0 / Letta / Zep:开箱即用的选项

如果不想自己从头搭记忆系统,几个成熟的开源/商业方案:

Mem0(原 EmbedChain)——轻量级,API 直接:mem.add(text, user_id=...) / mem.search(query, user_id=...)。底层用 Qdrant 或 pgvector,自动做 extract + consolidate。适合嵌入到现有应用里。

Letta(原 MemGPT)——更"重",是一个完整的 Agent 框架,把记忆当作操作系统里的内存管理来做。有 core_memory(永远在 prompt 里)、archival_memory(需要检索)、recall_memory(对话历史)的分层。适合做长期、持续运行的 Agent。

Zep——企业向,有完整的 session、user 管理、时间轴视图。适合需要多租户和审计的场景。

这三者的差别主要在"侵入性"。Mem0 最轻,拿来就用;Letta 最强,但你的 Agent 架构要迁过去;Zep 中间,是托管服务。选哪个看你的场景。

一个常被忽略的问题:记忆的一致性

当你的 Agent 同时有工作记忆、事实记忆、过程记忆时,它们之间可能冲突。用户本次对话说"我改主意了,用 MongoDB",但长期事实记忆里存着"用户之前决定用 PostgreSQL"。Agent 在下次任务规划时,引用哪个?

解决这个问题的原则是近期优先 + 显式确认。本次对话内的声明覆盖长期记忆,但对话结束时要更新长期记忆——要么覆盖,要么新增一条"2026-04-27:用户改为使用 MongoDB,原因...",让冲突显性化。

更复杂的场景(多用户共享一个 Agent、跨项目复用经验),冲突会更多。这属于生产级 Agent 的长尾问题,往后第 16 篇"可观测性"和第 17 篇"生产化"会再回到。

小结

Agent 的记忆不是一块东西,是三种不同目的的子系统组合。工作记忆解决"本次对话不丢"、事实记忆解决"下次对话认识你"、过程记忆解决"同类任务做得更好"。设计一个实际 Agent 时,先问自己"我真的需要哪几种",再决定要不要上 Mem0 这种框架——很多 Agent 其实只需要工作记忆 + 轻量事实记忆,别一上来就搞大而全。

下一篇讲上下文工程。记忆是"有什么可以喂",上下文工程是"怎么喂进去"——在有限的窗口里把最有用的信息组织得让模型真正能用上。这是 Agent 工程最被低估但也最决定成败的一环。

相关阅读

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:07. 记忆系统:短期对话、长期事实、过程记忆

本文链接:https://www.sshipanoo.com/blog/ai/ai-agent/07-记忆系统/

本文最后一次更新为 天前,文章中的某些内容可能已过时!