news 2026/9/30 3:46:48

agent记忆工程原理和实战落地解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
agent记忆工程原理和实战落地解析

简介

随着 Agent 从“问答机器人”逐渐走向真正执行任务,Memory(记忆)开始成为 Agent 系统的基础能力。

传统 RAG 解决的是:

“系统能从知识库里找到什么?”

而 Agent Memory 解决的是:

“系统应该记住谁的什么信息,并在什么时候使用?”

很多系统一提到 Memory,就想到“向量数据库 + Embedding”。但真正的 Memory 远不止向量检索。一个可用的 Agent Memory,至少需要解决归属、来源、事实、状态、版本和生命周期等问题。

本文从理论模型出发,给出一套轻量、可落地的 Java Agent Memory 方案。


一、先搞清楚:Agent Memory 到底是什么

一个用户说:

“继续处理我上次的旅行计划。”

Agent 可能需要知道:

  • 上次用户说了什么?
  • 这些信息来自哪里?
  • 哪些只是模型推测?
  • 哪些已经被确认?
  • 上次任务进行到哪里?
  • 这些信息属于用户、部门,还是当前任务?
  • 旧信息是否已经被新信息替代?

这些信息虽然都属于“旅行上下文”,但并不是同一种数据。

因此,Memory 不应该只是:

文本 → Embedding → Vector DB

而应该拆成几个核心对象。


二、Agent Memory 的核心模型

一个完整的记忆生命周期可以抽象为:

Memory Center (记忆中心 / 统一入口) │ ▼ Event 事件 / 原始事实 │ ▼ Evidence 证据 / 上下文 │ ▼ Candidate 候选记忆 │ ▼ Governance 记忆治理 / 生命周期 │ ▼ Memory Fact 正式记忆事实

同时任务状态独立存在:

Task ↓ Task State

1. Event:发生了什么

Event 是系统真实发生的事情。

例如:

用户说预算不超过 8000 元 用户把预算调整为 10000 元 用户确认某个酒店 Agent 调用了酒店查询接口 外部系统返回支付成功

Event 重点是事实发生过,因此应该具备:

event_id actor tenant user session task source occurred_at payload idempotency_key

Event 主要用于审计、去重和重放。


2. Evidence:依据来自哪里

Evidence 解决:

“这个记忆为什么成立?”

例如:

用户明确说过 某次聊天记录 一份文档 外部订单系统 工具调用结果

因此 Memory 不应该只有:

用户喜欢直飞

还应该能够追溯:

Evidence ↓ 某次用户对话 ↓ “我一般不坐转机航班”

来源不是附属信息,而是 Memory 可解释、可纠错的基础。


3. Candidate:模型认为值得记住什么

模型可以从 Event 中提取候选记忆:

用户可能偏好直飞航班

但这时候不能直接变成正式 Memory。

因为:

模型可以猜,但系统不能把猜测直接当事实。

Candidate 至少需要:

candidate_id memory_type content confidence evidence_refs extractor_model scope valid_time status

状态可以是:

PENDING ACCEPTED REJECTED NEEDS_CONFIRMATION SUPERSEDED

4. Memory Fact:正式记忆

通过治理后,Candidate 才成为 Memory Fact。

例如:

用户通常优先选择直飞航班

Memory Fact 至少需要知道:

谁的? 什么类型? 什么内容? 什么范围? 什么时候生效? 什么时候失效? 当前哪个版本? 依据是什么?

因此 Memory Fact 应该具备:

memory_id tenant_id owner user_id department_id scope memory_type content status revision valid_from valid_until evidence_refs supersedes

5. Task State:任务做到哪里

Task State 和 Memory Fact 必须分开。

例如:

目标:完成家庭旅行预订 已完成: - 确定目的地 - 筛选酒店 待完成: - 用户选择酒店 - 确认付款 下一步: - 展示候选酒店 状态: WAITING_CONFIRMATION

这不是用户的长期记忆,而是任务运行状态。

典型状态:

CREATED → PLANNING → WAITING_CONFIRMATION → EXECUTING → COMPLETED

