news 2026/9/7 20:00:33

拆解 Agent Memory:从认知心理学映射到工业级工程落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
拆解 Agent Memory:从认知心理学映射到工业级工程落地

前言

大多数 Agent Memory 设计的误区,是过早堆砌数据库、消息队列、向量引擎等中间件,从而混淆核心业务逻辑与工程优化组件。Agent Memory 的核心不是简单的一读一写接口,而是Memory Service 的 Recall/Write 两大主业务入口;真正的工程复杂度在 Write 侧内部的记忆提取、验证、去重、冲突解决与生命周期治理。所有中间件只为让这两个入口更快、更准、更可靠、更易扩展。本文围绕核心本质、数据模型、生命周期、召回架构与落地路径展开。

一、Agent Memory 核心本质

1.1 核心运行流程

一次标准 Agent 请求,记忆模块只参与三个核心步骤:

用户请求 → ① Recall 读取记忆 → LLM/工具执行任务 → ② 完成任务 → ③ Write 写入/更新记忆 → 响应用户

1.2 两大主业务入口,不是全部能力

  • Recall(读取记忆):检索相关历史记忆,为当前任务提供上下文依据。
  • Write(写入记忆):筛选本次对话有效信息,并完成候选记忆的生成与写入。

Write 不是单次POST /memory,它内部是一条完整闭环:

Memory ├── Recall ← 主业务入口 └── Write ← 主业务入口 ├── Extract ├── Validate ├── Resolve ├── Dedup ├── Conflict └── Store

因此不能把 Memory 理解成GET /memory+POST /memory就完成了。PostgreSQL、Redis、Milvus、OpenSearch、Kafka、Embedding、Reranker 等组件,最终都服务于这两个入口及其内部能力。

1.3 Memory Service 四大能力

  1. Recall(检索):精准匹配、召回相关历史记忆,优先级最高。
  2. Write(写入):生成候选记忆,并完成提取、验证、去重、冲突解决与持久化。
  3. Manage(管理):更新、删除、冲突修复、过期清理。
  4. 运维管控:版本管理、权限控制、操作审计。

二、各中间件核心职责

各组件各司其职,但不应以单一检索能力机械划分组件边界:PostgreSQL 作为唯一事实源负责记忆持久化与版本治理;Milvus 提供 Dense、Sparse 及 Hybrid 检索能力,承担主要语义与混合召回;Redis 负责结构化热点数据缓存;OpenSearch 作为可选的专业全文检索组件,在复杂关键词、全文分析及高级搜索需求出现时补强;Kafka 负责写入后的异步任务与索引构建解耦。

组件职责核心价值落地阶段
PostgreSQL唯一事实源;持久化 Memory、版本、关系、权限、审计;返回 Active Version Data保证记忆数据一致性与可治理性V1 必备
MilvusDense Vector / Sparse Vector 索引;语义、关键词及 Hybrid Search;保存 Memory ID 与检索字段统一承担主要语义/混合召回,避免重复引入搜索组件V2
Redis结构化热点 Memory、Preference、最近/高频访问数据缓存降低 PG 访问压力,提升高频精确查询速度V2/V5
OpenSearch可选的专业全文检索;复杂关键词、短语、模糊、多字段、过滤、聚合等当 Milvus 的 Sparse/Hybrid Search 无法满足复杂搜索需求时补强按需 / V3+
Reranker对 Dense/Sparse/Keyword 等召回候选进行二次相关性判断与排序提高最终 Recall PrecisionV3
KafkaWrite 后台任务、索引构建、Embedding、异步治理等事件投递解耦主链路,削峰与异步处理V5
LangGraph管理当前 Agent 任务 State、流程和临时上下文;不负责长期 Memory 持久化区分任务状态与长期记忆始终

2.8 Memory 与 RAG 的边界

Memory 与 RAG 都向 Agent 提供上下文,但解决的问题不同:

Memory 解决“过去发生过什么,以及 Agent 应该长期记住什么”;RAG 解决“外部知识源中有什么,以及当前任务需要查什么”。

