【AI Agent面试系列】AI Agent记忆机制知识点1

本文主要介绍Agent记忆 以及 如何设计Agent记忆系统。

1、Agent 的记忆和 LLM 的上下文窗口是什么关系?

这两个不是同一层的东西,上下文窗口是模型推理时的工作内存 ,Agent 记忆是模型之外的持久化状态。记忆的本质是在窗口有限的前提下,做信息的选择性进出。

上下文窗口有硬上限,不可能把全部历史塞进去,所以必须做取舍。记忆系统本质上就回答三个问题:什么值得写出去、下次怎么找回来、找回来怎么塞进上下文里 ------也就是写入、检索、注入。

2、现在上下文窗口越来越大(比如1M),还需要记忆吗?

需要,而且窗口越大越需要。记忆系统主要解决的是跨会话信息传递的问题。

  • 跨会话:窗口再大,也只覆盖本次请求。用户上周说的偏好、上个月定的方案,物理上就不在窗口里。这不是成本问题,是信息根本不存在的问题。

3、 Agent 的记忆分哪几类?各自怎么实现?

  • 按持久性分(工程视角)
记忆类型 存什么 实现方式
短期记忆(工作记忆) 当前任务的对话上下文 直接放 prompt / 上下文窗口,受窗口大小限制
长期记忆 跨会话持久化信息 向量数据库(语义检索)+ 结构化存储(KV 事实表)
  • 按内容分(CoALA/认知科学视角)
记忆类型 存什么 实现方式
工作记忆 当前任务的对话上下文 直接放 prompt / 上下文窗口,受窗口大小限制
情景记忆(Episodic) 具体经历/事件("上次做过 X") 会话日志 + 向量化存储,按相似度召回
语义记忆(Semantic) 抽象知识/事实("用户偏好 Python") 结构化 KV 存储(如 user_facts),精确匹配或检索
程序性记忆(Procedural) 技能/操作套路 沉淀为可复用的技能/脚本/模板,按场景触发加载

"长期记忆"是伞概念,情景/语义/程序性是长期记忆之下的三种内容类型,三类的区别在"抽象层级":

  • 情景记忆:存"事件"------带时间、带上下文的具体经历。例:"2024-05-01 用户让我写了一篇 Agent 面试文章"。
  • 语义记忆:存"事实"------从经历中抽象出的稳定知识。例:"用户偏好中文回复"。它往往是从多条情景记忆中提炼出来的。
  • 程序性记忆:存"怎么做"------成功操作的模式。例:"写技术文章时先列大纲再逐章展开"。它是从成功轨迹中沉淀的技能。

抽象层级递进:事件 → 事实 → 技能,对应"经历 → 知识 → 能力"。

记忆分类来源知识点:

认知科学概念 原本含义 移植到 Agent
工作记忆 人脑当前正在处理的信息 上下文窗口
情景记忆 对具体经历的自传式记忆 过往交互记录
语义记忆 抽象事实与概念知识 结构化知识/事实
程序性记忆 技能(如骑自行车,非陈述性) 沉淀的操作套路

4、如何设计记忆系统?

记忆系统本质上就回答三个问题:写入、检索、注入,系统设计也是基于这三个问题展开。完整架构图如下:

text 复制代码
┌─────────────────────────────────────────────────┐
│  写入侧(异步)                                   │
│  对话 → 门控 → 抽取(带key) → 冲突消解 → 落库        │
└─────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────┐
│  存储层                                          │
│  结构化KV(偏好/配置) + 向量库(语义) + 图库(关系) │
└─────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────┐
│  读取侧                                          │
│  query改写 → 混合召回 → 重排+时间衰减 → top-k       │
└─────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────┐
│  注入侧                                          │
│  预算分配 → 位置策略 → 格式化标注 → 拼入prompt       │
└─────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────┐
│  维护侧(离线)                                    │
│  固化 / 去重 / 衰减 / 纠错 / 评测                  │
└─────────────────────────────────────────────────┘

下面对这些问题进行具体说明:

4.1、写入

原则 :宁可漏记,不可滥记;写入要调 LLM 抽取,一般都采用异步方式。

记忆系统的失败模式几乎都是"记太多导致检索不准",而不是"记太少"。异步写入记忆可能出现"用户刚说完就追问,记忆可能还没写完"的情形,但这些信息仍在本轮对话的原文中,所以影响有限。