外部系统出现超时,还应该支持:

OUTCOME_UNKNOWN

避免 Agent 因为接口超时就误认为失败,从而重复执行。


三、企业 Agent Memory 还必须解决“记忆属于谁”

企业场景不能只有user_id。

至少需要:

tenant_id user_id department_id agent_id session_id task_id

但这些信息不应该全部复制成完整对象。

例如:

用户 User ID = U100 部门 Department ID = D200

Memory 只保存引用:

tenant_id = T001 user_id = U100 department_id = D200

用户姓名、部门名称、组织层级仍然由统一用户中心 / IAM / 组织系统提供。

因此:

Identity 系统负责回答“谁”,Memory 系统负责回答“记住了什么”。


四、Scope:决定这条记忆在哪里生效

Memory 最容易出现的问题,就是作用域错误。

例如:

“这次旅行预算 10000 元。”

不能变成:

“用户以后所有旅行预算都是 10000 元。”

因此建议至少支持:

TENANT DEPARTMENT USER AGENT SESSION TASK

例如:

用户偏好 Flink SQL scope = USER 团队生产环境使用 Flink 1.17 scope = DEPARTMENT 本次任务预算 10000 scope = TASK

Memory Retrieval 必须同时考虑:

用户 + 部门 + Agent + Task + 时间 + 权限

而不能简单地:

user_id + vector similarity

五、Memory 为什么需要版本

假设用户:

第一次: 预算不超过 8000 元 后来: 这次旅行预算调整到 10000 元

不能简单:

UPDATE memory SET budget = 10000;

否则无法解释:

  • 什么时候发生变化?
  • 为什么现在是 10000?
  • 8000 是否仍然适用于其他任务?

应该:

Revision 1 预算 <= 8000 ↓ superseded Revision 2 本次旅行预算 <= 10000

因此 Memory 必须支持:

revision valid_from valid_until supersedes

六、Memory 的核心数据库设计

第一版不需要把系统做得过于复杂。

推荐:

MySQL ├── memory_event ├── memory_evidence ├── memory_candidate ├── memory_fact ├── memory_fact_revision └── memory_task

1. Event:事件表

Event 是整个 Memory 系统最底层的数据。

