Agent 记忆架构演进:从全量上下文到层次化存储
2026/7/23 14:03:30 网站建设 项目流程

Agent 记忆架构演进:从全量上下文到层次化存储

一、个性化深度引言

Agent 对话到第 20 轮时,模型开始遗忘第 3 轮提到的关键约束。因为上下文窗口已经被之前的对话历史填满了,第 3 轮的信息被 FIFO 机制无情地挤出了注意力范围。

这就是"全量上下文记忆"的天花板:它把所有历史当作同等重要的信息,不区分主次、不归档、不索引。当 Agent 需要执行跨多轮的多步任务时,这种记忆方式的脆弱性暴露无遗。

见证奇迹的时刻,不是在换了个更大窗口的模型之后。而是意识到:人类的记忆不是线性的日志文件。我们有短期记忆、工作记忆、长期记忆,每一层有不同的保留策略和检索机制。Agent 的记忆架构,应该向这个方向演进。

二、个性化原理剖析

记忆架构的演进路径:

第一代:全量上下文记忆
所有历史消息按顺序拼接,每轮追加到 prompt 中。结构简单,但随对话增长迅速超出窗口限制,且不区分信息重要性。

第二代:摘要压缩记忆
使用模型对历史对话做定期摘要,只保留摘要 + 最近 N 轮。缓解了 Token 膨胀问题,但摘要可能丢失精确细节(如具体的数字、规则、代码片段)。

第三代:层次化存储记忆
模仿人类记忆的多层结构,按信息寿命和检索频率分为三层。

三层记忆的分工:

工作记忆层:保存最近 N 轮完整对话(默认 5-8 轮),保证当前上下文的连贯性。这一层不压缩、不摘要。

短期记忆层:对超出工作记忆窗口的历史做滑动摘要。同时提取关键实体(人名、数字、约束条件)单独存储,避免摘要丢失精确值。

长期记忆层:跨会话持久化存储。向量数据库支持语义相似度检索,知识图谱维护实体关系,规则引擎管理显式约束。

三、个性化代码实践

