
SpringAI框架实现连续对话聊天什么是SpringAI如何实现SpringAI的连续聊天。依赖jar包的选择配置文件spring的自动配置实现聊天功能的WEB接口什么是SpringAI此处纯属我个人理解从刚开始ChatGPT爆红网络到现在各大AI模型百花齐放AI在训练和使用上似乎一直是python的特权。现在spring终于放大招了出了一个SpringAI框架我们使用的Java语言也可以实现AI的使用了。并且SpringAI框架的出现定义了一套标准接口我们不用去学各个大模型厂商提供的SDK怎么用了只需要学习SpringAI就可以适配所有已接入的大模型。本次先记录下如何实现连续聊天AI可以通过聊天记录的上下文给出最合适的答案MCP服务及客户端的实现、文转图的实现我们随后就发布。有兴趣的同学可以订阅专栏或者关注我。如何实现SpringAI的连续聊天。本次的功能实现用的是阿里的通义大模型需要提前申请好大模型的密钥。密钥创建的传送门https://bailian.console.aliyun.com/?tabmodel#/api-key依赖jar包的选择spring boot的版本使用3.5.0以上我使用的是3.5.5dependencyManagementdependenciesdependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-bom/artifactIdversion1.0.0.4/versiontypepom/typescopeimport/scope/dependencydependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-bom/artifactIdversion1.1.0-M3/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagement!-- 阿里云通义集成模块 --dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter-dashscope/artifactId/dependency!-- 提供web服务 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- 提供响应式的流服务对客户端相应速度快 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webflux/artifactId/dependency踩坑要点spring-boot-starter-web和spring-boot-starter-webflux只能二选一spring-boot-starter-web的底层实现是用servlet而spring-boot-starter-webflux是基于netty的长连接启动时服务会因冲突报错。如果需要写响应式的服务接口需要将该接口单独写一个服务。配置文件使用application.yml进行配置server:port:8080spring:application:name:chat-gptai:dashscope:api-key:sk-xxxxxxxxxxxxxxxxxxxxxxxxxspring的自动配置SpringAI框架在对象创建、方法调用大量用了链式调用去实现一定得习惯这种方式。很多类是没有公共的构造方法的。packagecom.zhangzz.ai.core.config;importcom.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.MessageWindowChatMemory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * SpringAI配置类用于配置ChatClient和ChatMemory。 */ConfigurationpublicclassSpringAIConfig{/** * 保存历史聊天记录的Bean对象使用的是线程安全的Map保存也可自己实现接口ChatMemoryRepository持久化到数据库等。 * 若未实现ChatMemoryRepository可不用调用chatMemoryRepository(repository)默认会使用内存的存储实现方案 * return ChatMemory */BeanpublicChatMemorygetChatMemory(ChatMemoryRepositoryrepository){//最小消息数量按照自己的需求来设置并不是越大越好越大每次发送给大模型的内容就越多//消耗的token就越多可能一次消耗十几万的token也是有可能的returnMessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(3).build();}/** * 将通义大模型接入ChatClient * param model 阿里通义模型 * return ChatClient */BeanpublicChatClientgetChatClient(DashScopeChatModelmodel){//模型参数配置DashScopeChatOptionsoptionsDashScopeChatOptions.builder().build();// 这是阿里云自研的旗舰模型家族// qwen-max: 适合处理复杂、多步骤的任务能力最强。// qwen-plus: 在效果、速度和成本之间取得均衡适用于大多数场景。// qwen-turbo: 适合简单任务响应速度快成本极低。// qwen-coder: 专注于代码生成与理解在工具调用和环境交互方面表现卓越。// 此外还有专门的 qwen-vl (视觉语言)、qwen-audio (音视频) 等多模态模型。// 模型能力越强价格越贵综合能力和价格对比qwen-plus比较适合大部分业务功能options.setModel(qwen-plus);//限制模型生成的最大 token 数不包含输入可避免特别大的token消耗options.setMaxTokens(1500);//遇到集合中的字符停止生成。可用于阻止敏感信息输出。options.setStop(List.of(共产党,解放军));//随机种子用于复现结果。相同 seed 相同输入 → 相同输出options.setSeed(42);//控制输出随机性。值越小越确定0~2建议 0.1~1.0。设为 0 表示 deterministic确定性输出options.setTemperature(0.7);//核采样nucleus sampling阈值。只保留概率累积和 ≥ top_p 的 token。通常 0.8~1.0options.setTopP(0.9);//只从概率最高的 top_k 个 token 中采样。与 top_p 互斥或共用视模型支持options.setTopK(50);//重复惩罚系数。1.0 抑制重复1.0 鼓励重复通常 1.0~1.2options.setRepetitionPenalty(1.1);ChatOptionschatOptionsDashScopeChatOptions.fromOptions(options);returnChatClient.builder(model).build();}}ChatMemory接口该接口是用来记录与AI大模型每次的聊天记录的便于AI大模型每次生成的内容是基于之前的聊天历史问的不会出现答非所问的情况。Spring默认提供了一个实现类基于线程安全的Map去做存储的。相当于存储在内存里个人比较推荐重新实现ChatMemory接口使用Mongdb去做存储大数据量检索速度还快。ChatClient 接口是文本生成的重要接口我们的文本聊天基本上都是用这个接口实现的。实现聊天功能的WEB接口importcom.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.model.ChatResponse;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.http.MediaType;importreactor.core.publisher.Flux;importjava.util.Map;importjava.util.Objects;RestControllerRequestMapping(/ai)publicclassChatController{AutowiredprivateChatClientchatClient;AutowiredprivateDashScopeChatModelmodel;AutowiredprivateChatMemorychatMemory;PostMapping(/tongYiChat)publicMapString,ObjecttongYiChat(RequestParam(valuemessage)Stringmessage){StringtabIdnewTab1;//例如此处用户新开的聊天窗口ID。以此ID作为标识将每次的聊天记录保存MessageChatMemoryAdvisorchatMemoryAdvisorMessageChatMemoryAdvisor.builder(chatMemory).conversationId(tabId).build();ChatResponseresponsechatClient.prompt(newPrompt(message)).//用户的提问信息advisors(chatMemoryAdvisor).//聊天记录保存器此处为保存聊天记录到内存中。call().chatResponse();returnMap.of(result,Objects.requireNonNullElse(response,error));}/** * 响应式输出避免客户端等待时间太长 * 切记该接口的实现不可使用任何包含servlet的jar包 * 不可添加spring-boot-starter-web依赖我将两个接口写在一起只是因为懒而已 * param message 用户输入的消息 * return 聊天流 */PostMapping(value/chatStream,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxStringchatStream(RequestParam(valuemessage)Stringmessage,ServerWebExchangewebExchange){//ServerWebExchange 对象可获取请求头的内容ListStringheaderswebExchange.getRequest().getHeaders().get(Authorization);returnchatClient.prompt(newPrompt(message)).stream().content().map(content-content).timeout(Duration.ofSeconds(30)).//超时时间onErrorMap(e-newRuntimeException(e.getMessage())).//异常处理doOnComplete(()-log.info(聊天结束));//完成输出}Controller的Advisor类似于springAOP会在调用AI大模型的前、后或环绕通知里做一些业务。例如下面实现的连续对话的MessageChatMemoryAdvisor 会在发起AI大模型请求前将历史聊天读取出来一起发送给AI大模型。如下图启动服务使用postman进行测试1.接口“/tongYiChat”的结果2.接口“/chatStream”的结果此处若想实现响应式的流还需要去前端实现通过和后端建议长连接来进行持续输出就和文言一心一样AI的结果是一点一点输出的页面的而不是一下子全部显示在页面那样用户会等待的太久以上技术已在我的AI笔记系统中实现可去体验一下传送门链接地址笔记系统后续关于AI如何开发笔记系统的MCP工具我也会陆续记录在CSDN。