Agent的记忆困境:三层记忆体系的工程实践
Agent的记忆困境三层记忆体系的工程实践前几篇文章的Agent有一个共同的缺陷每次新对话都是初次见面。它不记得上次聊过什么不知道你是谁甚至在同一个对话里聊久了也会忘记开头说过的话。今天来解决这个问题。一、LLM为什么是健忘的这不是比喻。LLM在数学上就是无状态的。每一轮对话你必须把所有历史消息重新发送给它。一小时后你发来一条新消息LLM根本不记得一小时前聊了什么——除非你把一小时前的消息也一起发过来。这是Transformer架构的本质决定的。LLM的计算过程是输入文本 → 矩阵乘法 → 输出文本。计算完成了状态就完全消失了。没有存盘的地方没有一个记忆区可以下次读取。和人类大脑完全不同。人类有海马体短期记忆数小时和皮层长期记忆数年。LLM只有当前上下文这一个载体。所以Agent的记忆系统本质上是在LLM之外构建一个人工的海马体皮层。二、把所有历史消息发给LLM行不通最简单的记忆方案每次把全部历史发给LLM。两个致命问题Token成本爆炸。假设每轮对话2000 tokens。10轮 20000100轮 200000。deepseek-chat的价格看起来不贵但累积起来的记忆开销会远超实际对话内容。你用LLM回答用户问题花1000 tokens但为了让它记得上下文又花了20000 tokens。本末倒置。上下文窗口有限。deepseek-chat的窗口是64K tokens好像很大但20轮对话就占了一半。而且更大的问题是丢失在中间——研究表明LLM对上下文一开头和一结尾的信息记得最牢中间部分容易被忽略。你把全部历史按时间顺序排好最关键的最近对话可能在中间位置反而被忽略了。所以需要智能的记忆管理——不是全记住而是该记的记、该忘的忘、该压缩的压缩。三、三层记忆体系我们设计了三层记忆每一层服务于不同的时间尺度3.1 短期记忆会话级分钟到小时目标是维护当前对话的连贯性。回答的问题是“刚才聊到哪里了”策略一滑动窗口。不是保留全部历史而是只保留Token预算内的最近消息。预算 上下文窗口的50%。deepseek-chat 64K → 32K留给历史记忆剩下32K给System Prompt RAG结果 生成新内容。策略二摘要压缩。超过预算的消息不是直接扔掉——先用LLM做一个摘要再存到sessions.summary字段。摘要包含了关键信息聊了什么话题、做了什么决定、用户有什么偏好。下轮对话时上下文 历史摘要放在开头利用高注意力区域 最近消息放在结尾。中间的旧消息被摘要替代了。压缩是异步的——不影响当前对话的响应速度。背景线程默默干活。3.2 长期记忆用户级天到月目标是跨会话保持用户信息。回答的问题是“这个用户是谁”实现是信息提取 检索。每当用户说我叫张三、“我是Java程序员”、“我喜欢…”Agent从消息中提取这些事实存到user_profiles表。下次新对话时Agent从数据库检索用户画像注入到System Prompt中。用户不需要重新介绍自己Agent已经认识他了。当前版本用关键词匹配提取匹配我叫、“我是等触发词。这是80%场景够用的简单方案。架构预留了升级空间——以后可以换成LLM提取更复杂的信息如我最近在做一个微服务项目网关性能不太好”。3.3 工作记忆任务级秒到分钟目标是复杂任务中暂存中间结果。回答的问题是“我分析到哪一步了”这个概念来自Anthropic的Scratchpad模式。想象一个Agent要分析三个项目并做对比分析项目A → 记一笔A: 性能好, 学习成本高 分析项目B → 记一笔B: 简单, 功能少 分析项目C → 记一笔C: 均衡, 社区小 读笔记 → 三点对比 → 综合建议没有工作记忆Agent分析到项目C时项目A的分析结果可能已经被截断或遗忘了。有了工作记忆Agent可以随时记、随时查不受Token窗口限制。技术上Agent有两个工具noteToSelf(内容)和readMyNotes()。笔记存在数据库的notes表中独立于上下文窗口。四、Token估算不要追求没必要的精度记忆系统频繁需要算Token数。最精确的做法是用模型原生的tokenizer比如tiktoken但这引入额外依赖。我们用一个近似算法中文字符 ≈ 每2字1 token 英文字符 ≈ 每4字1 token误差约±20%。对Token预算管理来说完全够用。预算本身就是上下文的50%这种大致的比例——±20%的误差不影响系统正确性但省掉了一个第三方依赖。工程中经常面临这种trade-off是追求100%精确还是接受20%误差但是系统更简单答案是看场景。Token估算不是金融交易不需要极致精度。五、可观测性看见Agent脑子里在想什么传统程序出错看日志一眼找到哪一行报错。Agent出错LLM选错了工具、思考方向偏了——但你根本看不见。所以可观测性对Agent不是加分项是必备项。我们的做法是记录每一步[00:00.5] PLAN Orchestrator判断 → 需要SEARCH和TOOL [00:02.3] TOOL RagTool.search(Spring AI MCP) → 5条结果相似度0.78 [00:02.5] TOOL CalculatorSkill.calculate(3.14*256) → 803.84 [00:05.8] LLM Summarizer生成 → 流式输出最终回答 总耗时: 5.8s │ 工具调用: 2次 │ Token: 1834每个步骤Span记录类型PLAN/TOOL/AGENT_CALL/SUMMARY、名字、耗时、输入输出、状态。前端可视化呈现。还有一个小细节工具调用计数器用AtomicInteger而不是ThreadLocal。因为Spring AI的流式调用中工具方法在内部线程执行读取计数器在Controller线程——两个线程。ThreadLocal隔离会导致读不到。必须用跨线程共享的计数器。六、总结记忆系统解决了Agent健忘的问题通过三层时间尺度的记忆短期记忆Token预算滑动窗口 LLM异步摘要压缩 → 维护对话连贯性长期记忆关键词提取用户画像 → 跨会话保持用户信息工作记忆Scratchpad模式 → 复杂任务中暂存中间结果三者配合让Agent从每次都是初次见面变成了认识你、记得你、能帮你做复杂的事的智能助手。项目链接项目代码day6-agent