Agent 记忆系统设计:让智能体记得住、想得起、用得上
Agent 记忆系统设计让智能体记得住、想得起、用得上一、多轮对话里的记忆黑洞Agent 最常见的失败模式是什么不是推理错误不是工具调用失败而是把上一轮说过的事忘得一干二净。用户告诉它我的数据库在东京区三句话后它又去查 global endpoint。这种体验比报错更糟糕——因为用户会直接丧失信任。传统方案是往 prompt 里塞聊天历史。但 token 成本按上下文长度线性增长GPT-4 的 128K 窗口跑长对话时单次请求轻松破 $0.1。更隐蔽的问题是迷失在中间Lost-in-the-Middle模型对长文本中间部分的注意力衰减导致早期记忆照样丢失。一个工程上可行的解法是分层记忆架构工作记忆短程、情节记忆中程、语义记忆长程三层协同。flowchart TD A[用户输入] -- B[工作记忆缓冲区] B -- C{触发归档?} C --|摘要阈值达| D[情节记忆提取器] C --|未触发| E[直接注入Prompt] D -- F{冲突检测} F --|新信息| G[语义记忆写入] F --|冲突| H[记忆合并/覆盖] G -- I[向量数据库] H -- I I -- J[语义检索] E -- K[LLM推理] J -- K二、分层记忆的工程实现工作记忆Working Memory就是当前对话的完整上下文。不做压缩但限制长度建议最近 10 轮。关键设计点是滑动窗口 优先级标记——不是所有历史轮次都同等重要。情节记忆Episodic Memory会话级别的摘要归档。核心思路在对话达到 N 轮或 token 超过阈值时异步触发摘要提取。这里有一个容易被忽略的细节——摘要粒度要与检索场景对齐。如果按整段对话摘要检索时只能返回一大坨不够细。应该按语义块拆分每块标注时间戳和重要性权重。语义记忆Semantic Memory跨会话的持久化知识。存储到向量数据库Milvus/Qdrant/Pinecone检索时做 hybrid search向量相似度 关键词 BM25取交集。元数据里必须带时间衰减因子——两周前的偏好权重应低于昨天的。三、生产级记忆写入代码import asyncio import time from dataclasses import dataclass, field from typing import Optional import numpy as np from openai import AsyncOpenAI dataclass class MemoryEntry: content: str embedding: Optional[list[float]] None timestamp: float field(default_factorytime.time) importance: float 1.0 session_id: str ttl_days: int 30 class SemanticMemoryStore: 语义记忆存储层。 设计决策 - 使用异步客户端避免阻塞 Agent 主循环 - 时间衰减嵌入在检索阶段计算而非写入阶段减少存储耦合 - 重要性评分由 LLM 生成而非规则引擎——规则跟不上场景变化 def __init__(self, vector_dim: int 1536, decay_halflife: int 14): self._client AsyncOpenAI() self._store: list[MemoryEntry] [] self._vector_dim vector_dim self._decay_halflife decay_halflife async def embed(self, text: str) - list[float]: 文本转向量带重试和超时保护 for attempt in range(3): try: resp await asyncio.wait_for( self._client.embeddings.create( modeltext-embedding-3-small, inputtext ), timeout5.0, ) return resp.data[0].embedding except asyncio.TimeoutError: if attempt 2: raise await asyncio.sleep(1 * (attempt 1)) except Exception: if attempt 2: raise await asyncio.sleep(2**attempt) raise RuntimeError(unreachable) async def write( self, content: str, session_id: str, importance: float 1.0, ttl_days: int 30, ) - None: 异步写入记忆不阻塞 Agent 推理链路 embedding await self.embed(content) entry MemoryEntry( contentcontent, embeddingembedding, session_idsession_id, importanceimportance, ttl_daysttl_days, ) self._store.append(entry) def _time_decay(self, entry: MemoryEntry, now: float) - float: 指数衰减半衰期默认 14 天 age_days (now - entry.timestamp) / 86400.0 return np.exp(-np.log(2) * age_days / self._decay_halflife) async def search(self, query: str, session_id: str, top_k: int 5) - list[MemoryEntry]: 向量检索 时间衰减 重要性加权 q_embed await self.embed(query) now time.time() scored: list[tuple[float, MemoryEntry]] [] for entry in self._store: if entry.session_id ! session_id: continue if now - entry.timestamp entry.ttl_days * 86400: continue if entry.embedding is None: continue similarity np.dot(q_embed, entry.embedding) decay self._time_decay(entry, now) score similarity * decay * entry.importance scored.append((score, entry)) scored.sort(keylambda x: x[0], reverseTrue) return [entry for _, entry in scored[:top_k]]注释要点write不阻塞推理链路是刻意的——记忆归档优先级低于实时响应。时序解码用指数模型而非线性因为人类记忆遗忘曲线是指数级。四、边界分析与架构权衡适用场景对话式 Agent单会话轮次 20跨会话偏好记忆用户总问同类问题企业知识库场景需要人机联合记忆不适用场景单次问答 Agent记忆无关紧要对延迟极度敏感的系统每次检索增加 50-200ms需要精确审计的场景摘要过程不可逆原始对话可能丢失核心缺陷摘要精度损失摘要器压缩时必然丢失细节且这个损失不可恢复记忆冲突用户改口时旧记忆和新偏好之间的冲突处理策略是启发式的冷启动问题新用户没有记忆积累时系统退化为普通对话成本敏感性向量检索的 QPS 费用随存储量线性增长。10 万条记忆 1536 维向量Qdrant 单次检索约 10ms。如果加到 Agent 的每个 tool call 前都检索总延迟可能翻倍。五、总结分层记忆不是银弹。它的核心价值在于用可控的成本换取可预期的记忆能力。工作记忆保下限情节记忆提效率语义记忆做持久化。三层解耦后可以独立调优——这是最大的架构红利。但别指望它能完美复现人类记忆它只是把遗忘从随机丢失变成了可配置衰减。