Agent 的记忆、上下文与状态:不要混成一个概念
为什么要区分三个概念
用过 ChatGPT、Claude 这类 AI 助手的人,可能都遇到过类似的情况:聊了很久之后,AI 开始「忘记」前面说过的内容;或者隔了几天再打开,它对你的偏好一无所知;又或者一个多步骤的任务中途断了,只能从头来过。
这些体验问题的根源,通常不在模型本身,而在于构建 Agent 系统时,把三个不同层次的信息混在了一起——上下文(Context)、状态(State) 和记忆(Memory)。
三者都和信息有关,但服务的是不同的问题。混在一起会导致信息过载、任务丢失、偏好污染。分清它们的边界,是构建可靠 Agent 的第一步。
三个类比
在正式定义之前,先用三个日常场景来建立直觉。
上下文 = 工作台
想象你有一张工作台,面积有限。你正在修一块手表,于是把螺丝刀、放大镜、零件图纸摆在台上。你不需要、也不应该把整个工具箱都搬上来——工具太多,反而找不到手边需要的那把螺丝刀。
上下文就是 Agent 每次「思考」时的工作台。它决定了这一次模型调用能看到什么信息。工作台上的东西越多,模型对每条信息的注意力就越分散。所以上下文需要精心挑选,而不是全部堆上去。
状态 = 书签
你在读一本 500 页的书,今天读到了第 187 页。你放了一枚书签,合上书去忙别的事。第二天回来,拿起书翻到书签的位置,接着读。
状态就是 Agent 的书签。它记录的是一个多步骤任务执行到了哪里、做了什么、下一步是什么。如果书签丢了,你只能从头翻起;如果任务没有结构化的状态记录,Agent 也只能从头开始。
记忆 = 笔记本
你认识一个朋友,知道他不爱吃香菜、喜欢跑步、住在浦东。这些信息不是某一次聊天教给你的,而是你们认识以来慢慢积累的。你把它记在脑子里(或者笔记本上),下次见面时自然会用上。
记忆就是 Agent 跨会话保留的知识。它来自多次交互的积累——用户偏好、事实、学到的技能。记忆需要整理、需要更新、也需要遗忘。你不会把每一次对话的每个字都记下来,而是提炼出有价值的信息。
三个类比对应三种不同的时间尺度:
| 概念 | 类比 | 时间跨度 | 生命周期 | 核心问题 |
|---|---|---|---|---|
| 上下文 | 工作台 | 单次调用 | 调用结束即清空 | 这次思考需要看到什么 |
| 状态 | 书签 | 单次任务 | 任务结束可归档 | 任务做到哪一步了 |
| 记忆 | 笔记本 | 跨会话 | 长期保留,可管理 | 跨会话需要记住什么 |
关键差异在于作用域和失效条件:上下文在每次调用后失效,状态在任务完成后归档,记忆在用户删除前持续有效。
理论基础
这三个概念并不是凭空造出来的。学术界和工程界已经有了比较一致的认识。
认知科学的记忆分类
arXiv 上 2026 年 3 月的综述论文 Memory for Autonomous LLM Agents: Mechanisms, Evaluation, and Foreseeable Trends(arXiv:2603.07670)给出了一个被广泛引用的分类框架,和认知科学中的记忆分类对齐:
- 工作记忆(Working Memory):对应上下文,是 Agent 此刻正在处理的信息。
- 情景记忆(Episodic Memory):带时间戳的具体经历——某次对话、某次工具调用的结果。
- 语义记忆(Semantic Memory):从多次经历中抽象出来的事实——用户的日期格式习惯、项目的技术栈约定。
- 程序记忆(Procedural Memory):学到的可复用工作流——一种验证流程、一个调试步骤。
这篇论文同时指出一个关键结论:长上下文不等于记忆。即便上下文窗口已经扩展到 200k token 以上,长上下文模型在需要「选择性检索和主动管理」的任务上,仍然明显弱于专门设计的记忆系统。在被动回忆任务上接近满分的模型,到了需要主动使用记忆的任务上,准确率会跌到 40–60%。
换句话说,窗口再大,也不代表模型能「记住」所有东西。
注意力预算
Anthropic 在 2025 年 9 月发布的 Effective Context Engineering for AI Agents 中,把上下文窗口称为「注意力预算」(attention budget)。核心观点是:上下文是有限资源,塞进去的信息越多,模型对每条信息的注意力就越分散。他们把这种现象称为「上下文腐烂」(context rot),因为 token 之间的两两注意力关系是 O(n²) 级别。
这两个视角合在一起,可以给三个概念下一个工程化的定义:
| 概念 | 回答的问题 | 时间跨度 | 存储位置 |
|---|---|---|---|
| 上下文 | 这次模型调用需要看到什么 | 单次调用 | 上下文窗口(prompt) |
| 状态 | 任务执行到哪一步、做了什么、下一步是什么 | 单次任务 | 结构化存储(数据库 / 文件) |
| 记忆 | 跨会话保留的用户偏好、事实或可复用经验 | 跨会话 | 外部记忆系统(向量库 / KV / 图) |
三者关系:信息如何流动
三层信息在每次 Agent 调用时发生一次「组装 → 执行 → 沉淀」的循环。下图展示了这个循环的完整流程:
每次调用时,系统从外部存储中取出相关记忆和当前状态,和系统指令、工具定义一起组装成完整的 prompt 发给模型。模型输出动作后,系统更新任务状态、提炼新记忆、追加对话历史。
这个循环中有三个容易出错的环节:
- 记忆检索过宽:把不相关的记忆塞进上下文,模型被干扰。
- 状态未持久化:任务中断后无法恢复,只能从头开始。
- 上下文被当成记忆:把对话历史直接存入长期记忆,导致错误结论被固化。
接下来分别展开三层的原理。
上下文:注意力预算的管理
上下文是三层中生命周期最短的一层,也是对模型行为影响最直接的一层。它的核心挑战是:工作台空间有限,如何挑选放上去的东西。
组装优先级
上下文按优先级分配有限的 token 预算。一个工程上可行的优先级顺序是:
- 系统指令与工具定义:固定前缀,每次调用都一样,可以被缓存。
- 当前任务状态:本次任务的进度、已执行动作、待处理项。
- 检索到的相关记忆:经过相关性过滤的记忆片段,而非全部历史。
- 最近几轮对话:压缩后的摘要,而非原始文本。
- 按需注入的工具结果和环境信息:只在需要时加入。
这个顺序的核心思路是:稳定的、高优先级的信息放在前面,按需的、低优先级的信息放在后面。
Manus 在 2025 年 7 月的工程博客中提出了一个实用原则:保持 prompt 前缀稳定,让上下文尽量 append-only(只追加不修改),在需要时显式标记缓存断点。这样既能利用模型的 KV cache 减少重复计算,也能让上下文内容可追溯。
压缩策略
当对话接近上下文窗口上限时,必须有压缩机制。Anthropic 总结了三种主要策略:
| 策略 | 做法 | 适用场景 | 风险 |
|---|---|---|---|
| 压缩(Compaction) | 对历史对话生成摘要,用摘要替代原始文本 | 长对话、多轮迭代 | 低频细节在反复压缩中被悄悄丢弃 |
| 工具结果清理 | 深层历史中的工具返回结果只保留结论,丢弃原始输出 | 频繁调用工具的 Agent | 后续如果需要重新检查原始数据,无法回溯 |
| 子 Agent 卸载 | 把子任务交给独立 Agent 处理,只把浓缩结果返回主 Agent | 复杂研究、并行探索 | 子 Agent 的上下文对主 Agent 不可见,调试困难 |
Claude Code 的 compaction 机制是一个典型的实现:当对话接近上下文上限时,系统会生成一份摘要,保留架构决策、未解决的 bug 和关键约束,丢弃冗余的工具输出和重复讨论。然后用这份摘要作为新的起点继续工作。
代码片段:预算式上下文构建
下面是一段简化后的代码,展示「按优先级分配预算」的思路:
def build_context(user_id: str, query: str, task: Task | None):
budget = ContextBudget(total_tokens=120_000)
# 1. 固定前缀(系统指令 + 工具定义),可缓存
prefix = build_stable_prefix(system_prompt, relevant_tools(query))
budget.consume(prefix)
# 2. 任务状态(如果有进行中的任务)
if task:
budget.consume(render_task_state(task))
# 3. 检索相关记忆(带相关性过滤,有 token 上限)
memories = memory_store.retrieve(
query=query, user_id=user_id,
top_k=5, max_tokens=2000, min_relevance=0.7,
)
budget.consume(render_memories(memories))
# 4. 最近对话的压缩摘要
recent = conversation_store.get_recent_summary(
user_id=user_id, max_turns=5, max_tokens=3000,
)
budget.consume(recent)
# 5. 当前查询
budget.consume(f"User: {query}")
return budget.render()核心思路是:先分配固定部分(系统指令),再按优先级依次分配(任务状态 → 相关记忆 → 对话摘要 → 当前查询),每一层都有 token 上限,超出预算就截断或压缩。
与之对应的常见错误做法是:把所有历史对话、所有记忆、所有工具结果全部拼接在一起,不做任何过滤和压缩。这种做法在初期能跑通,但随着对话变长,token 消耗会线性甚至超线性增长,模型也会被无关信息干扰。
状态:任务进度的结构化保存
状态是三层中最容易被忽视的一层。很多 Agent 系统把任务进度「隐含」在对话历史里——系统靠回顾对话来推断任务执行到了哪一步。一旦对话被压缩或丢失,进度也就丢了。
MindStudio 在 2026 年 4 月的文章中把 Agent 的状态分为两类:
- 工作流状态(Workflow State):与具体会话无关,描述任务本身的进度。任何 Agent 实例都应该能读取这个状态并继续工作,不需要重建对话历史。
- 会话状态(Session State):与一次会话绑定,描述这次交互的临时上下文。会话结束即可丢弃。
区分这两种状态很重要。工作流状态是「这本书读到第几页」,会话状态是「这次阅读时桌上的笔记和便签」。前者需要持久化,后者可以随会话消亡。
状态机思路
arXiv 上 2024 年的论文 Enhancing LLM Task-Solving through State-Driven Workflows(StateFlow)提出了一个思路:把复杂任务建模为状态机。每个节点是一个明确的执行阶段,边是状态转移条件,由 LLM 在每一步决定下一步走哪条边。
这种做法的好处是:任务进度可以被检查、可以被恢复、可以被回放。坏处是:需要预先定义状态空间,对于高度开放的任务不够灵活。
一个折中的方案是「结构化状态 + 自由文本注释」。核心字段(当前步骤、已完成动作、待执行动作、阻塞项、已生成产物)用结构化数据记录,保证可以程序化地读取和恢复;同时保留一个自由文本字段,让模型可以记录一些不方便结构化的观察。
class TaskState:
task_id: str
status: 'planned' | 'in_progress' | 'blocked' | 'completed' | 'failed'
current_step: int
total_steps: int
completed_actions: list[Action] # 已执行的动作列表
pending_actions: list[Action] # 待执行的动作列表
blockers: list[Blocker] # 阻塞项
artifacts: list[Artifact] # 已生成的产物
last_checkpoint: datetime # 上次检查点时间
notes: str # 自由文本注释这个数据结构独立于对话历史存在。即使进程崩溃、会话断开,只要状态存储还在,任务就可以从最后一个检查点恢复。
持久化模式选择
任务状态需要存到哪里?这取决于任务的持续时间和失败代价:
| 模式 | 持久化方式 | 可靠性 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 内存状态 | 进程内存 | 低(重启即丢失) | 低 | 原型、短任务 |
| 文件状态 | JSON / SQLite 文件 | 中 | 低 | 单用户、本地 Agent |
| 数据库状态 | PostgreSQL / Redis | 高 | 中 | 多用户、多实例 |
| Workflow 引擎 | Temporal / Durable Functions | 高(event sourcing) | 高 | 长时间运行、关键业务 |
| 混合模式 | 热状态在 Redis,冷状态归档到对象存储 | 高 | 高 | 大规模、需要回放 |
一个几分钟的对话任务用文件状态就够;一个可能运行几天、涉及外部系统写入的任务,需要 Workflow 引擎或等价机制。Temporal 在 2025 年 10 月的工程博客中分享了用 Workflow 引擎实现 Agent 状态持久化的方案:每次状态变更都被记录为 event,进程崩溃后可以从最后一个 event 恢复,不需要把整个对话历史保留在内存中。
记忆:跨会话的长期资产
记忆是三层中生命周期最长、也最需要谨慎处理的一层。arXiv 2603.07670 论文指出一个被广泛忽视的问题:
记忆值得获得与 LLM 本身同等的工程投入。模型选型通常会花几个月仔细评测;记忆架构往往只得到一个下午。
记忆的类型与作用域
Mem0 在 2026 年 4 月的报告中提出了一个四维作用域模型,按 user_id、agent_id、session_id 和 org_id 四个维度组织记忆。对应到前面的认知科学分类:
| 记忆类型 | 作用域 | 内容示例 | 写入时机 | 检索方式 |
|---|---|---|---|---|
| 用户语义记忆 | user_id | 用户偏好、个人信息、长期习惯 | 从对话中提炼 | 按用户 + 查询语义检索 |
| Agent 能力记忆 | agent_id | Agent 学到的技能、有效的工具组合 | 任务完成后提炼 | 按任务描述检索 |
| 会话情景记忆 | session_id | 本次对话的关键决策、中间结论 | 对话过程中实时写入 | 按会话 ID 直接读取 |
| 组织知识记忆 | org_id | 团队约定、项目规范、领域知识 | 人工维护或从文档导入 | 全局检索 + 权限过滤 |
用户语义记忆是「这个用户喜欢用 Markdown 格式」「这个用户在上海」;Agent 能力记忆是「上次用这种工具组合解决了类似问题」;会话情景记忆是「这次对话中用户确认了方案 A」;组织知识记忆是「公司的 API 规范」「项目的部署流程」。
四种记忆的写入方式、检索方式和生命周期都不同。把它们放在同一个存储里、用同一种方式处理,会导致检索混乱和权限问题。
记忆写入的五个阶段
记忆写入是最容易出错的环节。论文指出,写入路径至少需要五个阶段:
- 提取:从对话中识别值得长期保留的信息。闲聊、确认、重复信息不值得写入。
- 过滤:给候选记忆打分,低信号内容不写入。
- 去重:与已有记忆比较,避免重复存储。如果新信息与旧信息冲突,更新旧信息而非新增。
- 打分:根据置信度、重要性等维度给记忆排优先级。
- 标注元数据:记录记忆的类型、来源、创建时间等元数据,方便后续检索和管理。
任何一个阶段缺失,都会导致记忆质量下降。最常见的错误是把模型输出直接存入记忆——对话中的每句话都被存下来,没有过滤、没有去重、没有元数据。这样做的结果是记忆库快速膨胀,检索精度持续下降。
async def on_message(user_id: str, message: str, response: str):
# 1. 让模型判断本次对话是否有值得长期保留的信息
extraction = await llm.call(
prompt=MEMORY_EXTRACTION_PROMPT,
context={"user_message": message, "agent_response": response},
)
for candidate in extraction.memories:
# 2. 过滤:低信号内容不写入
if candidate.signal_score < 0.6:
continue
# 3. 去重:与已有记忆比较
existing = await memory_store.find_similar(
user_id=user_id,
content=candidate.content,
threshold=0.85,
)
if existing:
# 有冲突,保留新版本
await memory_store.update(existing.id, content=candidate.content)
else:
# 4-5. 打分并带完整元数据写入
await memory_store.save(
user_id=user_id,
content=candidate.content,
memory_type=candidate.type,
confidence=candidate.confidence,
tags=candidate.tags,
)记忆检索:多信号融合
Mem0 的实践表明,单纯的向量相似度检索在记忆系统中不够用。高相似度不等于高相关性——一条语义上相似但已经过时的记忆,检索出来反而会误导模型。
更稳健的做法是多信号检索,把多个维度的分数融合为一个最终排序:
- 语义相似度:用向量数据库做 embedding 检索,找到语义上最接近的记忆。
- 关键词匹配:用传统关键词索引,捕捉向量检索可能遗漏的精确匹配。
- 实体匹配:提取查询中的实体(人名、项目名、日期等),找到涉及相同实体的记忆。
- 时效性加权:新记忆的权重更高,旧记忆逐渐衰减。
- 重排:用 cross-encoder 对 top 结果做精排,然后控制在 token 预算内。
这种多信号融合的方式,比单纯依赖向量相似度更能捕捉「当前真正需要」的记忆。
遗忘机制
一个值得警惕的发现是:「没有人能很好地评测遗忘」。但在长期运行的系统中,无法丢弃过时信息会逐渐毒化检索精度。
遗忘不能只是硬性的时间过期。一个用户三年前说的偏好可能今天仍然有效,一个月前说的偏好可能已经改变。一个实用的遗忘策略需要结合四种机制:
- 显式过期:用户主动删除、或设定了过期时间的记忆。
- 矛盾检测:当新记忆与旧记忆冲突时,标记旧记忆为 superseded(已被取代),而非直接删除,保留溯源能力。
- 重要性衰减:被频繁检索和引用的记忆保持活跃,长期未被引用的记忆逐渐降低优先级。
- 定期整合:周期性地把多条相关记忆合并为一条更抽象的总结,减少碎片。
遗忘不是丢失信息,而是让过时的信息不再影响当前决策。这是记忆系统最难做好的部分,也是区分「能用」和「好用」的关键。
设计原则
综合上面的讨论,可以提炼出六条设计原则:
| 原则 | 含义 | 违反时的症状 |
|---|---|---|
| 上下文有预算 | 每次调用前主动分配 token,而非被动堆积 | 响应变慢、token 成本失控、模型被无关信息干扰 |
| 状态独立于对话 | 任务进度是结构化数据,不依赖对话历史推断 | 进程重启后任务丢失、压缩对话后进度模糊 |
| 记忆需要提炼 | 长期记忆是从对话中提取的,不是对话本身的复制 | 记忆库膨胀、过时信息污染决策 |
| 记忆可以遗忘 | 有过期、去重、合并和删除机制 | 检索精度随时间下降、旧偏好覆盖新偏好 |
| 三层可追溯 | 每次调用的上下文组装过程可记录、可回放 | 出了问题无法定位是记忆错误、状态错误还是上下文错误 |
| 用户可管理记忆 | 用户能查看、修改、删除自己的长期记忆 | 隐私风险、用户信任丧失 |
前三条解决「能不能跑」的问题,后三条解决「能不能长期跑」的问题。
检查清单
上下文
- 每次调用前有明确的 token 预算分配,而非把可用信息全部塞入。
- 系统提示和工具定义作为稳定前缀,支持 KV cache 复用。
- 记忆检索有相关性过滤和 token 上限,不是全量注入。
- 对话历史在超过阈值后会被压缩,压缩时保留关键决策和未解决问题。
状态
- 任务进度是结构化数据(状态机或 checkpoint),不依赖对话历史推断。
- 任务中断后可以从最后一个检查点恢复,不需要从头开始。
- 状态存储与对话存储分离,可以独立备份和恢复。
记忆
- 长期记忆是从对话中提炼的,不是对话文本的直接复制。
- 记忆写入前有过滤、去重和元数据标注。
- 记忆检索使用多信号融合,而非单纯向量相似度。
- 有过期、去重、合并和删除机制,避免过时信息污染决策。
- 用户可以查看、修改和删除自己的长期记忆。
可观测性
- 每次调用的上下文组装过程可记录、可回放。
- 记忆的每次读、写、更新、删除都有审计日志。
- 出了问题时能定位是记忆错误、状态错误还是上下文组装错误。
参考资料
-
arXiv:2603.07670. Memory for Autonomous LLM Agents: Mechanisms, Evaluation, and Foreseeable Trends. 2026. 关于 LLM Agent 记忆机制的系统综述,提出了工作记忆 / 情景记忆 / 语义记忆 / 程序记忆的分类框架。
-
Anthropic Engineering. Effective Context Engineering for AI Agents. 2025. 提出「注意力预算」和「上下文腐烂」概念,介绍了 compaction、note-taking 和子 Agent 卸载三种上下文管理策略。
-
Mem0. State of AI Agent Memory 2026: Benchmarks, Architectures and Production Patterns. 2026. 介绍了四作用域记忆模型和多信号检索架构。
-
Manus Engineering Blog. Context Engineering for AI Agents: Lessons from Building Manus. 2025. 提出 prompt 前缀稳定、上下文 append-only、显式标记缓存断点三个工程原则。
-
arXiv:2403.11322. Enhancing LLM Task-Solving through State-Driven Workflows (StateFlow). 2024. 把复杂 LLM 任务建模为状态机的学术工作。
-
Temporal Blog. Building a Persistent Conversational AI Chatbot with Temporal Workflows. 2025. 介绍如何用 Workflow 引擎实现 Agent 状态的 event sourcing 和崩溃恢复。
-
MindStudio. What Is Workflow State vs Session State in AI Agents? 2026. 区分工作流状态和会话状态。