Agent记忆系统的后端设计:短期工作内存与长期向量存储的协同架构

📅 2026/7/12 19:47:45
Agent记忆系统的后端设计:短期工作内存与长期向量存储的协同架构
Agent记忆系统的后端设计短期工作内存与长期向量存储的协同架构一、引言在构建大模型应用的后端底座时Agent的记忆系统是决定其智能程度的核心基础设施之一。一个具备记忆能力的Agent需要回答三个关键问题当前会话中发生了什么短期记忆、历史交互中积累了什么长期记忆、以及如何在合适的时间召回合适的记忆检索策略。本文将深入探讨一种基于Redis工作内存与向量数据库长期存储的双层记忆架构设计。这一架构已在多个生产级AI Agent系统中落地验证支撑了日均百万级会话的记忆读写需求。graph TB subgraph Agent运行时 LLM[大语言模型] WM[工作内存br/Redis] TM[任务管理器] end subgraph 记忆系统 SM[短期记忆层br/Redis Hash/ZSET] LM[长期记忆层br/向量数据库] MS[记忆调度器] end subgraph 记忆生命周期 ENC[记忆编码器br/Embedding] RET[记忆检索器br/ANN Filter] FOR[遗忘策略引擎br/TTL Priority] end LLM --|上下文注入| SM LLM --|历史查询| LM SM --|重要性评估| MS MS --|持久化| LM MS --|衰减/淘汰| FOR LM --|相似检索| RET RET --|Top-K召回| SM WM --|会话状态| SM style LLM fill:#4a90d9,color:#fff style SM fill:#e67e22,color:#fff style LM fill:#27ae60,color:#fff style MS fill:#8e44ad,color:#fff二、双层记忆架构设计2.1 短期工作内存Redis短期记忆承载当前会话的上下文窗口。设计上采用Redis的多种数据结构组合/** * 短期记忆管理器 - 基于Redis的工作内存实现 */ Service public class ShortTermMemoryManager { private final StringRedisTemplate redis; private final ObjectMapper objectMapper; // 会话级记忆TTL设置为30分钟与典型会话时长匹配 private static final Duration SESSION_TTL Duration.ofMinutes(30); // 单条记忆最大保留条数 private static final int MAX_MEMORY_ITEMS 50; public ShortTermMemoryManager(StringRedisTemplate redis, ObjectMapper objectMapper) { this.redis redis; this.objectMapper objectMapper; } /** * 向会话写入一条记忆 */ public void writeMemory(String sessionId, MemoryItem item) throws JsonProcessingException { String key mem:session: sessionId; String member objectMapper.writeValueAsString(item); long score System.currentTimeMillis(); redis.opsForZSet().add(key, member, score); // 保持记忆条目数量在可控范围内 Long size redis.opsForZSet().zCard(key); if (size ! null size MAX_MEMORY_ITEMS) { redis.opsForZSet().removeRange(key, 0, size - MAX_MEMORY_ITEMS - 1); } redis.expire(key, SESSION_TTL); } /** * 获取会话的最近N条记忆按时间倒序 */ public ListMemoryItem getRecentMemories(String sessionId, int count) { String key mem:session: sessionId; SetString items redis.opsForZSet() .reverseRange(key, 0, count - 1); if (items null || items.isEmpty()) { return Collections.emptyList(); } return items.stream().map(this::deserialize).filter(Objects::nonNull).collect(Collectors.toList()); } /** * 计算记忆重要性分数并提交给调度器 */ public double calculateImportance(MemoryItem item) { // 综合考虑交互轮次(40%) 用户显式反馈(30%) 信息密度(30%) double turnWeight Math.min(item.getTurnCount() / 10.0, 1.0) * 0.4; double feedbackWeight (item.hasExplicitFeedback() ? 1.0 : 0.3) * 0.3; double densityWeight Math.min(item.getTokenCount() / 500.0, 1.0) * 0.3; return turnWeight feedbackWeight densityWeight; } private MemoryItem deserialize(String json) { try { return objectMapper.readValue(json, MemoryItem.class); } catch (JsonProcessingException e) { log.warn(Failed to deserialize memory item: {}, e.getMessage()); return null; } } }2.2 长期向量存储长期记忆采用向量数据库如Milvus/Qdrant核心在于将语义信息嵌入为高维向量并支持高效的近似最近邻ANN检索。/** * 长期记忆管理器 - 基于向量数据库的持久化存储 */ Component public class LongTermMemoryManager { private final VectorStore vectorStore; private final EmbeddingService embeddingService; // 候选召回数量大于最终需要的Top-K用于重排序 private static final int CANDIDATE_COUNT 20; // 最终返回的记忆数量 private static final int TOP_K 5; public LongTermMemoryManager(VectorStore vectorStore, EmbeddingService embeddingService) { this.vectorStore vectorStore; this.embeddingService embeddingService; } /** * 持久化长期记忆到向量库 */ public void persistMemory(MemoryItem item, double importance) { try { float[] embedding embeddingService.encode(item.getContent()); MemoryVector vector MemoryVector.builder() .id(item.getId()) .embedding(embedding) .content(item.getContent()) .importance(importance) .userId(item.getUserId()) .createdAt(Instant.now()) .metadata(buildMetadata(item)) .build(); vectorStore.upsert(vector); } catch (Exception e) { log.error(Failed to persist memory: id{}, error{}, item.getId(), e.getMessage()); // 失败重试写入死信队列 deadLetterQueue.send(item); } } /** * 检索相关长期记忆 */ public ListMemoryItem retrieveRelevantMemories(String userId, String query) { float[] queryEmbedding embeddingService.encode(query); // 第一阶段ANN粗筛 SearchResult candidates vectorStore.search( SearchRequest.builder() .vector(queryEmbedding) .filter(user_id userId ) .topK(CANDIDATE_COUNT) .build() ); // 第二阶段基于重要性 时效性的精排 return rerankByImportance(candidates, TOP_K); } private MapString, Object buildMetadata(MemoryItem item) { MapString, Object meta new HashMap(); meta.put(user_id, item.getUserId()); meta.put(session_id, item.getSessionId()); meta.put(importance, item.getImportance()); meta.put(memory_type, item.getType().name()); return meta; } private ListMemoryItem rerankByImportance(SearchResult candidates, int topK) { return candidates.getResults().stream() .sorted(Comparator.comparingDouble(r - r.getScore() * 0.6 r.getMetadata().getDouble(importance) * 0.4)) .limit(topK) .map(this::toMemoryItem) .collect(Collectors.toList()); } }三、记忆调度与遗忘策略记忆调度器是双层架构的核心协调组件负责决策哪些短期记忆需要提升为长期记忆、哪些记忆需要衰减或遗忘。flowchart TD START([新记忆到达]) -- ASSESS{重要性评估} ASSESS --|score 0.7| PERSIST[直接持久化到向量库] ASSESS --|0.3 score 0.7| BATCH[进入批量评估队列] ASSESS --|score 0.3| DISCARD[标记为短期等待TTL淘汰] BATCH -- PERIODIC[定时任务每5分钟] PERIODIC -- CLUSTER{聚合分析} CLUSTER --|同一主题 3条| MERGE[合并为一条结构化记忆] CLUSTER --|孤立记忆| DECAY[重要性衰减 -0.1] MERGE -- PERSIST DECAY -- RECHECK{衰减后检查} RECHECK --|score 0| BATCH RECHECK --|score 0| EVICT[删除] PERSIST -- INDEX[建立向量索引] INDEX -- COMPRESS{记忆压缩检查} COMPRESS --|同主题记忆 10条| SUMMARIZE[摘要化压缩] COMPRESS --|正常| STORE[存储完成] style START fill:#2ecc71,color:#fff style PERSIST fill:#3498db,color:#fff style EVICT fill:#e74c3c,color:#fff/** * 记忆调度器 - 协调短期与长期记忆的流转 */ Component public class MemoryScheduler { private final ShortTermMemoryManager shortTerm; private final LongTermMemoryManager longTerm; private final ScheduledExecutorService scheduler; // 重要性阈值高于此值直接持久化 private static final double IMPORTANCE_THRESHOLD 0.7; // 批量处理间隔 private static final Duration BATCH_INTERVAL Duration.ofMinutes(5); PostConstruct public void startScheduler() { scheduler.scheduleAtFixedRate( this::processBatchQueue, BATCH_INTERVAL.toMillis(), BATCH_INTERVAL.toMillis(), TimeUnit.MILLISECONDS ); } /** * 同步调度单个记忆到达时的决策 */ public void schedule(MemoryItem item) { double score shortTerm.calculateImportance(item); if (score IMPORTANCE_THRESHOLD) { // 高重要性立即持久化 item.setImportance(score); longTerm.persistMemory(item, score); log.info(Memory {} directly persisted with importance{}, item.getId(), score); } else if (score 0.3) { // 中等重要性进入批处理队列 redis.opsForList().leftPush(mem:batch:queue, serializeToJson(item)); } else { // 低重要性留在短期内存中等待自然过期 log.debug(Memory {} kept in short-term memory only, item.getId()); } } /** * 批量处理中等重要性记忆 */ private void processBatchQueue() { ListMemoryItem batch fetchBatchItems(); if (batch.isEmpty()) return; // 按主题聚合 MapString, ListMemoryItem grouped batch.stream() .collect(Collectors.groupingBy(MemoryItem::getTopicCluster)); for (Map.EntryString, ListMemoryItem entry : grouped.entrySet()) { if (entry.getValue().size() 3) { // 同主题多条记忆合并后持久化 MemoryItem merged mergeMemoryItems(entry.getValue()); longTerm.persistMemory(merged, 0.6); } else { // 孤立记忆衰减重要性后放回 for (MemoryItem item : entry.getValue()) { item.setImportance(item.getImportance() - 0.1); if (item.getImportance() 0) { redis.opsForList().leftPush(mem:batch:queue, serializeToJson(item)); } // importance 0 的记忆被自然丢弃 } } } } private MemoryItem mergeMemoryItems(ListMemoryItem items) { String mergedContent items.stream() .map(MemoryItem::getContent) .collect(Collectors.joining( | )); return MemoryItem.builder() .id(UUID.randomUUID().toString()) .content(mergedContent) .topicCluster(items.get(0).getTopicCluster()) .importance(0.6) .userId(items.get(0).getUserId()) .build(); } }四、跨会话记忆延续跨会话记忆延续是记忆系统最具挑战性的场景。当用户在一个新会话中继续之前的对话时系统需要快速定位到相关上下文。/** * 跨会话记忆延续服务 */ Service public class CrossSessionMemoryService { private final LongTermMemoryManager longTerm; private final ShortTermMemoryManager shortTerm; /** * 新会话初始化时预加载相关记忆 */ public SessionContext initializeSession(String userId, String initialQuery, String newSessionId) { // 1. 从长期记忆中检索与初始查询最相关的历史 ListMemoryItem relevantMemories longTerm.retrieveRelevantMemories(userId, initialQuery); // 2. 检索最近一次会话的核心摘要 OptionalMemoryItem lastSessionSummary longTerm.getLastSessionSummary(userId); // 3. 将相关记忆预热到短期工作内存 for (MemoryItem mem : relevantMemories) { try { shortTerm.writeMemory(newSessionId, mem); } catch (JsonProcessingException e) { log.error(Failed to preload memory to session: {}, e.getMessage()); } } return SessionContext.builder() .sessionId(newSessionId) .preloadedMemories(relevantMemories) .lastSessionSummary(lastSessionSummary.orElse(null)) .build(); } /** * 会话结束时生成摘要并持久化 */ public void finalizeSession(String sessionId, String userId) { ListMemoryItem sessionMemories shortTerm.getRecentMemories(sessionId, 50); if (sessionMemories.isEmpty()) return; // 生成会话摘要记忆 String summary generateSessionSummary(sessionMemories); MemoryItem summaryItem MemoryItem.builder() .id(summary: sessionId) .content(summary) .userId(userId) .type(MemoryItem.MemoryType.SESSION_SUMMARY) .importance(0.8) .build(); longTerm.persistMemory(summaryItem, 0.8); // 清理短期内存 redis.delete(mem:session: sessionId); } }五、总结Agent记忆系统的设计本质上是一个信息筛选与优先级排序的问题。双层架构的核心价值在于Redis层解决毫秒级低延迟的会话内上下文管理向量数据库层解决语义级的长期知识积累与检索。在生产实践中需要关注三个关键指标记忆写入延迟应控制在10ms以内Redis层和50ms以内向量库层检索召回率需达到85%以上通过精排策略保障记忆压缩比建议控制在15:1避免向量库存储膨胀。遗忘策略的参数重要性阈值0.3/0.7、衰减步长0.1、合并阈值3条需根据具体业务场景持续调优而非一成不变地照搬。