上一章-> 【LangGraph】跨会话持久化(Store)——让 Agent 记住你的长期偏好

前言:

在上一篇我们讲了:

跨会话持久化(Store)——让 Agent 记住长期偏好

我们已经让 AI 可以做到:

  • 记住你是谁
  • 记住你喜欢什么
  • 跨会话不丢失信息

但问题来了:

  • Agent 在一次对话过程中,是怎么“记住上下文”的?

  • 当对话变得很长,它会不会“记忆爆炸”?

  • 如何控制记忆的成本与质量?

这就是本篇要讲的内容:

持久化三大应用能力之一:记忆(Memory)


一、什么是记忆(Memory)?

1.1 记忆的本质

记忆,是⼀种能够记住之前互动信息的系统

对于⼈⼯智能代理来说,记忆⾄关重要因为它使他们能够记住之前的互动,从反馈中学习,并根据⽤⼾偏好进⾏调整随着代理处理涉及⼤量⽤⼾交互的更复杂任务

这⼀能力对效率和用户满意度都变得至关重要,对于 AI Agent 而言,其价值不仅在于回答问题,更在于:

  1. 理解上下文

  2. 持续对话

  3. 根据历史调整行为


1.2 记忆 vs 持久化(重要区分)

这两个概念容易混淆,需要明确:

持久化(Persistence)为 LangGraph 底层能⼒ 包含【线程级】持久化和【跨会话】持久化(如何存)

记忆为 (Memory)LangGraph 能实现的应用层能⼒ 包含【短期记忆】和【长期记忆】(如何用)

总结一下:持久化是基础,记忆是上层表现


1.3 两种记忆类型

  1. 短期记忆(Short-term Memory)

基于:线程级持久化(Checkpointer)

范围:单个会话(thread_id)

内容:对话上下文

示例:
用户说“我叫 小猫”,随后问“我叫什么?”
Agent 能回答“你叫小猫”


  1. 长期记忆(Long-term Memory)

基于:跨会话持久化(Store)

范围:跨 thread_id

内容:用户画像、偏好

示例:上一章我们介绍的 Store 机制


二、短期记忆的核心问题

随着对话进行,messages 列表会不断增长,将带来三个问题:

  1. LLM 上下文窗口限制
    大多数模型有固定上下文长度(如 8K、32K、128K token)
    一旦消息历史超过这个上限,最旧的内容会被截断或导致调用失败

  2. Token 成本线性上升
    每次请求都会把全部历史消息发送给模型,输入 token 越多,计费越高
    对于长对话,成本可能膨胀数十倍

  3. 推理延迟增加
    模型处理长序列需要更多计算时间,用户感知到的响应延迟明显变长,影响交互体验

因此,必须对短期记忆进行管理。如果不加干预,一个活跃对话可能在几十轮后就开始触碰模型限制,无法继续,非常影响我们的使用


常用的三种应对措施:

  1. 修剪(Trim)
    直接丢弃最早的一部分消息,保留尾部最新内容
    实现简单,但可能丢失重要早期信息

  2. 删除(Delete)
    主动移除特定类型或特定 ID 的消息
    灵活性高,但需要设计合理的删除规则

  3. 总结(Summarize)
    将长对话压缩为简短摘要,保留关键信息,同时删除原始消
    最推荐,兼顾信息保留与成本控制


三、记忆管理策略一:修剪消息(Trim)

3.1 思路

只保留最近的一部分对话,丢弃较早的内容。
也就是:早期信息对当前决策不再重要
适合场景如:

  • 客服机器人(用户问题通常在最近几轮内解决)

  • 指令跟随任务(用户只关心当前指令)

  • 实时问答(上下文窗口只需覆盖最后几分钟)

3.2 代码示例(基于 tiktoken 计数)

这个在之前langchain内容了解过:

import tiktoken
from langchain_core.messages import trim_messages

def trim_by_token_count(state: State):
    """按 token 数量修剪消息,保留最后的消息"""
    tokenizer = tiktoken.get_encoding("cl100k_base")
    
    messages= trim_messages(
        state["messages"],
        strategy="last",
        token_counter=model,
        max_tokens=512,
        start_on="human",
        end_on=("human", "tool"),
    )
    return {"messages": messages}

3.3 优缺点

  • 优点:实现简单、成本可控、无需额外 LLM 调用

  • 缺点:早期关键信息可能永久丢失(例如用户最初告知的姓名或偏好)
    如果后续对话需要这些信息,就会出问题