二者的核心区别不是“一个是用户数据、一个是知识数据”,而是信息的来源、生命周期和更新机制不同

维度Agent MemoryRAG
核心问题Agent 应该记住什么?外部知识库中有什么?
信息来源用户交互、历史任务、Agent 行为、验证经验、显式偏好文档、代码、手册、规范、数据库、网页等
信息性质个性化、历史性、经验性、关系性外部知识、事实性、参考性
典型内容用户偏好、项目环境、历史故障、解决经验、操作规则SDK 手册、API 文档、技术规范、项目文档
生命周期动态演化、版本化、冲突治理、过期/归档随知识源更新、重新索引、删除或版本更新
作用域User / Agent / Project 等Knowledge Base / Tenant / Document 等
主要召回方式结构化 + 语义 + 关键词 + Scope/状态过滤关键词 + 向量 + Metadata Filter + Reranker
最终作用让 Agent知道过去、了解协作对象、延续经验让 Agent获取当前任务所需的外部知识

例如:

Memory:

用户偏好:代码示例默认使用 C++20 Project Fact:当前项目部署在 RK3568 Episodic:上次排查 CUDA 首次 Run 约 788ms,最终通过 warmup 解决 Procedural:以后遇到 CUDA 首次 Run 慢,先检查是否包含 CUDA Context / cuDNN 初始化,并进行 warmup 验证

这些信息来自 Agent 与用户/项目过去的交互,并且需要长期治理,因此属于Memory

RAG:

RK3568 官方技术手册 ONNX Runtime CUDA EP 文档 OpenHarmony API 文档 C++ 标准文档 项目 SDK 手册 企业技术规范

这些信息来自外部知识源,Agent 在当前任务中按需检索,因此属于RAG

2.8.1 Memory 与 RAG 可以同时参与一次请求

二者不是互斥关系,而是可以共同构建 Agent Context:

User Query │ ┌──────────┴──────────┐ ↓ ↓ Memory Recall RAG Recall │ │ │ │ 用户/项目/历史/经验 文档/代码/规范/知识 │ │ └──────────┬──────────┘ ↓ Context Builder ↓ LLM

例如用户问:> “RK3568 上 ONNX Runtime CUDA 首次 Run 为什么慢?”

Memory 可以召回:

Episodic:之前曾遇到首次 Run 约 788ms 的问题 Procedural:先进行 warmup,再比较后续 Run latency

RAG 可以召回:

ONNX Runtime CUDA EP 文档 CUDA Context 初始化相关资料 cuDNN 初始化相关资料

最终 Agent 将:

Memory = “我以前怎么处理过” RAG = “外部资料怎么说明”

两者结合后才能形成更完整的回答。

2.8.2 一个重要边界:Memory 不应该替代 RAG

不能因为某次对话中 Agent 看过一份官方文档,就把整个文档内容复制成 Memory。更合理的是:

RAG:官方文档原始知识 ↓ Agent 使用 ↓ 如果产生稳定、可复用的个人/项目经验 ↓ Memory:“以后遇到类似问题应该怎么处理”

因此:

RAG 保存和检索外部知识,Memory 保存 Agent 在长期交互过程中形成的个性化事实、历史经验、偏好与可复用方法。

两者可以互相产生信息,但数据所有权和生命周期必须保持独立

三、长期记忆四类模型(当前 V1 核心模型)

当前平台第一版采用四类长期记忆模型。这四类不是随意分类,也不是为了凑数,而是认知科学理论在 AI 工程落地中的必然映射,直接回答 Agent 长期记忆中最核心的四个问题:

Semantic:世界/实体/项目/用户是什么?(事实与关系)
Preference:用户希望 Agent 怎么行为?(偏好与约束)
Episodic:过去具体发生过什么?(历史上下文与事件)
Procedural:以后遇到类似问题该怎么做?(方法论与规则)

四类的核心价值在于:数据结构、更新方式、检索方式、生命周期都不同。

3.0 为什么采用四类长期 Memory:理论参考与工程划分