import hashlib import json import time from dataclasses import dataclass, field from typing import Dict, List, Optional @dataclass class MemoryItem: """ 设计原因:统一记忆数据结构,所有记忆层共用。 type 区分记忆类型,priority 控制检索排序。 """ content: str memory_type: str # "working" | "short_term" | "long_term" priority: float = 0.0 timestamp: float = field(default_factory=time.time) metadata: Dict = field(default_factory=dict) @property def memory_id(self) -> str: """设计原因:用内容哈希做唯一ID,支持去重。""" return hashlib.md5( self.content.encode() ).hexdigest()[:16] class HierarchicalMemoryManager: """ 设计原因:统一管理三层记忆的写入、检索、衰减。 对外暴露单一接口,内部处理三层之间的协调。 """ def __init__( self, working_memory_size: int = 8, summary_interval: int = 10, ): # 设计原因:工作记忆用固定长度队列, # 超出窗口自动出队,不需要手动淘汰。 self._working_memory: List[MemoryItem] = [] self._working_memory_size = working_memory_size # 设计原因:短期摘要列表,定期合并。 self._short_term_summaries: List[MemoryItem] = [] # 设计原因:长期记忆用字典存储, # 实际场景应使用向量数据库(如 Milvus)。 self._long_term_store: Dict[str, MemoryItem] = {} # 设计原因:显式约束独立存储, # 每次 Prompt 组装时强制注入,不会丢失。 self._constraints: Dict[str, str] = {} self._summary_interval = summary_interval self._message_count = 0 self._summary_model = None # 摘要模型引用 def add_message(self, role: str, content: str) -> None: """ 设计原因:每收到一条消息,先写入工作记忆。 超出窗口触发短期记忆归档。 """ item = MemoryItem( content=f"{role}: {content}", memory_type="working", ) self._working_memory.append(item) self._message_count += 1 # 设计原因:超出工作记忆窗口,最旧的消息归档到短期记忆。 if len(self._working_memory) > self._working_memory_size: oldest = self._working_memory.pop(0) oldest.memory_type = "short_term" self._short_term_summaries.append(oldest) # 设计原因:达到摘要间隔,触发短期记忆压缩。 if self._message_count % self._summary_interval == 0: self._compress_short_term() def add_constraint(self, key: str, value: str) -> None: """ 设计原因:显式约束独立存储, 不受窗口大小影响,永久保留直到主动删除。 """ self._constraints[key] = value def add_long_term(self, content: str, metadata: Dict = None) -> None: """ 设计原因:跨会话持久化记忆。 使用内容哈希作为 Key,天然支持去重。 """ item = MemoryItem( content=content, memory_type="long_term", metadata=metadata or {}, ) self._long_term_store[item.memory_id] = item def retrieve_long_term( self, query: str, top_k: int = 5 ) -> List[MemoryItem]: """ 设计原因:基于关键词匹配的简易检索。 实际应用应替换为向量语义检索(embedding相似度)。 """ results = [] query_lower = query.lower() for item in self._long_term_store.values(): score = self._keyword_match_score( query_lower, item.content.lower() ) if score > 0: item.priority = score results.append(item) results.sort(key=lambda x: x.priority, reverse=True) return results[:top_k] def _compress_short_term(self) -> None: """ 设计原因:将短期记忆合并为一条摘要。 使用摘要模型而非简单截断,保留语义完整性。 """ if not self._short_term_summaries: return combined = "\n".join([ item.content for item in self._short_term_summaries ]) # 设计原因:摘要合并后清空短期记忆列表, # 但合并结果持久化到长期记忆。 summary = f"[摘要] {combined[:200]}..." summary_item = MemoryItem( content=summary, memory_type="long_term", priority=0.8, ) self._long_term_store[summary_item.memory_id] = summary_item self._short_term_summaries.clear() def build_context( self, current_query: str ) -> str: """ 设计原因:组装三层记忆到最终 Prompt 上下文。 顺序:约束 > 工作记忆 > 检索到的长期记忆。 约束必须排在最前面,确保模型优先遵守。 """ parts = [] # 设计原因:显式约束最高优先级。 if self._constraints: constraints_text = "\n".join([ f"- {k}: {v}" for k, v in self._constraints.items() ]) parts.append(f"【必须遵守的约束】\n{constraints_text}") # 设计原因:最近对话保证连贯性。 if self._working_memory: working_text = "\n".join([ item.content for item in self._working_memory ]) parts.append(f"【最近对话】\n{working_text}") # 设计原因:相关长期记忆补充背景知识。 relevant = self.retrieve_long_term(current_query, top_k=3) if relevant: long_term_text = "\n".join([ item.content for item in relevant ]) parts.append(f"【相关历史信息】\n{long_term_text}") return "\n\n".join(parts) @staticmethod def _keyword_match_score(query: str, content: str) -> float: """设计原因:简易关键词匹配,避免依赖外部库。""" query_words = set(query.split()) content_words = set(content.split()) intersection = query_words & content_words if not query_words: return 0.0 return len(intersection) / len(query_words)

四、个性化边界权衡

1. 摘要粒度:轮次触发 vs Token 触发

按轮次触发摘要(每 10 轮一次),实现简单但精度粗糙——有的轮次信息量大,有的很少。按 Token 触发(每 2000 Token 一次)更精确,但需要实时计算 Token 数量。推荐混合方案:轮次优先,Token 超标时提前触发。

2. 向量检索 vs 全文检索

向量检索(Embedding + 余弦相似度)对语义匹配效果好,但需要额外的 Embedding 模型和向量数据库。全文检索(关键词匹配)实现简单,但无法理解同义替换。长期记忆规模 >1000 条时,向量检索的收益超过其成本。

3. 约束存储:显式声明 vs 隐式学习

显式约束(规则引擎)保证 100% 遵守,但需要人工定义。隐式约束(从对话中学习)更灵活,但模型可能遗忘或误用。建议:核心业务约束用显式存储,用户偏好用隐式学习。

4. 跨会话持久化:本地文件 vs 云存储

本地文件适合单机部署,但多实例间无法共享记忆。云存储(Redis/数据库)支持分布式,但增加网络延迟约 1-5ms。推荐:生产环境用 Redis,开发环境用本地 JSON 文件。

5. 记忆衰减:固定窗口 vs 重要性衰减

固定窗口简单但粗暴,可能丢失重要但较早的信息。重要性衰减(每轮乘以衰减因子 0.95)保留重要信息更久,但衰减函数设计需要经验调整。建议:固定窗口作为基线,重要性衰减作为增强。

五、总结

Agent 记忆架构从全量上下文演进到层次化存储,核心改进在于按信息生命周期分层管理:工作记忆保证对话连贯性,短期记忆做摘要压缩,长期记忆支持跨会话检索。三层配合实现了在有限 Token 预算内的信息保留最大化。工程实践中需要权衡摘要粒度、检索方式、衰减策略,核心原则是:让重要的信息保留得足够久,让不重要的信息节省 Token 预算。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询