内存管理
AI 应用程序需要内存来跨多个交互共享上下文。在 Spring AI Alibaba Graph 中,您可以添加两种类型的内存:
添加短期内存
短期内存(线程级持久化)使智能体能够跟踪多轮对话。要添加短期内存:
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.CompileConfig;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.constant.SaverConstant;
// 创建内存检查点器
MemorySaver checkpointer = new MemorySaver();
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.MEMORY, checkpointer)
.build();
// 构建图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("agent", agentNode)
.addEdge(START, "agent")
.addEdge("agent", END);
// 使用检查点器编译图
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
// 使用线程 ID 调用图
RunnableConfig config = RunnableConfig.builder()
.threadId("user-session-1")
.build();
Map<String, Object> input = Map.of(
"messages", List.of(
Map.of("role", "user", "content", "你好!我是 Bob")
)
);
graph.invoke(input, config);
生产环境使用
在生产环境中,使用由数据库支持的检查点器:
PostgreSQL 检查点器
import com.alibaba.cloud.ai.graph.checkpoint.savers.PostgreSqlSaver;
import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.constant.SaverConstant;
// PostgreSQL 配置
String dbUrl = "jdbc:postgresql://localhost:5432/graphdb";
String username = "postgres";
String password = "password";
PostgreSqlSaver postgresSaver = new PostgreSqlSaver(dbUrl, username, password);
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.POSTGRES, postgresSaver)
.build();
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
详细的 PostgreSQL 配置请参阅 PostgreSQL 检查点持久化。
Redis 检查点器
import com.alibaba.cloud.ai.graph.checkpoint.savers.RedisSaver;
import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.constant.SaverConstant;
// Redis 配置
String redisHost = "localhost";
int redisPort = 6379;
RedisSaver redisSaver = new RedisSaver(redisHost, redisPort);
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.REDIS, redisSaver)
.build();
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
完整示例:使用短期内存的多轮对话
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import org.springframework.ai.chat.client.ChatClient;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
// 定义状态策略
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> keyStrategyMap = new HashMap<>();
keyStrategyMap.put("messages", new AppendStrategy());
return keyStrategyMap;
};
// 创建聊天节点
var chatNode = node_async(state -> {
List<Map<String, String>> messages =
(List<Map<String, String>>) state.value("messages").orElse(List.of());
// 使用 ChatClient 调用 AI 模型
ChatClient chatClient = chatClientBuilder.build();
String response = chatClient.prompt()
.user(messages.get(messages.size() - 1).get("content"))
.call()
.content();
return Map.of("messages", List.of(
Map.of("role", "assistant", "content", response)
));
});
// 构建图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("chat", chatNode)
.addEdge(START, "chat")
.addEdge("chat", END);
// 配置检查点
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.MEMORY, new MemorySaver())
.build();
// 编译图
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
// 第一轮对话
RunnableConfig config = RunnableConfig.builder()
.threadId("conversation-1")
.build();
graph.invoke(Map.of("messages", List.of(
Map.of("role", "user", "content", "你好!我是 Bob")
)), config);
// 第二轮对话(使用相同的 threadId)
graph.invoke(Map.of("messages", List.of(
Map.of("role", "user", "content", "我的名字是什么?")
)), config);
// AI 将能够记住之前的对话,回答 "Bob"
在子图中使用
如果您的图包含子图,您只需在编译父图时提供检查点器。Spring AI Alibaba Graph 将自动将检查点器传播到子图。
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import static com.alibaba.cloud.ai.graph.StateGraph.START;
// 定 义状态
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> keyStrategyMap = new HashMap<>();
keyStrategyMap.put("foo", new ReplaceStrategy());
return keyStrategyMap;
};
// 子图
var subgraphNode = node_async(state -> {
String foo = (String) state.value("foo").orElse("");
return Map.of("foo", foo + "bar");
});
StateGraph subgraphBuilder = new StateGraph(keyStrategyFactory)
.addNode("subgraph_node_1", subgraphNode)
.addEdge(START, "subgraph_node_1");
// 子图不需要检查点器
CompiledGraph subgraph = subgraphBuilder.compile();
// 父图
StateGraph parentBuilder = new StateGraph(keyStrategyFactory)
.addNode("node_1", state -> {
// 调用子图
return subgraph.invoke(state.data(),
RunnableConfig.builder().build());
})
.addEdge(START, "node_1");
// 只在父图编译时提供检查点器
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.MEMORY, new MemorySaver())
.build();
CompiledGraph graph = parentBuilder.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
如果您希望子图拥有自己的内存,可以使用适当的检查点器选项编译它。这在多智能体系统中很有用,如果您希望智能体跟踪其内部消息历史。
添加长期内存
使用长期内存跨对话存储用户特定或应用程序特定的数据。
长期内存可以通过以下方式实现:
使用外部数据库
您可以在节点中直接访问外部数据库来存储和检索长期数据:
import org.springframework.jdbc.core.JdbcTemplate;
// 在节点中使用数据库存储用户信息
var userProfileNode = node_async(state -> {
String userId = (String) state.value("userId").orElse("");
// 从数据库获取用户配置
JdbcTemplate jdbcTemplate = ...; // Spring Bean
Map<String, Object> userProfile = jdbcTemplate.queryForMap(
"SELECT * FROM user_profiles WHERE user_id = ?", userId
);
return Map.of("userProfile", userProfile);
});
使用 Redis 缓存
import org.springframework.data.redis.core.RedisTemplate;
var cacheNode = node_async(state -> {
String key = (String) state.value("cacheKey").orElse("");
RedisTemplate<String, Object> redisTemplate = ...; // Spring Bean
// 尝试从缓存获取
Object cachedData = redisTemplate.opsForValue().get(key);
if (cachedData == null) {
// 执行计算或查询
cachedData = performExpensiveOperation();
// 存储到缓存
redisTemplate.opsForValue().set(key, cachedData);
}
return Map.of("result", cachedData);
});
最佳实践
-
分离关注点:短期内存用于对话上下文,长期内存用于用户偏好和历史数据。
-
性能优化:对于频繁访问的长期数据,使用缓存层(如 Redis)。
-
数据一致性:确保长期内存的读写操作是原子的或使用适当的事务管理。
-
隐私和安全:对敏感的长期数据进行加密和访问控制。
-
清理策略:实施适当的数据保留和清理策略,避免无限增长。
完整示例:结合短期和长期内存
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.ai.chat.client.ChatClient;
// 定义状态
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> keyStrategyMap = new HashMap<>();
keyStrategyMap.put("userId", new ReplaceStrategy());
keyStrategyMap.put("messages", new AppendStrategy());
keyStrategyMap.put("userPreferences", new ReplaceStrategy());
return keyStrategyMap;
};
// 加载用户偏好(长期内存)
var loadUserPreferences = node_async(state -> {
String userId = (String) state.value("userId").orElse("");
// 从数据库加载用户偏好
JdbcTemplate jdbcTemplate = ...;
Map<String, Object> preferences = jdbcTemplate.queryForMap(
"SELECT * FROM user_preferences WHERE user_id = ?", userId
);
return Map.of("userPreferences", preferences);
});
// 聊天节点(使用短期和长期内存)
var chatNode = node_async(state -> {
List<Map<String, String>> messages =
(List<Map<String, String>>) state.value("messages").orElse(List.of());
Map<String, Object> preferences =
(Map<String, Object>) state.value("userPreferences").orElse(Map.of());
// 构建包含用户偏好的提示
String userPrompt = messages.get(messages.size() - 1).get("content");
String enhancedPrompt = "用户偏好: " + preferences + "\n用户问题: " + userPrompt;
// 调用 AI
ChatClient chatClient = chatClientBuilder.build();
String response = chatClient.prompt()
.user(enhancedPrompt)
.call()
.content();
return Map.of("messages", List.of(
Map.of("role", "assistant", "content", response)
));
});
// 构建图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("load_preferences", loadUserPreferences)
.addNode("chat", chatNode)
.addEdge(START, "load_preferences")
.addEdge("load_preferences", "chat")
.addEdge("chat", END);
// 配置检查点(短期内存)
SaverConfig saverConfig = SaverConfig.builder()
.register(SaverConstant.MEMORY, new MemorySaver())
.build();
// 编译图
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
// 执行
RunnableConfig config = RunnableConfig.builder()
.threadId("user-123-session-1")
.build();
graph.invoke(Map.of(
"userId", "user-123",
"messages", List.of(
Map.of("role", "user", "content", "推荐一部电影")
)
), config);
通过这种方式,您的应用程序可以同时利用短期内存(对话历史)和长期内存(用户偏好),提供更个性化和上下文感知的体验。
Manage checkpoints
You can view and delete the information stored by the checkpointer.
View thread state
Delete all checkpoints for a thread
thread_id = "1"
checkpointer.delete_thread(thread_id)
Prebuilt memory tools
LangMem is a LangChain-maintained library that offers tools for managing long-term memories in your agent. See the LangMem documentation for usage examples.
-->