
简介随着 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 State1. Event发生了什么Event 是系统真实发生的事情。例如用户说预算不超过 8000 元 用户把预算调整为 10000 元 用户确认某个酒店 Agent 调用了酒店查询接口 外部系统返回支付成功Event 重点是事实发生过因此应该具备event_id actor tenant user session task source occurred_at payload idempotency_keyEvent 主要用于审计、去重和重放。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 SUPERSEDED4. 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 supersedes5. 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 D200Memory 只保存引用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 TASKMemory Retrieval 必须同时考虑用户 部门 Agent Task 时间 权限而不能简单地user_id vector similarity五、Memory 为什么需要版本假设用户第一次 预算不超过 8000 元 后来 这次旅行预算调整到 10000 元不能简单UPDATE memory SET budget 10000;否则无法解释什么时候发生变化为什么现在是 100008000 是否仍然适用于其他任务应该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_task1. 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAgent 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAgent 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAgent 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 ) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAgent正式记忆;七、为什么第一版选择 MySQLMemory 本质上首先是结构化数据 状态 版本 关系 权限这些恰好是 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); ListMemoryFact retrieve(MemoryQuery query); }Retrievalpublic interface MemoryRetriever { ListMemoryResult retrieve( MemoryQuery query ); }治理public interface MemoryGovernance { GovernanceResult evaluate( MemoryCandidate candidate, ListMemoryFact existing ); }十四、最终架构把整个方案压缩成一张图Agent │ ▼ ┌───────────────┐ │ MemoryService │ └───────┬───────┘ │ ┌────────┴────────┐ ▼ ▼ Structured Semantic Retrieval Retrieval │ │ ▼ ▼ MySQL Vector DB │ ▼ Redis (Cache) 写入 User / Tool / Agent ↓ Event ↓ Evidence ↓ Candidate ↓ Governance ↓ Memory Fact ↓ MySQL ↓ Vector Index