引言:当大模型拥有“记忆”
大语言模型(LLM)虽然在参数中编码了海量知识,但它们本质上是“无状态”的。每一次对话都是从零开始,无法记住你是谁、聊过什么、有过怎样的决策。这种无状态特性在简单的问答场景中尚可接受,但对于构建能够持续服务、跨会话积累经验的 AI Agent 而言,记忆系统是其核心基础设施。
本文将深入剖析 AI Agent 记忆系统的完整架构设计,从认知科学中的记忆分类出发,逐步拆解短期记忆、工作记忆、长期记忆的工程实现方案,并给出可直接运行的代码示例。
一、认知科学中的记忆模型
在设计 AI Agent 的记忆系统之前,让我们先回顾认知科学中对人类记忆的经典分类。这不仅是理论参考,更是工程设计的灵感来源。
1.1 Atkinson-Shiffrin 记忆模型
1968年,Richard Atkinson 和 Richard Shiffrin 提出了经典的多存储模型,将记忆分为三个层次:
- 感觉记忆(Sensory Memory):持续时间极短(毫秒级),容量巨大,用于暂存感官输入
- 短期记忆(Short-Term Memory):持续数秒到数分钟,容量有限(7±2个信息块),是意识加工的工作台
- 长期记忆(Long-Term Memory):持续时间可达数年甚至终身,容量接近无限,分为陈述性记忆(语义记忆和情景记忆)和程序性记忆
1.2 Baddeley 的工作记忆模型
Alan Baddeley 进一步细化了短期记忆的概念,提出了工作记忆(Working Memory)模型,强调记忆不是被动存储,而是在实时加工和处理。工作记忆包含四个子系统:中央执行系统、语音环路、视觉空间画板和情景缓冲区。
1.3 对 AI Agent 的映射
这些认知模型为 AI Agent 记忆系统的设计提供了清晰框架:
- 感觉记忆 → Agent 的原始输入流(传感器数据、用户消息、环境观察)
- 工作记忆 → 当前对话上下文窗口 + 正在处理的任务状态
- 长期记忆 → 持久化的用户画像、历史经验、领域知识库
二、AI Agent 记忆系统的核心挑战
将认知科学中的记忆模型映射到工程实践时,我们面临几个核心挑战:
2.1 容量与相关性的矛盾
Agent 在与用户交互中会产生大量信息,但上下文窗口有限(即使扩展到128K、1M tokens)。如何决定哪些信息值得保留?如何在有限的上下文空间内呈现最相关的记忆?
# 信息价值评估示例
def calculate_memory_importance(memory_item):
"""计算记忆项的重要性分数"""
score = 0.0
# 情感权重:情感强烈的互动通常更重要
score += memory_item.emotional_weight * 0.3
# 时效性:近期事件更重要,但有衰减
score += recency_factor(memory_item.timestamp) * 0.2
# 提及频率:被反复引用的信息更重要
score += log(1 + memory_item.reference_count) * 0.25
# 实用性:能指导行动的信息更重要
score += memory_item.actionability * 0.25
return score
2.2 提取与遗忘的平衡
人类的遗忘并非缺陷,而是智能的关键特征——通过遗忘无关信息来减轻认知负担。AI Agent 同样需要遗忘机制,否则记忆库会无限膨胀,检索质量也会随之下降。
2.3 一致性与更新
当用户的偏好发生变化、过去的知识被纠正时,记忆系统如何检测冲突并进行更新?过时的记忆比没有记忆更危险,因为它可能导致错误的决策。
三、长期记忆的三种存储范式
3.1 向量存储(Vector Store)
向量存储是目前最主流的长期记忆实现方案。核心思路是将每条记忆编码为高维向量索引,查询时通过语义相似度检索最相关的记忆片段。
from typing import List, Dict, Any
from dataclasses import dataclass, field
import numpy as np
from datetime import datetime
@dataclass
class MemoryEntry:
"""单条记忆项"""
id: str
content: str # 记忆内容
embedding: np.ndarray # 向量编码
memory_type: str # 类型:episodic(情景)/ semantic(语义)/ procedural(程序)
timestamp: datetime = field(default_factory=datetime.now)
importance: float = 0.5 # 重要性分数
access_count: int = 0 # 被访问次数
metadata: Dict[str, Any] = field(default_factory=dict)
class VectorMemoryStore:
"""向量记忆存储引擎"""
def __init__(self, embedding_model, dim=1536):
self.embedding_model = embedding_model
self.dim = dim
self.memories: List[MemoryEntry] = []
# 使用 FAISS 或类似库构建高效索引
self.index = None
self._rebuild_index()
def store(self, content: str, memory_type: str = "episodic", **kwargs):
"""存储一条新记忆"""
embedding = self.embedding_model.encode(content)
entry = MemoryEntry(
id=generate_uuid(),
content=content,
embedding=embedding,
memory_type=memory_type,
**kwargs
)
self.memories.append(entry)
self._rebuild_index()
return entry.id
def retrieve(self, query: str, top_k: int = 5,
memory_type: str = None) -> List[MemoryEntry]:
"""语义检索相关记忆"""
query_embedding = self.embedding_model.encode(query)
# 按类型过滤
candidates = self.memories
if memory_type:
candidates = [m for m in candidates if m.memory_type == memory_type]
# 混合检索:语义相似度 × 重要性 × 时效性
scores = []
for m in candidates:
semantic_score = cosine_similarity(query_embedding, m.embedding)
importance_score = m.importance
recency_score = temporal_decay(m.timestamp)
combined = semantic_score * 0.5 + importance_score * 0.3 + recency_score * 0.2
scores.append((combined, m))
# 按分数排序,返回 top_k
scores.sort(key=lambda x: x[0], reverse=True)
results = [m for _, m in scores[:top_k]]
# 更新访问计数(记忆被访问会增强)
for m in results:
m.access_count += 1
return results
def forget(self, threshold_days: int = 30, min_importance: float = 0.3):
"""遗忘机制:清除低重要性且长期未被引用的记忆"""
cutoff = datetime.now() - timedelta(days=threshold_days)
self.memories = [
m for m in self.memories
if not (m.timestamp < cutoff
and m.access_count == 0
and m.importance < min_importance)
]
self._rebuild_index()
def consolidate(self):
"""记忆巩固:将关联的记忆合并或建立联系"""
# 查找高度相似的记忆对,合并去重
to_merge = []
for i, m1 in enumerate(self.memories):
for m2 in self.memories[i+1:]:
sim = cosine_similarity(m1.embedding, m2.embedding)
if sim > 0.95: # 极高的相似度
to_merge.append((m1, m2, sim))
# 合并:保留更重要的那个,吸收metadata
for m1, m2, _ in to_merge:
keep = m1 if m1.importance >= m2.importance else m2
discard = m2 if keep is m1 else m1
keep.importance = min(1.0, keep.importance + 0.1) # 巩固增强
keep.metadata.update(discard.metadata)
self.memories.remove(discard)
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def temporal_decay(timestamp: datetime, half_life_days: int = 30) -> float:
"""时间衰减函数:半衰期指数衰减"""
days_elapsed = (datetime.now() - timestamp).days
return 0.5 ** (days_elapsed / half_life_days)
def generate_uuid() -> str:
import uuid
return str(uuid.uuid4())
3.2 知识图谱存储(Knowledge Graph)
对于需要结构化推理的记忆,知识图谱是更合适的选择。实体-关系-实体的三元组结构使得 Agent 能够进行多跳推理。
@dataclass
class Entity:
"""知识图谱中的实体节点"""
id: str
name: str
type: str # Person, Place, Concept, Event, etc.
properties: Dict[str, Any] = field(default_factory=dict)
created_at: datetime = field(default_factory=datetime.now)
@dataclass
class Relation:
"""知识图谱中的关系边"""
subject_id: str # 主体实体
predicate: str # 谓词/关系
object_id: str # 客体实体
confidence: float = 1.0
source: str = "" # 记忆来源
timestamp: datetime = field(default_factory=datetime.now)
class KnowledgeGraphMemory:
"""基于知识图谱的记忆存储"""
def __init__(self):
self.entities: Dict[str, Entity] = {}
self.relations: List[Relation] = []
self.adjacency: Dict[str, List[Relation]] = {} # 邻接索引
def add_triple(self, subject: str, predicate: str,
object_: str, confidence: float = 1.0):
"""添加三元组(自动创建不存在的实体)"""
subj_entity = self._get_or_create_entity(subject)
obj_entity = self._get_or_create_entity(object_)
rel = Relation(
subject_id=subj_entity.id,
predicate=predicate,
object_id=obj_entity.id,
confidence=confidence
)
self.relations.append(rel)
# 更新索引
self.adjacency.setdefault(subj_entity.id, []).append(rel)
return rel
def query(self, entity_name: str, depth: int = 2) -> Dict:
"""从指定实体出发,做多跳推理查询"""
entity = self._find_entity_by_name(entity_name)
if not entity:
return {}
visited = set()
result = {"entities": [], "relations": []}
self._traverse(entity.id, depth, visited, result)
return result
def _traverse(self, entity_id: str, remaining_depth: int,
visited: set, result: Dict):
"""深度优先遍历知识图谱"""
if remaining_depth <= 0 or entity_id in visited:
return
visited.add(entity_id)
entity = self.entities.get(entity_id)
if entity:
result["entities"].append(entity)
for rel in self.adjacency.get(entity_id, []):
result["relations"].append(rel)
self._traverse(rel.object_id, remaining_depth - 1, visited, result)
def _find_entity_by_name(self, name: str) -> Entity:
for e in self.entities.values():
if e.name.lower() == name.lower():
return e
return None
def _get_or_create_entity(self, name: str,
entity_type: str = "Concept") -> Entity:
existing = self._find_entity_by_name(name)
if existing:
return existing
entity = Entity(id=generate_uuid(), name=name, type=entity_type)
self.entities[entity.id] = entity
return entity
def detect_conflicts(self) -> List[Tuple[Relation, Relation]]:
"""检测知识冲突(同一主体-谓词指向不同客体且置信度相近)"""
conflicts = []
relation_map = {}
for rel in self.relations:
key = (rel.subject_id, rel.predicate)
if key in relation_map:
other = relation_map[key]
if other.object_id != rel.object_id:
if abs(other.confidence - rel.confidence) < 0.3:
conflicts.append((other, rel))
else:
relation_map[key] = rel
return conflicts
3.3 时序记忆存储(Temporal Memory)
对于需要理解事件顺序、因果关系的场景,时序记忆至关重要。它记录了事件发生的先后顺序和因果链条。
@dataclass
class TemporalEvent:
"""时序事件记录"""
id: str
description: str
timestamp: datetime
actor: str # 事件主体
action: str # 动作
outcome: str # 结果
causal_links: List[str] = field(default_factory=list) # 关联事件ID
episode_id: str = "" # 所属情景片段
class TemporalMemoryStore:
"""时序记忆存储引擎"""
def __init__(self):
self.events: List[TemporalEvent] = []
self.episodes: Dict[str, List[str]] = {} # episode_id -> [event_ids]
def record(self, event: TemporalEvent):
"""记录一个事件,自动建立因果链"""
# 查找可能被此事件导致的结果(反向因果)
for past_event in self.events[-50:]: # 考察最近50个事件
if self._is_likely_consequence(past_event, event):
event.causal_links.append(past_event.id)
self.events.append(event)
if event.episode_id:
self.episodes.setdefault(event.episode_id, []).append(event.id)
def reconstruct_timeline(self, episode_id: str) -> List[TemporalEvent]:
"""重建某个情景片段的完整时间线"""
event_ids = self.episodes.get(episode_id, [])
events = [e for e in self.events if e.id in event_ids]
events.sort(key=lambda e: e.timestamp)
return events
def get_causal_chain(self, event_id: str, depth: int = 5) -> List[TemporalEvent]:
"""追溯因果链条"""
chain = []
to_visit = [event_id]
visited = set()
while to_visit and len(chain) < depth:
current_id = to_visit.pop(0)
if current_id in visited:
continue
visited.add(current_id)
event = self._find_event(current_id)
if event:
chain.append(event)
to_visit.extend(event.causal_links)
return chain
def _is_likely_consequence(self, cause: TemporalEvent,
effect: TemporalEvent) -> bool:
"""判断两个事件是否可能存在因果关系(简化实现)"""
time_diff = (effect.timestamp - cause.timestamp).total_seconds()
# 因果关系通常在合理的时间窗口内
return 0 < time_diff < 3600 # 1小时内
def _find_event(self, event_id: str) -> TemporalEvent:
for e in self.events:
if e.id == event_id:
return e
return None
四、工作记忆:Agent 的意识中心
工作记忆是 Agent 的“意识”——它处理当前任务、维护对话状态、准备调用的工具。与大模型的上下文窗口紧密耦合,但需要精心管理。
4.1 动态上下文窗口管理
class WorkingMemoryManager:
"""工作记忆管理器 — 动态上下文窗口"""
def __init__(self, max_tokens: int = 8000):
self.max_tokens = max_tokens
self.token_budget = {
"system_prompt": 0.20, # 20% 给系统提示
"current_task": 0.15, # 15% 给当前任务描述
"retrieved_memories": 0.25, # 25% 给检索到的相关记忆
"conversation_history": 0.25, # 25% 给对话历史
"tool_outputs": 0.10, # 10% 给工具输出
"buffer": 0.05 # 5% 留给缓冲
}
self.sections = {key: [] for key in self.token_budget}
def add_to_section(self, section: str, content: str):
"""向指定区域添加内容"""
if section in self.sections:
self.sections[section].append(content)
def assemble_context(self) -> str:
"""根据 token 预算组装最终上下文"""
context_parts = []
# 系统提示始终在最前
system_content = "\n".join(self.sections["system_prompt"])
if system_content:
context_parts.append(system_content)
# 当前任务
task_content = "\n".join(self.sections["current_task"])
if task_content:
context_parts.append(f"## 当前任务\n{task_content}")
# 检索到的记忆(精简摘要)
memory_content = self._summarize_section(
self.sections["retrieved_memories"],
budget=self.max_tokens * self.token_budget["retrieved_memories"]
)
if memory_content:
context_parts.append(f"## 相关记忆\n{memory_content}")
# 对话历史(带压缩)
history_content = self._compress_history(
self.sections["conversation_history"],
budget=self.max_tokens * self.token_budget["conversation_history"]
)
if history_content:
context_parts.append(f"## 对话历史\n{history_content}")
# 工具输出
tool_content = self._summarize_section(
self.sections["tool_outputs"],
budget=self.max_tokens * self.token_budget["tool_outputs"]
)
if tool_content:
context_parts.append(f"## 工具输出\n{tool_content}")
return "\n\n".join(context_parts)
def _summarize_section(self, items: List[str], budget: float) -> str:
"""在 token 预算内对内容做取舍或摘要"""
total_tokens = sum(count_tokens(item) for item in items)
if total_tokens <= budget:
return "\n".join(items)
# 按重要性排序,只保留预算内的内容
scored = [(self._quick_score(item), item) for item in items]
scored.sort(key=lambda x: x[0], reverse=True)
kept = []
remaining_budget = budget
for _, item in scored:
item_tokens = count_tokens(item)
if item_tokens <= remaining_budget:
kept.append(item)
remaining_budget -= item_tokens
return "\n".join(kept[:5]) # 最多保留5条
def _compress_history(self, messages: List[str], budget: float) -> str:
"""压缩对话历史:保留首尾,中间摘要"""
if len(messages) <= 6:
return "\n".join(messages)
# 保留最近3条和最早1条
recent = messages[-3:]
earliest = messages[:1]
middle = messages[1:-3]
# 对中间部分做摘要
if middle and count_tokens("\n".join(middle)) > budget * 0.4:
middle_summary = f"[{len(middle)} 条历史消息已省略]\\n"
return "\n".join(earliest + [middle_summary] + recent)
return "\n".join(earliest + middle + recent)
def _quick_score(self, content: str) -> float:
"""快速评估内容重要性"""
score = 0.0
if "用户说" in content or "用户问" in content:
score += 0.4 # 用户直接输入很重要
if "决策" in content or "选择" in content:
score += 0.3 # 决策信息重要
if len(content) < 200:
score += 0.1 # 简短信息通常更聚焦
return score
def count_tokens(text: str) -> int:
"""粗略token计算(中文约2-3字符/token)"""
chinese_chars = sum(1 for c in text if "\u4e00" <= c <= "\u9fff")
ascii_part = len(text) - chinese_chars
return int(chinese_chars / 2.5 + ascii_part / 4)
五、完整的记忆系统架构
将上面的各个组件整合为一个完整的 Agent 记忆系统:
class AgentMemorySystem:
"""AI Agent 完整记忆系统"""
def __init__(self, config: Dict[str, Any]):
# 三层记忆存储
self.vector_store = VectorMemoryStore(config["embedding_model"])
self.knowledge_graph = KnowledgeGraphMemory()
self.temporal_store = TemporalMemoryStore()
# 工作记忆管理器
self.working_memory = WorkingMemoryManager(
max_tokens=config.get("max_context_tokens", 8000)
)
# 用户画像
self.user_profile: Dict[str, Any] = {}
# 记忆参数
self.consolidation_interval = config.get(
"consolidation_interval", 10 # 每10次交互巩固一次
)
self.interaction_count = 0
async def process_interaction(self, user_input: str,
agent_response: str) -> None:
"""处理一次交互:提取信息、更新记忆"""
self.interaction_count += 1
# 步骤1: 信息抽取(使用LLM提取结构化信息)
extracted = await self._extract_information(
user_input, agent_response
)
# 步骤2: 存入不同记忆系统
for fact in extracted.get("facts", [], ):
# 事实性知识 → 向量存储 + 知识图谱
self.vector_store.store(
fact["content"],
memory_type="semantic",
importance=fact.get("importance", 0.5)
)
if fact.get("triples"):
for sub, pred, obj in fact["triples"]:
self.knowledge_graph.add_triple(sub, pred, obj)
for event in extracted.get("events", []):
# 事件 → 时序记忆
self.temporal_store.record(TemporalEvent(
id=generate_uuid(),
description=event["description"],
timestamp=datetime.now(),
actor=event.get("actor", "user"),
action=event["action"],
outcome=event.get("outcome", "")
))
for pref in extracted.get("preferences", []):
# 偏好 → 用户画像
self.user_profile[pref["key"]] = pref["value"]
# 步骤3: 周期性记忆巩固
if self.interaction_count % self.consolidation_interval == 0:
await self._consolidate_memories()
async def recall(self, current_context: str) -> str:
"""为当前上下文回忆相关记忆"""
# 多路召回
vector_results = self.vector_store.retrieve(
current_context, top_k=5
)
# 从知识图谱获取相关实体
key_entities = await self._extract_entities(current_context)
kg_results = []
for entity in key_entities[:3]:
kg_data = self.knowledge_graph.query(entity, depth=1)
if kg_data.get("relations"):
kg_results.append(kg_data)
# 合并去重
relevant_memories = self._merge_recall_results(
vector_results, kg_results
)
# 注入工作记忆
self.working_memory.add_to_section(
"retrieved_memories",
format_memories(relevant_memories)
)
return self.working_memory.assemble_context()
async def _extract_information(self, user_input: str,
agent_response: str) -> Dict:
"""使用 LLM 从交互中抽取结构化信息"""
# 实际实现中调用LLM进行信息抽取
# 此处返回模拟结构
return {
"facts": [],
"events": [],
"preferences": []
}
async def _extract_entities(self, text: str) -> List[str]:
"""识别文本中的关键实体"""
# 实际实现中调用NER模型或LLM
return []
def _merge_recall_results(self, vector_results, kg_results):
"""合并多路召回结果,去重排序"""
seen = set()
merged = []
for item in vector_results:
if item.id not in seen:
seen.add(item.id)
merged.append(item)
# 将知识图谱结果添加
for kg in kg_results:
merged.append(kg)
return merged
async def _consolidate_memories(self):
"""记忆巩固周期任务"""
# 1. 遗忘低价值记忆
self.vector_store.forget(threshold_days=30)
# 2. 合并相似记忆
self.vector_store.consolidate()
# 3. 检测知识冲突
conflicts = self.knowledge_graph.detect_conflicts()
if conflicts:
await self._resolve_conflicts(conflicts)
async def _resolve_conflicts(self, conflicts):
"""解决知识冲突(可结合LLM判断)"""
pass
def format_memories(memories) -> str:
"""将记忆格式化为文本摘要"""
parts = []
for i, m in enumerate(memories, 1):
if isinstance(m, MemoryEntry):
parts.append(f"[{i}] {m.content}")
elif isinstance(m, dict) and "relations" in m:
parts.append(f"[知识图谱] 发现 {len(m['relations'])} 条关联")
return "\n".join(parts)
六、工程实践中的关键决策
6.1 何时用什么记忆?
| 场景 | 推荐存储 | 检索方式 |
|---|---|---|
| 用户偏好(喜欢/不喜欢) | 知识图谱 + 用户画像 | 按键查询 |
| 对话中提到的项目细节 | 向量存储 | 语义相似度 |
| 用户的工作经历和背景 | 知识图谱 | 实体关系遍历 |
| 之前任务的成功/失败经验 | 时序记忆 + 向量存储 | 时间 + 语义混合 |
| 领域知识和参考资料 | 向量存储(带元数据) | 语义 + 标签过滤 |
6.2 记忆的安全与隐私
记忆系统在赋予 Agent “记忆力”的同时也引入了隐私风险:
- 数据最小化:只存储服务所必需的信息,而非全部对话记录
- 访问控制:在多用户场景下,确保记忆隔离
- 遗忘权:提供机制让用户查看、编辑和删除自己的记忆数据
- 敏感信息过滤:在存入记忆前,使用安全模型检测并脱敏密码、密钥等敏感信息
6.3 性能优化
- 异步写入:记忆存储不应阻塞用户交互,使用消息队列异步处理
- 分层缓存:L1 缓存最近使用的记忆,L2 用向量索引,L3 用持久化数据库
- 批量操作:定期对记忆进行批量整理而非每次交互都处理
- 索引优化:对高频查询的实体和类型建立额外索引
七、与 RAG 的关系:互补而非替代
很多人会将 Agent 记忆系统与 RAG(Retrieval-Augmented Generation)混淆,它们有本质区别:
- RAG:针对静态知识库的检索增强,每次查询独立,不保留跨会话状态
- Agent 记忆:动态累积的个性化知识,跨会话持久化,包含用户画像、交互历史、因果链条
实际系统中,Agent 记忆系统的“长期记忆”层可以作为 RAG 的底层存储,使 RAG 从静态文档检索升级为动态经验检索。
八、未来展望
AI Agent 记忆系统仍在快速演进,几个值得关注的方向:
- 生物启发记忆:借鉴海马体的索引机制,实现更高效的记忆编码和回忆
- 记忆压缩与泛化:从具体经历中提炼通用能力,从 episodic 到 semantic 的转化
- 多 Agent 共享记忆:Agent 间可安全地共享经验记忆,形成集体智慧
- 可解释记忆:Agent 能解释“为什么我记得这个”和“为什么我选择回忆这个”
结语
记忆系统是 AI Agent 从“工具”走向“伙伴”的关键一步。好的记忆系统不只是存储和检索,它涉及信息价值的判断、冲突的解决、遗忘的智慧,以及隐私的尊重。正如人类认知科学家 Endel Tulving 所言:“记忆让我们得以跨越时间。” 对 AI Agent 而言,记忆让它得以跨越会话,成为一个连贯的、持续成长的智能存在。

发表评论 取消回复