Memory 与 State:Agent 不只是记住聊天记录
这篇文章解决什么问题
很多人理解 Agent Memory 时,会直接想到"保存聊天记录"。但真实 Agent 系统里,Memory 和 State 要解决的问题远比这复杂:
- 当前任务进行到哪一步了?
- 用户这次会话说过什么?
- 用户长期偏好是什么?
- 哪些信息应该进入上下文?
- 哪些信息应该长期保存?
- 长任务上下文太长怎么办?
- 多 Agent 如何共享必要状态?
如果把所有聊天记录都塞进上下文,模型会因为 token 限制而无法处理;如果什么都不保存,Agent 每次都从零开始,无法提供连贯的服务。
这篇文章要回答的核心问题是:Agent 系统中的 Memory 和 State 到底应该怎么理解?Session、Context、Memory、State 和 Trace 有什么区别?
核心观点:Memory 不是聊天记录,State 也不是简单上下文。 生产级 Agent 要区分 Session、Context、Memory、State 和 Trace,每种信息有不同的生命周期和管理策略。
四个容易混淆的概念
在 Agent 系统中,Session、Context、Memory、State 和 Trace 经常被混用,但它们有明确的区别:
| 概念 | 作用 | 生命周期 | 示例 |
|---|---|---|---|
| Session | 当前会话容器,管理一次对话的完整上下文 | 从用户打开对话到关闭对话 | 用户和 Agent 的一次完整交互过程 |
| Context | 当前模型调用看到的信息,是 Memory 和 State 的子集 | 单次模型调用 | 传给模型的 system prompt、最近几轮对话、当前工具结果 |
| Memory | 可复用的长期信息,跨会话持久化 | 长期,直到被更新或删除 | 用户偏好、项目背景、历史经验 |
| State | 任务执行状态,记录当前任务的进度 | 单次任务执行 | 当前步骤、已完成步骤、待处理操作 |
| Trace | 执行过程记录,用于调试和审计 | 长期保存 | 每次模型调用、工具调用、状态变化的完整记录 |
理解这五个概念的区别,是设计 Agent Memory 系统的基础。
短期上下文
短期上下文是当前模型调用能看到的信息,通常包括:
- 当前用户问题。 用户这次问了什么。
- 最近几轮对话。 最近几次问答的摘要或原文。
- 当前任务目标。 这次任务要完成什么。
- 当前工具结果。 最近一次工具调用返回的结果。
- 当前 RAG 检索内容。 从知识库检索到的相关文档。
- 当前状态摘要。 任务进行到哪一步、有什么关键信息。
短期上下文的目标是支持当前模型调用,不需要长期保存。每次模型调用结束后,短期上下文可能被更新——新的对话轮次加入,旧的轮次被压缩或移除。
短期上下文的关键挑战是 token 限制。模型的上下文窗口是有限的,你不能把所有历史都塞进去。需要策略来决定哪些信息保留、哪些压缩、哪些丢弃。
长期记忆
长期记忆是跨会话持久化的信息,可以保存:
- 用户偏好。 用户喜欢什么格式的回答、常用的语言风格。
- 稳定身份信息。 用户的角色、部门、常用工具。
- 常用项目背景。 用户经常处理的项目、常用的文档。
- 历史任务经验。 过去处理过的类似任务和结果。
- 可复用事实。 被验证过的知识和结论。
- 经过确认的知识。 用户明确确认过的信息。
长期记忆需要写入策略,不能把所有对话都保存进去。如果每次对话都保存,Memory 会无限膨胀,读取时也会引入大量噪声。
任务状态 State
任务状态记录当前任务的执行进度,让任务可以继续推进、暂停、恢复和结束。一个典型的任务状态可能包括:
task_id:任务的唯一标识。run_id:这次执行的唯一标识。current_step:当前正在执行的步骤。status:任务状态,如 running、paused、completed、failed。intermediate_result:中间结果,如检索到的文档、生成的草稿。pending_tool_call:待执行的工具调用。retry_count:当前步骤的重试次数。error_state:错误信息,如果有的话。final_result:最终结果,任务完成后填充。
State 的作用是让任务可管理。没有 State,你不知道任务进行到哪一步;有了 State,你可以暂停任务、恢复任务、查看进度、处理错误。
Memory 与 RAG 的区别
RAG 和 Memory 都涉及信息检索,但它们的定位不同:
RAG 通常连接外部知识库,面向文档和知识检索。 RAG 的数据来源是文档、网页、数据库等外部知识,目标是为模型提供事实依据。
Memory 更偏用户、任务、历史交互和长期偏好。 Memory 的数据来源是用户的行为、对话和反馈,目标是让 Agent 了解用户和任务上下文。
二者可以结合使用:
- RAG 提供外部知识。当用户问"这个产品的退货政策是什么",RAG 从知识库中检索相关政策文档。
- Memory 提供用户和上下文。Memory 告诉 Agent 这个用户是 VIP 客户、之前有过退货经历。
- Runtime 负责选择哪些内容进入 Context。根据当前任务需要,决定传入 RAG 结果、Memory 信息还是两者都传。
Memory 与 Session 的区别
Session 是当前会话容器,Memory 是跨会话可复用信息。不是所有 Session 内容都应该变成 Memory。
应该变成 Memory 的信息:
- 用户明确表达的偏好。"以后回答请用表格格式"。
- 重要的项目背景。"我在做 XXX 项目"。
- 用户确认的知识。"是的,我们的退款周期是 7 天"。
不应该变成 Memory 的信息:
- 临时问题。"今天天气怎么样"——没有长期价值。
- 未确认的信息。Agent 推测的内容,用户没有确认。
- 敏感个人信息。密码、Token、个人隐私。
- 一次性任务中间结果。某次查询的临时数据。
- 可能过期的信息。新闻、实时数据等时效性内容。
Session 到 Memory 的转换需要策略——不是所有对话都值得记住。
上下文压缩
长任务会导致上下文膨胀。一个复杂的 Agent 任务可能执行几十步,每步都有模型调用、工具调用和状态更新。如果不压缩,上下文很快就会超出模型的 token 限制。
上下文压缩的策略:
- 关键状态摘要。 不保留所有状态变化,只保留当前状态的摘要。
- 工具调用记录。 不保留工具返回的完整数据,只保留关键结论和引用。
- 模型输出摘要。 不保留模型的完整输出,只保留最终决策和理由。
- 已完成步骤。 不保留每步的详细过程,只保留步骤名称和结果摘要。
- 待处理事项。 明确列出接下来要做什么。
- 重要决策记录。 记录关键决策点和选择理由。
压缩的目标是:保留推进任务所需的信息,减少低价值历史。 你需要判断哪些信息对后续步骤有用,哪些可以安全丢弃。
一个常见的压缩策略是"滑动窗口 + 摘要":保留最近 N 轮对话的原文,把更早的对话压缩成摘要。这样既保留了近期上下文的细节,又不会让上下文无限膨胀。另一个策略是"按相关性保留":根据当前任务目标,保留与任务相关的上下文,丢弃无关内容。
压缩不是简单截断——截断可能丢掉关键信息。比如用户在第一轮说过"我是管理员",如果被截断了,后续 Agent 可能不知道用户有管理权限。好的压缩策略会把这种重要信息提取出来保留。
Memory 写入策略
什么时候写入长期记忆?可以考虑以下条件:
- 用户明确要求记住。 "请记住我喜欢表格格式"——直接写入。
- 信息稳定且长期有用。 用户的角色、部门、常用项目——不会频繁变化。
- 多次重复出现。 用户多次提到同一个偏好或需求——说明是稳定的。
- 与项目背景强相关。 项目的技术栈、团队结构、工作流程——长期有用。
- 已经被用户确认。 Agent 推测后用户确认的信息——准确度高。
什么时候不写入:
- 临时闲聊。 没有长期价值。
- 未确认的信息。 Agent 的推测,用户没有确认。
- 敏感个人信息。 密码、Token、个人隐私——安全风险。
- 一次性任务中间结果。 某次查询的临时数据。
- 可能过期的信息。 新闻、实时数据等时效性内容。
写入策略的核心是:只保存长期有用、准确可靠、不敏感的信息。
Memory 读取策略
读取 Memory 时要考虑相关性和质量:
- 当前任务是否需要。 不是所有 Memory 都和当前任务相关。用户问技术问题时,不需要读取用户的饮食偏好。
- 信息是否过期。 过期的信息可能误导 Agent。
- 信息是否与任务相关。 相关性低的信息会引入噪声,干扰模型判断。
- 是否会引入噪声。 太多不相关的信息会稀释重要信息的权重。
- 是否需要用户确认。 某些敏感信息读取后可能需要用户确认。
一个实用的读取策略是"相关性打分":对 Memory 中的每条信息,根据当前任务关键词计算相关性分数,只读取分数高于阈值的信息。另一个策略是"分类读取":把 Memory 分成用户偏好、项目背景、历史经验等类别,根据任务类型选择性读取。
读取策略的核心是:只读取和当前任务相关的、准确的、有用的 Memory。 读取太多不相关的信息,不仅浪费 token,还会干扰模型判断。读取太少又会让 Agent 缺乏必要的上下文,影响回答质量。
多 Agent 中的 State 共享
在多 Agent 系统中,Agent 之间需要共享状态,但不能随意共享全部上下文。
更合理的共享内容:
- 任务目标。所有 Agent 都需要知道整体目标。
- 角色职责。每个 Agent 需要知道自己的职责边界。
- 已完成步骤。避免重复工作。
- 关键状态摘要。让其他 Agent 了解当前进度。
- 工具调用结果。如果一个 Agent 的结果对另一个有用。
- 最终输出要求。确保最终结果符合预期。
应该避免共享的内容:
- 无关对话历史。其他 Agent 不需要知道所有对话细节。
- 敏感信息。用户密码、Token 等不应该在 Agent 间传递。
- 低价值中间内容。过多的中间数据会增加上下文负担。
- 未确认推测。一个 Agent 的推测不应该被其他 Agent 当作事实。
状态共享的核心原则是:共享推进任务所需的信息,不共享噪声和敏感数据。
一个实用的共享方式是"共享黑板"(Shared Blackboard):所有 Agent 往一个公共黑板上写入自己的状态和结果,其他 Agent 可以读取黑板上的信息。黑板只保留关键结论,不保留详细过程。另一个方式是"消息传递":Agent 之间通过消息传递状态,消息有明确的格式和字段,避免传递无关信息。
无论用哪种方式,都需要明确:谁可以写入什么信息、谁可以读取什么信息、信息的生命周期是什么。没有规则的共享会导致上下文污染——一个 Agent 的错误推测被其他 Agent 当作事实,最终影响整个系统的判断。
共享状态还需要考虑版本问题:多个 Agent 可能同时读写状态,需要有冲突解决机制。简单的做法是用时间戳标记每条信息,以最新版本为准;更复杂的场景可能需要锁机制或乐观并发控制。
最小 State 结构示例
下面是一个最小的任务状态结构:
agent_state = {
"task_id": "task_001", # 任务唯一标识
"run_id": "run_001", # 这次执行的唯一标识
"status": "running", # 任务状态:running / paused / completed / failed
"current_step": "retrieval", # 当前执行步骤
"completed_steps": [ # 已完成的步骤列表
"parse_input",
"rewrite_query"
],
"pending_actions": [ # 待执行的操作
"call_vector_search"
],
"tool_results": [], # 工具调用结果
"state_summary": "用户需要基于知识库生成一份工单处理建议。", # 当前状态摘要
"retry_count": 0, # 当前步骤重试次数
}每个字段的作用:
task_id:标识这个任务,用于关联 Trace 和 Evaluation。run_id:标识这次执行,同一个任务可能执行多次。status:任务的生命周期状态,Runtime 据此决定下一步行动。current_step:当前正在做什么,让 Agent 知道自己在哪一步。completed_steps:已经做完了什么,避免重复工作。pending_actions:接下来要做什么,Runtime 据此调度下一步。tool_results:工具调用的结果,用于更新状态和生成最终结果。state_summary:当前状态的文字摘要,用于上下文压缩时快速了解状态。retry_count:重试次数,超过阈值时 Runtime 应该改变策略或终止。
这个结构是最小版本,生产系统可能还需要更多字段:error_state 记录当前错误信息,cost 累计 token 消耗,started_at 和 finished_at 记录时间戳,user_context 保存用户相关信息。State 的设计原则是:只记录推进任务所需的信息,不记录无关细节。
常见误区
把 Memory 当成聊天记录。 Memory 是经过筛选的长期有用信息,不是所有对话的完整记录。
把所有历史都塞进上下文。 上下文窗口有限,塞太多会稀释重要信息,还可能超出 token 限制。
不区分 Session 和 Memory。 Session 是临时的,Memory 是长期的。不是所有 Session 内容都值得变成 Memory。
不保存任务状态,导致任务无法恢复。 如果任务中断,没有 State 就无法从断点继续。
上下文压缩只做简单截断。 截断可能丢掉关键信息。需要策略性压缩——保留重要的,丢弃低价值的。
多 Agent 共享过多无关信息。 每个 Agent 只需要和自己任务相关的信息,过多共享会增加上下文负担。
记忆写入没有策略。 不是所有对话都值得保存,需要明确的写入条件和过滤规则。
读取记忆不做相关性判断。 不相关的 Memory 会引入噪声,干扰模型判断。
State 字段设计不完整。 只记录当前步骤,不记录已完成步骤和待处理操作,导致任务恢复时信息不足。
Memory 没有过期机制。 过时的信息一直保存,读取时可能误导 Agent。需要定期清理或标记过期。
对个人项目的启发
项目 A(RAG 工单系统):
RAG 工单系统可以保存当前工单状态、用户反馈和历史处理偏好。工单状态包括当前处理步骤、已检索的文档、已生成的草稿、用户的修改意见。不应该把所有问答历史都塞进模型上下文——用状态摘要管理长任务,把关键结论和待办事项保留,把详细过程压缩或丢弃。用户的历史处理偏好(比如喜欢简洁的回答、常用某种格式)可以沉淀为长期 Memory,下次处理类似工单时自动应用。
项目 B(多 Agent 运营中台 Copilot):
多 Agent Copilot 需要共享任务状态,而不是共享所有对话。每个 Agent 应该有清晰的上下文边界——数据查询 Agent 只需要查询相关上下文,内容生成 Agent 只需要生成相关上下文。状态共享要和 Trace、权限、安全策略结合——共享什么信息、谁有权查看、如何记录共享行为,都需要明确的规则。
面试表达
我不会把 Memory 简单理解为聊天记录。在面试中,我会这样表达:
我会区分 Session、Context、Memory、State 和 Trace 五个概念。Session 是当前会话容器,Context 是当前模型调用看到的信息,Memory 是跨会话的长期有用信息,State 是任务执行状态,Trace 是执行过程记录。它们有不同的生命周期和管理策略。
对长任务,我会用 State 管理任务推进——记录当前步骤、已完成步骤、待处理操作和关键状态摘要;用 Memory 保存长期有用信息——用户偏好、项目背景、历史经验;用 Trace 记录执行过程——每次模型调用、工具调用、状态变化都有完整记录。上下文压缩时,我会保留推进任务所需的信息,减少低价值历史,而不是简单截断。
对多 Agent,我会控制共享状态的范围。共享任务目标、角色职责、已完成步骤和关键状态摘要,避免共享无关对话历史、敏感信息和低价值中间内容。状态共享要和权限、安全策略结合,确保信息在正确的范围内流动。
后续 TODO
- 补充 Memory 写入规则示例,展示不同场景下的写入策略。
- 补充 State 状态机设计,展示任务状态的完整生命周期。
- 补充多 Agent 共享状态协议,展示 Agent 间状态传递的规范。
- 补充 Memory 与 RAG 的组合示例,展示如何在 RAG 查询中利用长期记忆。