AI AgentTechnical Deep Dive

记忆与上下文管理:不是记得越多越好,而是取用得刚好

发布时间2026/02/21
分类AI Agent
预计阅读10 分钟
作者吴长龙
*

Agent 记忆的难点从来不是“存下来”,而是如何在正确时机取回正确的信息,同时不让上下文膨胀到拖垮效果和成本。

01.记忆系统真正难的,不是“让模型记住”

做 Agent 时,很多人会自然地把“记忆”理解成一件很直观的事:把过去对话存起来,下次继续用。

但一旦系统开始变复杂,问题会立刻出现:

  • 历史消息越来越长,模型开始分心
  • 旧信息已经过时,却还在污染当前上下文
  • 用户偏好、业务事实、任务过程混在一起
  • 该在什么时候写入长期记忆,根本没有边界

所以从工程角度看,记忆系统的核心从来不是“存储更多”,而是:

在合适的作用域里保存信息,并在合适的时机把最相关的部分带回上下文。

这个视角会比“短期记忆 vs 长期记忆”的教科书分类更有用。

02.先把记忆分成两类作用域

LangGraph 当前官方 memory 文档把记忆按 recall scope 分成两类,这个划分非常实用:

  • short-term memory:线程级、会话内
  • long-term memory:跨线程、跨会话

短期记忆是什么

短期记忆就是当前 thread 内的状态。它通常包括:

  • 历史消息
  • 本轮检索结果
  • 临时生成草稿
  • 上传文件或中间产物

这些内容的共同点是:它们只在这条任务链里有意义。

长期记忆是什么

长期记忆则更像跨会话知识,例如:

  • 用户偏好
  • 账号资料
  • 已确认的业务事实
  • 经常复用的操作规则

官方文档里还进一步把长期记忆分成 semantic、episodic、procedural 等类型。对工程实现来说,不必先背术语,更重要的是先问:

  • 这条信息会不会跨会话复用
  • 它应该按用户、组织还是业务对象分组
  • 它值得持久化多久

03.短期记忆的关键,不是全量保留,而是 thread + checkpointer

旧的 LangChain 记忆文章经常从 ConversationBufferMemory 一类 API 开始讲,但按当前官方口径,更值得优先理解的是:

  • thread 是什么
  • checkpointer 如何保存状态
  • 状态在哪些步骤被读取和更新

官方 short-term memory 文档强调,短期记忆是 agent state 的一部分,会通过 checkpointer 持久化,从而让线程可以被恢复。

一个最小示意如下:

python snippetpython
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver


def get_weather(city: str) -> str:
    """查询天气。"""
    return f"{city} 当前天气晴。"


agent = create_agent(
    model="gpt-5-nano",
    tools=[get_weather],
    checkpointer=InMemorySaver(),
)


agent.invoke(
    {"messages": [{"role": "user", "content": "我叫小明"}]},
    {"configurable": {"thread_id": "user-1"}},
)

agent.invoke(
    {"messages": [{"role": "user", "content": "我刚才说我叫什么?"}]},
    {"configurable": {"thread_id": "user-1"}},
)

这里真正重要的是 thread_id,不是“内存对象”本身。只要线程一致,状态就会沿着同一条会话继续累积。

04.上下文膨胀时,真正该管理的是消息,不是“记忆概念”

官方文档对长对话的提醒非常明确:上下文窗口有限,历史消息越长,成本越高,模型也越容易被旧信息干扰。

所以短期记忆一旦变长,你真正需要管理的是消息历史,而不是继续讨论抽象概念。

常见做法包括:

  • 删除陈旧消息
  • 只保留最近若干轮
  • 把旧消息摘要化
  • 只把需要的 state 字段放进 prompt

一个简单的消息裁剪逻辑示意如下:

python snippetpython
from langchain.messages import RemoveMessage