当前平台采用Semantic、Preference、Episodic、Procedural四类长期 Memory。其中,Semantic 与 Episodic 属于 Tulving 多重记忆系统理论中的陈述性(外显)记忆;Procedural 属于该框架下的非陈述性(内隐)记忆。Preference 并非传统认知心理学中与前三者并列的标准记忆类型,而是为表达用户/项目持续行为偏好而抽象出的工程模型。

因此,本系统的四类划分可以理解为:

认知科学提供理论参考,Agent 工程需求决定最终的数据模型。

四类 Memory 分别解决 Agent 长期协作中的四个核心问题:

Semantic “知道什么?是什么?有什么关系?” Preference “应该按照什么方式服务这个用户/项目?” Episodic “过去发生过什么?” Procedural “以后遇到类似问题应该怎么做?”

四类并不是互斥的数据分类。同一段交互可以同时产生多个 Memory Candidate,并通过memory_relations建立关联。

3.0.1 四类 Memory 的工程基因

四类 Memory 真正需要区分的,不是名字,而是:

数据结构、更新语义、召回条件、生命周期和治理方式不同。

记忆类型核心内容数据结构倾向更新策略核心召回方式典型实现
Semantic事实、实体属性、关系属性表 / 主谓宾 / 关系表Update / Versioning / AppendEntity / Attribute / Relation + SemanticPostgreSQL / Milvus
Preference用户/Agent/Project 的持续偏好与约束Key-Value + ScopeOverride / VersioningScope + Key 精确匹配PostgreSQL / Redis Cache
Episodic具体历史事件、任务经历、结果结构化 Episode + SummaryAppend / Versioning / ArchiveSemantic + Keyword + Metadata FilterPostgreSQL / Milvus
Procedural方法、规则、SOP、经验Rule / Procedure / Step / ConditionRefine / VersioningStructured + Semantic + Keyword / Rule MatchPostgreSQL / Milvus / Rule Engine

这里的“典型实现”只是工程选择,不是类型与数据库的一一绑定关系。例如 Preference 是一种 Memory 类型,而 Redis 只是它的缓存实现:

Preference ↓ PostgreSQL ↓ Redis Cache

而不是:

Preference = Redis

同样,Episodic 可以使用 Milvus 做语义召回,但其真实数据仍然应该由 PostgreSQL 管理。

3.0.2 为什么需要四类,而不是一种统一 Memory

如果所有信息都简单存成:

{"content":"...","embedding":[...]}

虽然可以快速实现一个向量 Memory,但很难表达不同信息的不同更新语义。例如:

Semantic:

project → deploy_to → RK3568

重点是当前事实是什么。

Preference:

coding.cpp_standard scope = user value = C++20

重点是作用域覆盖关系。

Episodic:

Episode: 问题:CUDA 首次 Run 很慢 行动:增加 warmup 结果:后续 Run 恢复正常

重点是过去发生了什么。

Procedural:

Procedure: 遇到 CUDA 首次 Run 慢 ↓ 检查是否包含 CUDA Context 初始化 ↓ 执行 warmup ↓ 比较 warmup 前后 latency

重点则是未来如何处理类似问题。

因此四类 Memory 的区别本质上是:

不同的知识形态,需要不同的状态模型和更新语义。

3.0.3 四类 Memory 的关系

四类 Memory 并不是一条严格的线性流水线,而是可以相互关联:

Semantic / \ ↓ ↓ Preference Episodic ↓ Procedural

例如一次实际排障可能同时产生:

Semantic 项目使用 ONNX Runtime 1.29 │ ├──────────────┐ ↓ ↓ Episodic Procedural 某次 CUDA 首次 类似问题优先 Run 约 788ms 进行 warmup

Preference 也可能参与其中:

Preference 用户偏好:性能问题优先给出可验证的 benchmark

这些 Memory 通过:

memory_relations

建立关联,而不是强制将一次交互归入唯一类型。

3.0.4 为什么 V1 选择这四类

四类不是理论上“唯一正确”的分类,而是当前 Agent Memory 系统的一个工程最小完备集。它覆盖了 Agent 长期协作中最常见的四种信息:

