一个 Agent 如果只依赖当前请求里的上下文,它更像一次性脚本:能完成单次任务,但无法延续用户偏好,也无法从历史错误中吸取经验。真实业务里的 Agent 往往需要面对多轮交互、长期用户画像、工具调用历史、业务规则沉淀和流程复用,这些能力都离不开记忆模块。
记忆模块不是简单的“聊天记录表”。它至少要回答四个问题:
- 需要记住哪些信息?
- 哪些信息值得写入长期存储?
- 当前任务应该召回哪些历史信息?
- 历史经验如何被提炼、合并、遗忘?
如果这几个问题没有设计清楚,最常见的结果就是:数据库里堆满了对话流水,检索出来的内容噪音很大,Agent 反而被错误记忆干扰。
Agent 需要哪些记忆
Agent 的记忆可以分成四类:工作记忆、情景记忆、语义记忆、程序记忆。它们的生命周期、存储方式和检索方式都不一样。
| 记忆类型 | 记什么 | 生命周期 | 常见存储位置 | 典型例子 |
|---|---|---|---|---|
| 工作记忆 | 当前任务上下文、推理中间状态、工具返回结果 | 短,通常随任务结束清理 | LLM 上下文窗口、会话缓存 | 当前用户正在申请退款,订单号是 123 |
| 情景记忆 | 过去发生过的具体事件 | 中长期 | 向量数据库、事件表 | 用户上周要求航班座位靠窗 |
| 语义记忆 | 从事件中抽象出的稳定知识或规则 | 长期 | 向量数据库、用户画像表、知识库 | 用户偏好简洁回答,不喜欢长篇解释 |
| 程序记忆 | 可复用的操作流程、技能、SOP | 长期 | Workflow 配置、规则引擎、代码模板 | 退款流程:校验订单 → 判断政策 → 计算金额 → 调接口 |
这四类记忆之间存在流转关系。当前任务中的重要信息先进入工作记忆,其中有保留价值的片段沉淀为情景记忆;多个情景经过总结,可以变成语义记忆;反复执行且稳定的操作模式,则可以固化成程序记忆。
flowchart LR
A[工作记忆<br/>当前上下文和任务状态] -->|筛选重要片段| B[情景记忆<br/>具体事件和经历]
B -->|反思与总结| C[语义记忆<br/>稳定偏好、规则、知识]
B -->|模式抽取| D[程序记忆<br/>流程、技能、SOP]
C -->|检索注入| A
D -->|任务执行时调用| A
这套分类很重要,因为不同记忆不能混在一起处理。用户刚刚输入的订单号适合放在工作记忆里;用户长期偏好的回答风格适合放在语义记忆里;某次 API 超时的处理记录属于情景记忆;退款步骤则应该进入程序记忆。
写入侧:不要全量记录,要做记忆筛选
记忆写入最容易犯的错误是把每轮对话原封不动写入数据库。短期看实现很快,长期会带来三个问题:
- 存储膨胀很快,历史数据越来越难管理;
- 相似或重复记忆太多,检索时噪音变大;
- 重要信息被大量无意义寒暄、确认语句、临时状态淹没。
更合理的写入流程是:感知、判断、提炼、冲突检测、存储。
flowchart LR
A[输入内容<br/>用户消息/工具结果/推理状态] --> B{是否包含新信息}
B -->|否| X[不写入]
B -->|是| C[提炼为记忆条目]
C --> D[打标签<br/>类型、重要性、置信度、来源]
D --> E{是否与已有记忆冲突}
E -->|冲突| F[合并、覆盖或等待确认]
E -->|不冲突| G[写入对应存储]
F --> G
判断是否值得写入
不是所有信息都值得进入记忆库。可以从几个维度判断:
| 判断维度 | 说明 | 示例 |
|---|---|---|
| 新颖性 | 是否提供了此前没有的信息 | 用户说自己以后都希望用英文回复 |
| 稳定性 | 是否可能在未来继续成立 | 用户长期偏好、账号类型、常用地区 |
| 任务价值 | 是否会影响后续决策 | 用户是 VIP 客户,需要走专属流程 |
| 可验证性 | 信息来源是否可信 | 用户主动确认过的信息比模型推断更可信 |
| 风险等级 | 错误记忆是否会造成严重后果 | 医疗、金融、法务场景需要谨慎写入 |
一个简单规则是:临时变量放入工作记忆,可能复用的事实写入长期记忆,未经确认的推断要标注低置信度。
记忆条目的结构
长期记忆最好不要只存一段自然语言。为了后续检索、过滤、审计和删除,记忆条目应该有结构化字段。
{
"id": "mem_001",
"tenant_id": "tenant_a",
"user_id": "user_123",
"type": "semantic",
"content": "用户偏好简洁回答,通常不需要过多背景解释。",
"source": "conversation",
"source_refs": ["msg_1001", "msg_1002"],
"importance": 0.82,
"confidence": "medium",
"created_at": "2026-06-07T10:00:00+08:00",
"last_accessed_at": "2026-06-07T10:00:00+08:00",
"expires_at": null,
"metadata": {
"topic": "response_style",
"scope": "user_profile"
}
}
几个字段尤其关键:
type:区分情景记忆、语义记忆、程序记忆,避免检索时混乱;importance:表示记忆本身的重要程度,后续会参与召回排序;confidence:表示可信度,区分用户确认、系统观测、模型推断;source_refs:保留来源,方便追溯、纠错和删除;expires_at:适合存放会过期的信息,例如临时活动、短期状态。
检索侧:不能只靠向量相似度
记忆写入解决“记什么”,检索解决“想起什么”。很多实现会直接用 Embedding(向量嵌入)做相似度检索:把当前问题转成向量,到向量数据库里找最近的几条记忆。
这种方式能解决一部分问题,但不够。因为“语义相似”不等于“当前最该想起”。
例如:
- 用户刚刚说“这次不要开发票”,这条信息虽然很短,但当前任务里非常重要;
- “用户是 VIP 客户”可能和当前 query 字面不相似,却会影响客服策略;
- 一年前的偏好可能已经过时,不能和昨天确认过的信息同等对待。
更稳妥的做法是综合三个分数:时近性、相关性、重要性。
final_score = α × recency + β × relevance + γ × importance
| 评分维度 | 含义 | 常见实现 |
|---|---|---|
| Recency 时近性 | 越新的记忆越容易被召回 | 基于时间差做指数衰减 |
| Relevance 相关性 | 和当前任务语义越接近,分数越高 | Embedding 余弦相似度 |
| Importance 重要性 | 记忆本身越关键,分数越高 | 写入时由规则或 LLM 打分,后续可按访问次数调整 |
时近性可以用指数衰减函数计算:
recency = exp(-λ × age_in_hours)
λ 越大,旧记忆衰减越快。客服对话通常更看重近期上下文,可以提高 recency 权重;知识问答更看重语义匹配,可以提高 relevance 权重;高价值用户、合规规则、关键业务状态则需要较高的 importance 权重。
二阶段检索更适合工程落地
在长期记忆数量较大时,直接全库精排成本很高。可以采用和 RAG(检索增强生成)类似的“粗召回 + 精排”模式。
flowchart LR
A[当前任务 Query] --> B[元数据预过滤<br/>user_id/type/time_range]
B --> C[向量粗召回<br/>Top 50]
C --> D[综合打分<br/>recency/relevance/importance]
D --> E[Cross-Encoder 精排<br/>Top 5 或 Top 10]
E --> F[注入 LLM 上下文]
元数据预过滤非常有用。比如只检索当前用户的记忆,只检索最近 90 天内的情景记忆,或者只检索某个业务域下的程序记忆。这样可以减少向量库搜索范围,也能降低跨用户、跨业务误召回的风险。
一个简化版检索伪代码如下:
from math import exp
from datetime import datetime, timezone
def recency_score(created_at: datetime, now: datetime, decay: float = 0.01) -> float:
age_hours = (now - created_at).total_seconds() / 3600
return exp(-decay * age_hours)
def confidence_score(confidence: str) -> float:
mapping = {
"low": 0.5,
"medium": 0.8,
"high": 1.0
}
return mapping.get(confidence, 0.6)
def rank_memories(query_embedding, candidates, now):
ranked = []
for memory in candidates:
relevance = cosine_similarity(query_embedding, memory.embedding)
recency = recency_score(memory.created_at, now)
importance = memory.importance
confidence = confidence_score(memory.confidence)
score = (
0.25 * recency +
0.45 * relevance +
0.25 * importance +
0.05 * confidence
)
ranked.append((score, memory))
ranked.sort(key=lambda item: item[0], reverse=True)
return [memory for score, memory in ranked[:10]]
权重不要一开始就追求完美。更现实的做法是先根据业务直觉给一组默认值,再通过线上反馈、人工评估集和 A/B 实验调整。
反思:让事件记录变成稳定认知
只存情景记忆,Agent 会知道“发生过什么”;加入反思机制,Agent 才有机会总结“这些事情说明什么”。
假设客服 Agent 处理了 50 个退货请求,其中 30 个都和“商品描述不一致”有关。如果系统只保留 50 条情景记忆,那么下次遇到退货问题时还要在大量案例里检索。更好的做法是定期从这些事件中提炼出语义记忆:
近期退货请求的主要原因是商品描述和实物不一致。处理退货时应优先询问用户是否遇到描述不符问题,并检查商品详情页信息。
这条语义记忆比单个事件更稳定,也更容易在后续任务中发挥作用。
反思流程可以这样设计:
flowchart LR
A[新增情景记忆] --> B[累计重要性分数]
B --> C{是否超过反思阈值}
C -->|否| D[继续积累]
C -->|是| E[选择最近 N 条高价值记忆]
E --> F[LLM 总结规律和洞察]
F --> G[写入语义记忆]
G --> H[更新重要性和来源引用]
触发反思有几种方式:
| 触发方式 | 适合场景 | 说明 |
|---|---|---|
| 时间触发 | 日报、周报、定期用户画像更新 | 每天或每周总结近期记忆 |
| 阈值触发 | 高频交互 Agent | 重要性累计超过阈值后触发 |
| 事件触发 | 关键任务完成后 | 投诉结束、订单完成、故障恢复后总结 |
| 人工触发 | 高风险业务 | 需要运营或专家确认总结结果 |
反思生成的记忆也要保留来源引用。否则一旦总结错了,很难追溯它来自哪些事件。
合并、去重与遗忘
记忆系统如果只写不删,最终会变成历史垃圾场。长期可用的记忆模块必须具备三种整理能力:合并、去重、遗忘。
合并相似记忆
同一件事可能被多次记录:
- 用户喜欢 Python;
- 用户平时主要写 Python;
- 用户希望代码示例优先使用 Python。
这些记忆不一定完全重复,但可以合并成更稳定的语义记忆:
用户偏好 Python,代码示例优先使用 Python。
合并时不要简单删除原始记录,最好保留来源引用。这样既能减少检索噪音,又能在需要时追溯细节。
处理冲突记忆
用户偏好会变化,业务状态也会变化。比如早期记录是“用户使用旧版 API”,后来系统迁移完成,新记忆变成“用户已切换到新版 API”。这时不能让两条记忆同时以高权重存在。
冲突处理可以分成几类:
| 冲突类型 | 处理方式 |
|---|---|
| 新旧状态冲突 | 新状态覆盖旧状态,旧记忆归档 |
| 用户明确纠正 | 用户确认的信息优先级最高 |
| 模型推断冲突 | 降低置信度,必要时请求确认 |
| 业务规则变更 | 批量失效旧规则,写入新规则版本 |
主动遗忘
遗忘不是简单删除,而是降低不再有用的信息对当前决策的影响。常见方式有三种:
- 时间衰减:长期未访问的记忆降低召回分数;
- 访问增强:经常被使用且反馈良好的记忆提高重要性;
- 过期归档:带有时效的信息到期后不再参与默认检索。
可以给每条记忆维护 last_accessed_at 和 access_count,定期计算活跃度。活跃度低、重要性低、过期时间已到的记忆进入归档表或冷存储。
三层落地架构
工程实现上,可以把 Agent 记忆系统拆成三层:L1 工作记忆、L2 近期记忆、L3 长期记忆。
flowchart TB
U[用户请求] --> A[Agent Orchestrator]
subgraph L1[L1 工作记忆层]
C[LLM Context Window]
S[task_state JSON]
B[最近若干轮 Buffer]
M[历史摘要 Summary]
end
subgraph L2[L2 近期记忆层]
R[(Redis)]
RH[当前会话完整历史]
RM[近 7 天高频记忆]
end
subgraph L3[L3 长期记忆层]
V[(向量数据库<br/>Milvus / Chroma)]
P[(PostgreSQL<br/>结构化画像和业务数据)]
G[(Neo4j<br/>实体关系,可选)]
end
A --> C
A --> S
A --> B
A --> M
C -->|重要片段沉淀| R
R -->|高价值记忆持久化| V
R -->|结构化字段写入| P
A -->|按需检索| V
A -->|精确查询| P
A -->|关系查询| G
V -->|相关记忆注入| C
P -->|用户画像注入| C
G -->|关系背景注入| C
L1:工作记忆层
L1 主要依赖大模型(LLM,Large Language Model)的上下文窗口。它负责承载当前任务必须马上使用的信息。
常见策略是“最近对话 Buffer + 历史摘要 + 结构化任务状态”:
{
"task_state": {
"intent": "refund_request",
"order_id": "ORDER_123",
"refund_reason": "description_mismatch",
"current_step": "checking_policy"
},
"recent_messages": [
"用户:我想退这个订单",
"助手:请提供订单号",
"用户:ORDER_123"
],
"conversation_summary": "用户正在申请退款,原因可能与商品描述不一致有关。"
}
这种结构比单纯堆聊天记录更稳定。LLM 可以快速知道当前任务走到哪里,也能减少上下文窗口浪费。
L2:近期记忆层
L2 适合放 Redis 这类内存存储。它承载当前会话完整历史、近期高频记忆、短期状态和临时缓存。
Redis 的 sorted set 适合按时间管理近期记忆:
ZADD user:123:recent_memories 1780812000 mem_001
ZADD user:123:recent_memories 1780815600 mem_002
ZRANGEBYSCORE user:123:recent_memories 1780207200 1780815600
L2 的作用不是替代长期记忆,而是在上下文窗口不够时提供快速补充。比如用户在同一会话里十分钟前说过的信息,没必要每次都去向量数据库查。
L3:长期记忆层
L3 是真正的持久化记忆层,通常不是单一数据库能解决的。
| 存储组件 | 适合存放 | 检索方式 |
|---|---|---|
| 向量数据库 | 情景记忆、语义记忆、非结构化文本 | 相似度检索、Top-K 召回 |
| PostgreSQL | 用户画像、业务状态、权限、订单等结构化数据 | SQL 精确查询 |
| Neo4j | 用户、商品、组织、事件之间的复杂关系 | 图查询 |
| 对象存储 | 原始对话归档、日志、附件 | 按引用读取 |
不要把所有东西都塞进向量数据库。用户 ID、订单状态、会员等级这类强结构化数据,更适合关系型数据库。向量库擅长找“语义上相近”的内容,不擅长做严格一致的业务查询。
关键工程取舍
记忆粒度:太细会吵,太粗会丢细节
逐句存储会让记忆库迅速膨胀,检索时召回大量重复内容;只存高度总结又会丢掉关键限定条件。比较稳妥的做法是双层粒度:
| 粒度 | 用途 | 示例 |
|---|---|---|
| 摘要记忆 | 日常检索和上下文注入 | 用户偏好 Python 示例 |
| 原始归档 | 审计、纠错、细节追溯 | 对话原始消息、工具调用日志 |
默认检索摘要记忆,需要证据时再读取原始归档。
个性化与隐私:记忆越强,合规要求越高
Agent 记忆天然涉及用户数据。面向普通用户的产品必须提供查看、修改、删除记忆的能力,并做好租户隔离、访问控制和加密存储。
GDPR(通用数据保护条例)里的“被遗忘权”在 Agent 记忆系统里非常具体:用户要求删除个人记忆时,不仅要删关系型数据库里的画像,还要处理向量数据库、缓存、归档和备份里的相关数据。
可信度:LLM 总结出来的记忆不一定可靠
LLM 可能误解用户意图,也可能在摘要时遗漏限定条件。高风险场景不能无条件相信自动提炼的记忆。
可以按照来源给记忆分置信度:
| 来源 | 置信度建议 | 使用方式 |
|---|---|---|
| 用户明确确认 | 高 | 可直接影响决策 |
| 系统工具返回 | 高 | 适合写入业务事实 |
| 多次行为观测 | 中 | 可作为偏好参考 |
| LLM 单次推断 | 低 | 检索时降权,必要时请求确认 |
医疗、金融、法律等场景中,低置信度记忆不能直接驱动关键决策,只能作为辅助上下文。
一套可执行的设计清单
设计 Agent 记忆模块时,可以按这张清单推进:
| 设计问题 | 推荐做法 |
|---|---|
| 记什么 | 按工作记忆、情景记忆、语义记忆、程序记忆分类 |
| 怎么写 | 走“判断价值 → 提炼结构 → 冲突检测 → 存储”流程 |
| 怎么存 | L1 用上下文窗口,L2 用 Redis,L3 用向量库 + 关系库 |
| 怎么检索 | 结合时近性、相关性、重要性,不只看向量相似度 |
| 怎么总结 | 定期或按阈值触发反思,把情景记忆提炼成语义记忆 |
| 怎么控噪 | 做合并、去重、过期、归档和主动遗忘 |
| 怎么保证可信 | 记录来源、置信度、用户确认状态和审计引用 |
| 怎么保护隐私 | 支持用户查看、修改、删除记忆,做好隔离和加密 |
一个可用的 Agent 记忆模块,不是“存得越多越好”,而是能在合适的时间想起合适的信息。写入侧要克制,检索侧要综合排序,长期运行还要有反思、合并和遗忘机制。只有这样,Agent 才能从单轮任务执行器,逐步变成能延续上下文、理解用户偏好、复用历史经验的系统。