如何让 Agent 记住对话历史?
文章目录
前言:为什么我会学这个问题
我们都会遇见这个问题 Agent 第一轮对话很聪明,但第二轮就"失忆"了——用户问"那刚才说的那个功能怎么开通",Agent 完全不知道在说什么。
这让我意识到一个问题:LLM 本身是没有记忆的,每次调用都是独立的一次性请求。对话历史需要我们主动管理。
前文关联:本文接续《Planning Agent 和 Action Agent 有什么区别?》,在前文理解不同 Agent 架构的基础上,继续探讨所有 Agent 都会遇到的核心问题——如何让 Agent 在多轮对话中记住之前的内容。
一、问题背景
为什么 LLM 没有记忆
LLM 的运作机制是"输入-输出"。每次调用时,模型只会看到这一次输入的内容,不会自动保留之前的对话记录。
调用 1:
User: 你好 → LLM: 你好,有什么可以帮你?
调用 2:
User: 你是谁 → LLM: 我是一个 AI 助手
调用 3(模型"失忆"了):
User: 刚才我说什么了? → LLM: 抱歉,我不知道你刚才说了什么
记忆管理的挑战
- Token 限制:LLM 有上下文窗口限制(4K、16K、128K),对话太长装不下
- 成本问题:历史越长,Token 消耗越多,成本越高
- 相关性:不是所有历史都有用,需要筛选
- 隐私:对话历史可能包含敏感信息,不能全部保留
二、核心概念
对话历史的结构

记忆管理的三种策略
1. 完整历史(Full History)
- 把所有对话都放进上下文
- 简单但不可扩展
- 适合短对话
2. 摘要历史(Summarized History)
- 定期把对话压缩成摘要
- 省 Token,但会丢失细节
- 适合中等长度对话
3. 选择性记忆(Selective Memory)
- 只保留关键信息
- 通过向量检索相关历史
- 适合长对话
三、错误理解 / 常见误区
误区 1:给 LLM 越多历史越好
错。过多无关历史会稀释关键信息,还浪费 Token。LLM 也可能受到早期信息的"锚定效应"影响。
误区 2:摘要会丢失所有重要信息
不一定。好的摘要策略会保留"关键实体"、“用户意图”、"已完成的任务"等核心信息。
误区 3:向量检索适合所有场景
向量检索适合"需要从大量历史中找相关信息"的场景。对于短对话或连续对话,简单的滑动窗口可能更高效。
误区 4:不需要管理历史
错。无限增长的历史会导致:1) Token 超限 2) 成本爆炸 3) 模型困惑。记忆管理是必须考虑的工程问题。
四、正确实现思路
策略选择决策树
对话长度 < 1/4 上下文窗口?
├── 是:直接用完整历史
└── 否:需要压缩
对话有明确主线?
├── 是:用摘要策略
└── 否:用向量检索策略
分层记忆架构

