ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Agent记忆机制全解:Context、Memory、Session与State的边界与落地

2026/9/2 18:29:24 拓冰建站 浏览量
Agent记忆机制全解:Context、Memory、Session与State的边界与落地 好的我会严格遵守所有要求只输出符合规范的博客正文。开头不用模板句直接从一个真实工程困惑切入。过去做对话系统最常被问到的不是模型选型也不是 Prompt 怎么写而是一个听起来很基础、实际很麻烦的问题Agent 怎么记住用户说过的话这里的“记住”包含好几层意思是多轮对话里上一句的条件反射还是跨天使用的长期偏好还是打开页面重来之后还能恢复的历史如果理解不到位后续所有优化都会走弯路。这篇文章想拆清楚记忆类概念和它们对应的落地方式再结合一个电商客服助手的场景把短期状态、长期记忆、上下文超限处理这些环节完整串一遍。整体思路是先把概念边界理清再跑通一个最小可运行的构建流程最后聊长期维护和排查路径。如果你正在做 Agent 项目希望这篇内容能帮你少踩几个坑。1. 先搞清楚 Context、Memory、Session、State 到底在解决什么问题很多看起来复杂的 Agent 问题拆到根上都是这几件事没分清楚一段对话里模型能看到的上下文边界在哪里系统能保留多久的状态用户这次访问和上次访问之间怎么连接以及在不同阶段该把哪部分数据存入长期存储。这四个概念在日常讨论里经常被混着用但它们解决的问题完全不同。1.1 要理解 Agent 记忆先接受一个有点反直觉的事实模型本身没有记忆模型本身不记任何东西。一次请求发出去模型拿到的只有 Prompt 里包含的内容。它看起来“记得”你刚才说了什么只是因为系统把之前的对话内容也放进了 Prompt。也就是说多轮对话的记忆本质上是在用越来越长的输入换来的。这也解释了为什么“Agent 越用越聪明”不能靠模型自己完成。模型每一次都是“失忆”状态真正承担记忆工作的是外部系统谁来保存之前的消息、谁来决定哪些消息需要进入上下文、谁负责在超限时压缩或裁剪。这套外部机制才是 Agent 记忆工程的核心。1.2 Context 不等于 MemorySession 也不等于 State把四者在实际系统中的职责整理一下比背抽象定义有用得多。概念核心问题典型实现常见误区Context这一次请求模型能看到什么Prompt 拼接、对话历史窗口、系统提示词以为上下文越长越好忽略成本和噪音Memory哪些信息能跨任务、跨会话长期复用向量库、数据库、缓存、用户画像字段把所有消息都塞进 Memory导致检索噪音Session一次连续交互过程怎么组织Session ID、会话表、Cookie 关联把 Session 当成永久存储丢失后没有恢复机制State当前执行到哪一步下一步需要什么状态机、中间变量、步骤记录只存结果不存过程异常恢复无从下手Context 是“模型此刻能看到的信息窗口”Memory 是“系统长期保留的有价值信息”Session 是“一次连续交互的组织单位”State 是“当前执行流程进行到哪里的客观记录”。这四者相互配合不能互相替代。1.3 为什么这四个概念经常被搞混因为它们都指向“记忆”这个模糊感受用户不会感知到 Context 和 Memory 的差异。用户只知道上午问过某个问题下午再问Agent 好像忘了。开发者排查时如果只盯着 Prompt不会发现问题出在长期存储没有写入如果只盯着数据库又可能漏掉 Context 窗口已经超限。我一般会先给团队内部分一个简单框架这次交互范围之内的信息走 Session 和 Context这次交互结束之后还要用的信息走 Memory每一步执行的状态和结果走 State。这样分完之后大多数设计问题都能定位到具体环节。2. 电商客服助手案例短期状态怎么组织长期记忆怎么沉淀理论讲完直接落到一个真实场景。假设要做一个电商客服助手用户会来咨询订单状态、退换货政策、商品库存也会偶尔说“我上次申请过退款现在卡在哪一步”。后一句话就涉及长期记忆了因为“上次申请退款”大概率发生在另一次会话里。2.1 从最小可用流程开始先能连续对话再谈长期记忆第一步不要把架构设计得太复杂先把单次会话内连续对话跑通。最简单的方式是用一个列表维护对话历史每次请求时把最近若干轮拼进 Prompt。conversation_history [] def ask_model(user_input): conversation_history.append({role: user, content: user_input}) messages [{role: system, content: SYSTEM_PROMPT}] conversation_history response call_model(messages) conversation_history.append({role: assistant, content: response}) return response这个流程能跑但它有三个明显问题。第一会话越长Context 越大成本越高第二程序重启后 conversation_history 全部丢失第三所有消息都进 Prompt其中大量寒暄和重复信息会干扰模型判断。所以这个版本只能算验证连通性不能直接当生产方案。2.2 状态管理用 State 跟踪一个客服任务的核心节点客服场景里一个咨询任务通常有固定推进路径用户提交问题、Agent 判断问题类型、查询订单信息、确认处理结果、记录结束。跟踪这个路径就需要 State。State 里要存的是结构化信息不是完整聊天记录。比如用户 ID、订单号、当前步骤、是否已确认退换货、需要补充的材料。下面是一个简化示例{ user_id: user_12345, order_id: order_abc, current_step: collect_refund_reason, order_status: shipped, needs_additional_info: true, last_event_time: 2024-01-15T10:30:00Z }为什么要单独维护 State因为模型不是可靠的执行状态记录器。模型输出可能飘但 State 是外部系统维护的客观事实。每次模型返回后需要解析结果并更新 State下一次决策就能基于最新状态推进。2.3 长期记忆不是把所有内容都存进数据库而是只存值得跨会话复用的信息长期记忆遇到最常见的误区是“全量保存”。把所有聊天记录都塞进数据库以后每次检索时全是噪音。正确做法是先做信息抽取和过滤。电商场景里值得长期存储的信息主要有三类用户身份、用户偏好、特殊状态。用户身份包括用户 ID 和订单号核心信息用户偏好包括配送地址偏好、常用支付方式、对客服回复语气的接受度特殊状态包括“这个用户之前申请过退款”“上次没有解决完”这类事件。def extract_long_term_memory(user_id, conversation): memory_items [] if has_return_request(conversation): memory_items.append({ user_id: user_id, type: special_status, content: 用户存在未完结的退换货需求, timestamp: get_now() }) if has_delivery_preference(conversation): memory_items.append({ user_id: user_id, type: preference, content: 用户偏向于上门取件, timestamp: get_now() }) return memory_items这个函数只是为了说明思路。实际项目中抽取逻辑可以做成规则解析也可以让模型从每一轮对话里提炼关键信息再写入存储。但要记住抽取不是一次性的用户状态变化后要更新旧记录否则长期记忆会变成陈旧记忆。2.4 记忆检索把相关信息重新注入上下文长期记忆的价值体现在下一次会话。用户说“我上次那个退款处理得怎么样了”如果系统能在构造 Prompt 前从存储中检索到对应的特殊状态模型就能给出有依据的回答。检索逻辑要和场景匹配。如果是订单维度的咨询以订单 ID 为索引的精确查询就足够如果用户咨询的是模糊偏好问题可以走语义检索。电商客服场景里先用精确字段查询再退到语义检索是更稳妥的路径。3. Context 超限处理这是 Agent 长期运行最现实的坎热搜词里好多和错误相关不只是 Context 超限还有内存不足、会话中断等。这些看起来是不同问题但指向同一个工程结论记忆不是无限容器运行边界必须设计好。3.1 先理解为什么会超限模型有一个最大输入长度。一旦历史记录加上系统提示词、检索到的记忆、工具返回结果超过这个长度请求就会失败。报错信息往往类似“This models maximum context length is 1048576 tokens”听起来 100 万 token 很大但在大量文档、工具结果和多轮历史场景下很快会被占满。所以 Context 超限不是极端情况而是长期运行必然会遇到的正常事件。需要提前设计好处理策略。3.2 四种常见处理策略处理超限没有银弹关键在于组合使用。策略原理适用场景注意点滑动窗口只保留最近 N 轮对话纯闲聊、动态变化强早期信息丢失用户需要长期记忆时不能只靠窗口摘要压缩将历史对话生成摘要替换完整历史对话轮数多、关键信息分散摘要会丢细节压缩后无法精准回溯关键信息抽取只保留结构化关键字段客服、任务流程需要稳定的抽取规则否则抽不准外部检索把历史存入数据库按需检索长周期记忆检索质量决定最终效果更稳妥的做法是组合使用完整对话只保留最近几轮更早的内容进入摘要或结构化存储关键字段存入长期记忆需要时再检索回填到上下文。3.3 实操指南超限前先做降级而不是报错后再补救比较好的设计是在请求前先估算 token 数量。可以在历史记录里加一个 token 计数器当接近上限时先把最久远的完整对话换成摘要再弹出最早的非关键消息。如果压缩后仍然超限可以提示用户开启新会话但要确保核心状态已经写入长期存储。这个流程写出来不复杂但很管用判断当前历史记录估算长度。接近阈值时对最旧消息做摘要压缩。再超限时裁剪价值最低的中间过程消息。仍然超限时拒绝新消息引导开启新会话。开启新会话时把用户身份和当前任务状态重新注入上下文。3.4 “压不回去”并不总是技术问题有时是设计问题热搜里有“Context is too large and auto-compaction could not recover this turn”这类报错。很多时候不是压缩算法不行而是把太多不该进上下文的内容塞了进去。排查时要先问Prompt 里有没有重复系统指令历史记录里有没有包含大量工具返回的长文本同一个错误信息是不是反复出现了很多次如果这些根源不处理压缩策略再怎么调整也救不回来。4. 会话管理Session 的建立、恢复与过期Session 决定了“这一次交互”怎么识别、怎么延续、什么时候失效。电商客服场景里Session 是用户和 Agent 交互的组织单元也是把用户身份、State、短期临时状态串起来的容器。4.1 Session 里该放什么不该放什么Session 适合放一次会话内共用的轻量数据Session ID、用户 ID、当前对话上下文窗口、本次会话内生成的临时状态、最后一次活跃时间。它不适合放需要跨会话长期复用的核心数据。比如用户家庭住址应该进用户记忆表订单退款进度应该进订单状态表。Session 过期后这些数据不能丢因为真正的持久化在数据库里。如果 Session 里存了所有东西一旦缓存清空整个用户数据就没了。4.2 Session ID 生成与传递常见做法是服务端生成唯一 Session ID通过 Cookie、请求头或响应体返回给客户端。后续请求带上这个 ID服务端根据 ID 查找对应的会话上下文。import uuid from flask import session, request app.route(/chat, methods[POST]) def chat(): user_input request.json.get(message) if session_id not in session: session[session_id] str(uuid.uuid4()) return process_chat(session[session_id], user_input)这里的关键问题是客户端把会话 ID 传回来。如果前端重复创建新会话Agent 就会频繁“失忆”。排查时先看请求头里有没有正确携带 session_id。4.3 会话超时要有策略不能一刀切电商客服场景的用户可能中途去查物流也可能隔了几小时再回来。给 Session 设置一个固定过期时间比如 30 分钟没操作就清理之后用户回来了要能恢复。恢复方式不是把整个对话历史捞回上下文而是读取 Session 里保存的 State再按需从长期记忆里检索关键信息。可以设计两种过期层级短期会话超时后允许恢复状态长期无活动后要求重新开启新会话但用户长期记忆仍然生效。这样既控制了资源占用也不丢关键信息。5. Agent 框架与 LangGraph如何让 State 真正成为可编程流程如果不想全部手写状态机现在主流 Agent 框架基本都提供了 State 管理能力。LangGraph 这类框架的特点就是把 State 当成图节点之间传递的显式变量每个节点可以读取、修改 State然后决定下一跳走向。5.1 把思路迁移到图上而不是只堆函数调用LangGraph 的核心单位是 StateGraph。定义好初始 State 结构后把各个处理步骤声明为节点再通过边连接。每次节点执行完后返回新的 State 字段框架负责把更新后的 State 传给下一个节点。from typing import TypedDict, Annotated from langgraph.graph import StateGraph, START, END class AgentState(TypedDict): user_id: str order_id: str current_step: str conversation: list long_term_memory: dict def check_order_state(state: AgentState): # 查询订单状态更新 state state[current_step] refund_confirmation return state graph StateGraph(AgentState) graph.add_node(check_order_state, check_order_state) graph.set_entry_point(check_order_state)这段代码结构是为了展示用框架表达“步骤推进”。真实项目里节点内会调用工具、调用模型、解析结果再更新状态。用图的优势是流程可观测、可回放、可动态改变路径。5.2 在节点函数里修改 State 的常见规范LangGraph 中修改 State 是直接返回新字段。要注意的是修改要最小化不要每次把整个大对象复制来复制去。如果状态里有大量对话文本建议把不影响主流程的内容放到外部存储State 里只保留引用或摘要降低内存压力和序列化开销。5.3 框架能帮你做记忆但不能帮你判断该记什么框架解决了“State 怎么传递、节点怎么编排”的问题但“什么信息值得进入长期存储”“历史记录怎么压缩”“用户画像怎么更新”仍然需要业务规则。框架提供的是工程骨架记忆策略才是业务灵魂。如果一上来就把所有字段塞进 State所有消息塞进数据库结果会是状态表极其臃肿、检索噪音大、上下文利用率低。先定义清楚核心字段再开始写图能省很多重构时间。6. 从概念到可运行系统搭建客服助手的完整落地步骤有些团队在概念讨论上花了很多时间代码却还没跑通。建议换个顺序先搭一个可运行的最小闭环再逐步替换和优化模块。下面是一个可参考的落地顺序。6.1 第一步定义核心数据结构和初始化环境先不要急着接模型先定义清楚 State、Session、Memory 的数据结构。初始化一个 Python 项目准备好模型调用函数、存储客户端、日志配置。# 示例依赖 pip install flask langgraph如果输入材料没有明确指定框架版本落地前先确认当前环境的依赖版本。不同版本的框架 API 可能有差异。6.2 第二步实现最小对话循环用函数把“接收用户输入 - 查询 State - 构造 Prompt - 调用模型 - 更新 State - 返回结果”串起来。这一阶段可以不做长期记忆先把流程跑通。6.3 第三步接入 Session 管理和 State 恢复为每个用户建立 Session每次请求读取 Session 里的 State。如果 Session 过期按用户 ID 从长期存储恢复核心字段。6.4 第四步实现长期记忆写入和检索在每轮对话结束后调用抽取函数把值得长期存储的信息写入数据库。在下一次会话开始时检索用户长期记忆注入 Prompt。6.5 第五步实现 Context 超限监测和压缩策略在构造请求前估算 token 数。超过阈值时触发滑动窗口和摘要压缩。压缩结果写入日志方便后续排查。6.6 第六步日志、权限、异常处理最后这一步最容易被忽略但决定了系统能不能长期运行。至少需要记录 Session ID、每次请求的 token 数、压缩事件、工具调用时长、模型返回错误。权限上要确保不能跨用户读取记忆数据。7. 常见报错排查链路遇到问题先查哪一层做 Agent 项目报错是常态。下面是一条基于经验的排查链路适用于大多数与记忆相关的问题。7.1 先看现象区分报错层级现象优先怀疑层面Context 超限报错输入装配层输出结果混乱、忘记关键信息上下文裁剪策略会话丢失、用户无法恢复Session ID 传递、过期策略内存溢出、进程崩溃资源占用、状态存储方式工具返回结果不更新State 更新逻辑7.2 按顺序检查输入 - 环境 - 参数 - 工具边界先检查输入。用户消息有没有正常进入对话历史编码、格式是否正常有没有重复追加同一段内容再检查环境。依赖版本是否一致存储服务是否可达进程重启后 Session 是否还能找到然后检查参数。批量大小、并发数、token 阈值是否合理压缩策略有没有触发过最后看工具边界。当前模型版本是否支持目标上下文长度框架是否对某些节点类型有额外限制7.3 一个真实排查思路示例客服助手突然不记得订单号假设用户说“我的订单发货了吗”Agent 却回答“请提供订单号”而用户刚才已经给过订单号。排查顺序查看最新请求的 Prompt 是否真的包含订单号如果没有模型拿不到自然答不出来。检查历史记录是否被裁剪策略过早移除了早期消息。检查 State 中是否记录了订单号还是只在聊天文本里出现。检查 Session 是否有更新新会话是否丢掉了上一会话写入的 State。检查模型是否在查询订单前就已经完成了意图判断导致工具调用发生在错误节点。大多数“失忆”问题都能在定位到具体层后快速修复。8. 长期使用建议记忆系统需要持续运营不是上线就完记忆系统上线之后真正的工作才刚刚开始。它不是一次配置完就永久好用而是需要持续检查、修正、调整。8.1 定期检查记忆质量每隔一段时间抽看长期存储里的记忆项是否仍然有效。用户改过地址后旧地址是否被更新用户问过多次的产品偏好是否被记录进画像抽取逻辑是否产生了很多无效字段这些都需要定期复盘。8.2 建立记忆评估维度准确性存入的内容是否与真实事件一致。时效性过期状态是否有更新机制。覆盖率关键信息是否被漏掉。检索命中率检索到的内容是否真正有帮助。上下文占用注入 Prompt 的记忆是否占用了过多 token。8.3 不要追求“全知全记忆”“记住一切”不是目标。记住有用信息并且知道什么该忘掉才是好设计。过于庞大的记忆库会拖慢检索也会让模型在决策时产生噪音干扰。8.4 和记忆相关的能力矩阵把记忆能力分为四级便于团队规划级别能力典型表现L1会话内记忆连续对话不会断片L2会话恢复断线重连后能恢复状态L3跨会话记忆下次回来还能复用用户偏好和事件状态L4主动记忆维护系统能自动更新、清理、校准长期信息多数项目先做 L1 和 L2很快能完成真正拉开差距的是 L3 和 L4 的数据质量。9. 回到最初的问题Agent 怎么越用越聪明Agent 的聪明程度很大程度取决于它能不能把每一次交互留下的有效信息转化成下一次交互的输入。模型本身决定了推理能力的上限但记忆系统决定了它的经验能不能积累。真正有用的做法是让 State 负责流程推进让 Session 负责交互组织让 Context 负责当前可见信息让 Memory 负责跨会话复用。这四件事各有边界不能混着部署也不能只靠一个组件全包。如果你正在做的 Agent 项目遇到“越用越笨”“换会话就失忆”“上下文越来越大、响应越来越慢”这类问题可以按这篇文章的框架重新梳理一遍先看 State 存了什么再看 Session 怎么恢复再看 Context 怎么压缩最后看 Memory 抽了什么、检索对不对。大部分问题都能在这条线上找到根因。下一次如果再有人问“Agent 怎么才能记住我说的话”你可以回答它自己记不住真正能记住的是我们为它设计的外部记忆系统。把这一层做好它才会看起来像是真的记性好。