Semantic → 长期事实 Preference → 长期偏好 Episodic → 历史经历 Procedural → 可复用方法

相比只建立一个统一的Memory表,这种划分可以明确不同类型的:

数据结构 ↓ 更新方式 ↓ 召回方式 ↓ 冲突处理 ↓ 生命周期

因此,V1 采用四类并不是为了“凑四种 Memory”,而是为了让后续的数据模型和业务逻辑具有明确的语义边界。

同时,四类也不是最终形态。随着系统规模和业务复杂度增加,可以进一步拆分:

  • Entity Memory:当 Semantic 中实体、关系和图谱复杂度明显上升时独立出来。
  • Social Memory:需要建模多用户、多 Agent、组织关系和信任关系时引入。
  • Reflection Memory:需要专门记录 Agent 对失败任务、决策和行为进行反思时引入。

因此:

四类是当前 V1 的工程边界,而不是对人类记忆系统的完整模拟。

3.1 核心区别

类型回答的问题典型内容变化方式主要用途
Semantic知道什么、关系是什么?用户、项目、环境、实体事实与关系更新事实理解用户/项目/世界
Preference喜欢什么?C++20、中文、输出格式覆盖偏好个性化
Episodic发生过什么?某次故障排查、某次任务新增历史延续任务
Procedural应该怎么做?排查流程、显式方法、验证经验迭代规则复用能力

三句话看起来都是“记忆”,但数据库操作完全不同,而且它们并不互斥:

“我主要使用 C++20。”——事实/偏好
“昨天我们解决了 CUDA 首次 Run 788ms。”——历史事件
“以后遇到 CUDA 首次 Run 慢,先做 warmup。”——方法论### 3.2 为什么不能全部叫 Memory

三句话看起来都是“记忆”,但数据库操作完全不同,而且它们并不互斥:

“我主要使用 C++20。”——事实/偏好
“昨天我们解决了 CUDA 首次 Run 788ms。”——历史事件
“以后遇到 CUDA 首次 Run 慢,先做 warmup。”——方法论

3.3 Semantic(事实与关系记忆)

Semantic 保存的是相对稳定、可独立于具体对话存在的事实与关系,不局限于“用户画像”。典型范围包括:

User Fact:用户主要使用 C++ Project Fact:项目部署平台为 RK3568 Environment Fact:运行环境使用 CUDA 12 Entity Fact:模型输入特征为 Log-Mel Relationship Fact:项目 → 使用 → ONNX Runtime

数据天然适合主谓宾三元组或关系表:

{"subject":"project","predicate":"deploy_to","object":"RK3568"}

三个特点:

  1. 不依赖某次对话:记“项目目标平台是 RK3568”,而非“某天用户说过”。
  2. 可被后续信息修改:需要current value + version + history
  3. 检索由实体/属性/关系驱动:无需复杂向量检索,直接按project_id + predicate查询。

3.4 Preference(偏好记忆)

事实与偏好不是一回事:

Semantic: 用户使用 C++ → 客观事实 Preference: 示例都用 C++20 → 如何服务这个用户

Preference 本质是Configuration + Personalization

最大特点是Scope(作用域覆盖),由宽到窄:

Global → User → Agent → Project → Task

但必须注意:Task 层通常不是长期 Memory,而是任务期间的覆盖配置。例如“这次回答用英文”不应反向修改user.language = English。Task 级配置更适合进入 LangGraph State,并在任务结束时过期;长期 Memory 主要负责 Global/User/Agent/Project 层级。

数据模型天然适合key + value + scope

{"key":"coding.cpp_standard","value":"C++20","scope":"user"}

3.5 Episodic(场景记忆)

记录“某件事具体发生过什么”,不能只保存原始聊天记录,需要:

Conversation → Summary → Structured Episode

有价值的结构化字段应保留:

Goal / Problem / Context / Actions / Decision / Result / Outcome

因为 Agent 之后真正需要回答的是:“这个问题当时是怎么解决的?”而非“过去聊过很多关于 CUDA 的内容”。Episodic 也是 Procedural 的重要抽象来源之一,但二者不是先后强制关系。

