短期记忆:当前任务轨迹 + 工具结果缓存

一句话:短期记忆是 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 V3128K~120K
GPT-4o128K~120K
Claude 4200K~190K
Gemini 2.5 Pro1M~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保留的窗口太小也不够用"]

上下文窗口并不是越大越好

  1. 窗口越大,推理越慢、越贵(自注意力是 O(n²))
  2. LLM 对「窗口中间」的信息注意力衰减(Lost in the Middle 效应)
  3. 垃圾塞多了,回复质量反而下降

三、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 不麻烦。

相关链接


技术学习路线图 > 记忆与上下文

相关链接