教程
Memory — 让 Agent 拥有记忆
设计短期、长期和工作记忆系统,让 Agent 能记住和遗忘
第 7 章:Memory — 让 Agent 拥有记忆
学完本章,你将能够:
- 理解 Agent 记忆系统的三种类型
- 实现对话历史管理和压缩
- 构建跨会话的长期记忆
- 给 Agent 设计完整的记忆系统
7.1 短期记忆:对话历史管理
短期记忆就是当前对话中的历史消息。最简单的实现就是维护一个 messages 列表——我们在前几章一直在这么做。
问题:对话太长怎么办?
LLM 的上下文窗口是有限的(比如 128K Token)。当对话越来越长:
- Token 超限:对话历史超过了上下文窗口
- 成本爆炸:每次调用都要发送所有历史消息,费用越来越高
- 注意力稀释:太长的上下文会让 LLM “忘记”早期的对话内容
解决方案:对话摘要压缩
def summarize_conversation(messages: list, llm) -> str:
"""把长对话压缩成摘要"""
conversation_text = "\n".join([
f"{m['role']}: {m['content']}" for m in messages
])
summary = llm(f"""请将以下对话压缩为简短摘要,保留关键信息:
- 用户的核心需求和偏好
- 已完成的操作和结果
- 待解决的问题
对话内容:
{conversation_text}
摘要:""")
return summary
class ConversationMemory:
"""带摘要压缩的对话记忆"""
def __init__(self, llm, max_messages: int = 20):
self.messages = []
self.summary = ""
self.llm = llm
self.max_messages = max_messages
def add(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
# 超过阈值时压缩
if len(self.messages) > self.max_messages:
self._compress()
def _compress(self):
"""压缩旧消息为摘要"""
# 保留最近 5 条消息,其余压缩
old_messages = self.messages[:-5]
self.summary = summarize_conversation(old_messages, self.llm)
self.messages = self.messages[-5:]
def get_messages(self) -> list:
"""获取完整的对话上下文"""
result = []
if self.summary:
result.append({
"role": "system",
"content": f"之前的对话摘要:{self.summary}"
})
result.extend(self.messages)
return result
7.2 长期记忆:跨会话持久化
短期记忆在对话结束后就没了。长期记忆让 Agent 能跨会话记住用户信息。
用向量数据库存储长期记忆
import chromadb
from openai import OpenAI
class LongTermMemory:
"""基于向量数据库的长期记忆"""
def __init__(self, collection_name: str = "agent_memory"):
self.client = OpenAI()
self.chroma = chromadb.Client()
try:
self.chroma.delete_collection(collection_name)
except:
pass
self.collection = self.chroma.create_collection(name=collection_name)
def remember(self, content: str, metadata: dict = None):
"""保存一条记忆"""
embedding = self._get_embedding(content)
self.collection.add(
ids=[f"mem_{self.collection.count()}"],
documents=[content],
embeddings=[embedding],
metadatas=[metadata or {}],
)
def recall(self, query: str, n_results: int = 5) -> list[str]:
"""根据查询检索相关记忆"""
embedding = self._get_embedding(query)
results = self.collection.query(
query_embeddings=[embedding],
n_results=n_results,
)
return results["documents"][0]
def _get_embedding(self, text: str) -> list[float]:
response = self.client.embeddings.create(
model="text-embedding-3-small",
input=text,
)
return response.data[0].embedding
7.3 工作记忆:Agent 的”草稿纸”
工作记忆是 Agent 在执行任务时使用的临时存储空间,用于保存中间结果和状态。
类比:
- 短期记忆 = 你正在聊天的对话框
- 长期记忆 = 你的笔记本(永久保存)
- 工作记忆 = 你做数学题时的草稿纸(用完就扔)
class WorkingMemory:
"""工作记忆:Agent 的草稿纸"""
def __init__(self):
self.notes: dict[str, str] = {}
self.scratchpad: list[str] = []
def write_note(self, key: str, value: str):
"""保存一条笔记"""
self.notes[key] = value
def read_note(self, key: str) -> str:
"""读取一条笔记"""
return self.notes.get(key, "")
def add_to_scratchpad(self, entry: str):
"""写入草稿纸"""
self.scratchpad.append(entry)
def get_scratchpad(self) -> str:
"""获取草稿纸内容"""
return "\n".join(self.scratchpad)
def clear(self):
"""清空工作记忆"""
self.notes.clear()
self.scratchpad.clear()
完整记忆系统
把三种记忆组合在一起:
class AgentMemorySystem:
"""完整的 Agent 记忆系统"""
def __init__(self, llm):
self.short_term = ConversationMemory(llm, max_messages=20)
self.long_term = LongTermMemory()
self.working = WorkingMemory()
def process_message(self, role: str, content: str):
"""处理一条新消息"""
# 1. 加入短期记忆
self.short_term.add(role, content)
# 2. 判断是否需要保存到长期记忆
if self._is_important(content):
self.long_term.remember(content)
def get_context(self, query: str) -> str:
"""构建完整的记忆上下文"""
parts = []
# 短期记忆(当前对话)
parts.append("当前对话:" + str(self.short_term.get_messages()[-5:]))
# 长期记忆(检索相关历史)
relevant = self.long_term.recall(query, n_results=3)
if relevant:
parts.append("相关历史记忆:" + "\n".join(relevant))
# 工作记忆
if self.working.notes:
parts.append("当前任务笔记:" + str(self.working.notes))
return "\n\n".join(parts)
def _is_important(self, content: str) -> bool:
"""判断一条消息是否值得保存到长期记忆"""
# 简单实现:包含特定关键词的消息
important_keywords = ["偏好", "记住", "重要", "总是", "永远"]
return any(kw in content for kw in important_keywords)
动手练习
练习 1(基础):实现对话摘要压缩
实现
ConversationMemory类,当对话消息超过 20 条时自动压缩为摘要。 测试:模拟一个 30 轮的对话,验证压缩是否正确触发。
练习 2(进阶):跨会话记忆
实现一个能跨会话记住用户偏好的 Agent。 场景:第 1 次对话告诉 Agent “我喜欢 Python”,第 2 次对话 Agent 应该记住这个偏好。
练习 3(挑战):给 Agent 添加完整记忆系统
把短期、长期、工作记忆组合在一起,实现
AgentMemorySystem。 测试一个需要跨步骤传递信息、记住用户偏好的复杂任务。
常见踩坑 FAQ
Q: 对话摘要丢失了重要信息
压缩时保留更多关键信息的方法:
- 在摘要 Prompt 中明确指出需要保留的信息类型
- 增加
max_messages的值,保留更多原文 - 对不同类型的消息用不同的压缩策略
Q: 长期记忆检索不准确
- 尝试不同的 Embedding 模型
- 在记忆时添加 metadata(如时间、话题标签),检索时可以过滤
- 定期清理过时的记忆
Q: 记忆系统太慢
- 对 Embedding 计算做缓存
- 批量处理记忆存储(而不是每条消息都存)
- 长期记忆检索使用异步调用
下一章:Multi-Agent 系统。