【SpringAI Alibaba】ReactAgent记忆管理MessageTrimmingHook与MessageDeletionHook

目录

一、ReactAgent记忆管理MessageTrimmingHook

[1.1 为什么需要上下文管理?](#1.1 为什么需要上下文管理?)

需求背景

多种解决方案对比

[实现原理:通过 Hook 在模型调用前后介入](#实现原理:通过 Hook 在模型调用前后介入)

[1.2 案例实战:MessageTrimmingHook 修剪消息](#1.2 案例实战:MessageTrimmingHook 修剪消息)

需求说明

代码实现

关键点说明

二、ReactAgent记忆管理MessageDeletionHook

[2.1 案例实战:MessageDeletionHook 删除消息](#2.1 案例实战:MessageDeletionHook 删除消息)

需求说明

代码实现

[2.2 注意事项](#2.2 注意事项)


一、ReactAgent记忆管理MessageTrimmingHook

1.1 为什么需要上下文管理?

需求背景

  • 开启短期记忆后,对话历史会随对话轮次不断累积变长,就像一份一直在追加内容的会议记录,越来越厚

  • 过长的上下文:大模型上下文窗口超限报错、响应时间变长、Token 成本飙升、模型被过时信息"分散注意力"精度下降

  • 需要引入"上下文工程"策略,在合适的时机"修剪"或"清理"过时的消息

多种解决方案对比

策略 说明 适用场景
修剪消息 保留最新的 N 条消息,丢弃早期对话 长期运行的通用对话 Agent
删除消息 永久删除指定的旧消息 需要彻底清除敏感/过时信息
总结消息 用摘要替换早期消息,保留关键信息 信息密度高、长篇深度对话
自定义策略 按业务规则过滤消息(如只保留特定类型) 有特定业务需求的场景

实现原理:通过 Hook 在模型调用前后介入

1.2 案例实战:MessageTrimmingHook 修剪消息

需求说明

  • 限制每次调用大模型时传入的消息数量(如最多3条),防止上下文超限

  • 保留第一条消息(通常是系统指令)和最近几条对话

代码实现

java 复制代码
// Step1:继承 MessagesModelHook,实现消息修剪逻辑
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {

    private static final int MAX_MESSAGES = 3;  // 最大保留消息数

    @Override
    public String getName() {
        return "message_trimming";
    }

    @Override
    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
        // 消息数未超限,直接通过
        if (previousMessages.size() <= MAX_MESSAGES) {
            return new AgentCommand(previousMessages);
        }

        // 保留第一条消息(通常是 SystemMessage / 用户最初的问题)
        Message firstMsg = previousMessages.get(0);

        // 保留最新的几条消息(保证消息对数偶数对齐:user/assistant交替)
        int keepCount = previousMessages.size() % 2 == 0 ? 3 : 4;
        List<Message> recentMessages = previousMessages.subList(
            previousMessages.size() - keepCount,
            previousMessages.size()
        );

        // 组合:第一条 + 最新N条
        List<Message> trimmedMessages = new ArrayList<>();
        trimmedMessages.add(firstMsg);
        trimmedMessages.addAll(recentMessages);

        // UpdatePolicy.REPLACE:用修剪后的列表替换原始消息列表
        return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);
    }
}

// Step2:在 Agent 中启用修剪 Hook
ReactAgent agent = ReactAgent.builder()
    .name("trimming_agent")
    .model(chatModel)
    .hooks(new MessageTrimmingHook())  // 注册修剪 Hook
    .saver(new MemorySaver())
    .build();

RunnableConfig config = RunnableConfig.builder()
    .threadId("1")
    .build();

// Step3:多轮对话验证修剪效果
agent.call("你好,我叫 Bob", config);
agent.call("写一首关于猫的短诗", config);
agent.call("现在对狗做同样的事情", config);

// 第4轮:尽管对话越来越多,仍能记住第1轮的 Bob 这个名字
AssistantMessage finalResponse = agent.call("我叫什么名字?", config);
System.out.println(finalResponse.getText());
// 输出:你的名字是 Bob。你之前告诉我的。(修剪保留了第一条,所以还记得)

关键点说明

  • HookPosition.BEFORE_MODEL:在调用大模型之前触发,用于修剪传入的消息

  • UpdatePolicy.REPLACE:用修剪后的列表替换原始列表(区别于默认的追加)

  • 修剪时保留第一条消息的目的:通常第一条包含用户最关键的信息(如姓名、任务目标)

  • keepCount 奇偶对齐:确保保留的消息是 user/assistant 交替的完整对话对

二、ReactAgent记忆管理MessageDeletionHook

2.1 案例实战:MessageDeletionHook 删除消息

需求说明

  • 在每轮对话完成后(afterModel),自动删除最旧的几条消息,控制历史记录长度

代码实现

java 复制代码
// Step1:继承 MessagesModelHook,注册在 AFTER_MODEL 阶段
@HookPositions({HookPosition.AFTER_MODEL})
public class MessageDeletionHook extends MessagesModelHook {

    @Override
    public String getName() {
        return "message_deletion";
    }

    @Override
    public AgentCommand afterModel(List<Message> previousMessages, RunnableConfig config) {
        // 消息数超过2条时,删除最旧的2条
        if (previousMessages.size() > 2) {
            // subList(2, size) 跳过最旧的2条,保留剩余的
            List<Message> remainingMessages = previousMessages.subList(
                2, previousMessages.size()
            );
            // UpdatePolicy.REPLACE:用剩余消息替换整个历史
            return new AgentCommand(remainingMessages, UpdatePolicy.REPLACE);
        }
        // 不足2条不处理
        return new AgentCommand(previousMessages);
    }
}

// Step2:在 Agent 中启用删除 Hook
ReactAgent agent = ReactAgent.builder()
    .name("deletion_agent")
    .model(chatModel)
    .hooks(new MessageDeletionHook())  // 注册删除 Hook
    .saver(new MemorySaver())
    .build();

2.2 注意事项

  • ✅ Hook 的执行顺序 :BEFORE_MODEL → 调用大模型 → AFTER_MODEL,修剪在前、删除在后

  • ✅ 修剪和删除可以同时注册 :.hooks(new MessageTrimmingHook(), new MessageDeletionHook()) 两者协同工作

  • ❌ 修剪消息数要考虑奇偶性:保留奇数条(含首条)可能打破 user/assistant 交替结构,导致模型困惑

  • ✅ 推荐组合方案 :开发环境用 MemorySaver,生产环境用 RedisSaver + MessageTrimmingHook 控制 Token 成本

相关推荐
文丑颜不良啊23 分钟前
LeetCode2241~2260题目解析
java·leetcode
Dawson Zhu24 分钟前
【LLM推理与Serving⑧】多机多卡Serving:TP/PP/负载均衡与弹性扩缩容全攻略
人工智能·语言模型·架构·aigc·agi
文丑颜不良啊28 分钟前
LeetCode2321~2340题目解析
java·leetcode
飞哥数智坊36 分钟前
DSH+DeepSeek 实测 Anatomy Skill:让技术原理可视化
人工智能
长不大的杨瑞杰42 分钟前
ChatGPT 广告技术栈架构拆解:多轮意图、LLM 工具编排、ANN 召回与拍卖闭环
人工智能·chatgpt·系统架构·推荐系统·广告
西安小哥1 小时前
给 LLM 网关加一层熔断:主模型挂了 42 秒以后我做的事
ai编程
驭渊的小故事1 小时前
Spring AOP 核心概念与通知类型详解
java·后端·spring
vipxieliang1 小时前
ValidX vs 手写验证代码:开发效率对比实测
java·spring boot·后端