ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

企业级Agent长期记忆系统实战:从失忆到精准记忆的架构设计与代码实现

企业级Agent长期记忆系统实战:从失忆到精准记忆的架构设计与代码实现 开篇想用一个真实场景切入大模型对话时“失忆”、多轮对话上下文丢失、企业 Agent 无法记住用户偏好、重复问问题。然后引出长期记忆系统概念。 ## 写在前面最近在落地 AI Agent 相关项目时遇到一个非常典型的场景用户第一轮告诉 Agent “我常用的收货地址是北京市朝阳区 XX 街道”第二轮再问 “寄到我家” 时Agent 已经完全忘了又或者用户昨天刚在系统里配置过“所有邮件通知抄送经理”今天再次发起请求时它依然像第一次见面一样重复询问。这类问题在群里聊起来很多开发者的第一反应是“加大上下文窗口”。但实践之后会发现靠增大窗口解决记忆问题既不经济也不可靠。更合理的思路是给 Agent 单独设计一套长期记忆系统。这篇文章就从一个真实可落地的角度出发梳理企业级 Agent 记忆系统的核心概念、技术选型、存储设计和代码实现。文章既有原理说明也有完整的 Java Spring Boot Redis 向量数据库示例适合正在做 Agent 开发、想解决“多轮对话失忆”问题的后端工程师阅读。1. 先搞清楚Agent 为什么总“失忆”1.1 大模型本身的记忆局限先看一个让人困惑的现象ChatGPT、文心一言、通义千问这类产品经常能在连续对话里记住你上一句说了什么但一旦关闭窗口、隔一段时间再打开它就什么也不记得了。这是因为当前主流大模型本质上是“无状态”的。模型本身不维护任何持久化的对话记录每次调用接口时你需要把历史消息重新发送给模型模型才能“回忆”起之前发生了什么。所谓的多轮对话记忆其实是靠工程手段模拟出来的而不是模型天然具备的能力。在 API 调用层面代码通常长这样{ model: qwen-plus, messages: [ {role: system, content: 你是一个客服助手}, {role: user, content: 我的收货地址是北京市朝阳区}, {role: assistant, content: 已记录您的收货地址是北京市朝阳区}, {role: user, content: 请把这个订单寄到我家} ] }你会发现为了让模型知道“我家”等于“北京市朝阳区”必须把历史 messages 全部再发一遍。如果历史消息很长每次请求都要携带大量 token成本和延迟都会上升。1.2 短期记忆与长期记忆的差异在认知科学里人类记忆被划分为短期记忆和长期记忆。Agent 系统的记忆设计也从这里获得启发短期记忆指当前会话内的上下文依赖大模型上下文窗口直接通过 messages 传递。长期记忆指跨会话、跨用户、跨时间段的持久化信息需要外部存储系统配合。如果一个 Agent 只有短期记忆它就无法回答如下问题用户昨天反馈了什么工单用户当前项目用的是 Java 还是 Go用户是否已经授权了某个操作用户上次在哪个环节中断了流程这些信息如果每次都要用户重新提供体验会非常糟糕。1.3 企业级场景对记忆的要求更高个人开发者用 Agent 做 Demo失忆了还能忍企业级场景里记忆系统如果不可靠会直接带来业务损失。比如在客服场景中用户已经和 Agent 确认过退款原因结果转人工后Agent 把这段信息丢了用户需要重新复述在销售场景中客户已经明确表示预算范围Agent 下一轮却推荐了超出预算的产品在运维场景中Agent 昨天刚分析过某个生产问题的根因今天用户问起它却给不出结论。这些问题的共性在于Agent 缺少一个可靠的、结构化的、可查询的长期记忆模块。1.4 给 Agent 装上记忆后会发生什么想象一下当我们给 Agent 增加长期记忆后同一段对话会变成这样第一轮用户说“我的收货地址是北京市朝阳区 XX 街道 1 号”。Agent 从对话中抽取结构化记忆存入长期记忆系统。第二轮用户说“请把这个订单寄到我家”。Agent 先从记忆系统中检索用户历史偏好得到收货地址。Agent 将记忆作为上下文注入模型生成最终回复。从用户视角看Agent “记住了”他之前说过的话从技术视角看我们只是把原来纯靠上下文窗口的方案升级成了一套“外部记忆存储 检索增强”的方案。2. 搭建长期记忆系统的整体架构2.1 记忆系统需要解决哪些问题在设计一个企业级 Agent 记忆系统之前建议先把问题拆开而不是直接上手写代码。一个完整的记忆系统至少需要解决以下 5 个问题问题说明记忆的写入如何从用户对话中提取值得记住的信息记忆的存储存哪些数据用什么存储介质记忆的检索在需要的时候如何快速找到相关记忆记忆的更新用户信息变化后如何覆盖旧记忆记忆的删除用户要求遗忘时如何保证合规删除如果只解决“存储”问题并不算一套完整的记忆系统。真正上线时任何一个环节缺失都会导致体验断裂。2.2 分层设计思路我在项目里习惯把记忆系统分成四层接入层负责与大模型交互接收用户的输入和模型的输出。抽取层负责从对话中识别关键信息比如实体、偏好、事实。存储层负责保存结构化记忆和向量化记忆。检索层负责在生成回复前检索有用的记忆并组装上下文。这个分层的好处是每一层都可以独立演进。比如存储层从 Redis 换成 MySQL不影响抽取层抽取层从规则抽取升级为大模型自动抽取也不影响存储层。2.3 技术选型建议在技术选型时我通常根据团队已有技术栈和项目阶段来做取舍下图是我的默认推荐方案记忆存储Redis适合保存用户维度的结构化记忆比如用户偏好、状态、短期会话标记。事实存储MySQL适合保存需要统计、审计、复杂查询的企业级数据。向量存储Milvus / Elasticsearch / 本地 Chroma适合保存非结构化的语义记忆比如用户说过的一段长文本。大模型服务OpenAI / 通义千问 / 自研模型负责记忆抽取和最终回复生成。消息队列可选。如果对话量很大建议用 RocketMQ 或 Kafka 异步处理记忆写入避免阻塞主链路。这里要特别说明一个观点不要把“向量数据库”神化。长期记忆系统里向量检索解决的是语义召回问题但真正决定业务效果的往往是结构化记忆的准确更新后者依赖可靠的数据库和清晰的业务逻辑。3. 从 0 开始搭建一个最小可运行的 Agent 记忆系统3.1 场景设定为了把抽象概念讲透这里设计一个非常典型的企业场景用户在与“企业客服 Agent”对话时提供了自己的公司名称、行业、常用收货地址、价格偏好等信息。Agent 需要在多轮对话中记住这些信息并在后续对话中基于这些信息生成个性化回复。这个场景足够简单也足够有代表性。3.2 环境准备与版本说明本文示例以 Java 技术栈为主环境如下JDK 17Spring Boot 3.2.xRedis 7.xMySQL 8.xMaven 3.9大模型 API示例中直接模拟返回方便没有 API Key 的同学运行如果读者使用的是 Spring Boot 2.x 或 JDK 8大部分代码思路仍然适用只需注意依赖版本差异。这里需要提醒一下Spring Boot 3.x 默认使用 Jakarta EE 命名空间与 2.x 的 javax 命名空间不兼容。如果项目还在用 Spring Boot 2.7建议保持原有依赖管理不要直接照抄 3.x 版本坐标。3.3 创建项目结构我们先创建一个 Maven 项目包名建议使用com.example.agentmemory。项目结构如下agent-memory-system ├── pom.xml ├── src/main/java/com/example/agentmemory │ ├── AgentMemoryApplication.java │ ├── controller │ │ └── AgentController.java │ ├── service │ │ ├── AgentService.java │ │ ├── MemoryService.java │ │ └── MemoryExtractService.java │ ├── model │ │ ├── UserMemory.java │ │ └── ChatRequest.java │ └── repository │ └── MemoryRepository.java └── src/main/resources └── application.yml下面逐层实现。3.4 添加依赖打开pom.xml添加核心依赖。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.4/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2/artifactId version2.0.47/version /dependency /dependencies这里使用 fastjson2 做 JSON 序列化和反序列化你也可以替换为 Jackson不影响整体逻辑。3.5 配置文件 application.ymlserver: port: 8080 spring: application: name: agent-memory-system data: redis: host: localhost port: 6379 database: 0 timeout: 3000ms datasource: url: jdbc:mysql://localhost:3306/agent_memory?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver这里需要注意如果本机没有安装 Redis 和 MySQL需要先启动这两个基础服务。生产环境建议使用云数据库或容器部署本地演示直接使用默认配置即可。3.6 定义数据模型我们定义两个核心模型UserMemory和ChatRequest。UserMemory表示一条用户记忆包含用户 ID、记忆类型、记忆内容和更新时间。package com.example.agentmemory.model; import lombok.Data; import java.time.LocalDateTime; Data public class UserMemory { private Long id; /** * 用户唯一标识 */ private String userId; /** * 记忆类型例如profile用户画像、preference偏好 */ private String memoryType; /** * 记忆内容JSON 字符串 */ private String content; private LocalDateTime createTime; private LocalDateTime updateTime; }ChatRequest表示一次请求体。package com.example.agentmemory.model; import lombok.Data; Data public class ChatRequest { /** * 用户唯一标识 */ private String userId; /** * 用户输入的文本 */ private String message; }3.7 实现记忆仓库层为了减少对数据库表的依赖我们在示例中先用 Redis 存储结构化记忆用内存 Map 模拟 MySQL。实际项目里可以把 Map 替换成 JPA 或 MyBatis。先定义仓库接口。package com.example.agentmemory.repository; import com.example.agentmemory.model.UserMemory; import java.util.List; public interface MemoryRepository { void save(UserMemory memory); ListUserMemory findByUserId(String userId); }实现一个基于 Redis 的版本。package com.example.agentmemory.repository; import com.alibaba.fastjson2.JSON; import com.example.agentmemory.model.UserMemory; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Repository; import java.util.ArrayList; import java.util.List; import java.util.Set; Repository public class RedisMemoryRepository implements MemoryRepository { private static final String MEMORY_KEY_PREFIX agent:memory:; private final StringRedisTemplate redisTemplate; public RedisMemoryRepository(StringRedisTemplate redisTemplate) { this.redisTemplate redisTemplate; } Override public void save(UserMemory memory) { String key MEMORY_KEY_PREFIX memory.getUserId(); String field memory.getMemoryType(); redisTemplate.opsForHash().put(key, field, JSON.toJSONString(memory)); } Override public ListUserMemory findByUserId(String userId) { String key MEMORY_KEY_PREFIX userId; ListObject values redisTemplate.opsForHash().values(key); ListUserMemory memories new ArrayList(); for (Object value : values) { memories.add(JSON.parseObject(value.toString(), UserMemory.class)); } return memories; } }这段代码的核心逻辑是每个用户有一个独立的 Hash 结构字段名是记忆类型值是 JSON 序列化后的记忆对象。这样做的优势是天然支持按用户维度聚合也方便后续更新某个类型的记忆。3.8 实现记忆抽取服务记忆抽取是整个记忆系统最关键的一步。最简单的实现是规则匹配但真实项目中推荐使用大模型做信息抽取。为了演示我先提供一版基于规则的抽取逻辑它能识别“地址”“偏好”两类简单模式。package com.example.agentmemory.service; import com.example.agentmemory.model.UserMemory; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; Service public class MemoryExtractService { private static final Pattern ADDRESS_PATTERN Pattern.compile(地址是(.?)(|。|$|,|\\.)); public ListUserMemory extract(String userId, String message) { ListUserMemory memories new ArrayList(); Matcher matcher ADDRESS_PATTERN.matcher(message); while (matcher.find()) { String address matcher.group(1).trim(); UserMemory memory new UserMemory(); memory.setUserId(userId); memory.setMemoryType(profile); memory.setContent({\field\:\address\,\value\:\ address \}); memory.setCreateTime(LocalDateTime.now()); memory.setUpdateTime(LocalDateTime.now()); memories.add(memory); } return memories; } }规则抽取适合跑通流程但当我们把这段逻辑换成大模型抽取后能力会明显增强大模型可以抽取“用户是某公司的采购经理”这类隐含信息。大模型可以判断哪些信息值得长期保存哪些只是临时闲聊。大模型可以输出结构化的 JSON方便直接写入存储层。下面是大模型抽取的简化示例伪代码String prompt 请从用户对话中抽取值得长期记忆的事实输出JSON数组。; String response llmService.chat(prompt, userMessage); ListUserMemory memories parseJson(response);3.9 实现 Agent 服务层Agent 服务层是整个系统的核心编排者。它的职责是接收用户输入 - 检索历史记忆 - 抽取新记忆 - 生成回复。先实现记忆检索服务。package com.example.agentmemory.service; import com.example.agentmemory.model.UserMemory; import com.example.agentmemory.repository.MemoryRepository; import org.springframework.stereotype.Service; import java.util.List; Service public class MemoryService { private final MemoryRepository memoryRepository; public MemoryService(MemoryRepository memoryRepository) { this.memoryRepository memoryRepository; } public ListUserMemory getMemories(String userId) { return memoryRepository.findByUserId(userId); } public void saveMemories(ListUserMemory memories) { if (memories null || memories.isEmpty()) { return; } for (UserMemory memory : memories) { memoryRepository.save(memory); } } }再实现 AgentService。package com.example.agentmemory.service; import com.alibaba.fastjson2.JSON; import com.example.agentmemory.model.ChatRequest; import com.example.agentmemory.model.UserMemory; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; Service public class AgentService { private final MemoryService memoryService; private final MemoryExtractService memoryExtractService; public AgentService(MemoryService memoryService, MemoryExtractService memoryExtractService) { this.memoryService memoryService; this.memoryExtractService memoryExtractService; } public String chat(ChatRequest request) { String userId request.getUserId(); String message request.getMessage(); // 1. 检索用户历史记忆 ListUserMemory memories memoryService.getMemories(userId); String memoryContext memories.stream() .map(memory - memory.getContent()) .collect(Collectors.joining(;)); // 2. 抽取新的记忆 ListUserMemory newMemories memoryExtractService.extract(userId, message); memoryService.saveMemories(newMemories); // 3. 模拟大模型回复真实项目替换为LLM调用 if (memoryContext.isEmpty() newMemories.isEmpty()) { return 你好请问有什么可以帮你; } return 根据你的历史信息 memoryContext 我已经理解了你的需求 message; } }这里有一段代码值得展开说明在真实项目中第 2 步“抽取新记忆”和第 3 步“生成回复”应该合并成一次大模型调用否则会产生额外延迟。不过在示例中分开写更容易理解。3.10 实现 Controller 层最后提供 HTTP 接口方便我们用浏览器或 Postman 验证。package com.example.agentmemory.controller; import com.example.agentmemory.model.ChatRequest; import com.example.agentmemory.service.AgentService; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/agent) public class AgentController { private final AgentService agentService; public AgentController(AgentService agentService) { this.agentService agentService; } PostMapping(/chat) public String chat(RequestBody ChatRequest request) { return agentService.chat(request); } }3.11 启动类package com.example.agentmemory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class AgentMemoryApplication { public static void main(String[] args) { SpringApplication.run(AgentMemoryApplication.class, args); } }3.12 运行与验证启动 MySQL 和 Redis 后运行AgentMemoryApplication主类。然后使用 curl 模拟第一轮对话curl -X POST http://localhost:8080/api/agent/chat \ -H Content-Type: application/json \ -d {userId:10001,message:我的收货地址是北京市朝阳区XX街道1号}预期返回根据你的历史信息{field:address,value:北京市朝阳区XX街道1号}我已经理解了你的需求我的收货地址是北京市朝阳区XX街道1号接下来模拟第二轮对话curl -X POST http://localhost:8080/api/agent/chat \ -H Content-Type: application/json \ -d {userId:10001,message:请把订单寄到我家}预期返回根据你的历史信息{field:address,value:北京市朝阳区XX街道1号}我已经理解了你的需求请把订单寄到我家从结果可以直观看到第二轮回复中带上了第一轮沉淀的地址信息。这就是最简形态的 Agent 长期记忆。4. 升级引入向量检索实现语义记忆4.1 为什么需要向量检索上面这套方案解决的是“结构化记忆”问题比如地址、偏好、公司名。但企业场景里还有大量非结构化记忆比如用户说“我上次问过一个关于数据库连接池配置的问题当时你建议我把最大连接数调成 20”。用户说“我们在 5 月份讨论过容器迁移的事情”。用户说“这个项目的背景很复杂简单讲就是老系统性能瓶颈严重”。这类记忆不适合用 Hash 结构存储更适合用“文本块 向量”的方式存储也就是向量记忆。4.2 向量检索的工作流程向量记忆的完整流程包含三个步骤文本切块把用户历史消息或重要文档切成固定长度的文本块。向量化用 Embedding 模型把文本块转换成向量。相似度检索用户提问时把用户问题也转成向量然后计算余弦相似度召回最相关的记忆块。这个流程和 RAG检索增强生成非常像。事实上向量记忆就是 RAG 在“对话记忆”场景下的应用。4.3 轻量级向量存储实现在完整引入 Milvus 之前可以先看一个基于内存的向量检索示例帮助你理解核心逻辑。package com.example.agentmemory.service; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; Service public class SimpleVectorMemoryService { private final ListString memoryTexts new ArrayList(); /** * 保存一条文本记忆 */ public void saveMemory(String userId, String text) { memoryTexts.add(userId : text); } /** * 简单关键词检索模拟向量检索 */ public ListString search(String userId, String query) { ListString results new ArrayList(); for (String memory : memoryTexts) { if (memory.startsWith(userId :) memory.contains(query)) { results.add(memory); } } return results; } }这段代码只是为了演示流程没有任何向量计算。真正生产环境建议使用向量库Milvus、Qdrant、Elasticsearch含向量插件、pgvector。Embedding模型OpenAI Embeddings、通义千问 embedding、BGE 系列、M3E 系列。4.4 向量检索与结构化记忆如何配合实际项目里两类记忆不是互斥的而是配合使用结构化记忆负责精准回答“用户的地址是什么”。向量记忆负责模糊召回“用户之前提过类似的配置问题”。在生成最终回复前Agent 会先查结构化记忆再查向量记忆然后把两类结果都注入 Prompt。5. 记忆系统的生命周期管理5.1 记忆的更新策略记忆不是写一次就永久不变。用户可能搬家可能更换手机号可能改变偏好。因此必须设计更新策略。推荐做法先根据用户 ID 和记忆类型查询旧记忆如果存在就覆盖内容并更新时间如果不存在则新建。伪代码如下public void updateMemory(String userId, String memoryType, String content) { UserMemory memory memoryRepository.findByUserIdAndType(userId, memoryType); if (memory null) { memory new UserMemory(); memory.setUserId(userId); memory.setMemoryType(memoryType); memory.setCreateTime(LocalDateTime.now()); } memory.setContent(content); memory.setUpdateTime(LocalDateTime.now()); memoryRepository.save(memory); }这里需要注意一个业务问题如果用户今天说 A明天说 B系统是直接覆盖还是保留两条我的建议是对于明确的事实偏好采用“单值覆盖”策略避免信息冲突对于对话记录、过程记忆采用“追加保存”策略。5.2 记忆的过期与清理长期记忆不等于永久保存。要考虑以下几种情况用户注销账号相关记忆应随之删除。用户长时间未活跃记忆可以归档。某些敏感信息比如银行卡号、身份证号应设置较短过期时间。在 Redis 场景下可以为记忆键设置 TTLredisTemplate.expire(key, Duration.ofDays(180));在 MySQL 场景下可以增加expire_time字段由定时任务负责清理。5.3 记忆的合规性删除企业级系统必须考虑数据合规问题。用户有权要求删除自己的历史记忆这在法律层面被称为“被遗忘权”。实现上需要提供显式的删除接口public void deleteUserMemory(String userId) { redisTemplate.delete(MEMORY_KEY_PREFIX userId); memoryRepository.deleteByUserId(userId); }删除操作执行前建议增加二次确认和操作审计日志确保不是误触发。6. 与 LLM 的集成记忆注入与 Prompt 组装6.1 记忆注入方式拿到检索结果后如何把这些记忆告诉大模型最常见的做法是拼接到 System Prompt 中。示例你是一个企业客服助手。以下是关于用户的长期记忆请基于这些信息做出个性化回复。 长期记忆 - 用户收货地址北京市朝阳区XX街道1号 - 用户偏好喜欢顺丰快递 - 最近反馈上周申请了退货退款 用户本次输入请把订单寄到我家这里的关键技巧是记忆内容过多时不能全部塞进 Prompt否则会导致 token 浪费和模型注意力分散。需要做“记忆筛选”只选择与当前问题最相关的记忆。6.2 记忆冲突处理当历史记忆与当前输入冲突时大模型应以当前输入为准。例如历史记忆显示用户地址是上海但用户本次明确说“我搬到了深圳”那么 Agent 应该更新记忆并以深圳作为收货地址。建议在 Prompt 中加入一句指令如果用户本次提供了新的信息优先以用户本次表述为准并更新长期记忆。6.3 会话级记忆与用户级记忆的区分再补充一个容易混淆的概念会话级记忆只在当前 session 内有效比如用户正在进行的订单流程。用户级记忆跨会话有效比如用户的常用地址。在代码中建议为记忆增加一个scope字段取值为session或user。这样在检索时可以按需过滤。7. 常见问题与排查思路在开发和落地记忆系统的过程中有几个问题出现的频率特别高这里整理成表格。问题现象常见原因解决思路记忆写入失败Redis 连接超时或未启动检查 Redis 是否启动使用redis-cli ping验证检索不到记忆Key 前缀或用户 ID 不一致检查上下文中 userId 是否传对记忆内容重复没有先查询再更新改为主键冲突则更新大模型不参考记忆记忆没有写入 Prompt检查 Prompt 组装逻辑召回结果不相关Embedding 模型效果不佳更换更合适的向量模型或增加关键词过滤记忆数据冗余所有内容都保存抽层模块过滤低价值信息更新记忆后旧内容仍生效没有清理旧向量数据向量库中同步删除或更新旧向量8. 企业级落地时的工程建议8.1 优先保证记忆写入的可靠性Agent 的记忆写入不能直接依赖大模型输出的稳定性。大模型偶尔会漏抽取、错抽取。因此建议大模型抽取结果出来后增加一层规则校验。关键信息如订单号、金额必须有兜底校验。写入操作要有失败重试机制。8.2 记忆系统与业务系统解耦记忆系统不建议直接操作业务库。更好的方式是记忆服务独立部署或独立建库。通过 API 给 Agent 提供读写接口。业务系统发生变化时通过事件通知同步到记忆系统。8.3 异步化处理用户发起对话后应该在 1 秒内收到回复。如果把记忆抽取、向量化、存储都放在同步链路里响应时间会大幅上升。推荐的链路如下同步链路检索记忆 - 组装 Prompt - 调用 LLM - 返回结果。异步链路抽取新记忆 - 向量化 - 写入存储。异步部分可以通过 MQ 或线程池完成。8.4 测试与灰度方案记忆系统的测试比较特殊因为同一个 Prompt 在不同记忆状态下会输出不同结果。建议设计记忆测试集新用户场景无历史记忆验证默认回复。老用户场景有完整历史记忆验证个性化回复。冲突场景历史记忆与当前输入冲突验证更新逻辑。删除场景删除后再次对话验证记忆已消失。9. 下一步学习路线如果你准备在真实项目中落地 Agent 长期记忆系统建议按下面的顺序逐步推进先用 Redis 保存结构化记忆跑通最小闭环。接入大模型做记忆抽取替代规则抽取。引入向量数据库增加语义记忆能力。设计记忆清理和更新策略完善生命周期管理。结合 RAG 技术让 Agent 不仅能记住用户偏好还能检索企业知识库。最后把记忆系统接入监控和日志体系建立 Prompt 版本管理和效果评估机制。10. 总结这篇文章从“Agent 总是失忆”这个痛点出发梳理了 Agent 短期记忆与长期记忆的区别并给出了一套完整的记忆系统实现方案。核心要点可以归纳为以下几点大模型本身是无状态的靠上下文窗口实现记忆并不适合企业级场景。长期记忆系统至少包含写入、存储、检索、更新、删除五个环节。结构化记忆建议使用 Redis 或 MySQL非结构化记忆可以使用向量数据库。记忆抽取用大模型效果好于规则匹配。记忆注入要经过筛选不能全部塞进 Prompt。异步化处理能提升响应速度可靠性和合规性是企业落地的关键。如果你正在做 AI Agent 相关开发建议先按照本文的最小示例跑通流程再逐步替换为自己的业务场景和技术栈。记忆系统的架构并不复杂真正的难点在于如何让记忆准确、可靠、可更新、可删除。希望这篇文章能给你一个清晰的起点。如果对文中某个环节有疑问建议直接复制代码到本地运行一遍遇到问题再结合日志排查。动手试一次比看十遍文章更有用。
返回列表