目录
一、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 成本