Spring AI Alibaba 记忆(Memory)机制详解与完整实战指南
基于 Spring AI Alibaba 1.1.2.0 的 Agent 框架,深入讲解短期记忆的原理、实现及生产级代码示例
一、引言
在构建智能对话 Agent 时,记忆能力是决定用户体验的关键因素。Spring AI Alibaba 的 Agent 框架提供了完善的短期记忆(Short‑term Memory)管理机制,能够让 Agent 在同一个会话中记住先前的交互内容。本文将从环境搭建开始,逐步讲解记忆的核心概念、配置方法、消息修剪策略,并给出一个可直接运行的完整项目示例,帮助你快速上手。
二、环境准备
2.1 技术栈
- JDK 17+
- Spring Boot 3.2.5
- Spring AI Alibaba 1.1.2.0
- DashScope 模型(如 qwen-max 或 deepseek‑v4‑flash)
- Maven 3.6+
2.2 pom.xml 依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<groupId>com.example.ai</groupId>
<artifactId>spring-ai-memory-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
<jackson.version>2.16.2</jackson.version>
</properties>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Spring AI Alibaba DashScope Starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.3 application.yml 配置
yaml
server:
port: 885
spring:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY} # 从环境变量读取
chat:
options:
model: deepseek-v4-flash # 也可用 qwen-max
logging:
level:
com.alibaba.cloud.ai: debug
运行前请设置环境变量
DASHSCOPE_API_KEY为你的 DashScope API 密钥。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
三、核心概念梳理
3.1 什么是短期记忆(Short‑term Memory)
短期记忆让应用程序能够在单个线程或会话 中记住先前的交互。会话可以隔离同一个 Agent 实例中的多个不同交互,类似于电子邮件在单个对话中分组消息。在 Spring AI Alibaba 中,短期记忆作为 Agent 状态(State) 的一部分进行管理,存储在 Graph 的状态中。
3.2 记忆管理机制
- 持久化 :状态使用 checkpointer 持久化到数据库或内存,以便随时恢复线程。
- 更新时机:记忆在调用 Agent 或完成步骤(如工具调用)时更新,并在每个步骤开始时读取状态。
- 会话隔离 :通过
threadId实现不同会话之间的记忆隔离。
3.3 上下文过长问题
保留所有对话历史是最常见的短期记忆实现方式,但长对话可能导致:
| 问题 | 影响 |
|---|---|
| 上下文窗口超限 | LLM 有最大 token 限制,超限会导致错误或截断 |
| 模型表现下降 | 过时或偏离主题的内容会分散模型注意力 |
| 响应变慢 | 上下文越长,推理时间越长 |
| Token 成本增加 | 每次请求都需要处理大量历史消息 |
为解决以上问题,Spring AI Alibaba 提供了多种消息处理 Hook,如修剪 、删除 和总结。
四、记忆的使用方法
4.1 启用短期记忆(内存存储)
在创建 Agent 时指定 MemorySaver 作为 checkpointer:
java
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.alibaba.cloud.ai.graph.RunnableConfig;
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.saver(new MemorySaver()) // 使用内存存储
.build();
// 使用 thread_id 维护对话上下文
RunnableConfig config = RunnableConfig.builder()
.threadId("session-001")
.build();
agent.call("你好!我叫 Bob。", config);
4.2 生产环境:Redis Checkpointer
生产环境建议使用数据库支持的 checkpointer,例如 Redis:
java
import com.alibaba.cloud.ai.graph.checkpoint.savers.RedisSaver;
import org.redisson.api.RedissonClient;
RedisSaver redisSaver = new RedisSaver(redissonClient);
ReactAgent agent = ReactAgent.builder()
.name("my_agent")
.model(chatModel)
.saver(redisSaver)
.build();
Redis 的配置将在后文扩展部分详细说明。
4.3 消息修剪(Message Trimming)
通过自定义 MessagesModelHook 在模型调用前修剪消息列表,保留关键信息:
java
import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;
import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;
import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;
import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;
import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import org.springframework.ai.chat.messages.Message;
import java.util.ArrayList;
import java.util.List;
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {
private static final int MAX_MESSAGES = 5;
@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);
}
// 保留第一条和最近三条
Message firstMsg = previousMessages.get(0);
List<Message> recentMessages = previousMessages.subList(
previousMessages.size() - 3,
previousMessages.size()
);
List<Message> trimmedMessages = new ArrayList<>();
trimmedMessages.add(firstMsg);
trimmedMessages.addAll(recentMessages);
return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);
}
}
4.4 消息删除(Message Deletion)
可删除最早的消息或清空全部:
java
// 删除最早的两条消息
@HookPositions({HookPosition.AFTER_MODEL})
public class MessageDeletionHook extends MessagesModelHook {
@Override
public AgentCommand afterModel(List<Message> previousMessages, RunnableConfig config) {
if (previousMessages.size() > 2) {
List<Message> remaining = previousMessages.subList(2, previousMessages.size());
return new AgentCommand(remaining, UpdatePolicy.REPLACE);
}
return new AgentCommand(previousMessages);
}
}
4.5 消息总结(Message Summarization)
使用 LLM 对历史消息生成摘要,避免信息丢失:
java
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageSummarizationHook extends MessagesModelHook {
private final ChatModel summaryModel;
private final int maxTokensBeforeSummary;
private final int messagesToKeep;
// 构造方法略
@Override
public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
// 估算 token 数量,若超限则生成摘要
int estimatedTokens = previousMessages.stream()
.mapToInt(m -> m.getText().length() / 4)
.sum();
if (estimatedTokens < maxTokensBeforeSummary) {
return new AgentCommand(previousMessages);
}
// 生成摘要并替换
// ... 具体实现参考之前回答
}
}
五、完整项目示例
5.1 项目目录结构
spring-ai-memory-demo/
├── pom.xml
├── src/
│ └── main/
│ ├── java/
│ │ └── com/example/ai/
│ │ ├── SpringAiDemoApplication.java
│ │ ├── config/
│ │ │ └── AgentConfig.java
│ │ ├── controller/
│ │ │ └── AgentController.java
│ │ ├── service/
│ │ │ └── AgentService.java
│ │ └── hook/
│ │ └── MessageTrimmingHook.java
│ └── resources/
│ └── application.yml
5.2 启动类
java
package com.example.ai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDemoApplication.class, args);
}
}
5.3 Agent 配置类
java
package com.example.ai.config;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import com.example.ai.hook.MessageTrimmingHook;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AgentConfig {
@Bean
public ReactAgent reactAgent(ChatModel chatModel) {
return ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.saver(new MemorySaver())
.hooks(new MessageTrimmingHook())
.build();
}
}
5.4 消息修剪 Hook
java
package com.example.ai.hook;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;
import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;
import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;
import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;
import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;
import org.springframework.ai.chat.messages.Message;
import java.util.ArrayList;
import java.util.List;
@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {
private static final int MAX_MESSAGES = 5;
@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);
}
Message firstMsg = previousMessages.get(0);
int keepCount = 3;
List<Message> recentMessages = previousMessages.subList(
previousMessages.size() - keepCount,
previousMessages.size()
);
List<Message> trimmedMessages = new ArrayList<>();
trimmedMessages.add(firstMsg);
trimmedMessages.addAll(recentMessages);
return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);
}
}
5.5 Agent 服务层
java
package com.badao.ai.service;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;
@Service
public class AgentService {
private final ReactAgent reactAgent;
public AgentService(ReactAgent reactAgent) {
this.reactAgent = reactAgent;
}
/**
* 与 Agent 对话(带会话记忆)
*
* @param userMessage 用户消息
* @param sessionId 会话 ID(用于记忆隔离)
* @return Agent 回复
*/
public String chat(String userMessage, String sessionId) {
// 使用 threadId 维护对话上下文
RunnableConfig config = RunnableConfig.builder()
.threadId(sessionId)
.build();
AssistantMessage response = null;
try {
response = reactAgent.call(userMessage, config);
} catch (GraphRunnerException e) {
throw new RuntimeException(e);
}
return response.getText();
}
}
5.6 REST 控制器
java
package com.example.ai.controller;
import com.example.ai.service.AgentService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api/agent")
public class AgentController {
private final AgentService agentService;
public AgentController(AgentService agentService) {
this.agentService = agentService;
}
@PostMapping("/chat/session")
public Map<String, Object> chatWithSession(
@RequestParam String message,
@RequestParam String sessionId) {
String response = agentService.chat(message, sessionId);
return Map.of(
"success", true,
"response", response,
"sessionId", sessionId
);
}
}
六、运行与测试
6.1 启动应用
bash
export DASHSCOPE_API_KEY="你的API密钥"
mvn clean spring-boot:run
6.2 测试多轮对话记忆
bash
# 第一轮:告诉 Agent 名字
curl -X POST "http://localhost:885/api/agent/chat/session?message=你好,我叫张三&sessionId=test-001"
# 第二轮:询问名字(应能记住)
curl -X POST "http://localhost:885/api/agent/chat/session?message=我叫什么名字?&sessionId=test-001"
# 第三轮:新会话,记忆隔离
curl -X POST "http://localhost:885/api/agent/chat/session?message=我叫什么名字?&sessionId=test-002"
预期结果:
-
第一轮返回:
"你好,张三!很高兴认识你。" -
第二轮返回:
"你叫张三,你刚才告诉我的。"
-
第三轮返回:
"抱歉,我还不知道你的名字。"
七、生产环境扩展:使用 Redis 持久化
7.1 添加 Redisson 依赖
在 pom.xml 中增加:
xml
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.27.0</version>
</dependency>
7.2 修改 AgentConfig
java
@Configuration
public class AgentConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
return Redisson.create(config);
}
@Bean
public ReactAgent reactAgent(ChatModel chatModel, RedissonClient redissonClient) {
RedisSaver redisSaver = new RedisSaver(redissonClient);
return ReactAgent.builder()
.name("memory_agent")
.model(chatModel)
.saver(redisSaver)
.hooks(new MessageTrimmingHook())
.build();
}
}
7.3 优势
- 记忆不随应用重启丢失
- 支持分布式部署,多实例共享记忆
- 可通过 Redis 管理工具查看和清理会话状态
八、常见问题与最佳实践
8.1 如何选择修剪策略?
- 消息数量过多 → 使用修剪 Hook,保留系统提示和最近 N 条。
- 消息内容过长 → 使用总结 Hook,用 LLM 生成摘要。
- 需要强制遗忘 → 使用删除 Hook 清除敏感或过时消息。
8.2 关于消息顺序的注意事项
某些 LLM 提供商(如 OpenAI)期望消息历史以 user 消息开头,且 assistant 的 tool 调用后必须紧跟对应的 tool 结果。修剪或删除时需保证消息序列有效,否则模型可能报错。
8.3 监控与日志
建议开启 com.alibaba.cloud.ai 的 DEBUG 级别日志,观察状态变化和 Hook 执行情况。
九、总结
本文全面讲解了 Spring AI Alibaba 中的短期记忆机制,从核心概念、配置方法到消息修剪策略,并给出了一套完整的可运行项目示例。通过 MemorySaver/RedisSaver 和自定义 MessagesModelHook,你可以轻松构建具备会话记忆能力的智能 Agent,同时有效控制上下文长度,保障性能和成本。
扩展方向:
- 结合工具(Tools)让 Agent 在记忆基础上执行操作
- 使用
ModelInterceptor实现动态提示注入 - 探索长期记忆(Long‑term Memory)方案,如向量数据库
希望本文能帮助你快速掌握 Spring AI Alibaba 的记忆功能,并在实际项目中灵活运用。如有问题,欢迎查阅官方文档或参与社区讨论。