本文深入剖析 Spring AI Alibaba 框架中短期记忆(Short-term Memory)和长期记忆(Long-term Memory)的实现机制,结合源码和测试用例讲解两者的协作模式。
一、记忆系统概述
在 AI Agent 应用中,记忆系统是维持对话连贯性和个性化体验的核心能力:
- 短期记忆(Short-term Memory):保存当前会话的对话历史,类似人类的"工作记忆"
- 长期记忆(Long-term Memory):持久化存储关键信息,类似人类的"长时记忆"
Spring AI Alibaba 通过 MemorySaver 和 MemoryStore 两个组件实现了这两种记忆机制。
二、短期记忆实现原理
2.1 核心组件:MemorySaver
MemorySaver 是短期记忆的实现类,负责保存和恢复会话状态(Checkpoint)。其核心特点:
- 存储结构 :
ConcurrentHashMap<String, Map<String, Checkpoint>> - 隔离维度 :通过
threadId隔离不同会话 - 生命周期:应用重启后数据丢失(内存存储)
2.2 实现机制
保存检查点
当 Agent 执行完一轮对话后,会调用 MemorySaver.save() 保存状态:
java
// MemorySaver.java
public class MemorySaver implements CheckpointSaver {
private final ConcurrentHashMap<String, Map<String, Checkpoint>> storage = new ConcurrentHashMap<>();
@Override
public void save(Checkpoint checkpoint, RunnableConfig config) {
String threadId = config.threadId().orElseThrow();
String checkpointId = checkpoint.getId();
// 按 threadId 分组存储
storage.computeIfAbsent(threadId, k -> new ConcurrentHashMap<>())
.put(checkpointId, checkpoint);
}
}
关键要点:
- 每个
threadId对应一个独立的 Map - Checkpoint 包含完整的对话历史(messages)和状态(state)
恢复检查点
当用户继续对话时,Agent 会加载上一次的 Checkpoint:
java
@Override
public Optional<Checkpoint> load(RunnableConfig config) {
String threadId = config.threadId().orElseThrow();
Map<String, Checkpoint> threadCheckpoints = storage.get(threadId);
if (threadCheckpoints == null || threadCheckpoints.isEmpty()) {
return Optional.empty();
}
// 返回最新的 checkpoint
return threadCheckpoints.values().stream()
.max(Comparator.comparing(Checkpoint::getCreatedAt));
}
2.3 使用示例
来自测试文件 ShortTermMemoryTest.java:82-116:
java
@Test
void testBasicShortTermMemory() throws Exception {
// 1. 创建带 MemorySaver 的 Agent
ReactAgent agent = ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.saver(new MemorySaver()) // ← 启用短期记忆
.build();
// 2. 创建带 threadId 的配置
RunnableConfig config = RunnableConfig.builder()
.threadId("conversation_1") // ← 会话隔离标识
.build();
// 3. 第一轮对话:告诉名字
agent.invoke("你好!我叫张三。", config);
// 4. 第二轮对话:询问名字(依赖短期记忆)
Optional<OverAllState> result = agent.invoke("我叫什么名字?", config);
AssistantMessage response = extractLastMessage(result);
// Agent 能记住之前的对话
assertTrue(response.getText().contains("张三"));
}
执行流程:
- 第一轮对话后,MemorySaver 保存 Checkpoint(包含用户消息 + 回复)
- 第二轮对话时,Agent 加载 Checkpoint 恢复对话历史
- 将新消息追加到历史中,发送给 LLM
- LLM 基于完整历史回答问题
三、长期记忆实现原理
3.1 核心组件:MemoryStore
MemoryStore 用于存储结构化的长期信息,支持命名空间(namespace)组织数据:
- 存储结构 :
namespace + key + value(类似 K-V 数据库) - 持久化:支持扩展到 Redis/MongoDB(当前版本为内存实现)
- 跨会话:不依赖 threadId,可在不同会话间共享
3.2 数据模型
java
// StoreItem.java
public class StoreItem {
private List<String> namespace; // 命名空间:如 ["user_profiles"]
private String key; // 键:如 "user_001"
private Map<String, Object> value; // 值:如 {"name": "张三", "age": 28}
public static StoreItem of(List<String> namespace, String key, Map<String, Object> value) {
return new StoreItem(namespace, key, value);
}
}
3.3 实现机制
存储数据
java
// MemoryStore.java
public class MemoryStore implements Store {
private final Map<String, Map<String, StoreItem>> storage = new ConcurrentHashMap<>();
@Override
public void putItem(StoreItem item) {
String namespaceKey = String.join("/", item.getNamespace());
storage.computeIfAbsent(namespaceKey, k -> new ConcurrentHashMap<>())
.put(item.getKey(), item);
}
}
检索数据
java
@Override
public Optional<StoreItem> getItem(List<String> namespace, String key) {
String namespaceKey = String.join("/", namespace);
Map<String, StoreItem> namespaceMap = storage.get(namespaceKey);
if (namespaceMap == null) {
return Optional.empty();
}
return Optional.ofNullable(namespaceMap.get(key));
}
3.4 使用示例
来自测试文件 LongTermMemoryTest.java:88-188:
java
@Test
void testLongTermMemoryWithInterceptor() throws Exception {
// 1. 创建记忆拦截器
ModelHook memoryInterceptor = new ModelHook() {
@Override
public CompletableFuture<Map<String, Object>> beforeModel(
OverAllState state, RunnableConfig config) {
// 从配置获取用户 ID
String userId = (String) config.metadata("user_id").get();
// 从长期记忆加载用户资料
Optional<StoreItem> profile = memoryStore.getItem(
List.of("user_profiles"), userId);
if (profile.isPresent()) {
Map<String, Object> data = profile.get().getValue();
// 注入到系统消息
String context = String.format(
"用户信息:姓名=%s, 年龄=%s, 邮箱=%s",
data.get("name"), data.get("age"), data.get("email"));
List<Message> messages = state.value("messages").orElse(List.of());
List<Message> newMessages = new ArrayList<>(messages);
newMessages.add(0, new SystemMessage(context));
return CompletableFuture.completedFuture(Map.of("messages", newMessages));
}
return CompletableFuture.completedFuture(Map.of());
}
};
// 2. 预先存储用户资料到长期记忆
Map<String, Object> profileData = Map.of(
"name", "王小明",
"age", 28,
"email", "wang@example.com"
);
StoreItem item = StoreItem.of(List.of("user_profiles"), "user_001", profileData);
memoryStore.putItem(item);
// 3. 创建 Agent
ReactAgent agent = ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.hooks(memoryInterceptor) // ← 注入拦截器
.saver(new MemorySaver())
.build();
// 4. 询问个人信息
RunnableConfig config = RunnableConfig.builder()
.threadId("session_001")
.addMetadata("user_id", "user_001")
.build();
Optional<OverAllState> result = agent.invoke("请介绍一下我的信息。", config);
AssistantMessage response = extractLastMessage(result);
// Agent 能使用长期记忆中的资料回答
assertTrue(response.getText().contains("王小明") || response.getText().contains("28"));
}
执行流程:
- Hook 在
beforeModel位置从 MemoryStore 加载用户资料 - 将资料注入到 SystemMessage 中
- MemorySaver 保存包含注入内容的完整对话
- LLM 基于注入的用户资料生成回复
四、短期记忆与长期记忆的协作
4.1 协作模式
Spring AI Alibaba 提供三种协作模式:
模式1:Hook 自动注入
通过 ModelHook 在每次调用模型前自动注入长期记忆:
java
@HookPositions(HookPosition.BEFORE_MODEL)
public class MemoryInjectionHook extends ModelHook {
private final MemoryStore memoryStore;
@Override
public CompletableFuture<Map<String, Object>> beforeModel(
OverAllState state, RunnableConfig config) {
String userId = config.metadata("user_id").orElse(null);
if (userId == null) {
return CompletableFuture.completedFuture(Map.of());
}
// 加载用户偏好
Optional<StoreItem> prefs = memoryStore.getItem(
List.of("user_preferences"), userId);
if (prefs.isPresent()) {
// 注入到对话历史
String context = buildContextFromPreferences(prefs.get().getValue());
List<Message> messages = state.value("messages").orElse(List.of());
List<Message> newMessages = new ArrayList<>();
newMessages.add(new SystemMessage(context));
newMessages.addAll(messages);
return CompletableFuture.completedFuture(Map.of("messages", newMessages));
}
return CompletableFuture.completedFuture(Map.of());
}
}
优点:透明化,业务代码无需关心记忆加载
模式2:Tool 主动存取
通过工具(Tool)让 Agent 自主决定何时读写长期记忆:
java
// 保存记忆工具
BiFunction<SaveMemoryRequest, ToolContext, MemoryResponse> saveMemoryFunction =
(request, context) -> {
StoreItem item = StoreItem.of(
request.namespace(),
request.key(),
request.value()
);
memoryStore.putItem(item);
return new MemoryResponse("成功保存", request.value());
};
ToolCallback saveMemoryTool = FunctionToolCallback.builder("saveMemory", saveMemoryFunction)
.description("保存信息到长期记忆")
.inputType(SaveMemoryRequest.class)
.build();
// 获取记忆工具
BiFunction<GetMemoryRequest, ToolContext, MemoryResponse> getMemoryFunction =
(request, context) -> {
Optional<StoreItem> item = memoryStore.getItem(
request.namespace(),
request.key()
);
return item.isPresent()
? new MemoryResponse("找到记忆", item.get().getValue())
: new MemoryResponse("未找到", Map.of());
};
ToolCallback getMemoryTool = FunctionToolCallback.builder("getMemory", getMemoryFunction)
.description("从长期记忆获取信息")
.inputType(GetMemoryRequest.class)
.build();
// 创建 Agent
ReactAgent agent = ReactAgent.builder()
.name("memory_tool_agent")
.model(chatModel)
.tools(saveMemoryTool, getMemoryTool) // ← 注册工具
.saver(new MemorySaver())
.build();
使用示例 (来自 LongTermMemoryTest.java:244-265):
java
// 第一轮:保存信息
agent.invoke(
"请帮我记住:我最喜欢的颜色是蓝色。使用 saveMemory 工具保存。",
config
);
// 第二轮:检索信息
Optional<OverAllState> result = agent.invoke(
"我最喜欢的颜色是什么?使用 getMemory 工具。",
config
);
// Agent 调用 getMemory 工具获取答案
优点:灵活性高,Agent 可根据上下文决定是否使用记忆
模式3:跨会话共享
长期记忆不绑定 threadId,可在不同会话间共享:
java
@Test
void testMemoryAcrossSessions() throws Exception {
// Session 1: 保存密码
RunnableConfig session1 = RunnableConfig.builder()
.threadId("session_morning")
.addMetadata("user_id", "user_003")
.build();
agent.invoke("记住我的密码是 secret123", session1);
// Session 2: 读取密码(不同 threadId,相同 user_id)
RunnableConfig session2 = RunnableConfig.builder()
.threadId("session_afternoon") // ← 不同会话
.addMetadata("user_id", "user_003") // ← 相同用户
.build();
Optional<OverAllState> result = agent.invoke("我的密码是什么?", session2);
// 能够跨会话获取到密码
}
4.2 记忆转换:短期 → 长期
通过 Hook 自动提取短期记忆中的关键信息存入长期记忆(来自 LongTermMemoryTest.java:436-490):
java
@HookPositions(HookPosition.AFTER_MODEL)
public class PreferenceLearningHook extends ModelHook {
@Override
public CompletableFuture<Map<String, Object>> afterModel(
OverAllState state, RunnableConfig config) {
String userId = config.metadata("user_id").orElse(null);
if (userId == null) {
return CompletableFuture.completedFuture(Map.of());
}
// 1. 从短期记忆读取对话
List<Message> messages = state.value("messages").orElse(List.of());
// 2. 加载已有的长期记忆
Optional<StoreItem> prefsOpt = memoryStore.getItem(
List.of("user_data"),
userId + "_preferences"
);
List<String> prefs = prefsOpt.isPresent()
? (List<String>) prefsOpt.get().getValue().getOrDefault("items", new ArrayList<>())
: new ArrayList<>();
// 3. 提取偏好信息(简单示例:检测关键词)
for (Message msg : messages) {
String content = msg.getText().toLowerCase();
if (content.contains("喜欢") || content.contains("偏好")) {
prefs.add(msg.getText());
// 4. 写入长期记忆
Map<String, Object> prefsData = Map.of("items", prefs);
StoreItem item = StoreItem.of(
List.of("user_data"),
userId + "_preferences",
prefsData
);
memoryStore.putItem(item);
System.out.println("学习到用户偏好: " + msg.getText());
}
}
return CompletableFuture.completedFuture(Map.of());
}
}
使用示例:
java
ReactAgent agent = ReactAgent.builder()
.name("learning_agent")
.model(chatModel)
.hooks(new PreferenceLearningHook())
.saver(new MemorySaver())
.build();
// 用户表达偏好
agent.invoke("我喜欢喝绿茶。", config);
agent.invoke("我偏好早上运动。", config);
// 偏好已自动存入长期记忆
Optional<StoreItem> savedPrefs = memoryStore.getItem(
List.of("user_data"),
"user_004_preferences"
);
// savedPrefs.get().getValue().get("items") = ["我喜欢喝绿茶。", "我偏好早上运动。"]
五、短期记忆的上下文窗口管理
5.1 问题背景
短期记忆保存完整对话历史,但 LLM 有 token 限制,需要管理上下文窗口大小。
5.2 解决方案:MessagesModelHook
方案1:消息裁剪(Trimming)
保留系统消息 + 最近 N 条消息(来自 ShortTermMemoryTest.java:267-297):
java
@HookPositions(HookPosition.BEFORE_MODEL)
public class MessageTrimmingHook extends MessagesModelHook {
private static final int MAX_MESSAGES = 5;
private static final int KEEP_LAST = 4;
@Override
public AgentCommand beforeModel(List<Message> messages, RunnableConfig config) {
if (messages.size() <= MAX_MESSAGES) {
return new AgentCommand(messages); // 不需要裁剪
}
// 保留第一条(系统消息)+ 最后4条
Message firstMsg = messages.get(0);
List<Message> recentMessages = messages.subList(
messages.size() - KEEP_LAST,
messages.size()
);
List<Message> trimmed = new ArrayList<>();
trimmed.add(firstMsg);
trimmed.addAll(recentMessages);
return new AgentCommand(trimmed, UpdatePolicy.REPLACE);
}
}
效果:
- 对话超过5条时,自动裁剪中间部分
- LLM 只看到系统消息 + 最近4轮对话
方案2:消息删除(Deletion)
定期清理旧消息(来自 ShortTermMemoryTest.java:238-261):
java
@HookPositions(HookPosition.AFTER_MODEL)
public class MessageDeletionHook extends MessagesModelHook {
private static final int KEEP_LAST = 4;
@Override
public AgentCommand afterModel(List<Message> messages, RunnableConfig config) {
if (messages.size() > KEEP_LAST) {
// 只保留最近4条
List<Message> recentMessages = new ArrayList<>(
messages.subList(messages.size() - KEEP_LAST, messages.size())
);
return new AgentCommand(recentMessages, UpdatePolicy.REPLACE);
}
return new AgentCommand(messages);
}
}
区别:
- Trimming(裁剪) :在
beforeModel执行,只影响本次调用,不修改 Checkpoint - Deletion(删除) :在
afterModel执行,修改 Checkpoint,永久删除旧消息
六、短期记忆失效机制
6.1 失效时机
短期记忆(MemorySaver)在以下情况失效:
- 应用重启:内存数据全部丢失
- threadId 不再使用:无法访问对应的 Checkpoint
- 主动删除 :通过 Hook 清理旧消息(如上文的
MessageDeletionHook)
6.2 会话隔离
不同 threadId 之间完全隔离:
java
@Test
void testMemoryIsolationBetweenThreads() throws Exception {
ReactAgent agent = ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.saver(new MemorySaver())
.build();
// Thread 1: Alice
RunnableConfig config1 = RunnableConfig.builder()
.threadId("thread_alice")
.build();
agent.invoke("你好,我叫 Alice。", config1);
// Thread 2: Bob
RunnableConfig config2 = RunnableConfig.builder()
.threadId("thread_bob")
.build();
agent.invoke("你好,我叫 Bob。", config2);
// Thread 1 只记得 Alice
Optional<OverAllState> result1 = agent.invoke("我叫什么名字?", config1);
assertTrue(extractLastMessage(result1).getText().contains("Alice"));
// Thread 2 只记得 Bob
Optional<OverAllState> result2 = agent.invoke("我叫什么名字?", config2);
assertTrue(extractLastMessage(result2).getText().contains("Bob"));
}
七、实战案例:电商客服 Agent
7.1 需求分析
构建一个电商客服 Agent,需要:
- 短期记忆:记住本次会话的商品咨询、订单查询
- 长期记忆:记住用户的收货地址、购买偏好
7.2 实现代码
java
@Configuration
public class CustomerServiceAgentConfig {
@Bean
public MemoryStore memoryStore() {
return new MemoryStore();
}
@Bean
public ModelHook userProfileHook(MemoryStore memoryStore) {
return new ModelHook() {
@Override
public HookPosition[] getHookPositions() {
return new HookPosition[]{HookPosition.BEFORE_MODEL};
}
@Override
public CompletableFuture<Map<String, Object>> beforeModel(
OverAllState state, RunnableConfig config) {
String userId = config.metadata("user_id").orElse(null);
if (userId == null) {
return CompletableFuture.completedFuture(Map.of());
}
// 加载用户资料
Optional<StoreItem> profile = memoryStore.getItem(
List.of("user_profiles"), userId);
// 加载购买偏好
Optional<StoreItem> preferences = memoryStore.getItem(
List.of("user_preferences"), userId);
StringBuilder context = new StringBuilder("用户档案:\n");
if (profile.isPresent()) {
Map<String, Object> data = profile.get().getValue();
context.append(String.format(
"- 姓名:%s\n- 会员等级:%s\n- 默认地址:%s\n",
data.get("name"), data.get("vipLevel"), data.get("address")
));
}
if (preferences.isPresent()) {
Map<String, Object> prefs = preferences.get().getValue();
context.append(String.format("- 偏好品类:%s\n", prefs.get("categories")));
}
// 注入系统消息
List<Message> messages = state.value("messages").orElse(List.of());
List<Message> newMessages = new ArrayList<>();
newMessages.add(new SystemMessage(context.toString()));
newMessages.addAll(messages);
return CompletableFuture.completedFuture(Map.of("messages", newMessages));
}
};
}
@Bean
public ReactAgent customerServiceAgent(
ChatModel chatModel,
MemoryStore memoryStore,
ModelHook userProfileHook) {
return ReactAgent.builder()
.name("customer_service_agent")
.model(chatModel)
.saver(new MemorySaver()) // 短期记忆
.hooks(userProfileHook) // 长期记忆注入
.tools(
createOrderQueryTool(),
createAddressUpdateTool(memoryStore)
)
.build();
}
private ToolCallback createAddressUpdateTool(MemoryStore memoryStore) {
return FunctionToolCallback.builder("updateAddress",
(UpdateAddressRequest req, ToolContext ctx) -> {
String userId = ctx.getContext("user_id");
// 更新长期记忆中的地址
Optional<StoreItem> profile = memoryStore.getItem(
List.of("user_profiles"), userId);
if (profile.isPresent()) {
Map<String, Object> data = new HashMap<>(profile.get().getValue());
data.put("address", req.newAddress);
StoreItem updated = StoreItem.of(
List.of("user_profiles"), userId, data);
memoryStore.putItem(updated);
return "地址已更新为:" + req.newAddress;
}
return "用户不存在";
})
.description("更新用户收货地址")
.inputType(UpdateAddressRequest.class)
.build();
}
}
7.3 使用示例
java
@Service
public class CustomerServiceController {
@Autowired
private ReactAgent customerServiceAgent;
@Autowired
private MemoryStore memoryStore;
public String chat(String userId, String sessionId, String message) {
// 确保用户资料存在
initUserProfileIfNotExists(userId);
RunnableConfig config = RunnableConfig.builder()
.threadId(sessionId) // 会话隔离
.addMetadata("user_id", userId) // 用户标识
.build();
Optional<OverAllState> result = customerServiceAgent.invoke(message, config);
return extractLastMessage(result).getText();
}
private void initUserProfileIfNotExists(String userId) {
if (memoryStore.getItem(List.of("user_profiles"), userId).isEmpty()) {
// 从数据库加载用户资料
UserProfile profile = userRepository.findById(userId);
Map<String, Object> profileData = Map.of(
"name", profile.getName(),
"vipLevel", profile.getVipLevel(),
"address", profile.getDefaultAddress()
);
StoreItem item = StoreItem.of(List.of("user_profiles"), userId, profileData);
memoryStore.putItem(item);
}
}
}
7.4 对话示例
用户: 你好,我想买一个手机
Agent: 您好王先生(黄金会员)!根据您之前的购买记录,您偏好高端手机。我推荐...
用户: 送到哪里?
Agent: 默认发送到您的地址:北京市朝阳区xxx(从长期记忆获取)
用户: 我换地址了,送到上海市浦东新区yyy
Agent: [调用 updateAddress 工具更新长期记忆] 好的,地址已更新!
--- 第二天,新会话 ---
用户: 订单发货了吗?
Agent: 正在为您查询...您的订单将发往:上海市浦东新区yyy(从长期记忆读取)
八、架构设计总结
8.1 分层架构
┌─────────────────────────────────────────────┐
│ Agent Application Layer │
│ (业务逻辑、对话流程、工具调用) │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ Memory Coordination Layer │
│ ModelHook: 注入长期记忆到短期记忆 │
│ MessagesModelHook: 管理上下文窗口 │
└─────────────────────────────────────────────┘
↓
┌──────────────────────┬──────────────────────┐
│ Short-term Memory │ Long-term Memory │
│ (MemorySaver) │ (MemoryStore) │
│ - threadId 隔离 │ - namespace 组织 │
│ - 完整对话历史 │ - 结构化数据 │
│ - 内存存储 │ - 可持久化扩展 │
└──────────────────────┴──────────────────────┘
8.2 关键设计原则
-
职责分离:
- MemorySaver 负责对话连贯性
- MemoryStore 负责知识持久化
-
灵活注入:
- Hook 模式:自动注入(透明)
- Tool 模式:主动调用(灵活)
-
上下文管理:
- MessagesModelHook 控制窗口大小
- UpdatePolicy 控制更新策略(APPEND/REPLACE)
-
可扩展性:
- Store 接口支持扩展到 Redis/MongoDB
- CheckpointSaver 接口支持自定义存储
九、最佳实践建议
9.1 选择合适的记忆类型
| 数据类型 | 推荐方案 | 示例 |
|---|---|---|
| 对话历史 | MemorySaver | "我刚才问了什么?" |
| 用户资料 | MemoryStore | 姓名、地址、会员等级 |
| 业务状态 | MemoryStore | 订单号、物流信息 |
| 临时变量 | State (不持久化) | 当前选中的商品 |
9.2 上下文窗口优化
java
// 推荐配置
@Bean
public MessagesModelHook contextWindowHook() {
return new MessagesModelHook() {
private static final int MAX_MESSAGES = 20;
private static final int KEEP_SYSTEM = 1;
private static final int KEEP_RECENT = 10;
@Override
public AgentCommand beforeModel(List<Message> messages, RunnableConfig config) {
if (messages.size() <= MAX_MESSAGES) {
return new AgentCommand(messages);
}
// 保留 System 消息 + 最近 10 条
List<Message> trimmed = new ArrayList<>();
trimmed.addAll(messages.subList(0, KEEP_SYSTEM));
trimmed.addAll(messages.subList(messages.size() - KEEP_RECENT, messages.size()));
return new AgentCommand(trimmed, UpdatePolicy.REPLACE);
}
};
}
9.3 长期记忆索引设计
java
// 推荐的 namespace 组织方式
memoryStore.putItem(StoreItem.of(
List.of("users", userId, "profile"), // 用户资料
"basic_info",
profileData
));
memoryStore.putItem(StoreItem.of(
List.of("users", userId, "preferences"), // 用户偏好
"shopping_habits",
prefsData
));
memoryStore.putItem(StoreItem.of(
List.of("sessions", sessionId, "context"), // 会话上下文
"selected_products",
productsData
));
9.4 错误处理
java
@Override
public CompletableFuture<Map<String, Object>> beforeModel(
OverAllState state, RunnableConfig config) {
try {
String userId = config.metadata("user_id").orElseThrow();
Optional<StoreItem> profile = memoryStore.getItem(
List.of("user_profiles"), userId);
// 处理逻辑...
} catch (Exception e) {
logger.error("Failed to load long-term memory", e);
// 降级处理:不注入长期记忆,继续执行
return CompletableFuture.completedFuture(Map.of());
}
}
十、源码位置参考
如果您想深入研究源码,以下是关键文件位置:
- 短期记忆测试 :
spring-ai-alibaba-agent-framework/src/test/java/com/alibaba/cloud/ai/graph/agent/memory/ShortTermMemoryTest.java - 长期记忆测试 :
spring-ai-alibaba-agent-framework/src/test/java/com/alibaba/cloud/ai/graph/agent/memory/LongTermMemoryTest.java - MemorySaver :
spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/MemorySaver.java - MemoryStore :
spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/store/stores/MemoryStore.java - ReactAgent :
spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/ReactAgent.java
十一、总结
Spring AI Alibaba 通过 MemorySaver 和 MemoryStore 提供了完善的记忆管理能力:
- 短期记忆:维持对话连贯性,按 threadId 隔离,易失性
- 长期记忆:存储持久化知识,跨会话共享,可扩展
- 协作机制:Hook 自动注入、Tool 主动调用、记忆转换
这套设计模式既保证了对话的流畅性,又实现了知识的积累,为构建智能化的 AI Agent 应用奠定了坚实基础。
欢迎关注、一起学习、一起进步~