当前位置: 首页 > news >正文

SpringAI 使用通义千问进行聊天对话开发

文章目录

  • 前言
  • 一、正文
    • 1.1 项目结构
    • 1.2 项目环境
    • 1.3 完整代码
      • 1.3.1 spring-ai-demo的pom文件
      • 1.3.2 spring-ai-chat-server 的pom文件
      • 1.3.3 ChatConfig
      • 1.3.4 WebfluxConfig
      • 1.3.5 ChatController
      • 1.3.6 MainApplication
    • 1.4 完整配置
      • 1.4.1 application.yaml
    • 1.5 调用效果
  • 二、附录
    • 2.1 参考文档
    • 2.2 建议

前言

最近学习了下使用 Spring AI 进行和大模型的聊天对话,其中涉及到对话记忆,向量数据库,RAG 检索增强。

全部代码已经提交到代码仓库中,具体的可以去gitte中看看。

一、正文

1.1 项目结构

项目继承于 spring-ai-demo项目,父级主要控制依赖版本,子模块用于具体的功能实现。
在这里插入图片描述

1.2 项目环境

本次实践会使用到向量数据库 Qdrant,需要在 Qdrant下载地址 中下载,并安装启动。
另外,对外提供了聊天接口,向量数据库新增和清空文档接口,获取文档接口等。

java 版本选择21,springboot版本是3.4.2

1.3 完整代码

1.3.1 spring-ai-demo的pom文件

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.pine</groupId><artifactId>spring-ai-demo</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><name>spring-ai-demo</name><url>http://maven.apache.org</url><modules><module>dynamic-datasource-mcp-sse-server</module><module>dynamic-datasource-mcp-stdio-server</module><module>spring-ai-chat-server</module></modules><properties><java.version>21</java.version><spring-boot.version>3.4.2</spring-boot.version><spring-ai.version>1.0.0</spring-ai.version><spring-ai-alibaba.version>1.0.0.1</spring-ai-alibaba.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId><version>${spring-ai-alibaba.version}</version></dependency></dependencies></dependencyManagement></project>

1.3.2 spring-ai-chat-server 的pom文件

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.pine</groupId><artifactId>spring-ai-demo</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>spring-ai-chat-server</artifactId><packaging>jar</packaging><name>spring-ai-chat-server</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version><optional>true</optional></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-api</artifactId><version>1.65.1</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webflux</artifactId></dependency><!-- 阿里ai的starter --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-qdrant</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><inherited>true</inherited><configuration><source>${java.version}</source><target>${java.version}</target><parameters>true</parameters><showWarnings>true</showWarnings></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>org.feng.MainApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
</project>

1.3.3 ChatConfig

package org.feng.config;import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
import io.qdrant.client.QdrantClient;
import lombok.SneakyThrows;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 聊天配置** @author pine* @version v1.0* @since 2025-08-12 20:22*/
@Configuration
public class ChatConfig {/*** 配置ChatClient,注册系统指令和工具函数*/@Bean@SneakyThrowspublic ChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("""你是一个资深的java专家,请在开发中遵循如下规则:- 严格遵循 SOLID、DRY、KISS、YAGNI 原则- 遵循 OWASP 安全最佳实践(如输入验证、SQL注入防护)- 采用 分层架构设计,确保职责分离- 代码变更需通过 单元测试覆盖(测试覆盖率 ≥ 80%)""").build();}/*** 内存聊天记忆*/@Beanpublic MessageWindowChatMemory chatMemory() {return MessageWindowChatMemory.builder().maxMessages(100).build();}/*** 向量数据库 qdrant*/@Beanpublic VectorStore vectorStore(@Autowired QdrantClient qdrantClient, @Autowired DashScopeEmbeddingModel dashScopeEmbeddingModel) {return QdrantVectorStore.builder(qdrantClient, dashScopeEmbeddingModel).build();}
}

1.3.4 WebfluxConfig

package org.feng.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;/*** webflux配置** @author pine* @version v1.0* @since 2025-08-12 20:27*/
@Configuration
public class WebfluxConfig {@Beanpublic RestClient.Builder restClientBuilder() {return RestClient.builder();}
}

1.3.5 ChatController