3.6 Procedural(流程经验记忆)

Procedural 描述“以后怎么做”。它不要求必须“多次成功案例之后才产生”,来源包括:

用户显式提供的方法 历史 Episode 抽象 Agent/Tool 验证出的经验 外部规则/策略

不同来源应保留不同confidencesource,便于后续治理。

Procedural = 可迁移到未来任务的方法/规则

因此 Procedural 是“方法论/经验层”,让 Agent 具备可复用的经验能力。

3.7 四种 Memory 的关系是一条链,但不是互斥分类

四者层层递进:Semantic 提供事实底座,Preference 决定服务方式,Episodic 沉淀历史事件,Procedural 提炼可复用经验,共同构成完整长期记忆。

但必须强调:四类长期记忆之间不是互斥的“四选一”。同一段对话可能同时产生多种记忆,后续通过关联关系连接,而不是强制归入某一类。

3.8 从对话到记忆:Memory Extraction Pipeline

核心问题不是“四类记忆是什么”,而是:一段对话进来后,如何判断它属于哪类记忆。

关键结论:不要让 LLM 对整段对话做四选一;分类对象不是 Conversation,而是 Extractor 生成的每个 Memory Candidate。一段对话可以产生多个 Candidate,每个 Candidate 独立分类,允许多标签及关联。

One Conversation → Multiple Memory Candidates
正确的 Pipeline
用户对话 ↓ Conversation Normalizer ↓ Memory Extractor → 生成多个 Memory Candidate ↓ 每个 Candidate 独立分类(Semantic / Preference / Episodic / Procedural,可多标签) ↓ Validator → Dedup → Conflict → Persist

命中多个类别时,建立memory_relations关联关系,而不是强制四选一。

四个判断标准

Semantic = 相对稳定、跨任务仍然成立的事实或关系。
Preference = 对未来 Agent 行为具有持续影响的用户偏好/指令。
Episodic = 对未来交互可能有价值的历史事件/任务经历。
Procedural = 可迁移到未来任务的方法/规则;来源可为显式提供、事件抽象、验证经验或外部策略。

多标签分类策略
对每个 Memory Candidate,依次判断以下问题(可同时命中): 1. 是否描述相对稳定的事实或关系? → 标记 Semantic 2. 是否表达对未来 Agent 行为的持续偏好/指令? → 标记 Preference 3. 是否是有边界、有复用价值的历史事件? → 标记 Episodic 4. 是否是可迁移到未来任务的方法/规则? → 标记 Procedural 命中多个类别时,保留多标签并建立关联关系。

示例:

“上次发现 CUDA 首次 Run 788ms,加 warmup 恢复;以后遇到类似问题先 warmup。” → Episodic(历史事件) → Procedural(可迁移方法) 两个 Candidate 通过 memory_relations 关联。

3.9 Conversation 与 Long-term Memory 的边界

核心前提:

Conversation History ≠ Long-term Memory

三者必须分开:

  • Conversation记“原话”,是原始数据源。
  • Memory记“提炼后的长期认知”,需要动态治理:Candidate → Active → Updated → Superseded → Archived → Deleted
  • LangGraph State记“现在做到哪里”,也承载任务级临时配置,如本次输出的语言/格式覆盖。

因此 Memory Service 不应负责保存原始聊天,而应消费 Conversation/Event,再提炼长期记忆:数据模型拆成conversations / messagesmemories / memory_versions / memory_relations / memory_events。完整对话之所以重要,是因为它可以作为 Memory 的原始依据,在提取错误或模型升级后重新提取、验证、修正记忆

四、标准记忆生命周期与完整请求链路

4.1 全生命周期

Recall → 业务使用 → 生成 Memory Candidate → Extract / Validate / Resolve → Dedup → Conflict → 落地 PG(唯一事实源) → 构建索引(Milvus/OpenSearch) → 生命周期治理(更新/归档/过期)

4.2 单次完整 Agent 请求链路

用户请求 → Recall 多源召回 → 融合/过滤/排序 → 结合 RAG 构建 Prompt → LLM 执行任务 → 生成 Memory Candidate → Extract / Validate / Resolve → Dedup / Conflict → 落地 PG(Active Version)→ 同步更新索引

