芥末
发布于 2026-03-30 / 0 阅读
0
0

Agent 记忆模块设计:从记忆分类到写入、检索与反思

一个 Agent 如果只依赖当前请求里的上下文,它更像一次性脚本:能完成单次任务,但无法延续用户偏好,也无法从历史错误中吸取经验。真实业务里的 Agent 往往需要面对多轮交互、长期用户画像、工具调用历史、业务规则沉淀和流程复用,这些能力都离不开记忆模块。

记忆模块不是简单的“聊天记录表”。它至少要回答四个问题:

  1. 需要记住哪些信息?
  2. 哪些信息值得写入长期存储?
  3. 当前任务应该召回哪些历史信息?
  4. 历史经验如何被提炼、合并、遗忘?

如果这几个问题没有设计清楚,最常见的结果就是:数据库里堆满了对话流水,检索出来的内容噪音很大,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”。这时不能让两条记忆同时以高权重存在。

冲突处理可以分成几类:

冲突类型处理方式
新旧状态冲突新状态覆盖旧状态,旧记忆归档
用户明确纠正用户确认的信息优先级最高
模型推断冲突降低置信度,必要时请求确认
业务规则变更批量失效旧规则,写入新规则版本

主动遗忘

遗忘不是简单删除,而是降低不再有用的信息对当前决策的影响。常见方式有三种:

  1. 时间衰减:长期未访问的记忆降低召回分数;
  2. 访问增强:经常被使用且反馈良好的记忆提高重要性;
  3. 过期归档:带有时效的信息到期后不再参与默认检索。

可以给每条记忆维护 last_accessed_ataccess_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 才能从单轮任务执行器,逐步变成能延续上下文、理解用户偏好、复用历史经验的系统。


评论