package org.feng.controller;import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.document.Document;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.generation.augmentation.ContextualQueryAugmenter;
import org.springframework.ai.rag.postretrieval.document.DocumentPostProcessor;
import org.springframework.ai.rag.preretrieval.query.expansion.MultiQueryExpander;
import org.springframework.ai.rag.retrieval.join.ConcatenationDocumentJoiner;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;/*** 聊天控制器** @author pine* @version v1.0* @since 2025-08-12 20:28*/
@Controller
@Slf4j
@RequestMapping("/api")
public class ChatController {@Resourceprivate ChatClient chatClient;@Resourceprivate ChatMemory chatMemory;@Resourceprivate VectorStore vectorStore;private final ChatClient.Builder chatBuilder;public ChatController(ChatClient.Builder chatBuilder) {this.chatBuilder = chatBuilder;}/*** 聊天*/@GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)@SneakyThrows@ResponseBodypublic Flux<String> chat(@RequestParam(name="conversationId", required = false, defaultValue = "feng123") String conversationId, @RequestParam(name="message", defaultValue = "你好,你有什么功能") String message) {log.info("chat(), conversationId:{},message:{}", conversationId, message);// 查询扩展MultiQueryExpander queryExpander = MultiQueryExpander.builder()// 扩展数量:3.numberOfQueries(3).chatClientBuilder(chatBuilder)// 包括源查询条件.includeOriginal(true).build();// 增加上下文的信息ContextualQueryAugmenter contextualQueryAugmenter = ContextualQueryAugmenter.builder().allowEmptyContext(true).build();// RAGRetrievalAugmentationAdvisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()// 使用向量数据库作为文档源.documentRetriever(VectorStoreDocumentRetriever.builder().vectorStore(vectorStore).build())// 扩充查询条件.queryExpander(queryExpander)// 拼接查询到的文档.documentJoiner(new ConcatenationDocumentJoiner())// 取多个doc中的第一个.documentPostProcessors(new SelectedFirstDocumentPostProcessor())// 对生成的查询增强上下文.queryAugmenter(contextualQueryAugmenter).build();return chatClient.prompt(message)// 日志.advisors(new SimpleLoggerAdvisor())// 聊天记忆(暂时使用内存,后续支持jdbc).advisors(MessageChatMemoryAdvisor.builder(chatMemory).conversationId(conversationId).build())// RAG.advisors(retrievalAugmentationAdvisor).stream().content();}/*** 聊天记录*/@GetMapping(value = "/messages")@SneakyThrows@ResponseBodypublic List<Message> messages(@RequestParam(name="conversationId", defaultValue = "feng123") String conversationId) {return chatMemory.get(conversationId);}/*** 清空聊天记录*/@GetMapping(value = "/clearMessages")@SneakyThrows@ResponseBodypublic String clearMessages(@RequestParam(name="conversationId", defaultValue = "feng123") String conversationId) {chatMemory.clear(conversationId);return "success";}/*** 向量数据库增加数据*/@ResponseBody@SneakyThrows@PostMapping(value = "/addDocuments")public String addDocuments(@RequestParam(name = "documents") List<String> documents) {List<Document> collected = documents.stream().map(Document::new).collect(Collectors.toList());vectorStore.add(collected);return "success";}@SuppressWarnings("all")private static class SelectedFirstDocumentPostProcessor implements DocumentPostProcessor {@Overridepublic List<Document> process(Query query, List<Document> documents) {if (documents.isEmpty()) {return Collections.emptyList();}return Collections.singletonList(documents.get(0));}}
}

1.3.6 MainApplication

package org.feng;import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.web.bind.annotation.CrossOrigin;
/*** 启动类** @author pine* @version v1.0* @since 2025-08-12 20:23*/
@SpringBootApplication
@CrossOrigin(origins = "*",allowedHeaders = "*",exposedHeaders = {"Cache-Control", "Connection"}  // 暴露必要头
)
@ConfigurationPropertiesScan
public class MainApplication {public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(MainApplication.class);springApplication.setWebApplicationType(WebApplicationType.REACTIVE);springApplication.run(args);}
}

1.4 完整配置

1.4.1 application.yaml

server:port: 8181tomcat:uri-encoding: UTF-8keep-alive-timeout: 30000max-connections: 100servlet:encoding:charset: UTF-8force: trueenabled: truecompression:enabled: false # 禁用压缩(否则流式数据可能被缓冲)spring:main:allow-bean-definition-overriding: trueweb-application-type: reactivebanner-mode: consoleapplication:name: spring-ai-chat-demoai:# 配置阿里的密钥,模型dashscope:api-key: sk#你自己的keychat:options:model: qwen-plus# 向量数据库vectorstore:qdrant:host: localhostport: 6334collection-name: vector-storeuse-tls: falseinitialize-schema: true# 日志增强
logging:level:org:springframework:ai:chat:client:advisor: DEBUG

1.5 调用效果

使用get请求,进行聊天对话。
在这里插入图片描述

二、附录

2.1 参考文档

  • https://docs.spring.io/spring-ai/reference/api/chatclient.html
  • https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html
  • https://docs.spring.io/spring-ai/reference/api/vectordbs/qdrant.html
  • https://springdoc.cn/spring-ai/index.html

2.2 建议

https://element-plus-x.com/zh/

前端页面可以采取 element-plus-x 来实现。

http://www.dtcms.com/a/328507.html

相关文章:

  • 考研复习-计算机组成原理-第五章-CPU
  • [NoC]Outstanding和Credit的概念详解
  • Fluent Bit 日志合并正则表达式(上)
  • Nginx 高级配置
  • Python训练Day41
  • 基于PAI-ChatLearn的GSPO强化学习实践
  • LLM - 搭建 Grounded SAM 2 模型的视觉检测与分割服务 API
  • CMake笔记:PUBLIC/PRIVATE/INTERFACE的使用
  • FreeRTOS---基础知识6---事件组
  • Effective C++ 条款37:绝不重新定义继承而来的缺省参数值
  • Linux系统编程Day13 -- 程序地址空间
  • Vue3 整合高德地图完成搜索、定位、选址功能,已封装为组件开箱即用(最新)
  • 前端对接豆包AI(vue3+TS版本)
  • 力扣-739.每日温度
  • Leetcode-138. 复制带随机指针的链表
  • AI智能体的“四大支柱”:CAP框架核心层、执行层、约束层、操作层详解​
  • 手机蓝牙无感开锁在智能柜锁与智能箱包中的整体解决方案
  • Iptables 详细使用指南
  • 10-docker基于dockerfile自动制作镜像
  • 计算机网络摘星题库800题笔记 第5章 传输层
  • Ansible 详细笔记
  • _init__.py的作用
  • 电路板的GND与外壳地EARTH通过电容电阻相连
  • 操作系统1.6:虚拟机
  • 图形设计器-Qt Designer (一)包含 LinuxCNC 小部件
  • 基于LLVM的memcpy静态分析工具:设计思路与原理解析(C/C++代码实现)
  • 浏览器面试题及详细答案 88道(12-22)
  • word——选项自动对齐(针对试卷中选项对齐)
  • 2025牛客暑期多校训练营3(FDJAEHB)
  • SuperMap GIS基础产品FAQ集锦(20250811)