Spring AI Alibaba(8)记忆管理:短期记忆和长期记忆
概述
前面我们学习了工具使用和Hook等。但是有一个问题还没有解决。
比如,我们和AI会话时,很可能出现,我前面告诉他我喜欢吃苹果,再问他我喜欢吃什么水果,他就不记得了。我们使用chatgpt或deepseek时,在同一个会话框里他好像也记得啊,那真是因为他有了“记忆”。也就是整体能否记住我之前说过的话。
那么有了记忆,智能体不再是个健忘的机器人了,他能:
- 记住先前的交互:在多轮对话中保持上下文连贯性
- 从反馈中学习:根据用户的纠正或补充信息调整后续行为
- 适应用户偏好:随着时间的推移,越来越了解用户的习惯和需求
Spring AI Alibaba 将记忆分为两个层次,短期记忆和长期记忆:
| 维度 | 短期记忆(Short-term Memory) | 长期记忆(Long-term Memory) |
|---|---|---|
| 存储位置 | Graph 状态(State) | Store(持久化存储) |
| 生命周期 | 单个会话(threadId)内有效 | 跨会话、跨时间持久化 |
| 存储内容 | 对话历史(Messages) | 用户画像、偏好、知识 |
| 实现方式 | CheckpointSaver(如 MemorySaver、RedisSaver) |
MemoryStore |
| 更新时机 | 每次 Agent 调用或工具调用后自动更新 | 通过工具或 Hook 显式读写 |
| 典型用途 | 多轮对话上下文 | 跨会话的用户记忆 |
通俗点来讲:
- 短期记忆就像“即时通讯软件的聊天记录”,只在这个会话窗口内有效,关掉窗口就没了(除非保存到服务器)。
- 长期记忆就像“用户档案”,无论什么时候打开聊天,系统都能调出这个用户的历史偏好和信息。

