$catMANUAL||~27 min

AI Agent 记忆系统实战:从 Context Window 到持久化记忆,我踩过的坑全在这了

advertisement

AI Agent 记忆系统实战:从 Context Window 到持久化记忆,我踩过的坑全在这了

上周在调试 Hermes Agent 的时候遇到一个很离谱的问题:用户跟 Agent 聊了大概二十多轮,Agent 突然"忘了"用户三分钟前说过的话。不是那种语义理解偏差,是真的完全不记得了。排查了半天,发现是 Context Window 溢出,早期的对话被直接截断了。

这个事让我认真研究了一下 AI Agent 的记忆系统。折腾了大概一周,把市面上主流的方案都试了一遍,踩了不少坑,也总结了一些经验。今天写下来,算是给自己做个笔记,也给有同样困惑的朋友一个参考。

Context Window 到底是什么

先说个基础但容易被忽略的东西。很多人知道 Context Window 这个概念,但对它的具体机制其实不太清楚。

简单来说,Context Window 就是大模型一次能"看到"的信息量。你发给模型的每一条消息、每一个工具调用的返回结果、每一段系统提示词,都会占用这个窗口的空间。GPT-4o 大概是 128K tokens,Claude 3.5 Sonnet 是 200K tokens,Gemini 1.5 Pro 号称能到 1M tokens。

听起来很大对吧?但实际用起来根本不够。

我给你算一笔账。一个典型的 AI Agent 对话:

  • 系统提示词:大概 2000-5000 tokens
  • 工具定义(function schema):每个工具 500-2000 tokens,10 个工具就是 5000-20000 tokens
  • 对话历史:每轮对话 500-2000 tokens(用户消息 + Agent 回复 + 工具调用结果)
  • 工具返回的数据:这个最恐怖,一次 API 调用的返回可能就有 3000-10000 tokens

所以实际上,一个看起来"128K"的窗口,真正留给对话历史的空间可能只有 50-80K tokens。如果你的 Agent 做了几次工具调用(比如搜索网页、读取文件、调用 API),窗口消耗得飞快。

我在 Hermes Agent 上实测过:做一次完整的代码审查任务(读文件 + 搜索 + 分析 + 生成报告),大概消耗 30-40K tokens。也就是说,128K 的窗口,做个三四轮就到极限了。

短期记忆:滑动窗口和摘要压缩

最简单的解决方案就是滑动窗口。对话太长了?把早期的对话扔掉,只保留最近 N 轮。

python
1
def sliding_window(messages, max_turns=20):
2
    """简单粗暴的滑动窗口"""
3
    system = [m for m in messages if m["role"] == "system"]
4
    history = [m for m in messages if m["role"] != "system"]
5
    return system + history[-max_turns * 2:]  # 每轮包含用户+助手

这个方案的好处是简单,坏处也很明显:早期的重要信息会丢失。用户第一轮说"我的项目用的是 Python 3.11 + FastAPI",聊了二十轮之后这个信息就没了。Agent 可能会开始建议你用 Flask,场面一度很尴尬。

比滑动窗口好一点的是摘要压缩。每隔 N 轮,让 LLM 把之前的对话压缩成一段摘要,然后用摘要替代原始对话。

python
1
def summarize_and_compress(messages, threshold=15):
2
    """对话超过 threshold 轮时,把前半段压缩成摘要"""
3
    system = [m for m in messages if m["role"] == "system"]
4
    history = [m for m in messages if m["role"] != "system"]
5
 
6
    if len(history) <= threshold * 2:
7
        return messages
8
 
9
    # 把前半段对话拿出来做摘要
