Spring-AI初级使用记录-(单、多轮对话)
Spring-AI官方文档:https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html
当前环境:
- Spring AI version: 1.0.1
- Spring Boot version: 3.5.4
- Java version: 21.0.2
- Maven version:无所谓,大多数都可以。
创建项目
1.创建SpringBoot项目:
点击下一步后添加相关依赖,不添加也行,后面在pom文件中可以自己配置。这里选择Open Ai和Web
创建完成后可以检查下pom文件,完整的pom文件如下(为了测试方便我多加了swagger,不需要可以去掉):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--新版的Swagger,jdk17以后老版不能用了-->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version> <!-- 或更高版本,如2.12.x -->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
恭喜🎉至此项目就创建完了。
配置项目
2.开始配置yml,注意:completions-path这个属性不配置默认是/v1/chat/completions(根据自己实际情况修改)。
spring:
ai:
openai:
api-key: XXXXXXXXXXX # 将这里替换为你自己的OpenAI API Key
base-url: https://XXX.XXX.com # OpenAI API的基础URL
chat:
options:
# 使用的模型,可根据需求更换
model: deepseek-reasoner
temperature: 0.7 # 控制生成文本的随机性,取值范围0 - 1,值越大越随机
completions-path: /v2/chat/completions
结束配置,开始编程。
单轮对话调用
编写测试案例:
@RestController
@RequestMapping("/ChatClient")
@Tag(name = "Spring-AI客户端")
public class AiController {
//具体人设信息
public static final String system="你是一个高中生,名字叫做小明,今年16岁,喜欢唱跳rap篮球。";
//对话客户端
private final ChatClient chatClient;
//构造注入
AiController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/chat")
@Operation(summary = "同步调用")
String chat(String userInput) {
return this.chatClient.prompt()
.advisors(new SimpleLoggerAdvisor()) //开启日志
.system(system) //人设
.user(userInput) //userMessage
.call() //同步调用
.content();
}
@GetMapping(value = "/chatStream",produces = "text/html;charset=utf-8")
@Operation(summary = "流式调用")
Flux<String> chatStream(String userInput) {
return this.chatClient.prompt()
.advisors(new SimpleLoggerAdvisor()) //开启日志
.system(system) //人设
.user(userInput) //userMessage
.stream() //流式调用
.content();
}
}
因为在pom文件中导入了Swagger,方便调试直接调用请求,经测试单轮对话同步、流式请求都测试成功。swagger默认地址:http://localhost:8080/swagger-ui/index.html
由于swagger中看不出来流式的效果,可以直接在浏览器中测试,会出现打字机效果。
(http://localhost:8080/ChatClient/chatStream?userInput=你吃饭了吗)
多轮对话调用
使用SpringAI加入记忆进行多轮对话测试
@Autowired
ChatMemoryRepository chatMemoryRepository; //注入对话记忆
@GetMapping("/chatMemory")
@Operation(summary = "带记忆的同步调用")
String chatMemory(String userInput) {
// 1. 构建对话记忆存储配置
// 使用MessageWindowChatMemory实现窗口记忆策略
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository) // 底层记忆存储仓库(测试使用内存实现)
.maxMessages(20) // 设置历史消息最大保留轮次(滑动窗口大小)
.build();
// 2. 生成唯一会话ID(实际项目中由)
String conversationId = "123456789"; // 示例固定值,生产环境需动态生成
// 3. 构建对话请求并配置各组件
return this.chatClient.prompt()
// 3.1 设置对话角色
.system(system) // 系统角色设定(AI人设/指令)
// 3.2 设置基础参数
.advisors(
a -> a.param(ChatMemory.CONVERSATION_ID, conversationId) // 绑定当前对话ID到请求上下文
)
// 3.3 添加增强功能(Advisors)
.advisors(
new SimpleLoggerAdvisor(), // 启用请求日志记录(用于调试)
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 启用记忆管理功能
new SystemFirstSortingAdvisor() // 确保系统消息优先排序
)
// 3.4 设置当前用户输入
.user(userInput)
// 3.5 执行调用
.call() // 发送同步请求到对话服务
// 3.6 处理响应
.content(); // 提取响应中的文本内容
}
@GetMapping(value = "/chatMemoryStream",produces = "text/html;charset=utf-8")
@Operation(summary = "带记忆的流式调用")
public Flux<String> generateStream(String userInput) {
// 1. 构建对话记忆存储配置
// 使用MessageWindowChatMemory实现窗口记忆策略
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository) // 底层记忆存储仓库(测试使用内存实现)
.maxMessages(20) // 设置历史消息最大保留轮次(滑动窗口大小)
.build();
// 2. 生成唯一会话ID(实际项目中由)
String conversationId = "123456789"; // 示例固定值,生产环境需动态生成
// 3. 构建对话请求并配置各组件
return this.chatClient.prompt()
// 3.1 设置对话角色
.system(system) // 系统角色设定(AI人设/指令)
// 3.2 设置基础参数
.advisors(
a -> a.param(ChatMemory.CONVERSATION_ID, conversationId) // 绑定当前对话ID到请求上下文
)
// 3.3 添加增强功能(Advisors)
.advisors(
new SimpleLoggerAdvisor(), // 启用请求日志记录(用于调试)
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 启用记忆管理功能
new SystemFirstSortingAdvisor() // 确保系统消息优先排序
)
// 3.4 设置当前用户输入
.user(userInput)
// 3.5 执行调用
.stream() // 发送流式请求到对话服务
// 3.6 处理响应
.content(); // 提取响应中的文本内容
}
如上示例中有SystemFirstSortingAdvisor类需要单独便编写来确保系统人设始终保持在第一位(具体为什么要使用它,是因为1.0.1版本对Spring Ai多轮对话有BUG,可以参考这篇文章:https://blog.csdn.net/YnagLei/article/details/150619680?spm=1001.2014.3001.5502),内容如下:
/**
* 保证SYSTEM在最前面的增强
*/
public class SystemFirstSortingAdvisor implements BaseAdvisor {
@Override
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
List<Message> processedMessages = chatClientRequest.prompt().getInstructions();
processedMessages.sort(Comparator.comparing(m -> m.getMessageType() == MessageType.SYSTEM ? 0 : 1));
return chatClientRequest.mutate()
.prompt(chatClientRequest.prompt().mutate().messages(processedMessages).build())
.build();
}
@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
return chatClientResponse; // no-op
}
@Override
public int getOrder() {
return 0; // larger than MessageChatMemoryAdvisor so it runs afterwards
}
}
至此带记忆的多轮对话已编写完成,进行测试。
第一轮:你吃饭了吗?
第二轮:我刚问你什么了?
很明显模型已经知道咱们之前问的问题了,说明测试成功。