短期记忆
短期记忆是指在单个线程或会话中还能记住先前的对话。会话可以隔离同一个 Agent 实例中的多个不同交互,类似于电子邮件在单个对话中分组消息的方式。
在Spring AI Alibaba 中,短期记忆是作为 Agent 状态(State)的一部分进行管理。前面我们学过状态是通过 Checkpointer(检查点器) 持久化到数据库(或内存)的,以便随时恢复线程。
短期记忆也一样,使用Checkpointer在调用 Agent 或完成步骤(如工具调用)时自动更新,并在每个步骤开始时自动读取状态。
使用方法
在 Spring AI Alibaba 中,要向 Agent 添加短期记忆(会话级持久化),你需要在创建 Agent 时指定 Checkpointer。
下面看一个示例,使用 MemorySaver,MemorySaver 将状态保存在 JVM 内存中,适用于开发和测试场景:
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.RunnableConfig;
// 创建 Agent 并配置 checkpointer
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.tools(getUserInfoTool)
.saver(new MemorySaver()) // 启用短期记忆
.build();
// 使用 threadId 维护对话上下文
RunnableConfig config = RunnableConfig.builder()
.threadId("1") // threadId 指定会话 ID
.build();
// 第一轮对话
agent.call("你好!我叫 Bob。", config);
// 第二轮对话 - Agent 还记得上一轮的内容
agent.call("你还记得我叫什么吗?", config);
// 输出:当然记得!你叫 Bob。
注意:
threadId是会话的唯一标识,同一个threadId的多次调用共享同一个会话上下文- 不同的
threadId之间的对话历史完全隔离
生产环境我们还可以使用数据库支持的Checkpointer,比如Redis Checkpointer:
import com.alibaba.cloud.ai.graph.checkpoint.savers.RedisSaver;
import org.redisson.api.RedissonClient;
// 配置 Redis 连接
RedissonClient redissonClient = // ... 创建 Redisson 客户端
// 创建 Agent 并配置 Redis checkpointer
RedisSaver redisSaver = new RedisSaver(redissonClient);
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.tools(getUserInfoTool)
.saver(redisSaver) // 使用 Redis 持久化
.build();
// 使用方式与 MemorySaver 完全相同
RunnableConfig config = RunnableConfig.builder()
.threadId("user-session-123")
.build();
agent.call("你好!", config);
上下文过长的问题
保留所有对话历史是实现短期记忆最常见的形式。但较长的对话历史也会带来很多问题。比如,我们和大模型对话的时候,发给大模型的消息长度也是有限制的,超过限制会导致上下文丢失或报错。不仅如此,太大的上下文也会影响模型的性能,分散注意力影响效果,同时Token 数量增加导致推理时间延长,成本上升。
常见的解决方案包括:
- 修剪消息:在调用 LLM 之前移除前 N 条或后 N 条消息
- 删除消息:从 Graph 状态中永久删除某些消息
- 总结消息:总结历史中较早的消息并用摘要替换它们
- 自定义策略:根据业务需求定制消息过滤或压缩逻辑
这些技术统称为 “上下文工程” ,将在第 17 章中详细展开。
长期记忆
长期记忆让 Agent 能够跨会话记住用户信息、偏好和知识。
Spring AI Alibaba 将长期记忆以 JSON 文档的形式存储在 Store 中。每个记忆都在自定义的 namespace(命名空间) 下组织,并使用唯一的 key 来标识。命名空间通常包含用户或组织 ID 或其他标签,以便更容易地组织信息。
存储模型的核心概念:
| 概念 | 说明 | 类比 |
|---|---|---|
| Store | 长期记忆的存储抽象接口 | 文件系统 |
| Namespace | 记忆的层级组织,通常包含用户 ID 或组织 ID | 文件夹 |
| Key | 记忆的唯一标识符 | 文件名 |
| StoreItem | 一个完整的记忆条目,包含 namespace、key 和 value | 一个文件 |
命名空间通常包含用户 ID 或其他标签,以便更容易地组织信息。这种结构支持记忆的层次化组织,并支持通过内容过滤器进行跨命名空间搜
基础使用
以MemoryStore 为例。
MemoryStore 是将数据保存到内存字典中的实现,适用于开发和测试。生产环境请使用基于数据库的存储实现。
package com.cys.saa.chapter08;
import com.alibaba.cloud.ai.graph.store.StoreItem;
import com.alibaba.cloud.ai.graph.store.StoreSearchRequest;
import com.alibaba.cloud.ai.graph.store.StoreSearchResult;
import com.alibaba.cloud.ai.graph.store.stores.MemoryStore;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class MemoryStoreUse {
public static void main(String[] args) {
// MemoryStore 将数据保存到内存字典中。在生产环境中请使用基于数据库的存储实现
MemoryStore store = new MemoryStore();
String userId = "my-user";
String applicationContext = "chitchat";
List<String> namespace = List.of(userId, applicationContext);
// 保存记忆
Map<String, Object> memoryData = new HashMap<>();
memoryData.put("rules", List.of(
"用户喜欢简短直接的语言",
"用户只说中文和Java"
));
memoryData.put("my-key", "my-value");
StoreItem item = StoreItem.of(namespace, "a-memory", memoryData);
store.putItem(item);
// 通过ID获取记忆
Optional<StoreItem> retrievedItem = store.getItem(namespace, "a-memory");
// 在此命名空间内搜索记忆,通过内容等价性过滤,按向量相似度排序
StoreSearchRequest storeSearchRequest = new StoreSearchRequest();
storeSearchRequest.setNamespace(namespace);
storeSearchRequest.setFilter(Map.of("my-key", "my-value"));
StoreSearchResult result = store.searchItems(storeSearchRequest);
List<StoreItem> items = result.getItems();
}
}
在工具中读取长期记忆
下面的示例展示了如何创建一个工具,让 Agent 能够查询用户信息
package com.cys.saa.chapter08;
import com.alibaba.cloud.ai.graph.store.Store;
import com.alibaba.cloud.ai.graph.store.StoreItem;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import org.springframework.ai.chat.model.ToolContext;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
public class GetUserInfoTool implements BiFunction<String, ToolContext, String> {
@Override
public String apply(String userId, ToolContext context) {
// 从上下文中获取 Store
RunnableConfig config = (RunnableConfig) context.getContext().get("config");
Store store = config.store();
// 从长期记忆中读取用户信息
List<String> namespace = List.of("users", userId);
Optional<StoreItem> userItem = store.getItem(namespace, "profile");
if (userItem.isEmpty()) {
return "未找到用户 " + userId + " 的信息";
}
Map<String, Object> userData = userItem.get().getValue();
return String.format("用户信息:姓名=%s, 偏好=%s",
userData.get("name"),
userData.get("preference")
);
}
}
在工具中写入长期记忆
同样,Agent 也可以通过工具将信息写入长期记忆:
package com.cys.saa.chapter08;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.store.Store;
import com.alibaba.cloud.ai.graph.store.StoreItem;
import org.springframework.ai.chat.model.ToolContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
public class SaveUserInfoTool implements BiFunction<Map<String, Object>, ToolContext, String> {
@Override
public String apply(Map<String, Object> request, ToolContext context) {
RunnableConfig config = (RunnableConfig) context.getContext().get("config");
Store store = config.store();
String userId = (String) request.get("userId");
String name = (String) request.get("name");
String preference = (String) request.get("preference");
// 构建记忆数据
Map<String, Object> userData = new HashMap<>();
userData.put("name", name);
userData.put("preference", preference);
userData.put("updatedAt", System.currentTimeMillis());
// 保存到长期记忆
List<String> namespace = List.of("users", userId);
StoreItem item = StoreItem.of(namespace, "profile", userData);
store.putItem(item);
return "用户信息已保存成功!";
}
}
ModelHook 管理长期记忆
结合上一张我们学的Hook,可以使用用 ModelHook 在模型调用前后自动加载和保存长期记忆。
package com.cys.saa.chapter08;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;
import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;
import com.alibaba.cloud.ai.graph.store.Store;
import com.alibaba.cloud.ai.graph.store.StoreItem;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class memoryInterceptor extends ModelHook {
@Override
public String getName() {
return "memory_interceptor";
}
@Override
public HookPosition[] getHookPositions() {
return new HookPosition[]{HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL};
}
@Override
public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
// 从配置中获取用户ID
String userId = (String) config.metadata("user_id").orElse(null);
if (userId == null) {
return CompletableFuture.completedFuture(Map.of());
}
Store store = config.store();
// 从记忆存储中加载用户画像
Optional<StoreItem> itemOpt = store.getItem(List.of("user_profiles"), userId);
if (itemOpt.isPresent()) {
Map<String, Object> profile = itemOpt.get().getValue();
// 将用户上下文注入系统消息
String userContext = String.format(
"用户信息:姓名=%s, 年龄=%s, 邮箱=%s, 偏好=%s",
profile.get("name"),
profile.get("age"),
profile.get("email"),
profile.get("preferences")
);
// 获取消息列表
List<Message> messages = (List<Message>) state.value("messages").orElse(new ArrayList<>());
List<Message> newMessages = new ArrayList<>();
// 查找是否已存在 SystemMessage
SystemMessage existingSystemMessage = null;
int systemMessageIndex = -1;
for (int i = 0; i < messages.size(); i++) {
Message msg = messages.get(i);
if (msg instanceof SystemMessage) {
existingSystemMessage = (SystemMessage) msg;
systemMessageIndex = i;
break;
}
}
// 如果找到 SystemMessage,更新它;否则创建新的
SystemMessage enhancedSystemMessage;
if (existingSystemMessage != null) {
// 更新现有的 SystemMessage
enhancedSystemMessage = new SystemMessage(
existingSystemMessage.getText() + " " + userContext
);
} else {
// 创建新的 SystemMessage
enhancedSystemMessage = new SystemMessage(userContext);
}
// 构建新的消息列表
if (systemMessageIndex >= 0) {
// 如果找到了 SystemMessage,替换它
for (int i = 0; i < messages.size(); i++) {
if (i == systemMessageIndex) {
newMessages.add(enhancedSystemMessage);
} else {
newMessages.add(messages.get(i));
}
}
} else {
// 如果没有找到 SystemMessage,在开头添加新的
newMessages.add(enhancedSystemMessage);
newMessages.addAll(messages);
}
return CompletableFuture.completedFuture(Map.of("messages", newMessages));
}
return CompletableFuture.completedFuture(Map.of());
}
@Override
public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {
// 可以在这里实现对话后的记忆保存逻辑
return CompletableFuture.completedFuture(Map.of());
}
}
记忆管理在Agent中流程
短期记忆和长期记忆在 Spring AI Alibaba 中是如何协同工作的?下图展示了完整的记忆架构:
┌─────────────────────────────────────────────────────────┐
│ ReactAgent │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ 短期记忆 │ │ 长期记忆 │ │
│ │ (MemorySaver) │ │ (MemoryStore) │ │
│ └──────────────────┘ └────────────────────┘ │
│ │ │ │
│ threadId namespace/key │
│ ↓ ↓ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ 对话历史 │ │ 用户画像 │ │
│ │ Conversation │ │ User Profiles │ │
│ │ History │ │ Preferences │ │
│ └──────────────────┘ └────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ModelHook / 拦截器 │ │
│ │ - beforeModel: 加载并注入记忆 │ │
│ │ - afterModel: 保存并学习交互内容 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 记忆工具 Tools │ │
│ │ - saveMemory: 显式保存到长期记忆 │ │
│ │ - getMemory: 显式从长期记忆检索 │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
集成的关键点:
- 短期记忆通过
CheckpointSaver(如MemorySaver、RedisSaver)实现,绑定到threadId - 长期记忆通过
Store(如MemoryStore)实现,通过namespace/key组织 - ModelHook 可以在模型调用前后自动加载和保存长期记忆
- 工具(Tools) 可以通过
ToolContext访问Store,实现显式的记忆读写
结合短期和长期记忆
在实际应用中,短期记忆和长期记忆通常配合使用:
- 短期记忆存储当前的对话上下文,让 Agent 在多轮对话中保持连贯
- 长期记忆存储用户的持久化信息,让 Agent 跨会话记住用户偏好
完整示例:
package com.cys.saa.chapter08;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.store.StoreItem;
import com.alibaba.cloud.ai.graph.store.stores.MemoryStore;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.beans.factory.annotation.Value;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ShortAndLongMemory {
public static void main(String[] args) throws GraphRunnerException {
// 1. 创建短期记忆(会话级)
MemorySaver memorySaver = new MemorySaver();
// 2. 创建长期记忆(跨会话)
MemoryStore memoryStore = new MemoryStore();
// 3. 预先写入一些示例数据
Map<String, Object> userData = new HashMap<>();
userData.put("name", "张三");
userData.put("language", "中文");
userData.put("preference", "喜欢技术类内容");
StoreItem userItem = StoreItem.of(List.of("users", "user_123"), "profile", userData);
memoryStore.putItem(userItem);
// 4. 创建工具回调
ToolCallback getUserInfoTool = FunctionToolCallback
.builder("getUserInfo", new GetUserInfoTool())
.description("根据用户 ID 获取用户信息,返回用户的姓名和偏好")
.inputType(String.class) // 输入类型为 String
.build();
ToolCallback saveUserInfoTool = FunctionToolCallback
.builder("saveUserInfo", new SaveUserInfoTool())
.description("保存或更新用户信息,包括姓名和偏好")
.inputType(Map.class) // 输入类型为 Map
.build();
// 5. 构建 Agent,同时配置短期和长期记忆
String apiKey = System.getenv("SPACE_API_KEY");
DashScopeChatModel chatModel = DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder()
.withModel(DashScopeChatModel.DEFAULT_MODEL_NAME)
.withTemperature(0.7)
.withMaxToken(2048)
.build())
.build();
ReactAgent agent = ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.tools(getUserInfoTool) // 工具可以从长期记忆读取
.tools(saveUserInfoTool) // 工具可以写入长期记忆
.saver(memorySaver) // 短期记忆
.build();
// 6. 执行:配置中包含 Store
RunnableConfig config = RunnableConfig.builder()
.threadId("session_001")
.store(memoryStore) // 长期记忆
.addMetadata("user_id", "user_123")
.build();
// 7. 第一轮对话 - Agent 可以从长期记忆中读取用户信息
agent.invoke("你好!", config);
// 7. 第二轮对话 - 使用不同的 threadId,但相同的 Store
RunnableConfig configB = RunnableConfig.builder()
.threadId("session_002")
.store(memoryStore) // 同一个 Store
.addMetadata("user_id", "user_123")
.build();
agent.invoke("你还记得我是谁吗?", configB);
}
}
总结:这一章学习了短期以及和长期记忆,以及如何使用他们,他们的作用是什么,怎么影响我整个Agent流程的。
更多推荐



所有评论(0)