短期记忆:当前任务轨迹 + 工具结果缓存
一句话:短期记忆是 Agent 的”工作台面”——当前对话的所有消息、推理步骤、工具返回结果都暂存在上下文窗口中,支撑 Agent 知道「刚才发生了什么」,但窗口满了就得清理。
一、什么是短期记忆?
把 Agent 想象成一个正在做菜的人:
- 他面前有一张操作台(上下文窗口),上面摆着菜谱(System Prompt)、食材清单(用户目标)、刚才切的菜(工具返回结果)
- 操作台就这么大,摆不下了就得把不用的挪走
- 菜做完,操作台清空——下次做新菜又是从头开始
短期记忆 = 当前会话的上下文窗口(Context Window)。它存着:
| 内容 | 例子 | 占多少 |
|---|---|---|
| System Prompt | ”你是一个简历分析助手…” | 固定几百 token |
| 用户消息历史 | Q1: “分析我的简历” Q2: “那我的项目经验呢?“ | 逐轮增长 |
| 推理步骤(Thought) | “我需要先调用 read_resume 工具…” | 每步几十到几百 token |
| 工具调用与返回 | analyze_resume() → 返回 500 字分析 | 最占地方的 |
| 用户目标与约束 | ”用中文回答,不超过 200 字” | 少量 |
二、上下文窗口限制:满了会怎样?
每个 LLM 都有上下文窗口上限(Context Window Limit)——能同时”看到”的最大 token 数:
| 模型 | 上下文窗口 | 实际可用(系统+控制开销后) |
|---|---|---|
| DeepSeek V3 | 128K | ~120K |
| GPT-4o | 128K | ~120K |
| Claude 4 | 200K | ~190K |
| Gemini 2.5 Pro | 1M | ~950K |
窗口满了之后,你有三种选择——但每种都有代价:
graph TD A["新消息进来"] --> B{"上下文窗口满了?"} B -- "没满" --> C["直接追加到窗口"] B -- "满了" --> D["要腾位置了"] D --> E["方案1: 截断\n丢掉最老的消息"] D --> F["方案2: 压缩\n把旧消息摘要成一段"] D --> G["方案3: 滑动窗口\n保留最近 N 轮"] E --> E1["⚠️ Agent 失忆:\n忘了之前在聊什么"] F --> F1["⚠️ 信息损失:\n摘要可能丢掉关键细节"] G --> G1["⚠️ 中间历史丢失:\n保留的窗口太小也不够用"]
上下文窗口并不是越大越好
- 窗口越大,推理越慢、越贵(自注意力是 O(n²))
- LLM 对「窗口中间」的信息注意力衰减(Lost in the Middle 效应)
- 垃圾塞多了,回复质量反而下降
三、LangGraph MemorySaver:短期记忆的落盘机制
你项目中用的是 LangGraph 的 MemorySaver——它是把 Agent 的「当前状态」快照存到内存里,支撑多步推理时「回看历史步骤」。
它解决什么问题?
没有 MemorySaver 的时候,Agent 每步推理都是金鱼记忆:执行完 Tool A 回来,已经忘了为什么要调 Tool A。
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
# 1. 创建 checkpointer(短期记忆持久化)
checkpointer = MemorySaver()
# 2. 编译 Graph 时挂载
graph = StateGraph(AgentState)
graph.add_node("analyze", analyze_resume)
graph.add_node("rerank", rerank_results)
# ... 更多节点
app = graph.compile(checkpointer=checkpointer)
# 3. 每次调用指定 thread_id(会话隔离)
result = app.invoke(
{"query": "分析我的 Python 经验"},
config={"configurable": {"thread_id": "user_session_001"}}
)
# 下次同一 thread_id 继续,Agent 能「记住」之前做了什么MemorySaver 的核心行为
| 功能 | 说明 |
|---|---|
put() | 每步执行完,自动把状态写入内存 |
get() | 下一步开始时,读出上一步状态 |
thread_id 隔离 | 不同会话互不干扰 |
get_state() | 调试时查看任意时间点的状态快照 |
# 查看某次会话的状态
state = app.get_state(
config={"configurable": {"thread_id": "user_session_001"}}
)
print(state.values) # 当前状态的所有字段
print(state.next) # 下一步要执行的节点四、工具结果缓存:省 Token 的实用技巧
问题场景:Agent 在多步推理中,同一个工具被调了两次,参数一模一样。第二次调用纯属浪费——既耗时又烧 Token(还要把结果再塞一次进上下文)。
解决方案:在短期记忆层加一层工具结果缓存。
import hashlib
import json
from functools import wraps
from typing import Any, Callable, Dict
class ToolResultCache:
"""工具结果缓存:同一会话内,相同参数的工具调用只执行一次"""
def __init__(self):
self._cache: Dict[str, Any] = {} # hash → 结果
def _make_key(self, tool_name: str, args: dict) -> str:
"""生成缓存键:工具名 + 参数哈希"""
raw = json.dumps({"tool": tool_name, "args": args}, sort_keys=True)
return hashlib.md5(raw.encode()).hexdigest()
def get(self, tool_name: str, args: dict) -> Any | None:
"""查缓存"""
key = self._make_key(tool_name, args)
return self._cache.get(key)
def set(self, tool_name: str, args: dict, result: Any):
"""写缓存"""
key = self._make_key(tool_name, args)
self._cache[key] = result
def clear(self):
"""会话结束清空缓存"""
self._cache.clear()
# 用装饰器包装工具调用
cache = ToolResultCache()
def cached_tool(tool_func: Callable) -> Callable:
"""装饰器:工具调用自动缓存"""
@wraps(tool_func)
def wrapper(*args, **kwargs):
tool_name = tool_func.__name__
# 先查缓存
cached_result = cache.get(tool_name,
{"args": args, "kwargs": kwargs})
if cached_result is not None:
print(f"[CACHE HIT] {tool_name} → 跳过执行,省 Token")
return cached_result
# 缓存未命中 → 执行
result = tool_func(*args, **kwargs)
cache.set(tool_name,
{"args": args, "kwargs": kwargs}, result)
return result
return wrapper
# 使用示例
@cached_tool
def read_file(path: str) -> str:
"""读文件(假设耗时 0.5s,返回 2000 字符)"""
with open(path, "r") as f:
return f.read()
# 第一次调用:真读文件
content = read_file("/data/resume.txt") # 实际 I/O
# 第二次调用:命中缓存,直接返回
content = read_file("/data/resume.txt") # [CACHE HIT] 0ms
# 不同参数:不会命中缓存
other = read_file("/data/other.txt") # 实际 I/O▶ 对应原理:28-Agent记忆基础与状态管理
速记卡(面试闪卡)
Q1:一句话讲清「短期记忆:当前任务轨迹 + 工具结果缓存」到底是什么? A:短期记忆是 Agent 的上下文窗口,存当前轨迹与工具结果,满了就得清理或压缩。
Q2:一、什么是短期记忆 —— 怎么理解? A:像做菜的操作台(上下文窗口):摆着菜谱(System Prompt)、食材清单(用户目标)、切好的菜(工具返回)。台面有限,摆不下就挪走;菜做完清空,下次从头来。
Q3:二、上下文窗口限制 —— 怎么理解? A:窗口满有三种处理:截断(丢最老,Agent 失忆)、压缩(摘要,丢细节)、滑动窗口(留最近 N 轮,中间丢失)。窗口非越大越好——O(n²) 更慢更贵,且 Lost in the Middle 注意力衰减。
Q4:三、LangGraph MemorySaver —— 怎么理解? A:MemorySaver 把 Agent 每步状态快照存内存,靠 thread_id 隔离会话。没它 Agent 是金鱼记忆:执行完 Tool A 回来忘了为何调它。put/get 自动存取,get_state 可调试看快照。
Q5:四、工具结果缓存 —— 怎么理解? A:同一会话相同参数的工具调用只执行一次:用 tool名+参数 hash 做键,命中直接返回。像记菜谱的便签,同样步骤不重做,省 Token 又省时。装饰器一行包装即可。
Q6:核心速记主线有哪些?
- 短期记忆 = 上下文窗口
- 满了:截断 / 压缩 / 滑动窗口
- MemorySaver:状态落盘,thread_id 隔离
- 工具结果缓存:同参只算一次,省 Token
口诀 A:短期记忆是台面,消息轨迹上面摊; 窗口满了三法清,截断压缩滑窗宽。 MemorySaver 存快照,thread 隔离不混乱; 工具同参只算一,缓存省 Token 不麻烦。
相关链接
- 目录:00-AI
- 下一篇:32-长期记忆:跨会话用户偏好、历史持久化
- 系列:30-Fallback兜底值设计
- 理论基础:Agent 四要素:LLM+工具+记忆+规划