Agent 记忆分层设计:从 Context、Session、State 到长期记忆的完整实践 刚跑客服 Agent 的时候我被context length exceeded这个报错折磨了好几天。一开始我以为问题是模型上下文窗口不够大于是拼命压缩 system prompt结果发现治标不治本。后来把 Context、Memory、Session、State 这四个概念拆开重新设计才真正解决了问题Agent 的记忆不是一个“大篮子”而是一套分层读写流程。这套流程跑通之后做电商客服助手这类多轮、跨会话、带用户画像的场景才谈得上“越用越聪明”。这篇文章适合三类人看正在做 Agent 开发、想给机器人加记忆功能、或者已经被 token 超限和会话串号坑过的人。最值得关注的点不是堆 prompt而是搞清楚短期状态放哪、长期记忆放哪、上下文快满的时候先压缩哪一层。下面按实际落地顺序拆一遍。1. 先把四个概念拆清楚Context、Memory、Session、State 分别管什么1.1 四个词不是同一个东西很多人把 Context、Memory、Session、State 混着用写出来的代码也像一锅粥。先逐个说清楚。Context 是“当前这一次请求里模型能看到的所有内容”。它由系统提示词、用户输入、历史消息、检索结果拼接而成。它的上限是硬性的超过就报context length exceeded。所以 Context 本质上是一个可消耗的资源不是用来存记忆的地方。Session 是一条会话的边界。它决定一段对话从哪里开始、到哪里结束通常对应一个会话 ID比如用户和客服助手的某一次聊天。Session 的作用是隔离不同用户的客服对话不能混在一起同一个用户的两段对话也不能互相污染。State 是程序运行时保存的可变数据。它可以包含当前正在处理的订单号、用户已经选择的产品、当前走到了售后流程的哪一步。State 的特点是变化快、和当前任务强相关通常放在内存里任务结束或会话过期后就可以清理。Memory 是跨请求、跨会话保留的信息。它分成短期和长期短期记忆是当前 session 内历史消息经过压缩后形成的摘要长期记忆是用户偏好、历史订单、售后记录这些需要跨会话保存的内容一般落到数据库或向量库。一句话总结Context 是模型的输入视野Session 是会话边界State 是当前任务的临时数据Memory 才是真正让 Agent 记住用户的机制。1.2 为什么很多人把它们混在一起最常见的错误写法是把全部历史消息都塞进 Context然后告诉模型“这是你的记忆”。短期体验似乎没问题但用户多聊几轮token 马上失控更麻烦的是模型会从大量历史里抓到过时信息。比如用户三天前问过退货今天问物流旧信息还挂在 Context 里回答反而被干扰。Session 层面也容易出现串号问题。电商客服是典型的“一个用户多个会话”场景如果所有会话共享同一个 state用户上次问 A 商品的记录就会污染这次 B 商品的对话。反过来如果只按 session 存用户第二次来又完全“失忆”体验也很差。所以正确做法是session 隔离对话流程state 管当前任务memory 管用户长期画像三者分开存再按需组合进 context。注意Session 管理不只是设一个 ID还要明确 session 的失效时机、用户身份绑定方式、以及 state 的清理策略。否则线上跑久了内存和数据库里会堆一堆永远不关的会话。2. 电商客服助手的记忆需求先按场景拆分再设计分层2.1 三类典型客服场景记忆需求完全不一样商品咨询比如“这个手机壳支持 iPhone 15 吗”。这类问题基本只需要当前对话内容不需要长期记忆。你只需 session 内的短期状态比如用户刚才看了哪个商品、当前停留在哪个 SKU。订单查询比如“我的订单 12345 到哪了”。这里需要把会话内的用户标识和当前订单号对齐然后查订单系统。如果用户换了会话再来问最好能从长期记忆里找到他常用的收货人、最近的订单减少重复询问。售后处理比如“我要退货”。这是最复杂的场景。它需要跨多个步骤保持状态用户选了哪个订单、退货原因是什么、是否已经提交申请。同时还要结合长期记忆里的历史售后记录判断是否重复退货、是否符合政策。如果只靠模型自由发挥很容易在第三步就忘了第一步选的是哪个订单。2.2 记忆分层的设计目标设计目标不是“记得越多越好”而是“需要的信息才拿出来不需要的留在外面”。电商客服助手读取用户信息时建议按三层取数第一层当前会话消息本轮对话的原始消息用于即时理解。 第二层会话摘要层把一个 session 的对话压缩成结构化摘要比如用户咨询商品、选择的规格、最后的结论。 第三层用户画像层跨会话的长期数据比如常用收货城市、历史订单、过往投诉记录。这样取数的好处是Context 里永远只放当前需要的内容而不是整个历史。既能控制 token又能减少过时信息干扰。很多 Agent 项目跑着跑着就“变笨”不是模型退化了而是 Context 里塞了太多无关历史导致模型找不到重点。3. 从零搭可运行的双层记忆方案会话短记 长期存储3.1 环境与依赖准备这个方案不依赖特定 Agent 框架用 Python 也能跑通。你需要一个支持函数调用的大模型接口、一个数据库SQLite 起步就够、一个向量库本地开发可以先不加向量检索用关键词匹配代替。下面代码只是结构示例不绑定某个具体 SDK落地时以你选的库为准。建议先按最小结构搭一张会话表、一张用户画像表、一个摘要服务。把“写入 memory”当成显式步骤来做而不是让模型自己决定记什么。模型自己决定记忆结果通常不可控。3.2 数据结构示例用最简单的结构说明分层逻辑# 会话状态对应 State session_state { session_id: S1001, user_id: U001, current_node: order_query, pending_order_id: 12345, last_intent: 查询物流, } # 会话内短期记忆对应 Session Memory session_memory { session_id: S1001, summary: 用户想查询订单 12345 的物流状态可能是 iPhone 15 手机壳订单。, last_messages: [用户我的订单到哪了, 助手请提供订单号, 用户12345], } # 长期记忆对应 Long-term Memory user_profile { user_id: U001, common_city: 上海, recent_orders: [12345, 12233], after_sale_count: 1, last_after_sale_reason: 尺寸不合 }这些数据建议落到不同的表或集合里不要全部写在一个 JSON 里。原因很简单State 更新频繁长期记忆更新慢混在一起会导致每次更新都要处理大量无关字段还容易出现并发写冲突。3.3 写入流程什么时候更新哪一层建议按三步写第一步每轮对话结束后先更新 session_state 里的当前任务信息比如用户选中的订单号。 第二步当 session 内消息达到 5 到 8 条或本轮有关键结论时调用一次摘要服务更新 session summary。 第三步当用户明确表达了某个行为完成比如提交退货申请才把结构化结果写进 user_profile。不要每个词都往长期记忆里塞。客服场景里长期记忆的价值在于低频、稳定、能影响决策的信息比如常用地址、历史投诉次数、偏好品牌。临时聊到的“今天下雨”根本不需要记。3.4 读取流程构造一次干净的 Context拿到用户新消息后按这个顺序组装 Context系统提示词说明助手身份和行为规则。用户画像摘要和当前问题相关的才放比如售后问题才放历史售后记录。会话摘要上一个 session 的结论。最近 N 条消息当前 session 的原始对话。def build_context(user_query, session_memory, user_profile): parts [] parts.append(你是电商客服助手请根据用户信息和历史记录回答。) if user_profile: parts.append(f用户长期信息{user_profile}) if session_memory: parts.append(f会话摘要{session_memory[summary]}) parts.append(f最近对话{session_memory[last_messages]}) parts.append(f用户当前问题{user_query}) return \n.join(parts)这里的核心是每次请求都重新组装而不是把老的 context 对象一直往后传。这样才能保证 token 可控也方便排查如果某轮回答异常直接把组装日志打出来看是哪一层信息引入的问题。4. 上下文超限处理tokens 用满之前怎么办4.1 报错背后的三种情况网上常见这样一串报错maximum context length is 1048576 tokens、context length exceeded、context is too large and auto-compaction could not recover。这类报错看起来都是“模型窗口不够”实际是三种不同情况第一种历史消息没有压缩全量塞进去了。最常见。 第二种单次检索结果太多把大量无关文档拼进 Context。 第三种system prompt 写得过长加上用户输入后很快就超限。排查顺序应该是先看 Context 是由哪些部分组成的算出每部分的 token 占比再决定压缩哪一部分。不要一上来就换更大窗口的模型很多问题在组装层就能解决。换模型只是把阈值推高治标不治本。4.2 超限处理的四步漏斗我建议按这个顺序处理第一步滑动窗口。只保留最近 N 条原始消息比如 6 到 8 条更早的内容用一句摘要代替。这是最先做的成本最低。 第二步摘要压缩。对窗口外的历史内容做分轮摘要一个长 session 变成一个三百字以内的结构化 summary。 第三步检索筛选。长期记忆不做全量拼接而是按相关性召回。召回条数要限制比如 top 3 到 top 5每条控制在 200 字以内。 第四步临时截断。如果以上都做完仍然超限才考虑丢弃最不重要的内容。一般不建议走到这一步因为信息丢失不可控。4.3 什么时候该摘要什么时候该检索判断标准是信息的重要性。当前任务进行到一半比如正在填退货流程必须保留状态不能只留摘要。跨会话的结论比如用户之前确定要换货需要保留摘要。用户长期行为比如历史投诉记录通过检索按需召回。不要在回答里让模型自己决定“我要不要记一下”。应该由程序判断事件是否触发记忆写入。这样可复用、可测试也方便以后加日志和监控。注意auto-compaction 无法恢复这种提示说明会话历史已经被反复压到极限。真正要处理的不是再压一次而是检查是不是有历史消息从未被压缩、或 system prompt 里塞了太多示例。5. 实测跑通一单完整客服对话验证标准与排查链路5.1 一个最小测试场景假设用户第二次来问“我上次那个手机壳订单能换成蓝色的吗”。理想结果应该是助手能识别这是同一个用户能从长期记忆里找到 recent_orders 里的订单能从会话摘要里知道用户上次确认过颜色能进入换货流程并保存当前状态。如果回答里连订单商品都猜不对先不要怀疑模型不行。先检查长期记忆有没有正确写入再检查组装 Context 时有没有读出对应字段。这就是“先看日志再改参数”。5.2 成功结果怎么判至少满足四个指标身份识别正确同一用户在不同 session 里能被关联。订单信息正确能提到具体订单而不是泛泛而谈。流程状态能延续从查询到换货的中间状态不丢。跨会话记忆有效用户第三次进来还记得第二次的操作。我一般会做一张验证表输入用户问题、期望输出、实际输出、命中哪层记忆逐行打勾。能连续跑 20 轮不混乱再考虑批量。5.3 常见问题排查顺序问题一用户身份错乱导致记忆串人。查 session 初始化时有没有正确绑定 user_id。要单独排查不要让跨 session 的用户数据互相覆盖。问题二Context 越拼越长。看组装日志里的 token 统计。每轮调用记录 input tokens如果下一轮明显比上一轮多说明可能有历史没有被压缩或者检索结果过大。问题三State 一直更新失败。先看节点执行顺序和 State 的写入 key。LangGraph 这类框架里节点之间的 State 传递经常因为 key 不一致导致数据丢失。问题四摘要信息过时。说明摘要更新时机不对应该在关键节点后强制更新而不是等到轮数满了才更新。问题五程序内存不足。如果日志里出现 Java 的 OutOfMemoryError或者进程被系统杀死那不是模型 context 超限是程序自身的内存泄漏或会话对象堆积。这类问题要去看 session 清理策略和缓存释放。6. 边界与进阶别把 Demo 当生产系统6.1 这个方案的适用边界上面的双层记忆方案适合中小流量、自建客服机器人或者学习项目。它有一个明显前提用户身份可识别。如果没有登录态只有匿名聊天长期记忆基本只能靠会话内摘要用户画像层做不出来。所以做产品设计时先确认能不能拿到稳定的 user_id否则记忆分层会失去根基。低配置机器也能跑但要把并发数、检索召回条数、摘要频率都调低。能跑通单会话不等于适合批量服务。批量场景要额外考虑任务队列、失败重试、输出命名、日志切割。这些不是模型能力但体验差距很大。6.2 进阶方向向量检索、记忆路由、评估闭环如果用户画像字段太多可以引入向量检索把用户的历史订单、投诉记录、偏好描述转成向量只在需要时按相似度召回。进阶一点可以在记忆写入前加一个路由判断由程序决定事件属于“当前会话更新”还是“长期画像更新”避免无意义写入。最容易被忽略的是评估闭环。记忆系统不是写一次就完事。要让助手“越用越聪明”需要定期抽样检查用户画像里的数据是否准确、摘要是否遗漏关键信息、Context 组装是否引入了过时内容。把这些问题返回给写入逻辑去调整才是真正的迭代。6.3 最后的落地建议如果只是学习用 SQLite 加一个模型接口就够不需要上复杂的向量库和框架。如果要做生产我更建议先把日志、用户身份、token 统计这三件事做好再优化记忆策略。踩过几次之后我发现很多 Agent 记忆问题不是模型不够聪明而是状态写乱了、历史没压缩、用户身份没对齐。先把这三条线理直了Context、Memory、Session、State 各司其职“越用越聪明”才不会只是一句口号。