10
    to_summarize = history[:len(history) // 2]
11
    summary_text = call_llm(
12
        f"请把以下对话压缩成摘要,保留关键信息:\n{format_messages(to_summarize)}"
13
    )
14
 
15
    # 用摘要替代原始对话
16
    summary_msg = {"role": "system", "content": f"之前的对话摘要:{summary_text}"}
17
    remaining = history[len(history) // 2:]
18
    return system + [summary_msg] + remaining

摘要压缩比滑动窗口好,但有两个问题:

  1. 每次压缩都要额外调用一次 LLM,增加延迟和成本
  2. 压缩会丢失细节。"用户在第三轮提到了他的服务器是 2 核 4G 的阿里云 ECS" 这种信息,摘要可能会写成"用户有云服务器",细节就没了

我试过几种变体:

  • 分级压缩:最近 5 轮保留原文,5-10 轮做轻度压缩(去掉工具调用的详细返回),10 轮以前做重度压缩(只保留关键决策)
  • 关键信息提取:用 LLM 从每轮对话中提取关键实体(文件名、变量名、技术栈、决策结果),存成结构化的"记忆卡片"
  • 混合方案:摘要 + 关键信息卡片,两者同时保留

实测下来,混合方案效果最好,但实现复杂度也最高。

长期记忆:向量数据库、知识图谱和文件存储

短期记忆解决的是"同一段对话内的记忆"问题。但更难的是跨对话的记忆。

用户今天跟 Agent 聊了部署方案,明天再来聊的时候,Agent 应该记得昨天讨论过什么。这需要持久化的长期记忆。

目前主流的长期记忆方案有三种:

方案一:向量数据库

最流行的方案。把对话内容、用户偏好、关键信息做 embedding,存到向量数据库(Pinecone、Qdrant、ChromaDB 等)。每次新对话开始时,根据当前话题检索相关记忆。

python
1
# 存储记忆
2
def store_memory(text, user_id):
3
    embedding = get_embedding(text)
4
    vector_db.upsert(
5
        vectors=[{"id": generate_id(), "values": embedding, "metadata": {"user_id": user_id, "text": text}}]
6
    )
7
 
8
# 检索记忆
9
def retrieve_memory(query, user_id, top_k=5):
10
    query_embedding = get_embedding(query)
11
    results = vector_db.query(vector=query_embedding, top_k=top_k, filter={"user_id": user_id})
12
    return [r["metadata"]["text"] for r in results]

优点:实现简单,检索速度快,社区生态好。

缺点:语义检索不等于精确检索。用户问"我上次部署用的什么端口",向量检索可能会返回"用户讨论过部署方案",但不一定会返回具体的端口号。因为"端口号"在 embedding 空间里跟"部署方案"的距离可能很近,但具体是 3000 还是 8080,embedding 不一定记得住。

我在 Hermes Agent 上用向量数据库做记忆的时候,发现一个很有意思的问题:embedding 对"事实性信息"的检索效果不好,但对"话题相关性"的检索效果很好。比如用户问"之前聊过什么技术选型",向量检索能很好地找到相关的讨论。但如果问"当时选的是 PostgreSQL 还是 MySQL",就经常答不上来。

方案二:知识图谱

比向量数据库更结构化的方案。把信息组织成"实体-关系-实体"的图结构。

比如:

  • (用户) - [使用] -> (Python 3.11)
  • (用户) - [部署在] -> (阿里云 ECS)
  • (项目) - [依赖] -> (FastAPI 0.100+)
  • (上次部署) - [使用端口] -> (8080)

Zep 就是用这种方案。它会自动从对话中提取实体和关系,构建一个时序知识图谱(temporal knowledge graph)。检索的时候不只是找语义相似的内容,还能沿着关系链找到相关联的信息。

python
1
# Zep 的用法(简化示意)
2
from zep_cloud.client import Zep
3
 
4
client = Zep(api_key="your-key")
5
 
6
# 添加对话上下文
7
client.memory.add(session_id="session-1", messages=[
8
    {"role": "user", "content": "我的项目用的是 PostgreSQL,部署在阿里云 ECS 上,端口 5432"},
9
])
10
 
11
# 检索记忆(关系感知)
12
memory = client.memory.get(session_id="session-1")
13
 
14
# 返回的不只是原文片段,还有提取出的实体和关系

优点:对事实性信息的检索更准确,能处理复杂的关联查询。

缺点:构建和维护知识图谱的成本高,实体提取和关系推理依赖 LLM 的质量,冷启动慢。

方案三:文件存储 + 结构化索引

最朴素但有时候最靠谱的方案。直接把记忆存成文件(JSON、Markdown、SQLite),用简单的关键词或标签索引。

Hermes Agent 就是用这种方案。每条记忆存成一条文本记录,按 target(user/memory)分类,支持 add/replace/remove 操作。检索的时候直接把所有记忆注入到系统提示词里。

python
1
# Hermes Agent 的记忆结构
2
memories = {
3
    "user": [
4
        "用户是全栈开发者,主要用 Python 和 TypeScript",
5
        "用户偏好简洁的回复风格",
6
        "用户在东八区(UTC+8)"
7
    ],
8
    "memory": [
9
        "项目使用 Next.js + Vercel 部署",
10
        "数据库是 Turso(SQLite 边缘数据库)",
11
        "Git 仓库在 github.com/0311yet/aaiweb"
12
    ]
13
}

优点:实现简单,没有额外依赖,检索精确(因为是全量注入),调试方便。

缺点:记忆条目多了以后会占用大量 Context Window 空间。目前 Hermes Agent 的记忆大概有几十条,注入后占 2000-3000 tokens。如果记忆条目增长到几百条,这个方案就不现实了。

主流框架横评

市面上做 AI Agent 记忆的框架不少,我挑了几个认真试了一下。

Mem0

GitHub 星标 25K+,YC S24 项目,算是这个赛道最受关注的项目了。

核心理念是"Universal Memory Layer"——给任何 AI 应用加一个记忆层。它把记忆分三层:

  • User Memory:跨会话的用户偏好和历史
  • Session Memory:当前会话的上下文
  • Agent Memory:Agent 自身学到的知识

2026 年 4 月他们更新了一版记忆算法,用的是"单次 ADD 提取"策略——每次只往记忆库里加新信息,不覆盖旧的。配合实体链接和多信号检索(语义 + BM25 关键词 + 实体匹配),在 LoCoMo 基准测试上跑到了 91.6 分,比之前的算法提升了 20 分。

实际用起来,Mem0 的 API 设计确实很简洁:

python
1
from mem0 import Memory
2
 
3
m = Memory()
4
 
5
# 添加记忆
6
m.add("我喜欢用 Python 写后端,TypeScript 写前端", user_id="user-1")
7
 
8
# 检索记忆
9
results = m.search("用户喜欢什么编程语言", user_id="user-1")

但有一个坑:它默认用 OpenAI 的 embedding 模型。如果你不想用 OpenAI(比如想用本地模型或者国内的 embedding 服务),配置起来比较麻烦。我折腾了半天才把它换成 Jina 的 embedding。

另外,Mem0 的托管服务(Mem0 Platform)和开源自部署版本的功能差距挺大的。很多高级功能(比如 Entity Linking、Multi-signal Retrieval)只在平台版有。自部署版本的功能相对基础。

Letta(前身 MemGPT)

这个项目的思路很有意思。它不是简单地给 LLM 加个外部记忆,而是让 LLM 自己管理自己的记忆。

核心论文叫"MemGPT: Towards LLMs as Operating Systems",把 Context Window 类比成操作系统的"主存",把外部存储类比成"磁盘"。LLM 可以通过 function call 来"读写"自己的记忆,就像程序通过系统调用来管理内存一样。

python
1
# Letta 的记忆管理(示意)
2
 
3
# Agent 可以主动调用这些函数来管理自己的记忆
4
functions = [
5
    "core_memory_append(key, value)",   # 往核心记忆里加一条
6
    "core_memory_replace(key, old, new)",  # 替换核心记忆
7
    "archival_memory_insert(content)",   # 存入长期档案
8
    "archival_memory_search(query)",     # 搜索长期档案
9
    "conversation_search(query)",        # 搜索历史对话
10
]

这个设计很优雅,但实际用起来我发现一个问题:LLM 并不擅长管理自己的记忆。它不知道什么时候该存、什么时候该查、什么时候该更新。我测试的时候经常出现 Agent 把一些无关紧要的信息存进核心记忆(比如"用户说了'嗯'"),却漏掉了重要的技术决策。

Letta 的平台版做得不错,有可视化的记忆管理界面,可以手动编辑 Agent 的记忆。但开源版本的文档比较欠缺,上手门槛高一些。

Zep

Zep 走的是"Context Engineering Platform"的路线,不只是做记忆,而是做整个上下文工程。

它的核心特色是时序知识图谱。不是简单地把对话存成文本,而是自动提取实体和关系,构建一个随时间演化的知识图谱。比如:

  • 第一天用户说"我在用 PostgreSQL"
  • 第三天用户说"我把数据库换成了 MySQL"
  • Zep 会记录这个变化,知道用户的"当前状态"是 MySQL,而不是 PostgreSQL

这个"时序"特性在实际应用中很有价值。很多记忆系统只知道"用户提到过 PostgreSQL",不知道"用户后来换了 MySQL"。Zep 的图谱能处理这种状态变迁。

官方宣称延迟在 200ms 以内,支持 SOC2 Type 2 和 HIPAA 合规。看起来是面向企业级场景的。

不过 Zep 有一个让我犹豫的点:它的开源版本功能有限,核心的 Graph RAG 和 Context Assembly 逻辑主要在云平台版。自部署的话,体验会打折扣。

其他值得一看的

  • Cognee:开源的记忆框架,支持多种向量数据库后端,GraphRAG 方案做得不错
  • MemoryScope(智谱 AI):面向中文场景的记忆管理,对中文实体提取做了优化
  • LangChain Memory:LangChain 自带的记忆模块,方案最多但也最杂(Buffer、Summary、VectorStore、Combined 等),适合已经在用 LangChain 的项目

我的实战经验:Hermes Agent 的记忆系统

说了一堆框架,回到我自己踩的坑。

Hermes Agent 的记忆系统目前是最朴素的方案:文件存储 + 全量注入。每条记忆是一段文本,存在 ~/.hermes/memories/ 目录下,分 usermemory 两个 target。每次对话开始时,把所有记忆注入系统提示词。

这个方案的好处是简单可靠,调试方便。我可以直接打开文件看 Agent 记住了什么,也可以手动编辑。

但问题也很明显:

问题一:记忆会"漂移"

Agent 有时候会把错误信息存进记忆。比如有一次它把"用户喜欢简短回复"存成了"用户不喜欢回复"。差一个字,意思完全变了。更离谱的是,一旦存进去,后续每次对话都会加载这条记忆,影响 Agent 的行为。

解决办法:定期手动检查和清理记忆。我现在养成了一个习惯,每周看一遍 Agent 的记忆文件,删掉过时的和错误的。

问题二:记忆冲突

两条记忆互相矛盾。比如:

  • "用户偏好 Python 3.11"
  • "用户最近在用 Python 3.12"

Agent 不知道该信哪条。这时候就需要 replace 操作,用新记忆覆盖旧的。但 Agent 并不总是能识别出冲突,有时候两条矛盾的记忆会共存很久。

问题三:记忆膨胀

随着使用时间增长,记忆条目越来越多。目前我的 Hermes Agent 有大约 60-70 条记忆,注入后占 3000-4000 tokens。还在可接受范围内,但如果增长到几百条,就会影响 Context Window 的可用空间。

未来计划:把低频记忆(超过一个月没被"使用"的)迁移到向量数据库,只在需要的时候检索回来。高频记忆继续全量注入。

实际选型建议

折腾了一圈,我觉得选记忆方案主要看三个维度:

第一,你的场景需要什么粒度的记忆?

  • 只需要当前对话内的记忆 → 滑动窗口 + 摘要压缩就够了
  • 需要跨对话的用户偏好 → 向量数据库 + 简单的 key-value 存储
  • 需要精确的事实检索(端口号、版本号、配置项)→ 知识图谱或结构化存储
  • 需要处理状态变迁(用户从 A 方案换到 B 方案)→ 时序知识图谱

第二,你的预算是多少?

记忆系统不是免费的。每次存储记忆要调用 embedding API,每次检索要调用一次向量搜索,摘要压缩要调用 LLM。如果用 Mem0 的托管服务,还有平台费用。

粗略估算:

  • Embedding 成本:约 $0.02/百万 tokens(OpenAI text-embedding-3-small)
  • 向量数据库:Pinecone 免费版支持 100 万向量,够用了
  • Mem0 托管:免费版每月 1000 次调用,Pro 版 $49/月

如果预算有限,自部署 ChromaDB/Qdrant + 本地 embedding 模型(比如 Jina embeddings v3)是最经济的方案。

第三,你的技术栈是什么?

  • 已经在用 LangChain → 直接用 LangChain Memory 模块
  • 需要企业级合规 → Zep Cloud
  • 想要最简洁的 API → Mem0
  • 想要 Agent 自主管理记忆 → Letta
  • 想要完全控制 → 自己实现(文件 + 向量 DB + 简单的检索逻辑)

成本到底有多高?给你算笔账

很多人觉得记忆系统是个"加了就好"的东西,没考虑到持续运行的成本。我来给你算一笔实际的账。

假设你有一个 AI Agent,每天跟用户交互 50 次,每次交互平均产生 3 条需要存储的记忆。

Embedding 成本:每次存储要调用 embedding API。用 OpenAI text-embedding-3-small,$0.02/百万 tokens。每条记忆平均 100 tokens,一天 150 条,一个月 4500 条。Embedding 总消耗约 45 万 tokens,成本不到 $0.01。这块基本可以忽略。

向量数据库成本:Pinecone 免费版支持 100 万向量,Qdrant 和 ChromaDB 可以自部署。小规模使用(10 万条以内)基本不花钱。

真正贵的是检索:每次对话开始时要检索相关记忆。如果用 Mem0 的托管服务,免费版每月 1000 次调用,Pro 版 $49/月。自部署的话,每次检索的 LLM 调用成本大概 $0.001-0.005(取决于用什么模型做 reranking)。

最贵的是摘要压缩:每 15 轮对话做一次摘要压缩,每次消耗 2000-5000 tokens。用 GPT-4o 的话大概 $0.01-0.02 每次。一天做 3-5 次,一个月 $1-3。

所以一个月下来,一个中等使用频率的 Agent 的记忆系统成本大概在 $5-15 之间。不算贵,但如果 Agent 数量上去了(比如你做了个 SaaS 服务给几百个用户用),这个成本会线性增长。

省钱的窍门:

  • 用本地 embedding 模型(比如 Jina embeddings v3 或 BGE-M3),embedding 成本直接降到 0
  • 用开源向量数据库自部署(Qdrant 或 ChromaDB),省掉平台费
  • 检索的时候用小模型做初筛,大模型做精排,减少大模型调用次数
  • 摘要压缩用便宜的模型(GPT-4o-mini 或 Claude Haiku),质量差不多但便宜 10 倍

一些你可能没想到的坑

写到这里,再补充几个我在实际使用中遇到的、网上很少有人提到的问题。

坑一:记忆的安全性

Agent 的记忆里可能包含敏感信息。用户告诉 Agent 自己的 API Key、服务器密码、个人信息,这些都会被存进记忆文件或向量数据库。如果 Agent 被多人共享,或者记忆存储被攻击,后果很严重。

目前大部分记忆框架对这个问题的处理都很粗糙。Mem0 的平台版有基本的用户隔离,但自部署版本需要你自己处理。

坑二:记忆的"遗忘"机制

人会自然遗忘不重要的信息,但 AI Agent 不会。一条三个月前存的记忆,即使完全过时了,也会被原封不动地注入系统提示词。这会导致 Agent 基于过时信息做出错误判断。

好的记忆系统应该有 TTL(Time To Live)或衰减机制。越久没被检索到的记忆,权重越低。但目前大部分框架都没有内置这个功能。

坑三:多 Agent 共享记忆

如果你的系统有多个 Agent(比如一个写代码的、一个做测试的、一个做部署的),它们之间怎么共享记忆?

全部共享的话,每个 Agent 的 Context Window 会被其他 Agent 的记忆撑爆。完全隔离的话,Agent 之间又无法协作。

目前比较靠谱的方案是分层记忆:

  • 全局记忆:所有 Agent 共享(项目信息、用户偏好、技术栈)
  • 角色记忆:每个 Agent 独有(自己的任务历史、学到的经验)
  • 任务记忆:当前任务内共享,任务结束后归档

写在最后

AI Agent 的记忆系统目前还在一个"百花齐放但都不太成熟"的阶段。没有一个框架能完美解决所有问题,大部分项目在实际使用中都需要根据自己的场景做定制。

我个人的判断是:短期内,"文件存储 + 向量检索 + 摘要压缩"的混合方案是最实用的。知识图谱听起来很美,但构建和维护的成本太高,适合有专门团队来做的企业级项目。

接下来我打算在 Hermes Agent 上做两件事:一是接入 Mem0 做长期记忆,替代目前的纯文件方案;二是加一个记忆 TTL 机制,自动清理过时的记忆。搞完了再来写一篇实战记录。

有在做 AI Agent 记忆系统的朋友,欢迎评论区交流。踩过的坑、用过的方案、遇到的问题,都可以聊聊。

  • 本文写于 2026 年 6 月 13 日,基于作者对 Hermes Agent、Mem0、Letta、Zep 等项目的实际使用和研究。技术细节可能会随框架更新而变化,建议参考各项目的最新文档。*

advertisement

AI Agent 记忆系统实战:从 Context Window 到持久化记忆,我踩过的坑全在这了 — AI Hub