五、记忆召回精准架构

传统“Redis→Milvus→OpenSearch→PG”瀑布式降级链路并不严谨。更准确的定义是:索引负责“找到谁”,PostgreSQL 负责“这个 Memory 现在到底是什么”。其中 OpenSearch 为可选组件,仅在需要复杂全文检索、高级关键词查询、聚合分析或搜索分析时才引入。

Recall Request │ ┌─────┼─────┐ ▼ ▼ ▼ 结构化召回 语义召回 关键词召回(可选) (Redis/PG) (Milvus) (OpenSearch) │ │ │ └─────┼─────┘ ▼ Fusion(多源融合) ▼ Filter(过滤无效项) ▼ Reranker(精准排序) ▼ Memory IDs / metadata ▼ PostgreSQL(唯一事实源,返回 Active Version Data) ▼ Context Builder

召回应明确分为三类,其中关键词召回为可选:

  • Structured Recall:Redis / PG,适配精确查询、热点记忆、偏好、最近/高频访问。
  • Semantic Recall:Milvus,适配模糊自然语言语义匹配。
  • Keyword Recall(可选):OpenSearch,适配精准关键词、专有名词、设备型号;仅在需要复杂全文检索、高级关键词查询、Aggregation 或 Search Analytics 时引入。
  • Structured Recall:Redis / PG,适配精确查询、热点记忆、偏好、最近/高频访问。
  • Semantic Recall:Milvus,适配模糊自然语言语义匹配。
  • Keyword Recall:OpenSearch,适配精准关键词、专有名词、设备型号。

Redis 不应被当作一个普通 Recall Engine,它定位是结构化 hot memory 缓存;PostgreSQL 也不是“兜底”,而是唯一事实源,负责返回某个 Memory 当前有效版本的真实数据。

六、工业级落地分阶段方案

严格按「先核心、后优化、再工业化」迭代,避免初期堆砌组件。

阶段新增内容核心目标
V1FastAPI + Memory Service + PostgreSQL定义记忆领域模型、PG 表结构、Recall/Write 主入口及基础 Write Pipeline
V2Embedding + Milvus语义化记忆召回
V3OpenSearch + 混合检索 + Reranker语义 + 关键词双模式精准召回
V4LLM 记忆提取、校验、去重、冲突修复记忆写入智能化
V5Kafka、Redis 结构化热点缓存、定时调度、可观测体系解耦、并发、监控、工业化高可用

七、项目启动第一优先级核心工作

停止架构名词堆砌与 PPT 设计,优先落地工程核心:

  1. 定义完整 Memory 领域模型(单条记忆结构与全部字段)。
  2. 梳理记忆完整生命周期:产生、提取、验证、落地、召回、更新、冲突、过期、删除。
  3. 基于模型设计 PG 数据表、索引、版本机制。
  4. 实现基础 Memory Service 与 LangGraph 适配层,明确区分任务级 State 与长期 Memory。

核心结论:模型是所有架构的根基。模型不清晰,所有中间件堆砌均无意义。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 19:59:01

“堆“的全面拆解:数据结构堆与内存堆的底层逻辑与实战

看到“堆”这个词,很多程序员都会愣一下,因为它在不同场景里代表的东西完全不一样。做数据结构的课程作业时,老师让你手写堆排序;深夜排查服务内存暴涨时,你用jmap看的是Java堆;写C语言时,mallo…

作者头像 李华
网站建设 2026/9/7 19:55:20

深入解析 Java GC 调优:减少 Minor GC 频率,优化系统吞吐

目录 一、问题描述 (一)GC 频率与影响 1. GC 频率统计 2. GC 对请求延迟的影响 2.1 Minor GC 影响的请求数 2.2 Major GC 影响的请求数 3. TP90/TP99 的影响 (二)主要问题 1. Minor GC 过于频繁 2. Major GC 触发频率偏高 二、分析 GC 机制 (一)Java 内存回收…

作者头像 李华