Skip to content

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 结构示例 ​

下面是一个最小的任务状态结构:

python
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 查询中利用长期记忆。