需要解决的问题:

  • 问题1 :什么信息需要写入

    并不是所有信息都需要写入到记忆系统中,而是有选择性的写入。三类高价值信号:

    • 显式声明 :"记住..."、"以后都..."、"我的 X 是 Y"

    • 纠正信息 :"不对,是..."(必须写,且要覆盖旧值)

    • 重复模式 :多次提到同一偏好

    实现:规则(关键词/正则)打底 + LLM 打分做门控,设阈值。

  • 问题2 :怎么存储

    原则 :必须有 key,否则无法覆盖、无法去重、无法治理。

    在写入之前,必须先调 LLM 把对话抽成结构化条目(抽取),示例如下:

    JSON 复制代码
    {
      "id": "m_002",                          // 记忆条目唯一标识,创建时生成、永不变;被 superseded_by 引用,作为外部引用锚点
      "key": "project.tech_stack",            // 槽位标识,取自受控词表;与 scope 共同定位"跟谁比",决定覆盖对象
      "scope": "prod",                        // 作用域(prod/test/user/项目名);与 key 构成唯一约束,跨 scope 不构成冲突
      "value": "Go",                          // 归一化后的值(如 "golang" → "Go");覆盖判断的比较对象,检索时返回的内容
      "type": "preference",                   // 信息类别,决定覆盖策略(preference/decision/config/event/fact_mutable/fact_immutable),隐含 mutable
      "source": {                             // 来源追溯信息,用于校验、回溯、解释置信度
        "session_id": "session_42",           // 来源会话,定位上下文
        "turn_id": "t_17",                    // 来源轮次,精确定位到具体一句话
        "quote": "我们后端统一用 Go",          // 用户原话片段,保真度锚点:value 被归一化时,只有原话能证明归一化没出错
        "source_type": "explicit"             // 来源类型 explicit | inferred | corrected;决定 confidence 是否有意义,explicit 时 confidence 恒为 1.0
      },
      "confidence": 1.0,                      // 置信度 0~1,仅 inferred 时有意义(表示模型对本次抽取的确信度,非"信息可信度");低于阈值不写入或降级为候选
      "created_at": "2025-03-01T10:00:00",    // 首次写入时间;用于时效性排序、判断"这条记了多久"
      "last_confirmed_at": "2025-03-20T14:00:00", // 最近一次被确认/重复提及的时间;用于时效性排序、判断是否陈旧
      "confirm_count": 3,                     // 被确认的次数(含首次);用于重复模式检测、置信度加固
      "expires_at": null,                     // 到期时间,null 表示长期有效;语义是"到期触发动作(提醒/复核)"而非"到期自动删除"
      "superseded_by": null,                  // 被哪条新记录取代(新记录 id),null 表示当前活跃;沿链可回溯变更历史
      "superseded_at": null                   // 被取代的时间,null 表示未被取代;与 superseded_by 成对出现,用于回答"什么时候改的"
    }

    在写入之前,还有非常重要的工作:冲突消解,绝不允许两条矛盾事实同时存在于活跃记忆里。

    txt 复制代码
    	相似检索 → 命中同 key → 判断类型
    	├─ type对应mutable=true  → update(覆盖旧值,保留 updated_at)
    	└─ type对应mutable=false → insert 新版本(时序查询时取最新)

4.2、检索

并不是直接拿用户问题直接来检索,在检索之前必须进行Query 改写。这是因为用户查询和记忆表述有语义鸿沟,需要处理以下问题:

  • 指代消解:"我上次说的那个方案" → 结合会话历史还原
  • 时间归一:"上个月" → 绝对时间范围
  • 意图扩展:"帮我安排会议" → 召回时区、偏好时段、常用参会人

Query改写不要只输出一个字符串,而是输出结构化检索条件,让检索层可以分路执行。示例如下:

txt 复制代码
{
  "original": "那个方案怎么样了",
  "rewritten": "A 方案 进展",
  "filters": {
    "key": ["project.migration_plan"],
    "scope": "prod",
    "type": ["decision", "event"],
    "time_range": "last_30_days"
  },
  "fallback_query": "方案 进展"     // 改写失败时的降级检索式
}

Query改写完成后,下一步就需要去记忆系统检索。在具体检索方案中,通常会采用混合检索:

方式 擅长 短板
向量 语义相似 精确标识符差
BM25 精确匹配、专有名词 同义表达失效
结构化过滤 user_id、时间、类型 无语义能力

多路召回后,通常采用RRF融合选择top-50,然后用 cross-encoder 或 LLM 做重排重排选 top-5。同时,可叠加时间衰减:旧偏好降权,避免三个月前的结论压过上周的。

