引言
在构建 AI Agent 的过程中,我们常常会遇到一个核心问题:如何让 Agent 记住用户、记住对话、记住上下文?
记忆机制是 AI Agent 从"一次性聊天工具"进化为"智能助手"的关键。一个具备良好记忆能力的 Agent,能够理解用户的长期偏好、维护对话的连贯性、甚至在多次会话之间建立知识关联。
本文将深入探讨 AI Agent 记忆机制的设计原理与工程实践,从短期记忆到长期记忆,从理论到代码,帮助你构建更智能的 Agent 系统。
一、为什么记忆对 AI Agent 如此重要?
1.1 没有记忆的 Agent 面临的问题
想象一下这样的场景:
用户:"帮我预订明天去上海的机票。" Agent:"好的,已为您查询到明天去上海的航班。" 用户:"要上午的。" Agent:"请问您要去哪个城市?"
这就是典型的上下文丢失问题。没有记忆机制的 Agent,每次交互都是独立的,无法理解指代关系,也无法维护对话状态。
1.2 记忆带来的价值
具备记忆能力的 Agent 可以实现:
- 个性化服务:记住用户的偏好、习惯、历史行为
- 连贯对话:理解上下文,处理指代消解
- 知识积累:在多次交互中持续学习和改进
- 情感连接:记住与用户的互动历史,建立信任关系
二、AI Agent 记忆的分类
根据记忆的持续时间、存储方式和用途,我们可以将 AI Agent 的记忆分为以下几类:
┌─────────────────────────────────────────────────────────────┐
│ AI Agent 记忆架构 │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ 工作记忆 │ 短期记忆 │ 长期记忆 │ 外部记忆 │
│ (Working) │ (Short-term)│ (Long-term) │ (External) │
├──────────────┼──────────────┼──────────────┼────────────────┤
│ 当前对话上下文 │ 近期对话历史 │ 用户画像 │ 知识库 │
│ 临时计算结果 │ 会话状态 │ 历史摘要 │ 文档存储 │
│ 实时感知数据 │ 多轮意图 │ 学习到的模式 │ 向量数据库 │
└──────────────┴──────────────┴──────────────┴────────────────┘
2.1 工作记忆(Working Memory)
工作记忆是 Agent 的"大脑缓存",用于存储当前正在处理的信息:
- 当前对话上下文:正在进行的对话内容
- 临时计算结果:工具调用的中间结果
- 实时感知数据:传感器输入、用户行为等
特点:容量有限、访问速度快、生命周期短
2.2 短期记忆(Short-term Memory)
短期记忆存储近期的事件和对话历史:
- 对话历史:最近 N 轮对话记录
- 会话状态:当前任务的进度和状态
- 多轮意图:用户在当前会话中的核心需求
特点:容量中等、按时间衰减、支持上下文理解
2.3 长期记忆(Long-term Memory)
长期记忆存储持久化的用户信息和知识:
- 用户画像:用户的基本信息、偏好设置
- 历史摘要:过去对话的压缩摘要
- 学习到的模式:从交互中提炼的规律
特点:容量大、持久化存储、需要检索机制
2.4 外部记忆(External Memory)
外部记忆是 Agent 可以访问的外部知识源:
- 知识库:结构化或半结构化的领域知识
- 文档存储:产品文档、帮助手册等
- 向量数据库:语义检索的知识库
三、短期记忆的实现:上下文窗口管理
3.1 基于 Token 的滑动窗口
最简单的短期记忆实现方式是维护一个对话历史的滑动窗口:
class ShortTermMemory:
def __init__(self, max_tokens=4000):
self.max_tokens = max_tokens
self.messages = []
def add_message(self, role, content):
self.messages.append({"role": role, "content": content})
self._trim_to_fit()
def _trim_to_fit(self):
"""当超出 token 限制时,移除最早的消息"""
while self._count_tokens() > self.max_tokens:
if len(self.messages) > 1:
self.messages.pop(0) # 移除最早的消息
else:
break
def _count_tokens(self):
# 简化的 token 计算,实际应使用 tiktoken 等库
return sum(len(m["content"]) for m in self.messages)
def get_context(self):
return self.messages
3.2 智能摘要策略
当对话历史过长时,直接截断会丢失重要信息。更好的做法是使用摘要机制:
class SummarizingMemory:
def __init__(self, llm_client, max_messages=10):
self.llm = llm_client
self.max_messages = max_messages
self.recent_messages = []
self.summary = ""
def add_message(self, role, content):
self.recent_messages.append({"role": role, "content": content})
if len(self.recent_messages) > self.max_messages:
self._summarize_oldest()
def _summarize_oldest(self):
"""将最早的消息摘要化"""
to_summarize = self.recent_messages[:5]
self.recent_messages = self.recent_messages[5:]
summary_prompt = f"""
请对以下对话进行摘要,保留关键信息:
{to_summarize}
现有摘要:{self.summary}
"""
self.summary = self.llm.generate(summary_prompt)
def get_context(self):
"""返回:摘要 + 近期消息"""
context = []
if self.summary:
context.append({
"role": "system",
"content": f"历史对话摘要:{self.summary}"
})
context.extend(self.recent_messages)
return context
3.3 关键信息提取
除了摘要,我们还可以提取实体和关键事实:
class EntityMemory:
def __init__(self, llm_client):
self.llm = llm_client
self.entities = {} # 存储提取的实体
self.facts = [] # 存储关键事实
def extract_from_message(self, message):
"""从消息中提取实体和事实"""
extraction_prompt = f"""
从以下对话中提取关键信息(JSON格式):
{message}
请提取:
1. 提到的实体(人名、地名、组织、产品等)
2. 关键事实(偏好、需求、约束条件等)
"""
result = self.llm.generate(extraction_prompt)
extracted = json.loads(result)
# 合并到记忆中
for entity in extracted.get("entities", []):
self.entities[entity["name"]] = entity
self.facts.extend(extracted.get("facts", []))
def get_relevant_context(self, query):
"""获取与当前查询相关的实体和事实"""
relevant = []
# 简单的关键词匹配(实际可用向量检索)
for name, entity in self.entities.items():
if any(word in query for word in name.split()):
relevant.append(f"{name}: {entity.get('description', '')}")
for fact in self.facts:
if any(word in query for word in fact.split()):
relevant.append(fact)
return relevant
四、长期记忆的实现:持久化与检索
4.1 用户画像系统
用户画像是长期记忆的核心,通常包含:
@dataclass
class UserProfile:
user_id: str
basic_info: Dict # 基本信息
preferences: Dict # 偏好设置
interaction_history: List # 交互历史摘要
learned_patterns: Dict # 学习到的行为模式
class UserProfileManager:
def __init__(self, storage_backend):
self.storage = storage_backend
self.cache = {}
async def get_profile(self, user_id: str) -> UserProfile:
if user_id not in self.cache:
data = await self.storage.load(user_id)
self.cache[user_id] = UserProfile(**data)
return self.cache[user_id]
async def update_profile(self, user_id: str, updates: Dict):
profile = await self.get_profile(user_id)
# 更新字段
for key, value in updates.items():
if hasattr(profile, key):
setattr(profile, key, value)
# 持久化
await self.storage.save(user_id, profile.__dict__)
4.2 基于向量数据库的记忆检索
对于大规模的记忆存储,向量数据库是最佳选择:
class VectorMemory:
def __init__(self, embedding_model, vector_db):
self.embedder = embedding_model
self.db = vector_db
async def add_memory(self, user_id: str, content: str,
memory_type: str = "fact"):
"""添加记忆到向量数据库"""
# 生成嵌入向量
embedding = await self.embedder.embed(content)
# 存储到向量数据库
await self.db.upsert(
vectors=[{
"id": f"{user_id}_{uuid.uuid4()}",
"values": embedding,
"metadata": {
"user_id": user_id,
"content": content,
"type": memory_type,
"timestamp": datetime.now().isoformat()
}
}]
)
async def retrieve_relevant(self, user_id: str, query: str,
top_k: int = 5) -> List[str]:
"""检索与查询相关的记忆"""
# 生成查询向量
query_embedding = await self.embedder.embed(query)
# 向量检索
results = await self.db.query(
vector=query_embedding,
filter={"user_id": user_id},
top_k=top_k
)
return [r.metadata["content"] for r in results]
4.3 记忆的重要性评分
不是所有记忆都同等重要,我们需要机制来评估和筛选:
class ScoredMemory:
def __init__(self, llm_client):
self.llm = llm_client
async def calculate_importance(self, content: str, context: Dict) -> float:
"""计算记忆的重要性分数"""
scoring_prompt = f"""
评估以下信息的重要性(0-10分):
信息内容:{content}
上下文:{context}
评分标准:
- 10分:核心信息、长期偏好、重要信息
- 7-9分:近期重要事件、明确的用户需求
- 4-6分:一般性信息、临时性内容
- 1-3分:闲聊、无关紧要的细节
只返回数字分数。
"""
score_text = await self.llm.generate(scoring_prompt)
try:
return float(score_text.strip())
except:
return 5.0 # 默认中等重要性
async def should_remember(self, content: str,
threshold: float = 6.0) -> bool:
"""判断是否应该记住这条信息"""
score = await self.calculate_importance(content, {})
return score >= threshold
五、记忆系统的架构设计
5.1 分层记忆架构
一个完整的记忆系统应该采用分层设计:
┌─────────────────────────────────────────────────────────────┐
│ Agent 核心层 │
│ (决策与行动执行) │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────────┐
│ 记忆管理层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ 工作记忆 │ │ 短期记忆 │ │ 长期记忆 │ │
│ │ (内存) │ │ (Redis) │ │ (向量DB + SQL) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 对话历史 │ │ 用户画像 │ │ 知识库 │
└─────────┘ └─────────┘ └─────────┘
5.2 记忆检索流程
当 Agent 需要记忆时,应该遵循以下检索流程:
class MemoryManager:
def __init__(self):
self.working_memory = WorkingMemory()
self.short_term = ShortTermMemory()
self.long_term = LongTermMemory()
self.vector_memory = VectorMemory()
async def retrieve_context(self, user_id: str,
current_query: str) -> Dict:
"""检索完整的上下文信息"""
context = {
"working": [],
"short_term": [],
"long_term": [],
"relevant_memories": []
}
# 1. 获取工作记忆(最高优先级)
context["working"] = self.working_memory.get_all()
# 2. 获取短期记忆
context["short_term"] = self.short_term.get_recent(n=10)
# 3. 获取用户画像
context["user_profile"] = await self.long_term.get_profile(user_id)
# 4. 向量检索相关记忆
context["relevant_memories"] = await self.vector_memory.retrieve_relevant(
user_id, current_query, top_k=5
)
# 5. 组装最终上下文(按优先级排序)
return self._assemble_context(context)
def _assemble_context(self, context: Dict) -> str:
"""将各类记忆组装成 LLM 可用的上下文"""
parts = []
# 用户画像
if context.get("user_profile"):
parts.append(f"用户画像:{context['user_profile']}")
# 相关历史记忆
if context.get("relevant_memories"):
parts.append("相关历史信息:")
for mem in context["relevant_memories"]:
parts.append(f"- {mem}")
# 近期对话
if context.get("short_term"):
parts.append("近期对话:")
for msg in context["short_term"]:
parts.append(f"{msg['role']}: {msg['content']}")
return "\n".join(parts)
5.3 记忆的更新与遗忘
记忆系统需要支持动态更新和合理的遗忘机制:
class AdaptiveMemory:
def __init__(self):
self.access_counts = {} # 访问计数
self.last_accessed = {} # 最后访问时间
self.creation_time = {} # 创建时间
async def access(self, memory_id: str):
"""记录记忆访问"""
self.access_counts[memory_id] = self.access_counts.get(memory_id, 0) + 1
self.last_accessed[memory_id] = datetime.now()
async def should_forget(self, memory_id: str) -> bool:
"""判断是否应该遗忘这条记忆"""
# 基于访问频率和时间的遗忘策略
access_count = self.access_counts.get(memory_id, 0)
last_access = self.last_accessed.get(memory_id, datetime.now())
age = datetime.now() - self.creation_time.get(memory_id, datetime.now())
# 高频访问的记忆不轻易遗忘
if access_count > 10:
return False
# 很久没访问的低频记忆可以遗忘
if age.days > 90 and access_count < 3:
return True
return False
async def consolidate_memories(self):
"""记忆巩固:合并相似记忆,遗忘不重要记忆"""
# 1. 找出应该遗忘的记忆
to_forget = []
for memory_id in self.access_counts:
if await self.should_forget(memory_id):
to_forget.append(memory_id)
# 2. 执行遗忘
for memory_id in to_forget:
await self._delete_memory(memory_id)
# 3. 合并相似记忆
await self._merge_similar_memories()
六、实战案例:构建一个具备记忆的客服 Agent
让我们通过一个完整的例子来演示如何构建具备记忆能力的 Agent:
import openai
from typing import List, Dict
import json
class MemoryEnabledAgent:
def __init__(self, api_key: str):
self.client = openai.AsyncOpenAI(api_key=api_key)
self.memory_manager = MemoryManager()
async def chat(self, user_id: str, message: str) -> str:
# 1. 检索上下文
context = await self.memory_manager.retrieve_context(
user_id, message
)
# 2. 构建系统提示
system_prompt = f"""你是一个智能客服助手。请基于以下上下文回答用户问题:
{context}
请保持友好、专业的态度,如果需要更多信息请主动询问。"""
# 3. 调用 LLM
response = await self.client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": message}
]
)
reply = response.choices[0].message.content
# 4. 更新记忆
await self.memory_manager.add_interaction(
user_id=user_id,
user_message=message,
assistant_reply=reply
)
return reply
# 使用示例
async def main():
agent = MemoryEnabledAgent(api_key="your-api-key")
user_id = "user_123"
# 第一轮对话
reply1 = await agent.chat(user_id, "我想退货")
print(f"Agent: {reply1}")
# 第二轮对话(Agent 应该记得用户想退货)
reply2 = await agent.chat(user_id, "订单号是 12345")
print(f"Agent: {reply2}")
# 第三轮对话(Agent 应该记得之前的上下文)
reply3 = await agent.chat(user_id, "什么时候能到账?")
print(f"Agent: {reply3}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
七、性能优化与最佳实践
7.1 Token 优化
记忆会占用大量 Token,需要优化:
- 摘要压缩:长对话定期摘要
- 信息筛选:只保留相关记忆
- 分层检索:先粗筛再精筛
7.2 延迟优化
记忆检索不应阻塞响应:
- 异步加载:后台预加载用户画像
- 缓存策略:热点数据内存缓存
- 并行检索:多种记忆并行查询
7.3 隐私与安全
记忆涉及敏感信息:
- 数据脱敏:PII 信息加密存储
- 访问控制:用户只能访问自己的记忆
- 过期策略:敏感信息定期清理
八、未来展望
AI Agent 的记忆机制仍在快速发展中,值得关注的方向包括:
- 神经符号记忆:结合神经网络和符号推理
- 终身学习:Agent 持续学习和自我改进
- 多模态记忆:整合文本、图像、音频等多种模态
- 联邦记忆:分布式记忆共享与隐私保护
结语
记忆是 AI Agent 从"工具"进化为"伙伴"的关键。通过合理设计工作记忆、短期记忆和长期记忆的分层架构,我们可以构建出真正理解用户、持续学习、不断进化的智能 Agent。
希望本文能为你的 AI Agent 开发之旅提供有价值的参考。如果你有任何问题或想法,欢迎在评论区交流讨论。
参考资源:
本文首发于稀土掘金,转载请注明出处。