短期记忆
短期记忆是 Agent 记忆系统的"工作台",存储当前对话的上下文信息。它直接参与 LLM 推理,决定了 Agent 的即时响应能力和对话连贯性。
一、什么是短期记忆?
1.1 定义
短期记忆(Short-Term Memory) 是 Agent 在当前会话中维护的上下文信息集合,存储在 LLM 的 Context Window 中,随会话结束而消失。
1.2 核心特征
┌─────────────────────────────────────────────────────────────┐
│ 短期记忆核心特征 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 存储位置:LLM Context Window │ │
│ │ 生命周期:会话期间 │ │
│ │ 容量限制:受模型上下文窗口限制 │ │
│ │ 访问速度:极快,直接参与推理 │ │
│ │ 管理成本:每次请求都消耗 Token │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 典型内容: │
│ • 系统提示词 (System Prompt) │
│ • 对话历史 (Conversation History) │
│ • 检索到的外部知识 (Retrieved Context) │
│ • 用户当前输入 (User Input) │
│ │
└─────────────────────────────────────────────────────────────┘1.3 与长期记忆的区别
| 维度 | 短期记忆 | 长期记忆 |
|---|---|---|
| 存储位置 | LLM Context Window | 外部存储(向量库/数据库) |
| 生命周期 | 会话期间 | 永久 |
| 访问方式 | 直接参与推理 | 需要检索后注入 |
| 访问速度 | 毫秒级 | 百毫秒到秒级 |
| 容量 | 受模型限制(4K-200K Token) | 理论无限 |
| 成本 | 每次请求都消耗 Token | 检索成本 + 存储成本 |
| 内容 | 当前对话相关 | 历史知识、用户偏好 |
二、短期记忆的组成结构
2.1 上下文窗口结构
┌─────────────────────────────────────────────────────────────┐
│ LLM Context Window 结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ System Prompt(系统提示词) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ You are a helpful assistant... │ │ │
│ │ │ 用户偏好:喜欢简洁回答 │ │ │
│ │ │ 当前任务:回答技术问题 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Conversation History(对话历史) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ User: 什么是 RAG? │ │ │
│ │ │ Assistant: RAG 是检索增强生成... │ │ │
│ │ │ User: 它有什么优势? │ │ │
│ │ │ Assistant: RAG 的优势包括... │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Retrieved Context(检索上下文) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ [知识库检索结果] │ │ │
│ │ │ RAG 技术详解:... │ │ │
│ │ │ 向量数据库对比:... │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Current User Input(当前用户输入) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ User: 能举个例子说明吗? │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 总 Token 数 = System + History + Retrieved + Input │
│ 不能超过模型的 Context Window 限制 │
│ │
└─────────────────────────────────────────────────────────────┘2.2 各部分详解
系统提示词 (System Prompt)
系统提示词定义了 Agent 的角色和行为规范:
system_prompt = """
你是一个专业的技术问答助手。
## 角色定义
- 名字:小智
- 定位:技术领域的智能问答助手
- 风格:专业、简洁、友好
## 用户偏好
- 语言:中文
- 详细程度:简洁(优先给出结论)
- 代码风格:Python
## 行为规范
1. 回答技术问题时,先给出核心结论
2. 需要时补充详细解释和代码示例
3. 不确定时明确说明
"""对话历史 (Conversation History)
对话历史是短期记忆的核心,包含多轮交互:
conversation_history = [
{"role": "user", "content": "什么是向量数据库?"},
{"role": "assistant", "content": "向量数据库是一种专门存储和检索向量数据的数据库..."},
{"role": "user", "content": "有哪些主流产品?"},
{"role": "assistant", "content": "主流向量数据库包括:\n1. Milvus\n2. Pinecone\n3. Chroma..."},
{"role": "user", "content": "Chroma 怎么用?"} # 当前问题
]检索上下文 (Retrieved Context)
从长期记忆检索到的相关信息:
retrieved_context = """
[相关文档 1]
Chroma 是一个开源的向量数据库,支持本地运行...
[相关文档 2]
Chroma 基本用法:
```python
import chromadb
client = chromadb.Client()
collection = client.create_collection("my_collection")
collection.add(documents=["文档内容"], ids=["id1"])"""
---
## 三、短期记忆管理
### 3.1 容量管理策略
┌─────────────────────────────────────────────────────────────┐ │ 短期记忆容量管理 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 问题:Context Window 有限,对话历史会不断增长 │ │ │ │ 解决方案: │ │ │ │ 1. 截断策略 (Truncation) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ • 保留最近 N 轮对话 │ │ │ │ • 简单直接,但可能丢失重要上下文 │ │ │ │ • 适用于:对话历史不太重要场景 │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ 2. 滑动窗口 (Sliding Window) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ • 保持 Token 总数不超过阈值 │ │ │ │ • 自动丢弃最早的对话 │ │ │ │ • 适用于:大多数对话场景 │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ 3. 摘要压缩 (Summarization) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ • 将旧对话压缩为摘要 │ │ │ │ • 保留关键信息,减少 Token │ │ │ │ • 适用于:长对话、关键信息多的场景 │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ 4. 重要性筛选 (Importance Filtering) │ │ ┌─────────────────────────────────────────────────┐ │ │ │ • 评估每轮对话的重要性 │ │ │ │ • 保留高重要性对话,丢弃低重要性 │ │ │ │ • 适用于:对话质量要求高的场景 │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
### 3.2 LangChain 中的实现
#### 基于窗口的记忆管理
```python
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import ConversationChain
from langchain_openai import OpenAI
# 只保留最近 5 轮对话
memory = ConversationBufferWindowMemory(k=5)
llm = OpenAI(temperature=0)
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 多轮对话
conversation.predict(input="你好,我是小明")
conversation.predict(input="我想了解 Python")
conversation.predict(input="你记得我的名字吗?") # 如果超过5轮可能不记得基于摘要的记忆管理
from langchain.memory import ConversationSummaryMemory
from langchain_openai import OpenAI
# 自动将旧对话压缩为摘要
memory = ConversationSummaryMemory(llm=OpenAI(temperature=0))
llm = OpenAI(temperature=0)
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 对话历史会被自动压缩
conversation.predict(input="我想学习机器学习")
conversation.predict(input="从哪里开始?")
conversation.predict(input="需要什么基础?")
# 查看当前记忆状态
print(memory.load_memory_variables({}))
# 输出类似:{'history': '用户想学习机器学习,询问了入门路径和所需基础...'}基于Token限制的记忆管理
from langchain.memory import ConversationTokenBufferMemory
from langchain_openai import OpenAI
# 限制总 Token 数不超过 2000
memory = ConversationTokenBufferMemory(
llm=OpenAI(temperature=0),
max_token_limit=2000
)
llm = OpenAI(temperature=0)
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 对话历史会自动截断以符合 Token 限制
for i in range(20):
conversation.predict(input=f"这是第 {i+1} 轮对话")3.3 自定义记忆管理
from typing import List, Dict
import tiktoken
class SmartMemoryManager:
"""智能短期记忆管理器"""
def __init__(self, max_tokens: int = 4000, model: str = "gpt-3.5-turbo"):
self.max_tokens = max_tokens
self.encoding = tiktoken.encoding_for_model(model)
self.history: List[Dict] = []
self.summary: str = ""
def count_tokens(self, text: str) -> int:
"""计算文本的 Token 数"""
return len(self.encoding.encode(text))
def add_message(self, role: str, content: str):
"""添加消息到历史"""
self.history.append({"role": role, "content": content})
self._manage_capacity()
def _manage_capacity(self):
"""管理容量,确保不超过限制"""
while self._estimate_total_tokens() > self.max_tokens:
if len(self.history) > 2:
# 移除最早的非系统消息
self.history.pop(0)
else:
break
def _estimate_total_tokens(self) -> int:
"""估算总 Token 数"""
total = 0
for msg in self.history:
total += self.count_tokens(msg["content"])
return total
def get_context(self) -> List[Dict]:
"""获取当前上下文"""
return self.history.copy()
def compress_old_messages(self, keep_recent: int = 5):
"""压缩旧消息为摘要"""
if len(self.history) <= keep_recent:
return
old_messages = self.history[:-keep_recent]
recent_messages = self.history[-keep_recent:]
# 将旧消息转为摘要(实际应用中调用 LLM)
summary_text = "\n".join([
f"{m['role']}: {m['content']}"
for m in old_messages
])
self.summary = f"[历史摘要]\n{summary_text}"
# 更新历史
self.history = [
{"role": "system", "content": self.summary}
] + recent_messages四、短期记忆的应用场景
4.1 多轮对话
┌─────────────────────────────────────────────────────────────┐
│ 多轮对话示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ User: 我最近想学 Python │
│ Assistant: 很好的选择!Python 适合初学者... │
│ │
│ User: 应该从哪里开始? ← 需要理解上下文 │
│ Assistant: 既然你想学 Python,建议从基础语法开始... │
│ │
│ User: 有推荐的教程吗? ← 延续之前的话题 │
│ Assistant: 针对Python学习,我推荐... │
│ │
│ 【短期记忆的作用】 │
│ • 理解"应该"指的是学Python │
│ • 理解"有推荐的教程"指的是Python教程 │
│ • 保持对话主题的连贯性 │
│ │
└─────────────────────────────────────────────────────────────┘4.2 指代消解
# 指代消解依赖短期记忆
conversation = [
{"role": "user", "content": "什么是 RAG?"},
{"role": "assistant", "content": "RAG 是检索增强生成技术..."},
{"role": "user", "content": "它有什么优势?"}, # "它"指 RAG
{"role": "assistant", "content": "RAG 的优势包括..."},
{"role": "user", "content": "如何实现?"}, # 省略主语,仍是 RAG
]
# 没有短期记忆,LLM 无法理解"它"和省略主语4.3 任务执行跟踪
┌─────────────────────────────────────────────────────────────┐
│ 任务执行跟踪示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Agent 正在执行数据分析任务: │
│ │
│ Step 1: 读取数据文件 ✓ │
│ → 短期记忆:文件路径、数据结构 │
│ │
│ Step 2: 数据清洗 ✓ │
│ → 短期记忆:清洗规则、处理数量 │
│ │
│ Step 3: 数据分析 ⏳ (当前) │
│ → 需要前两步的信息来决定分析方法 │
│ │
│ Step 4: 生成报告 │
│ → 需要所有步骤的结果 │
│ │
│ 【短期记忆的作用】 │
│ • 跟踪任务进度 │
│ • 传递中间结果 │
│ • 支持任务回滚和恢复 │
│ │
└─────────────────────────────────────────────────────────────┘五、短期记忆的优化技巧
5.1 Token 优化技巧
┌─────────────────────────────────────────────────────────────┐
│ Token 优化技巧 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 精简系统提示词 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ❌ 冗长版本 (500+ tokens): │ │
│ │ 你是一个非常专业的、有多年经验的、知识渊博的... │ │
│ │ │ │
│ │ ✅ 精简版本 (50 tokens): │ │
│ │ 角色:技术问答助手 │ │
│ │ 风格:专业、简洁 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 2. 压缩对话历史 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 合并连续的同角色消息 │ │
│ │ • 去除无关的寒暄 │ │
│ │ • 使用摘要替代长对话 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 3. 按需加载检索上下文 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 只检索与当前问题相关的内容 │ │
│ │ • 限制检索结果数量 │ │
│ │ • 压缩检索到的文档 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 4. 使用更高效的编码 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ • 中文使用中文 Tokenizer(如 GPT-4 的 cl100k) │ │
│ │ • 避免使用过多英文缩写 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘5.2 质量优化技巧
class QualityMemoryManager:
"""高质量短期记忆管理"""
def __init__(self):
self.history = []
self.key_facts = {} # 提取的关键事实
def add_message(self, role: str, content: str):
"""添加消息,同时提取关键事实"""
self.history.append({"role": role, "content": content})
# 提取关键事实(简化示例)
if role == "user":
# 实际应用中可以用 LLM 提取
key_fact = self._extract_key_fact(content)
if key_fact:
self.key_facts[len(self.history)] = key_fact
def _extract_key_fact(self, content: str) -> str:
"""提取关键事实"""
# 简化实现,实际应用中可用 LLM
keywords = ["我想", "我需要", "我偏好", "请记住"]
for kw in keywords:
if kw in content:
return content
return None
def get_context_for_inference(self, max_tokens: int = 4000):
"""获取推理用的上下文,优先保留关键信息"""
context = []
token_count = 0
# 先添加关键事实摘要
facts_summary = self._summarize_facts()
context.append({"role": "system", "content": facts_summary})
token_count += self._count_tokens(facts_summary)
# 再添加最近对话
for msg in reversed(self.history):
msg_tokens = self._count_tokens(msg["content"])
if token_count + msg_tokens > max_tokens:
break
context.insert(1, msg) # 插入到系统提示后
token_count += msg_tokens
return context
def _summarize_facts(self) -> str:
"""总结关键事实"""
if not self.key_facts:
return ""
return "关键信息:" + "; ".join(self.key_facts.values())六、面试高频问题
Q1: 短期记忆的容量限制是什么?如何解决?
答案要点:
容量限制:
- 受 LLM Context Window 限制(4K-200K Token)
- 包括系统提示、对话历史、检索上下文、当前输入
- Token 数随对话增长而增加
解决方案:
- 截断:保留最近 N 轮,丢弃更早的
- 滑动窗口:保持 Token 总数不超过阈值
- 摘要压缩:将旧对话压缩为摘要
- 重要性筛选:只保留高重要性对话
- 混合策略:摘要 + 最近对话
Q2: 为什么不能把所有信息都放进 Context Window?
答案要点:
-
成本问题:
- 输入 Token 按量计费
- 长上下文会显著增加 API 成本
-
注意力分散:
- 过多无关信息会稀释注意力
- 模型可能"迷失"在长上下文中
-
推理质量下降:
- 实验表明,过长上下文会降低回答质量
- "Lost in the Middle" 现象
-
响应延迟:
- 处理长上下文需要更多计算时间
- 用户等待时间增加
Q3: ConversationBufferWindowMemory 和 ConversationSummaryMemory 有什么区别?
答案要点:
| 特性 | BufferWindow | Summary |
|---|---|---|
| 保留方式 | 保留最近 N 轮原文 | 将旧对话压缩为摘要 |
| 信息完整性 | 高(原文) | 低(摘要可能丢失细节) |
| Token 效率 | 一般 | 高(压缩后更少 Token) |
| 适用场景 | 短对话、需要精确上下文 | 长对话、关注关键信息 |
| 成本 | 较高(存储原文) | 较低(摘要后更少 Token) |
Q4: 如何判断应该保留哪些对话历史?
答案要点:
判断标准:
1. 重要性:包含关键决策、用户偏好的对话更重要
2. 相关性:与当前话题相关的对话更重要
3. 时效性:最近的对话通常更重要
4. 信息密度:信息量大的对话更重要
实现方式:
• 规则判断:基于关键词、句式规则
• LLM 评估:让 LLM 给对话打重要性分
• 向量相似度:与当前问题的相似度越高越重要
• 混合策略:综合多个维度打分Q5: 长上下文模型(如 Claude 200K)还需要短期记忆管理吗?
答案要点:
仍然需要,原因如下:
-
成本考量:
- 200K Token 的输入成本很高
- 精准管理可显著降低成本
-
注意力问题:
- 长上下文仍有"Lost in the Middle"问题
- 关键信息放在开头/结尾效果更好
-
无限历史:
- 用户与 Agent 交互历史可能超过任何窗口
- 长期记忆 + 检索仍必要
-
响应效率:
- 处理 200K Token 比处理 10K Token 慢很多
- 精简上下文提升响应速度
七、总结
核心概念回顾
| 概念 | 定义 | 关键要点 |
|---|---|---|
| 短期记忆 | Context Window 中的上下文信息 | 直接参与推理,容量有限 |
| 上下文结构 | System + History + Retrieved + Input | 四部分组成完整上下文 |
| 容量管理 | 确保不超过 Token 限制 | 截断、滑动窗口、摘要压缩 |
| 质量优化 | 保留重要信息,丢弃噪声 | 重要性评估、关键事实提取 |
一句话总结
短期记忆是 Agent 的"工作台",通过合理的容量管理和质量优化,在有限 Context Window 内保持对话连贯性和推理质量。
设计口诀
短期记忆管理口诀:
容量有限要规划,系统历史加检索
滑动窗口保最近,摘要压缩存精华
重要信息优先留,无关噪声及时丢
长窗口虽好,成本质量都要顾最后更新:2026年3月18日