五、代码示例
基础版:固定窗口历史
class SimpleHistoryAgent:
"""简单的固定窗口历史管理"""
def __init__(self, llm, max_messages: int = 10):
self.llm = llm
self.max_messages = max_messages # 保留最近 N 条消息
self.history = []
def chat(self, user_input: str) -> str:
"""处理对话"""
# 1. 添加用户消息
self.history.append({"role": "user", "content": user_input})
# 2. 保留最近 N 条消息
if len(self.history) > self.max_messages:
self.history = self.history[-self.max_messages:]
# 3. 构建消息列表(加上系统消息)
messages = [{"role": "system", "content": "你是一个有用的助手"}]
messages.extend(self.history)
# 4. 调用 LLM
response = self.llm(messages)
# 5. 添加助手回复到历史
self.history.append({"role": "assistant", "content": response})
return response
进阶版:自动摘要历史
import json
from typing import List, Dict
class SummarizingHistoryAgent:
"""带自动摘要的记忆管理"""
def __init__(self, llm, max_messages: int = 20, summary_threshold: int = 15):
self.llm = llm
self.max_messages = max_messages
self.summary_threshold = summary_threshold
self.history = []
self.summary = "" # 对话摘要
def chat(self, user_input: str) -> str:
"""处理对话"""
# 添加用户消息
self.history.append({"role": "user", "content": user_input})
# 检查是否需要摘要
if len(self.history) >= self.summary_threshold:
self._summarize_history()
# 构建消息
messages = self._build_messages()
response = self.llm(messages)
# 添加助手回复
self.history.append({"role": "assistant", "content": response})
return response
def _summarize_history(self):
"""生成对话摘要"""
# 用 LLM 生成摘要
prompt = f"""请总结以下对话的要点:
{chr(10).join([f"{m['role']}: {m['content']}" for m in self.history])}
要求:
- 保留关键实体(人名、地点、产品名等)
- 保留用户的主要意图和需求
- 保留已确定的信息和结论
- 用简洁的语言总结
"""
self.summary = self.llm([{"role": "user", "content": prompt}])
# 清空历史,只保留摘要
self.history = [{"role": "system", "content": f"对话摘要:{self.summary}"}]
print(f"📝 生成摘要:{self.summary[:50]}...")
def _build_messages(self) -> List[Dict]:
"""构建发送给 LLM 的消息列表"""
messages = [{"role": "system", "content": "你是一个有用的助手。"}]
# 如果有摘要,添加摘要作为上下文
if self.summary:
messages.append({
"role": "system",
"content": f"之前的对话摘要:{self.summary}"
})
messages.extend(self.history)
return messages
高级版:向量检索记忆
import hashlib
from typing import List, Dict, Tuple, Optional
class VectorMemoryAgent:
"""基于向量检索的长期记忆管理"""
def __init__(self, llm, vector_store, max_context_messages: int = 10):
self.llm = llm
self.vector_store = vector_store # 向量数据库
self.max_context_messages = max_context_messages
self.conversation_history = [] # 当前对话
self.current_session_id = self._new_session_id()
def chat(self, user_input: str) -> str:
"""处理对话"""
# 1. 添加用户消息
self.conversation_history.append({
"role": "user",
"content": user_input,
"session_id": self.current_session_id
})
# 2. 保存用户消息到向量存储
self._add_to_memory(user_input, "user")
# 3. 检索相关记忆
relevant_memories = self._retrieve_memories(user_input)
# 4. 构建上下文
messages = self._build_context(relevant_memories)
# 5. 调用 LLM
response = self.llm(messages)
# 6. 保存助手回复
self.conversation_history.append({
"role": "assistant",
"content": response
})
self._add_to_memory(response, "assistant")
return response
def _add_to_memory(self, content: str, role: str):
"""添加内容到向量记忆"""
self.vector_store.add(
text=content,
metadata={
"role": role,
"session_id": self.current_session_id
}
)
def _retrieve_memories(self, query: str, top_k: int = 3) -> List[Dict]:
"""检索相关记忆"""
results = self.vector_store.search(query, top_k=top_k)
return results
def _build_context(self, memories: List[Dict]) -> List[Dict]:
"""构建上下文"""
messages = [{"role": "system", "content": "你是一个有用的助手。"}]
# 添加相关记忆
if memories:
memory_context = "以下是相关的历史记忆:\n"
for m in memories:
memory_context += f"- [{m['metadata'].get('role', 'unknown')}]: {m['text']}\n"
messages.append({"role": "system", "content": memory_context})
# 添加当前对话历史(限制数量)
messages.extend(self.conversation_history[-self.max_context_messages:])
return messages
def _new_session_id(self) -> str:
"""生成新的会话 ID"""
import time
return hashlib.md5(str(time.time()).encode()).hexdigest()[:8]
def start_new_session(self):
"""开始新会话"""
self.conversation_history = []
self.current_session_id = self._new_session_id()
# 简单的向量存储实现(用于演示)
class SimpleVectorStore:
"""简化的向量存储(实际生产用 Milvus/Pinecone 等)"""
def __init__(self):
self.documents = []
def add(self, text: str, metadata: dict = None):
"""添加文档"""
# 简化:用文本长度做伪向量
self.documents.append({
"text": text,
"metadata": metadata or {},
"vector": self._simple_vector(text)
})
def search(self, query: str, top_k: int = 3) -> List[Dict]:
"""搜索相关文档(简化实现)"""
query_vec = self._simple_vector(query)
# 计算相似度(简化:向量点积)
results = []
for doc in self.documents:
similarity = sum(q * d for q, d in zip(query_vec, doc["vector"]))
results.append((similarity, doc))
# 返回 top_k
results.sort(key=lambda x: x[0], reverse=True)
return [r[1] for r in results[:top_k]]
def _simple_vector(self, text: str) -> List[float]:
"""简化向量化:用字符编码和"""
return [ord(c) / 255.0 for c in text[:50]]
生产级完整实现
from datetime import datetime
from typing import Optional
class ProductionMemoryAgent:
"""生产级记忆 Agent"""
def __init__(
self,
llm,
vector_store=None,
max_session_messages: int = 50,
max_context_tokens: int = 4000,
summarize_threshold: int = 20
):
self.llm = llm
self.vector_store = vector_store
# 配置
self.max_session_messages = max_session_messages
self.max_context_tokens = max_context_tokens
self.summarize_threshold = summarize_threshold
# 记忆层次
self.short_term = [] # 当前对话的原始消息
self.summary = "" # 压缩后的摘要
self.long_term = [] # 持久化的关键记忆
# 元信息
self.session_id = self._new_session_id()
self.created_at = datetime.now()
def chat(self, user_input: str, user_id: str = None) -> str:
"""完整的对话处理"""
# 1. 记录用户消息
user_msg = {
"role": "user",
"content": user_input,
"timestamp": datetime.now().isoformat(),
"user_id": user_id
}
self.short_term.append(user_msg)
# 2. 决定使用哪个记忆层次
context = self._build_context()
# 3. 调用 LLM
response = self.llm(context)
# 4. 记录助手回复
assistant_msg = {
"role": "assistant",
"content": response,
"timestamp": datetime.now().isoformat()
}
self.short_term.append(assistant_msg)
# 5. 更新记忆
self._update_memory()
# 6. 如果配置了向量存储,持久化关键信息
if self.vector_store and self._is_key_information(user_input):
self._persist_to_long_term(user_input, response)
return response
def _build_context(self) -> list:
"""构建上下文"""
messages = [
{"role": "system", "content": "你是一个专业的客服助手。"}
]
# 添加长期记忆(摘要)
if self.summary:
messages.append({
"role": "system",
"content": f"对话摘要:{self.summary}"
})
# 添加长期记忆(关键事实)
if self.long_term:
facts = "用户关键信息:\n"
for fact in self.long_term[-5:]: # 最近 5 条
facts += f"- {fact}\n"
messages.append({"role": "system", "content": facts})
# 添加短期记忆(最近的消息)
messages.extend(self.short_term[-self.max_session_messages:])
return messages
def _update_memory(self):
"""更新记忆"""
# 如果短期记忆太长,生成摘要
if len(self.short_term) >= self.summarize_threshold:
self._generate_summary()
def _generate_summary(self):
"""生成对话摘要"""
messages = [
{"role": "system", "content": "你是一个对话摘要专家。"},
{"role": "user", "content": f"请总结以下对话的要点:\n\n{self._format_history(self.short_term)}"}
]
self.summary = self.llm(messages)
# 保留摘要和最近几轮对话
self.short_term = [{"role": "system", "content": f"摘要:{self.summary}"}] + self.short_term[-6:]
print(f"📝 记忆压缩:生成新摘要")
def _is_key_information(self, text: str) -> bool:
"""判断是否为关键信息"""
keywords = ["地址", "电话", "订单号", "账号", "密码", "银行卡", "邮箱"]
return any(kw in text for kw in keywords)
def _persist_to_long_term(self, user_input: str, response: str):
"""持久化关键信息到长期记忆"""
# 简化:直接存储文本
# 实际应该用 LLM 提取实体和关系
if "地址" in user_input:
self.long_term.append(f"用户地址:{user_input}")
if "订单" in user_input or "订单号" in user_input:
self.long_term.append(f"订单相关信息:{user_input}")
def _format_history(self, history: list) -> str:
"""格式化历史消息"""
return "\n".join([
f"{msg['role']}: {msg['content']}"
for msg in history[-20:] # 最近 20 条
])
def _new_session_id(self) -> str:
"""生成会话 ID"""
import hashlib
import time
return hashlib.md5(str(time.time()).encode()).hexdigest()[:8]
def reset(self):
"""重置记忆(新会话)"""
self.short_term = []
self.summary = ""
self.session_id = self._new_session_id()
self.created_at = datetime.now()
六、运行结果 / 流程图
运行效果示意:
用户: 我想查询订单号 12345 的状态
助手: 正在为您查询...
用户: 那这个订单预计什么时候到?
助手: 根据查询结果,您的订单预计明天送达。
用户: 刚才说的订单号是多少?
助手: 您刚才查询的是订单号 12345。
记忆管理流程:
七、项目中怎么用
实际选型建议
| 场景 | 推荐方案 |
|---|---|
| 简单客服(10 轮以内) | 固定窗口历史 |
| 复杂咨询(需要跨会话) | 摘要 + 长期记忆 |
| 个人助手(需要记住用户偏好) | 向量检索记忆 |
| 高价值场景(金融、医疗) | 分层记忆 + 审计日志 |
注意事项
- 隐私合规:对话历史可能包含敏感信息,需要脱敏处理
- Token 预算:始终监控 Token 消耗,设置上限
- 记忆一致性:多轮对话中,同一实体应该保持一致
- 降级策略:向量服务不可用时,降级到简单历史管理
系列关联:本文探讨了 Agent 的对话历史管理问题,这是构建实用 Agent 的基础技术。下一篇《Multi-Agent 协作怎么设计?》会讲到,当单个 Agent 能力有限时,如何通过多 Agent 协作来完成任务,每个 Agent 可能有不同的记忆配置。
八、总结 + 下篇预告
核心要点
- LLM 没有记忆:每次调用是独立的,需要主动管理历史
- 三种策略:固定窗口(简单)、摘要压缩(中等)、向量检索(复杂)
- 分层记忆:短期记忆 + 摘要 + 长期记忆,各有用途
- 隐私优先:关键信息脱敏,敏感数据不上传
下篇预告
下一篇我们将探讨《Multi-Agent 协作怎么设计?》,理解当单个 Agent 无法完成复杂任务时,如何设计多 Agent 协作架构,包括 Agent 间的通信、任务分解和结果聚合。
延伸阅读
- LangChain ConversationMemory
- RAG(检索增强生成):结合向量检索和 LLM 生成
- MemGPT:类似操作系统的多层记忆架构
更多推荐



所有评论(0)