相关新闻

最新新闻

国产MCU+SA85550驱动42步进电机:定速转动脉冲频率与PWM实现详解

国产MCU+SA85550驱动42步进电机:定速转动脉冲频率与PWM实现详解

简介:本资源是一套基于国产国民技术N32G430单片机与矽塔SA85550驱动芯片的步进电机定速控制完整工程,面向嵌入式初学者、国产芯片开发者及自动化控制实践者,解决国产MCU国产驱动IC协同控制2相4线42步进电机的核心问题,适用于工业定…

2026/8/31 13:20:10
51单片机噪声监测系统设计:LCD1602+ADC0832完整实现

51单片机噪声监测系统设计:LCD1602+ADC0832完整实现

简介:本资源是一套完整的单片机毕业设计项目资料,面向电子信息、自动化等专业的本科生及单片机初学者,解决环境噪声实时监测与阈值报警的典型嵌入式应用问题。资源包共64个文件,涵盖Keil C51源程序工程(含.c/.h/.a51文…

2026/8/31 13:20:10
Summer2025-Internships:5分钟筛出第一份暑期实习岗位

Summer2025-Internships:5分钟筛出第一份暑期实习岗位

Summer2025-Internships:5分钟筛出第一份暑期实习岗位 【免费下载链接】Summer2027-Internships Summer 2027 software engineering, data science, AI, quant, product management, and hardware internship postings. Updated daily by Simplify and Pitt CSC. …

2026/8/31 13:20:10
仓颉Skill实战:AI Agent驱动的资料整理工具部署与API集成全指南

仓颉Skill实战:AI Agent驱动的资料整理工具部署与API集成全指南

把一摞乱七八糟的资料文件丢给 AI,让它自己读完、理解,再按你要的格式整理出来——这就是“仓颉Skill”这类工具要做的事。 仓颉Skill 本质上不是某一个单一的模型,而是一个把“文件读取、内容解析、指令理解、组织生成”串起来的技能组件。…

2026/8/31 13:20:10
Umi-OCR 完整使用指南:离线识别图片与PDF文字,敏感文件不出电脑

Umi-OCR 完整使用指南:离线识别图片与PDF文字,敏感文件不出电脑

Umi-OCR 完整使用指南:离线识别图片与PDF文字,敏感文件不出电脑 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成…

2026/8/31 13:20:10
cdai cli:用“意图”替代路径的智能目录切换工具

cdai cli:用“意图”替代路径的智能目录切换工具

不知道你有没有遇到过这样的场景:在项目里待了大半天,想切到另一个模块目录时,记不清完整路径,只能先 pwd 看当前位置,再一层一层 ls 确认目录名,最后才敢敲 cd 。如果项目路径很深、目录命名又相似&…

2026/8/31 13:15:09