CREATE TABLE memory_event ( id BIGINT UNSIGNED NOT NULL COMMENT '事件ID', tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', actor_type VARCHAR(32) NOT NULL COMMENT 'USER/AGENT/SYSTEM/TOOL', actor_id VARCHAR(64) NOT NULL COMMENT '事件产生者', user_id VARCHAR(64) DEFAULT NULL COMMENT '用户ID', department_id VARCHAR(64) DEFAULT NULL COMMENT '部门ID', agent_id VARCHAR(64) DEFAULT NULL COMMENT 'Agent ID', session_id VARCHAR(64) DEFAULT NULL COMMENT '会话ID', task_id VARCHAR(64) DEFAULT NULL COMMENT '任务ID', event_type VARCHAR(64) NOT NULL COMMENT '事件类型', source VARCHAR(64) NOT NULL COMMENT '事件来源', payload JSON NOT NULL COMMENT '事件内容', idempotency_key VARCHAR(128) DEFAULT NULL COMMENT '幂等键', occurred_at DATETIME(3) NOT NULL COMMENT '事件发生时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Memory事件';

2. Evidence:证据表

Evidence 保存 Event、对话、文档、外部系统等原始依据。

CREATE TABLE memory_evidence ( id BIGINT UNSIGNED NOT NULL COMMENT '证据ID', tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', evidence_type VARCHAR(32) NOT NULL COMMENT 'CONVERSATION/DOCUMENT/TOOL/API/DATABASE', source_system VARCHAR(64) NOT NULL COMMENT '来源系统', source_id VARCHAR(128) DEFAULT NULL COMMENT '来源ID', event_id BIGINT UNSIGNED DEFAULT NULL COMMENT '关联事件ID', user_id VARCHAR(64) DEFAULT NULL, department_id VARCHAR(64) DEFAULT NULL, content TEXT NOT NULL COMMENT '证据内容', locator VARCHAR(512) DEFAULT NULL COMMENT '原始位置,例如消息ID、文档路径', source_version VARCHAR(64) DEFAULT NULL COMMENT '来源版本', metadata JSON DEFAULT NULL COMMENT '扩展元数据', occurred_at DATETIME(3) DEFAULT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Memory证据';

3. Candidate:候选记忆

Candidate 是 LLM 提取出来的“可能值得记住的信息”。

CREATE TABLE memory_candidate ( id BIGINT UNSIGNED NOT NULL COMMENT '候选ID', tenant_id VARCHAR(64) NOT NULL, user_id VARCHAR(64) DEFAULT NULL, department_id VARCHAR(64) DEFAULT NULL, agent_id VARCHAR(64) DEFAULT NULL, session_id VARCHAR(64) DEFAULT NULL, task_id VARCHAR(64) DEFAULT NULL, memory_type VARCHAR(32) NOT NULL COMMENT 'FACT/PREFERENCE/CONSTRAINT/DECISION/EXPERIENCE', subject VARCHAR(256) DEFAULT NULL, predicate VARCHAR(256) DEFAULT NULL, object_value TEXT DEFAULT NULL, content TEXT NOT NULL COMMENT '候选记忆内容', scope VARCHAR(32) NOT NULL COMMENT 'TENANT/DEPARTMENT/USER/AGENT/SESSION/TASK', confidence DECIMAL(5,4) DEFAULT NULL COMMENT '模型置信度', extractor_model VARCHAR(128) DEFAULT NULL, extractor_version VARCHAR(64) DEFAULT NULL, valid_from DATETIME(3) DEFAULT NULL, valid_until DATETIME(3) DEFAULT NULL, status VARCHAR(32) NOT NULL COMMENT 'PENDING/ACCEPTED/REJECTED/NEEDS_CONFIRMATION/SUPERSEDED', governance_reason VARCHAR(512) DEFAULT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent Memory候选记忆';

4. Candidate-Evidence 关联表

一个 Candidate 可能来自多个 Evidence。

例如:

Evidence 1 Evidence 2 Evidence 3 ↓ Candidate

所以建议单独建关联表。

CREATE TABLE memory_candidate_evidence ( candidate_id BIGINT UNSIGNED NOT NULL, evidence_id BIGINT UNSIGNED NOT NULL, relation_type VARCHAR(32) NOT NULL DEFAULT 'SUPPORT' COMMENT 'SUPPORT/CONFLICT/REFERENCE', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), PRIMARY KEY ( candidate_id, evidence_id ), KEY idx_evidence ( evidence_id, candidate_id ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='候选记忆与证据关联';

这张表其实很重要。

因为:

一条 Memory 不一定只有一个证据。


5. Memory Fact:正式记忆

这是整个系统最核心的一张表。

CREATE TABLE memory_fact ( id BIGINT UNSIGNED NOT NULL COMMENT '记忆ID', tenant_id VARCHAR(64) NOT NULL COMMENT '租户ID', owner_type VARCHAR(32) NOT NULL COMMENT 'USER/DEPARTMENT/AGENT/TENANT', owner_id VARCHAR(64) NOT NULL COMMENT '记忆归属主体', user_id VARCHAR(64) DEFAULT NULL COMMENT '用户ID', department_id VARCHAR(64) DEFAULT NULL COMMENT '部门ID', agent_id VARCHAR(64) DEFAULT NULL COMMENT 'Agent ID', scope VARCHAR(32) NOT NULL COMMENT 'TENANT/DEPARTMENT/USER/AGENT/SESSION/TASK', session_id VARCHAR(64) DEFAULT NULL, task_id VARCHAR(64) DEFAULT NULL, memory_type VARCHAR(32) NOT NULL COMMENT 'FACT/PREFERENCE/CONSTRAINT/DECISION/EXPERIENCE', subject VARCHAR(256) DEFAULT NULL, predicate VARCHAR(256) DEFAULT NULL, object_value TEXT DEFAULT NULL, content TEXT NOT NULL COMMENT '自然语言记忆', status VARCHAR(32) NOT NULL COMMENT 'ACTIVE/SUPERSEDED/EXPIRED/INVALIDATED', confidence DECIMAL(5,4) DEFAULT NULL, revision BIGINT UNSIGNED NOT NULL DEFAULT 1, valid_from DATETIME(3) DEFAULT NULL, valid_until DATETIME(3) DEFAULT NULL, supersedes_id BIGINT UNSIGNED DEFAULT NULL COMMENT '被当前记忆替代的旧记忆', source_candidate_id BIGINT UNSIGNED DEFAULT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Agent正式记忆';

七、为什么第一版选择 MySQL

Memory 本质上首先是:

结构化数据 + 状态 + 版本 + 关系 + 权限

这些恰好是 MySQL 擅长的。

例如:

SELECT * FROM memory_fact WHERE tenant_id = ? AND user_id = ? AND status = 'ACTIVE' AND valid_from <= NOW() AND ( valid_until IS NULL OR valid_until > NOW() );

这类查询根本不需要向量数据库。

所以:

MySQL 是 Memory 的 Source of Truth。


八、Vector DB 到底做什么

Vector DB 不负责保存 Memory 的完整生命周期。

它只负责:

语义检索。

例如:

用户喜欢什么样的旅行方式?

Vector Search 可以找到:

用户倾向直飞 用户喜欢性价比高的酒店 用户不喜欢长途自驾

Vector 中只需要保存:

memory_id text embedding

真正的:

status scope revision valid_time permission evidence

仍然由 MySQL 决定。

因此:

MySQL = Source of Truth Vector DB = Semantic Index

这也是为什么:

向量数据库不能单独代表 Agent Memory。


九、Redis 做什么

Redis 也不是 Memory 数据库。

它主要解决访问性能问题。

适合缓存:

当前 Session Context 当前 Task State 用户热点 Memory 最近一次 Memory Retrieval

例如:

agent:memory:user:U100 agent:task:T200 agent:session:S300

因此:

MySQL ↓ 真实数据 Redis ↓ 热点缓存

如果系统规模较小,Redis 甚至可以暂时不使用。


十、为什么第一版不建议直接上 ES、Kafka、Flink

组件越多,不代表 Memory 越成熟。

第一版建议:

Spring Boot │ ├── MySQL │ ├── Vector DB │ └── Redis(可选)

已经足够跑通完整链路。

ES

主要解决:

全文检索 BM25 复杂关键词搜索

Memory 数量和检索复杂度真正上来后再增加。

Kafka

解决的是:

事件异步化 削峰 解耦

不是 Memory 存储。

Flink

适合后续做:

Memory 生命周期处理 Memory Quality Memory Analytics 异常检测

而不是第一版 Memory 必需组件。


十一、完整的 Memory 写入流程

用户说:

“我以后尽量都坐直飞。”

系统:

User Message ↓ Event ↓ Evidence ↓ Candidate Extractor ↓ Candidate ↓ Memory Governance ↓ Memory Fact ↓ MySQL ↓ Vector Index

例如:

Candidate type: PREFERENCE content: 用户通常偏好直飞航班 confidence: 0.95 scope: USER evidence: EV10001

通过治理后:

Memory Fact owner: USER/U100 type: PREFERENCE scope: USER status: ACTIVE

十二、Memory 查询流程

用户说:

“帮我继续安排上次旅行。”

Agent 不应该直接去 Vector DB 搜索。

应该:

User Query ↓ 识别 Task ↓ 读取 Task State ↓ 读取结构化 Memory ↓ 必要时语义检索 ↓ 权限 / Scope / 时间过滤 ↓ 冲突处理 ↓ Context Builder ↓ Agent

例如最终上下文:

[Task] 三亚家庭旅行 [Task State] 已经筛选酒店 等待用户选择 [Constraint] 预算 <= 10000 [User Preference] 倾向直飞 偏好性价比 [Evidence] 预算由用户在最近一次对话中明确调整

Agent 获得的是经过治理的上下文,而不是 Vector TopK。


十三、最终的 Java 工程架构

推荐第一版:

agent-memory │ ├── memory-api │ ├── memory-domain │ ├── memory-service │ ├── memory-retrieval │ ├── memory-index │ └── memory-server

核心接口:

public interface MemoryService { MemoryFact create(MemoryCandidate candidate); MemoryFact update( String memoryId, MemoryCandidate candidate ); void invalidate(String memoryId); List<MemoryFact> retrieve(MemoryQuery query); }

Retrieval:

public interface MemoryRetriever { List<MemoryResult> retrieve( MemoryQuery query ); }

治理:

public interface MemoryGovernance { GovernanceResult evaluate( MemoryCandidate candidate, List<MemoryFact> existing ); }

十四、最终架构

把整个方案压缩成一张图:

Agent │ ▼ ┌───────────────┐ │ MemoryService │ └───────┬───────┘ │ ┌────────┴────────┐ ▼ ▼ Structured Semantic Retrieval Retrieval │ │ ▼ ▼ MySQL Vector DB │ ▼ Redis (Cache) 写入: User / Tool / Agent ↓ Event ↓ Evidence ↓ Candidate ↓ Governance ↓ Memory Fact ↓ MySQL ↓ Vector Index
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 3:46:25

MySQL慢SQL定位:用EXPLAIN读懂执行计划,告别低效索引

开门见山说一句&#xff1a;我见过太多人&#xff0c;SQL写得花里胡哨&#xff0c;一慢下来就直接扔给DBA&#xff0c;自己对着EXPLAIN的输出一脸懵。其实在MySQL里定位低效SQL&#xff0c;explain就是那个最趁手的放大镜。你不需要读几十页官方文档&#xff0c;只要把explain输…

作者头像 李华
网站建设 2026/9/30 3:46:03

快而不完美的过程建模:用灰度交付思路绘制可迭代的业务流程图

我参加过一次流程梳理会&#xff0c;90分钟的会议有一半时间花在一个争论上&#xff1a;这条连接线该不该从采购模块的出口画到库存模块的入口&#xff0c;方框底色用不用统一成浅灰。会后所有人都在点头&#xff0c;但没有人能说清楚下一步要做什么。这种场面我在项目里见过太…

作者头像 李华
网站建设 2026/9/30 3:45:13

DeepSeek法律文档智能摘要:抽象式生成与法律效力校验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 3:45:03

MySQL 5.5 Windows安装配置全攻略:从下载到排查1067错误

MySQL 实验1&#xff1a;Windows 环境下 MySQL5.5 安装与配置&#xff0c;这个标题放在现在看确实有点复古&#xff0c;但恰恰是很多人的第一堂数据库实验课。这几年我在实验室和公司里帮人处理过不少次 MySQL 在 Windows 上的安装配置问题&#xff0c;5.5 版本又特别容易在服务…

作者头像 李华
网站建设 2026/9/30 3:44:09

CMPP2.0短信网关协议实战:从组包到状态报告处理

简介&#xff1a;这份PDF文档是中国移动短信网关通讯协议CMPP2.0的完整技术规范&#xff0c;面向从事短信业务开发的工程师、SP服务商技术人员及通信协议学习者&#xff0c;用于解决第三方平台接入中国移动短信网络时的接口对接与消息交互问题。文档系统梳理了协议的范围、缩略…

作者头像 李华
网站建设 2026/9/30 3:44:07

Flutter鸿蒙跨平台开发:Scaffold布局基石与避坑指南

做 Flutter 跨平台开发这些年&#xff0c;有一个控件几乎每个页面都会用到&#xff0c;很多人却只是把它当成一个“装东西的容器”&#xff0c;从未仔细想过它到底替我们扛下了多少事。这个控件就是 Scaffold。尤其是当项目从 Android、iOS 延伸到鸿蒙平台后&#xff0c;Scaffo…

作者头像 李华