Spring-AI初级使用记录 spring-ai-bom版本1.0.1-(单、多轮对话)
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 Keybase-url: https://XXX.XXX.com # OpenAI API的基础URLchat:options:# 使用的模型,可根据需求更换model: deepseek-reasonertemperature: 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加入记忆进行多轮对话测试
@AutowiredChatMemoryRepository 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 {@Overridepublic 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();}@Overridepublic ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {return chatClientResponse; // no-op}@Overridepublic int getOrder() {return 0; // larger than MessageChatMemoryAdvisor so it runs afterwards}
}
至此带记忆的多轮对话已编写完成,进行测试。
第一轮:你吃饭了吗?
第二轮:我刚问你什么了?
很明显模型已经知道咱们之前问的问题了,说明测试成功。