def trim_messages(state):
    messages = state["messages"]
    if len(messages) <= 8:
        return {}

    # 只保留最近 8 条
    removed = [RemoveMessage(id=m.id) for m in messages[:-8]]
    return {"messages": removed}

注意这类操作的目标不是“省 token”这么单一,而是让当前步骤只看到仍然有决策价值的信息。

05.长期记忆更像“可检索的持久化事实”

在当前 LangChain / LangGraph 体系里,长期记忆不是一个魔法记忆盒,而是建立在 store 之上的持久化数据。

官方 long-term memory 文档把它描述成 JSON documents organized by namespace and key。这个设计非常重要,因为它提醒我们:

  • 长期记忆不一定非要向量化
  • 它可以先是结构化数据
  • namespace 才是组织和检索的关键

一个最小例子:

python snippetpython
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore


@dataclass
class Context:
    user_id: str


store = InMemoryStore()
store.put(("users",), "user_123", {"preferences": "回答尽量简洁"})


@tool
def get_user_preferences(runtime: ToolRuntime[Context]) -> str:
    """读取用户偏好。"""
    memory = runtime.store.get(("users",), runtime.context.user_id)
    return memory.value["preferences"] if memory else "无偏好"


agent = create_agent(
    model="gpt-5-nano",
    tools=[get_user_preferences],
    store=store,
    context_schema=Context,
)

这里的重点是:

  • 长期记忆通过 store 管理
  • 具体取哪条记忆,由 namespace + key 决定
  • 工具通过 runtime 访问它

这比“把所有历史都塞进向量数据库”更接近真实工程设计。

06.什么信息适合写入长期记忆

不是每一段对话都值得持久化。更稳妥的判断方式是:

适合写入

  • 用户明确声明且后续可能复用的偏好
  • 稳定身份信息
  • 已确认的业务事实
  • 明确可复用的操作规则

不适合直接写入

  • 临时情绪
  • 未确认猜测
  • 只在当前任务里有效的中间过程
  • 明显会过期的短时状态

官方 memory 文档也提到一个重要区分:记忆可以在 hot path 写入,也可以在后台写入。这个区分很有价值。

07.什么时候在 hot path 写,什么时候后台写

hot path 写入

适合以下信息:

  • 用户刚明确声明的偏好
  • 当前回合必须立刻影响回答的事实

优点是即时生效,缺点是会增加主链路复杂度。

后台写入

适合以下信息:

  • 需要归纳、清洗、去重的记忆
  • 可能要合并多轮结果后才有意义的知识

优点是主流程更轻,缺点是不会立刻生效。

经验上,不要让主执行链路承担过多“记忆整理”工作。主链路应该优先完成当前任务,重加工可以异步做。

08.记忆系统最容易踩的三个坑

1. 把所有历史都当记忆

这会让上下文越来越脏,也会让长期存储越来越难维护。

2. 把长期记忆等同于 RAG

RAG 是一种检索方式,不是长期记忆的全部。很多长期记忆更适合先用结构化 store 表达,再决定是否需要语义检索。

3. 只设计“怎么存”,不设计“怎么取”

一个记忆系统如果没有明确的召回时机、作用域和过滤规则,存得再多也很难稳定产生价值。

09.一个更稳妥的设计顺序

如果你要给 Agent 加记忆,我更建议按这个顺序做:

  • 先区分 thread 内状态和跨线程事实
  • 先设计消息裁剪策略
  • 再设计长期记忆的 namespace 和 key
  • 最后才决定是否要引入语义检索

这样做能避免一上来就把问题过早复杂化。

10.总结

记忆系统真正的核心,不是“让模型一直记得”,而是把信息放进正确作用域,并在正确时机取回:

  • thread 内状态解决当前任务连续性
  • store 解决跨会话持久化
  • 消息裁剪解决上下文膨胀
  • hot path / background 写入解决主流程和记忆整理之间的平衡

当这些边界清楚以后,记忆系统才会变成 Agent 的增益项,而不是新的成本黑洞。

11.参考资料