注意:衰减只对偏好类适用,事实类("我住北京")不该衰减。

检索流程如下:

txt 复制代码
用户 query: "我们后端用的啥语言"
    ↓
1. Query 改写 → 补全指代、对齐槽位
    ↓
2. 双路检索(并行)
   ├─ 精确路:按 key="project.tech_stack" 查主库
   └─ 语义路:embed(query) → 向量检索 top_k=10
    ↓
3. 合并去重(按 id)
    ↓
4. 过滤:superseded_by=null、scope 匹配、未过期
    ↓
5. 重排:精确命中 > 语义相似度 > 时效性
    ↓
6. 取 top_n 注入上下文

检索常见坑:

  • 只做向量检索,精确查询失效
  • top-k 设太大,噪声进窗口污染上下文
  • 缺时间衰减,新旧信息权重相同
  • 检索同步阻塞主链路,延迟高(应做预取/缓存)

4.3、注入

要解决的问题 :检索结果可能很多,窗口预算有限,且位置影响效果。注入的本质是在预算内做排序与格式化 。

在检索完成后,需要将检索到的记忆注入到上下文中,这个时候需要考虑:预算、位置和格式化。

  • 预算:给记忆分配固定 token 预算(窗口的 10--20%),超出截断。固定比例,不动态挤占------保证行为可预测。

  • 位置 :利用 lost-in-the-middle:

    • 关键约束 → 放开头(紧跟 system)
    • 次重要 → 放结尾(紧邻 query)
    • 背景信息 → 放中段,且压缩
  • 格式化 :不要直接拼原文,要标注来源和时间,比如:

    txt 复制代码
    [用户偏好 · 2025-03 确认] 技术栈:Go
    [历史事件 · 2025-01] 曾使用 Python,已迁移

    标注来源和时间,模型才能判断可信度和时效性;不标注,模型会把推测和事实同等对待。

注入侧常见坑:

  • 无预算控制,检索多少塞多少 → 挤掉任务本身
  • 关键信息放中段 → 被忽略
  • 不标注来源 → 模型无法区分"用户明说"和"模型推测"
  • 每次全量重拼 → 破坏 prompt 缓存,成本上升

4.4、离线维护

记忆系统上线后,还是需要定期维护的,以避免记忆膨胀、腐化等。

维护机制:

机制 作用 时机
固化 N 条情景记忆 → 1 条语义记忆 离线、定期
去重合并 相似条目合并 写入时 + 定期
衰减归档 长期未命中的低权重条目降权 定期
纠错 用户说"记错了" → 定位条目删除 实时

固化是控制规模增长的核心手段------10 次"用中文"归纳成 1 条"偏好中文"。

当然,记忆系统的评测也是必不可少的,不然无法知道记忆系统好不好。

分环节评测指标:

环节 指标
写入 该记的记了吗?不该记的记了吗?
检索 召回率、精确率
端到端 任务成功率
风险 错误记忆被引用率

评测集构造:多会话长程对话 + 需要跨会话引用的事实点,做 needle-in-haystack 式检验。

没有评测闭环的记忆系统,等于没有记忆系统------你无法知道改动是变好还是变坏。

相关推荐
2601_965448872 小时前
AI视频制作:做系列历史人物视频音色不搭,AI 音色克隆的方法实测
人工智能·音视频
2分钟速写快排3 小时前
ggg! 潜行摸鱼被 Trae 爆改成 3D!
人工智能·程序员·数据可视化
“AI国潮设计-小江”3 小时前
【AIGC实战】Python+Stable Diffusion打造“英歌舞财神”IP,解锁“马到成功”等爆款情绪营销文案设计
开发语言·人工智能·python·prompt·aigc
明志数科3 小时前
实景数据与训练场数据的分布差异:机器人操作数据的分层采集与工程边界
人工智能
zhippt_10143 小时前
汇报总结PPT怎么做更快?5款AI生成PPT工具实测横评
人工智能·powerpoint
寒墨默涵3 小时前
Trigger.Dev Webhooks 驱动 AI 智能体实战效果展示
人工智能
manyingAi3 小时前
AI漫剧生产系统架构解析:从文本拆解到音视频合成
人工智能·音视频
长不大的杨瑞杰3 小时前
OpenCode v2 会话可靠性实战:Snapshots、Compaction、Instructions 与 Formatters
人工智能·安全·云计算·软件工程·devops