前四篇我们做了一件了不起的事:搭了骨架(第01篇)、装了操作系统(第02篇)、省了80% Token费(第03篇)、给它装上了双手(第04篇)。
但你的 Agent 有一个致命缺陷------
它转头就忘。
- 上一轮对话里你告诉它的事,下一轮就清零了
- 三天前搜索过的知识,今天又要重新来一遍
- 用户的偏好、项目的背景------每次都得从头解释
- 像个金鱼,7秒记忆
这不是模型的错。大模型本身是「无状态」的------每次调用都是一张白纸。记忆是 Agent 开发者自己造的。今天这篇,我们给 Agent 装上记忆。从短期到长期,从简单到架构化。
一、Agent 记忆的三个层次
人脑记忆不是一层,而是分层的。Agent 也一样。我们把 Agent 记忆分为三层:
1.1 为什么需要分层
很简单:你不能把所有记忆都塞进上下文。代价有三个------
- Token 成本爆炸:10万条对话 = 数百万 tokens = 每轮对话烧几十块钱
- 速度急剧下降:上下文越大,推理越慢,用户体验崩溃
- 注意力稀释:信息太多时模型反而抓不住重点(参考第03篇上下文工程)
分层记忆的核心思想:当前任务用上下文,近期对话缓存起来,长期知识存向量库里检索。
二、第一层:上下文记忆(你已会的)
这是最基本的形式。把对话历史拼进 messages 数组里送回去。
python
# 最基本的内存形式------每次把历史全塞进去
class NaiveMemory:
def __init__(self, max_tokens=None):
self.messages = []
self.max_tokens = max_tokens or 128000
def add(self, role: str, content: str):
self.messages.append({"role": role, "content": content})
def get_context(self) -> list:
# 简单截断,不超窗口
total = 0
result = []
for msg in reversed(self.messages):
cost = len(msg["content"]) // 3 # 粗略估算 token
if total + cost > self.max_tokens * 0.8:
break
result.insert(0, msg)
total += cost
return result
def clear(self):
self.messages = []
这能跑,但太粗糙了------没有压缩、没有分层、没有持久化。我们的目标是:超出上下文的记忆也能被 Agent 用到。
三、第二层:短期记忆------带摘要的对话历史
3.1 设计思路
短期记忆要解决的是「最近 N 轮对话里发生了什么」。核心原则:
- 最近 5-10 轮保留完整原文
- 更早的对话压缩为结构化摘要(谁说了什么、做了什么决定、改了什么文件)
- 摘要定期更新------不是一次性压缩就不管了
3.2 实现
python
import json
class ShortTermMemory:
def __init__(self, max_recent=8, max_summary_tokens=3000):
self.recent = [] # 最近N轮完整对话
self.summary = "" # 更早对话的摘要
self.max_recent = max_recent
self.max_summary = max_summary_tokens
self.decisions = [] # 记录关键决策
def add_turn(self, user_msg: str, assistant_msg: str):
self.recent.append({
"user": user_msg,
"assistant": assistant_msg
})
# 超过阈值时,把最老的一轮并入摘要
if len(self.recent) > self.max_recent:
oldest = self.recent.pop(0)
self._merge_to_summary(oldest)
def record_decision(self, decision: str):
self.decisions.append(decision)
def _merge_to_summary(self, turn: dict):
# 累积式摘要:追加简要信息
snippet = f"[用户问: {turn['user'][:80]}... | Agent: {turn['assistant'][:80]}...]"
self.summary = (self.summary + " " + snippet)[-self.max_summary:]
def get_memory_context(self) -> str:
parts = []
if self.decisions:
parts.append(f"## 关键决策记录\n" + "\n".join(
f"- {d}" for d in self.decisions[-10:]
))
if self.summary:
parts.append(f"## 对话历史摘要\n{self.summary}")
if self.recent:
parts.append(f"## 最近 {len(self.recent)} 轮对话\n" + "\n".join(
f"Q: {t['user'][:120]}\nA: {t['assistant'][:120]}"
for t in self.recent
))
return "\n\n".join(parts)
3.3 关键设计点
- 决策记录:有些信息比普通对话重要得多------「我们决定用 Redis 而不是 MySQL」这种需要单独存档
- 累积式摘要:不是每次重新调用 LLM 做总结(那本身就要烧 Token),而是在消息被逐出时增量追加一句
- 截断保护:摘要不能无限膨胀,用滑动窗口限制最大长度
短期记忆已经可以让 Agent 记住「半小时前我们聊了什么」。但三天前的对话呢?用户偏好去哪了?这就是长期记忆要解决的问题。
四、第三层:长期记忆------向量检索
4.1 什么该进长期记忆
不是所有对话都值得永久记住。筛选标准:
4.2 架构:Embedding + 向量检索
经典方案:把记忆文本通过 Embedding 模型转为向量,存到向量数据库,检索时用语义相似度匹配。
python
import numpy as np
from openai import OpenAI
client = OpenAI(api_key="your_key")
class LongTermMemory:
def __init__(self, vector_db=None):
self.memories = [] # 简化版:用列表代替向量数据库
self.embed_model = "text-embedding-3-small"
def embed(self, text: str) -> list:
resp = client.embeddings.create(
model=self.embed_model,
input=text
)
return resp.data[0].embedding
def cosine_sim(self, a: list, b: list) -> float:
a = np.array(a)
b = np.array(b)
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
def store(self, content: str, metadata: dict = None):
vec = self.embed(content)
self.memories.append({
"content": content,
"vector": vec,
"metadata": metadata or {},
"timestamp": __import__("time").time()
})
def search(self, query: str, top_k: int = 5) -> list:
q_vec = self.embed(query)
scored = [(self.cosine_sim(q_vec, m["vector"]), m)
for m in self.memories]
scored.sort(key=lambda x: x[0], reverse=True)
return [m for _, m in scored[:top_k]]
def store_if_important(self, content: str):
# 只有重要信息才存长期记忆
keywords = ["偏好", "决定", "配置", "项目", "架构",
"偏好", "技术栈", "API"]
if any(kw in content for kw in keywords):
self.store(content)
4.3 典型检索场景
场景:用户说「帮我优化一下上次那个搜索接口的性能」
ini
# 1. 从长期记忆中检索"搜索接口"相关记忆
results = lt_memory.search("搜索接口 性能优化")
# 2. 把检索结果作为上下文注入
context = "\n".join([
f"[相关记忆 {i+1}] {m['content']}"
for i, m in enumerate(results)
])
# 3. 拼入 System Prompt
task_msg = f"""## 长期记忆
{context}
## 当前任务
用户要求优化搜索接口性能,请根据以上记忆中的技术栈和之前的优化记录,给出建议。
"""
五、混合记忆架构:AgentMemory
把三层记忆整合成一个统一的 AgentMemory 类:
python
class AgentMemory:
def __init__(self):
self.short_term = ShortTermMemory()
self.long_term = LongTermMemory()
self.user_profile = {} # 用户画像
def on_user_message(self, msg: str):
# 自动提取并记忆关键信息
self._extract_and_store(msg)
def on_agent_reply(self, reply: str):
# 记录决策和结论
self._detect_decisions(reply)
def build_context(self) -> str:
parts = []
# 1. 用户画像(压缩版)
if self.user_profile:
profile_lines = [f"- {k}: {v}"
for k, v in self.user_profile.items()]
parts.append("## 用户画像\n" + "\n".join(profile_lines))
# 2. 短期记忆(最近对话 + 摘要)
stm = self.short_term.get_memory_context()
if stm:
parts.append(stm)
# 3. 长期记忆(按当前问题检索)
# 由调用方传入检索结果
return "\n\n".join(parts)
def recall_for(self, query: str, top_k: int = 5) -> list:
return self.long_term.search(query, top_k)
# --- 记忆提取 ---
def _extract_and_store(self, msg: str):
# 简化版规则提取
import re
patterns = {
"name": r"我是(.*?)[,,。\s]",
"language": r"用(Python|Go|Rust|TypeScript|Java)",
"framework": r"(React|Vue|Django|FastAPI|Flask|Gin)",
}
for field, pattern in patterns.items():
match = re.search(pattern, msg)
if match:
self.user_profile[field] = match.group(1)
self.long_term.store(
f"用户偏好 {field}: {match.group(1)}",
{"type": "user_preference", "field": field}
)
def _detect_decisions(self, reply: str):
if "决定" in reply or "方案" in reply:
self.short_term.record_decision(
reply.split("。")[0][:200]
)
# --- 集成到 Agent 中 ---
class Agent:
def __init__(self):
self.memory = AgentMemory()
self.context_manager = ContextManager() # 第03篇的上下文管理
async def run(self, user_input: str):
# 1. 存入短期记忆
self.memory.on_user_message(user_input)
# 2. 检索长期记忆
long_memories = self.memory.recall_for(user_input, top_k=5)
# 3. 组装上下文
memory_ctx = self.memory.build_context()
if long_memories:
long_ctx = "\n".join([
f"- {m['content']}" for m in long_memories
])
memory_ctx += f"\n\n## 相关长期记忆\n{long_ctx}"
# 4. 调用 LLM
messages = [
{"role": "system", "content": f"你是 AI Agent。\n{memory_ctx}"},
{"role": "user", "content": user_input}
]
response = await self._call_llm(messages)
# 5. 记录回复
self.memory.on_agent_reply(response)
self.memory.short_term.add_turn(user_input, response)
return response
六、进阶话题
6.1 记忆的遗忘与更新
记忆不是「只增不减」。信息会过时、会矛盾。好的记忆系统需要------
- TTL 过期:旧记忆的权重随时间衰减(指数衰减或线性衰减)
- 冲突检测:新旧信息矛盾时,用更新的覆盖旧的,并标记冲突
- 主动遗忘:用户说「不用那个了」,Agent 要能从长期记忆中删掉
6.2 生产环境的向量数据库选择
6.3 CACHE:上下文记忆的加速器
回到第03篇讲的 Prompt Caching。如果你的系统指令包含用户画像和长期记忆,用缓存可以省掉重复传输成本。DeepSeek 和 Anthropic 都支持自动 Prompt Caching------只要系统指令不变,Token 成本大降。
七、最佳实践总结
- 分层设计:短期(最近N轮) / 长期(向量检索) / 用户画像 三层各司其职
- 不是所有话都记:用规则或小模型判断是否值得存入长期记忆
- 检索优于全传:永远不要把所有记忆塞进上下文,用语义检索只取最相关的 top_k 条
- 摘要优于原文:超过 10 轮对话后,自动压缩为摘要
- 决策独立存档:关键决策从对话流中提取出来单独标记
- 遗忘是功能不是 bug:设置 TTL、冲突检测、主动删除
- 用户画像独立维护:姓名、偏好、技术栈等结构化存储,不混入对话流
- 与上下文工程配合:记忆检索结果注入上下文时遵守预算分配(参考第03篇)
八、下期预告
- 第06篇: Multi-Agent 模式------一个 Agent 不够?那就来一队
- 第07篇: Skills 工程------像安装 App 一样给 Agent 装技能
- 第08篇: 生产化部署------Agent 从 demo 到上线的全部坑
提示:记忆系统是 Agent 从「工具」变成「伙伴」的分水岭。
没有记忆的 Agent,每次对话都像第一次见面------你烦,它也烦。有了分层记忆,Agent 能记住你的偏好、你的项目、你做过的决定------这才叫「你的」Agent。
实现上,先上短期记忆(几乎零成本),再根据需要加向量数据库做长期记忆。不要一步到位上全套------先用最简单的实现,跑通了再迭代。
下一篇,我们把多个 Agent 组队------Multi-Agent 模式。一个 Agent 干活不够?那就请一个团队。
提示 :本文由 码农大坚果 出品,欢迎转发分享,转载请注明出处。
参考: OpenAI Embeddings 文档、LangChain Memory 模块、Qdrant 文档、Weaviate 向量数据库实践 | 整理 by 码农大坚果