因此,修剪策略更适合与 Store(长期记忆) 配合使用:
将重要信息先存入 Store,然后再放心修剪消息

四、记忆管理策略二:删除消息(Delete)

4.1 思路

主动移除指定的消息,保留剩余部分

应用场景:

  • 对话中出现错误消息(如工具调用失败返回了错误内容),需要将其移除以防模型被误导

  • 只想保留 Human 和 AI 消息,删除内部的 ToolMessage(节省 token 且不影响语义)

  • 用户主动要求“忘记刚才那句话”

  • 重置对话(清空所有消息)开始全新话题

4.2 按数量删除示例

from langchain_core.messages import RemoveMessage

def delete_old_messages(state: State):
    """保留最后 5 条消息,其余删除"""
    messages = state["messages"]
    if len(messages) > 5:
        to_delete = messages[:-5]
        return {"messages": [RemoveMessage(id=m.id) for m in to_delete]}
    return {}

4.3 删除全部消息

from langgraph.graph.message import REMOVE_ALL_MESSAGES

def clear_all_messages(state: State):
    """清空整个消息历史"""
    return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}

注意:
删除操作不可逆,必须确保删除后上下文仍能维持合理的对话逻辑

五、记忆管理策略三:总结消息(Summarize)

这是最推荐的方式,尤其适合长对话

  1. 核心思想:
    将长对话压缩成一段摘要(summary),保留关键信息,同时删除原始消息,从而控制 token 成本

  2. 初始状态

class State(MessagesState):
    summary: str   # 存储对话摘要
  1. 工作流程

-每次调用 LLM 前,将 summary 作为上下文前缀

定期(如每 N 轮后)触发总结节点:
用现有摘要 + 新消息生成新摘要,删除已被总结的旧消息

代码实现:

from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage

def call_model_with_summary(state: State):
    """将摘要和最新消息一起发送给 LLM"""
    summary = state.get("summary", "")
    if summary:
        summary_msg = SystemMessage(content=f"之前的对话摘要:{summary}\n请基于此继续对话。")
        messages = [summary_msg] + state["messages"]
    else:
        messages = state["messages"]
    response = model.invoke(messages)
    return {"messages": [response]}

def trigger_summarization(state: State, threshold: int = 6):
    """当消息数量超过阈值时,生成或更新摘要"""
    if len(state["messages"]) < threshold:
        return {}
    
    existing_summary = state.get("summary", "")
    # 选择待总结的消息(例如全部消息,或除去最后几条)
    to_summarize = state["messages"][:-2]  # 保留最后2条不总结
    
    prompt = "请总结以下对话的核心内容。"
    if existing_summary:
        prompt = f"已有摘要:{existing_summary}\n请结合新对话更新摘要。"
    
    summarize_input = [HumanMessage(content=prompt)] + to_summarize
    new_summary = model.invoke(summarize_input).content
    
    # 删除已被总结的消息
    delete = [RemoveMessage(id=m.id) for m in to_summarize]
    
    return {
        "summary": new_summary,
        "messages": delete 
    }
  1. 优势
  • 关键信息不丢失

  • Token 成本可控

  • 适配长周期、多轮对话场景

六、三种记忆组合(生产级方案)

在实际系统中,通常不会只用一种策略,而是组合使用:

层级 存储/机制 作用
短期记忆 Checkpointer + thread_id 保留会话内最近消息细节,支撑断点、中断、回溯重跑
压缩记忆 summary 汇总字段 精简历史对话,保留关键信息,减少 token 占用
长期记忆 Store + user_id 永久保存用户画像、个人偏好、业务档案,跨会话复用

最终实现效果:

Agent 知道“你是谁”(Store)

Agent 记得“你刚说了什么”(短期)

Agent 了解“你们之前聊过什么”(summary)


七、总结

核心结论:

记忆 = 持久化能力在应用层的体现,包含短期记忆和长期记忆

短期记忆基于 Checkpointer(thread_id),解决当前会话的上下文问题

长期记忆基于 Store(user_id),解决跨会话的用户信息问题

记忆管理三板斧:

  1. 修剪(Trim):按 token 或消息数量截断

  2. 删除(Delete):主动移除特定消息

  3. 总结(Summarize):将历史压缩为摘要(最推荐)

我们可以通过合理组合这三种策略, LangGraph Agent 在有限的上下文窗口中,同时拥有细节、全局和长期的记忆能力,从而构建真正智能的对话系统


本次分享就到这里,下期我们讲 👉【LangGraph】持久化实现的三大能力——人机交互
在这里插入图片描述

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