【2025最新最全】SpringAI Alibaba + 阿里云百炼 详细教程(上)
目录
1.4 SpringAI VS SpringAI Alibaba VS LangChain4J
业务类第3版(ChatModel + ChatClient混合使用)
五、Server-SentEvents(SSE) 实现Stream流式输出及多模型共存
5.2 SSE(Server-Sent Events)服务器发送事件
Server-SentEvents(SSE)服务器发送事件实现流式输出

一、SpringAI Alibaba理论概述
1.1 SSA为什么会出现?
随着人工智能(AI)技术的迅猛发展,越来越多的开发者开始将目光投向AI应用的开发。然而,目前市场上大多数AI框架和工具如LangChain、PyTorch等主要支持Python,而Java开发者常常面临工具缺乏和学习门槛较高的问题,所以阿里推出Java相关的技术,也就是SSA。
1.2 是什么?
SpringAI Alibaba是一款以Spring AI为基础,深度集成阿里云百炼平台,支持ChatBot、工作流、多智能体应用开发模式的AI框架。
其实就是Spring AI + 阿里云百炼。
1.3 能干嘛?
Spring AI Alibaba 基于 Spring AI 构建,因此SAA继承了SpringAI 的所有原子能力抽象并在此
基础上扩充丰富了模型、向量存储、记忆、RAG 等核心组件适配,让其能够接入阿里云的 AI 生态。
1.4 SpringAI VS SpringAI Alibaba VS LangChain4J

二、HelloWord案例
2.1 阿里云百炼平台入口官网
接入阿里百炼平台的通义模型
https://bailian.console.aliyun.com/
大模型调用三件套
(1)获得Api-key

(2)获得模型名


(3)获得baseUrl开发地址

2.2 创建父工程
父工程

使用 bom 管理依赖版本
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>SpringAIAlibaba-Maven父工程POM配置</name> <url>http://maven.apache.org</url> <modules> <module>SAA-01HelloWorld</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <java.version>17</java.version> <!-- Spring Boot 新建2025.9--> <spring-boot.version>3.5.5</spring-boot.version> <!-- Spring AI 新建2025.9--> <spring-ai.version>1.0.0</spring-ai.version> <!-- Spring AI Alibaba 新建2025.9--> <SpringAIAlibaba.version>1.0.0.2</SpringAIAlibaba.version> </properties> <dependencyManagement> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring AI Alibaba --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-bom</artifactId> <version>${SpringAIAlibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring AI --> <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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 2.3 开发五步骤
创建Module

改pom.xml
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-01HelloWorld</artifactId> <packaging>jar</packaging> <name>SAA-01HelloWorld</name> <url>http://maven.apache.org</url> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入 springai alibaba DashScope 模型适配的 Starter --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml
server.port=8001 #大模型对话中文乱码UTF8编码处理 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-01HelloWorld # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api} spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1 spring.ai.dashscope.chat.options.model=qwen-plus创建主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa01HelloWorldApplication { public static void main(String[] args) { SpringApplication.run(Saa01HelloWorldApplication.class, args); } } 业务类
ApiKey不可以明文需配置进环境变量
修改环境变量

K-V键值对设置

重启IDEA
配置类SaaLLMConfig
方式一
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { /** * 方式1:${} * 持有yml文件配置:spring.ai.dashscope.api-key=${aliQwen-api} */ @Value("${spring.ai.dashscope.api-key}") private String apiKey; @Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(apiKey).build(); } } 方式二
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @auther [email protected] * @create 2025-07-22 0:51 */ @Configuration public class SaaLLMConfig { /** * 方式2:System.getenv("环境变量") * 持有yml文件配置:spring.ai.dashscope.api-key=${aliQwen-api} * @return */ @Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build(); } }对话模型(Chat Model)


创建controller
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class ChatHelloController { @Resource //阿里云百炼 private ChatModel dashScopeChatModel; /** * http://localhost:8001/hello/dochat * @param msg * @return */ @GetMapping("/hello/dochat") public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { String result = dashScopeChatModel.call(msg); System.out.println("响应:" + result); return result; } /** * http://localhost:8001/hello/streamchat * @param msg * @return */ @GetMapping("/hello/streamchat") public Flux<String> streamChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { return dashScopeChatModel.stream(msg); } } 测试
访问:http://localhost:8001/hello/dochat 和 http://localhost:8001/hello/streamchat


三、Ollama私有化部署和对接本地大模型
3.1 Ollama本地大模型部署
安装ollama
安装通义千问大模型
ollama run qwen:4b3.2 微服务对接本地大模型
创建Module

修改pom.xml
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-02Ollama</artifactId> <packaging>jar</packaging> <name>SAA-02Ollama</name> <url>http://maven.apache.org</url> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--ollama--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-ollama</artifactId> <version>1.0.0</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml
server.port=8002 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-02Ollama # ====ollama Config============= spring.ai.dashscope.api-key=${aliQwen-api} spring.ai.ollama.base-url=http://localhost:11434 spring.ai.ollama.chat.model=qwen2.5:latest 创建主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa02OllamaApplication { public static void main(String[] args) { SpringApplication.run(Saa02OllamaApplication.class,args); } }创建controller
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class OllamaController { @Resource(name = "ollamaChatModel") private ChatModel chatModel; /** * http://localhost:8002/ollama/chat?msg=你是谁 * @param msg * @return */ @GetMapping("/ollama/chat") public String chat(@RequestParam(name = "msg") String msg) { String result = chatModel.call(msg); System.out.println("---结果:" + result); return result; } @GetMapping("/ollama/streamchat") public Flux<String> streamchat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { return chatModel.stream(msg); } }四、ChatClient VS ChatModel
4.1 二者区别?
之前的调用都是使用ChatModel进行,现在我们认识一个新的接口ChatClient。
对话模型(ChatModel)是底层接口,直接与具体大语言模型交互,提供call()和stream()方法,适合简单大模型交互场景。ChatClient是高级封装,基于ChatModel构建,适合快速构建标准化复杂AI服务,支持同步和流式交互,集成多种高级功能。
其实总结起来最大的区别就是写代码的方式不同,如下:

对于我个人来说,我对链式写法并不感冒,我还是倾向于使用样板代码。
4.2 编码案例
创建Module

修改pom.xml
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-03ChatModelChatClient</artifactId> <packaging>jar</packaging> <name>SAA-03ChatModelChatClient</name> <url>http://maven.apache.org</url> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml文件
server.port=8003 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-03ChatModelChatClient # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api}主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa03ChatModelChatClientApplication { public static void main(String[] args) { SpringApplication.run(Saa03ChatModelChatClientApplication.class, args); } }业务类第1版(只有ChatModel)
新建配置类SaaLLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { @Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build(); } } 创建controller
package com.atguigu.study.controller; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ChatModelController { @Resource //阿里云百炼 private ChatModel dashScopeChatModel; @GetMapping("/chatmodel/dochat") public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { String result = dashScopeChatModel.call(msg); System.out.println("响应:" + result); return result; } } 业务类第2版(只有ChatClient)
新建ChatClientController
package com.atguigu.study.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * 知识出处: * https://java2ai.com/docs/1.0.0.2/tutorials/basics/chat-client/?spm=5176.29160081.0.0.2856aa5cmUTyXC#%E5%88%9B%E5%BB%BA-chatclient */ @RestController public class ChatClientController { private final ChatClient dashScopechatClient; /** * 使用自动配置的 ChatClient.Builder * @param dashscopeChatModel */ public ChatClientController(ChatModel dashscopeChatModel) { this.dashScopechatClient = ChatClient.builder(dashscopeChatModel).build(); } /** * http://localhost:8003/chatclient/dochat * @param msg * @return */ @GetMapping("/chatclient/dochat") public String doChat(@RequestParam(name = "msg",defaultValue = "2加4等于几") String msg) { String result = dashScopechatClient.prompt().user(msg).call().content(); System.out.println("响应:" + result); return result; } }业务类第3版(ChatModel + ChatClient混合使用)
修改配置类SaaLLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.client.ChatClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { @Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build(); } /** * 知识出处: * https://java2ai.com/docs/1.0.0.2/tutorials/basics/chat-client/?spm=5176.29160081.0.0.2856aa5cmUTyXC#%E5%88%9B%E5%BB%BA-chatclient * @param dashscopeChatModel * @return */ @Bean public ChatClient chatClient(ChatModel dashscopeChatModel) { return ChatClient.builder(dashscopeChatModel).build(); } }新建ChatClientControllerV2
package com.atguigu.study.controller; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ChatClientControllerV2 { /** * chatModel + ChatClient 混合使用 */ @Resource private ChatModel chatModel; @Resource private ChatClient dashScopechatClientv2; /** * http://localhost:8003/chatclientv2/dochat * @param msg * @return */ @GetMapping("/chatclientv2/dochat") public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { String result = dashScopechatClientv2.prompt().user(msg).call().content(); System.out.println("ChatClient响应:" + result); return result; } /** * http://localhost:8003/chatmodelv2/dochat * @param msg * @return */ @GetMapping("/chatmodelv2/dochat") public String doChat2(@RequestParam(name = "msg",defaultValue = "你是谁") String msg) { String result = chatModel.call(msg); System.out.println("ChatModel响应:" + result); return result; } }二者对比

五、Server-SentEvents(SSE) 实现Stream流式输出及多模型共存
5.1 Response Streaming流式输出
是什么?
流式输出(StreamingOutput)
是一种逐步返回大模型生成结果的技术,生成一点返回一点,允许服务器将响应内容
分批次实时传输给客户端,而不是等待全部内容生成完毕后再一次性返回。
这种机制能显著提升用户体验,尤其适用于大模型响应较慢的场景(如生成长文本或复杂推理结果)。
SpringAI Alibaba流式输出有两种
(1)通过ChatModel实现stream实现流式输出
(2)通过ChatClient实现stream实现流式输出
5.2 SSE(Server-Sent Events)服务器发送事件
Server-SentEvents(SSE)服务器发送事件实现流式输出
Server-Sent Events (SSE) 是一种允许服务端可以持续推送数据片段(如逐词或逐句)到前端的 Web 技术。通过单向的HTTP长连接,使用一个长期存在的连接,让服务器可以主动将数据"推"给客户端,SSE是轻量级的单向通信协议,适合AI对话这类服务端主导的场景
核心概念
SSE 的核心思想是:客户端发起一个请求,服务器保持这个连接打开并在有新数据时,通过这个连接将数据发送给客户端。这与传统的请求-响应模式(客户端请求一次,服务器响应一次,连接关闭)有本质区别。SSE下一代(Stream able Http)

一句话
一种让服务器能够主动、持续地向客户端(比如你的网页浏览器)推送数据的技术。
SSE适用场景

5.3 开发步骤
要求同时存在多种大模型产品在系统里共存使用。
新建子模块Module
SAA-04StreamingOutput
改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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-04StreamingOutput</artifactId> <packaging>jar</packaging> <name>SAA-04StreamingOutput</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.38</version> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml配置文件
server.port=8004 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-04StreamingOutput # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api} 主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Description 流式输出 */ @SpringBootApplication public class Saa04StreamingOutputApplication { public static void main(String[] args) { SpringApplication.run(Saa04StreamingOutputApplication.class, args); } } 业务类
通过ChatModel实现stream实现流式输出
配置类LLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description ChatModel+ChatClient+多模型共存 */ @Configuration public class SaaLLMConfig { // 模型名称常量定义 private final String DEEPSEEK_MODEL = "deepseek-v3"; private final String QWEN_MODEL = "qwen-plus"; @Bean(name = "deepseek") public ChatModel deepSeek() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build() ) .build(); } @Bean(name = "qwen") public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder() .withModel(QWEN_MODEL) .build() ) .build(); } }controller第1版
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; /** * @Description 流式输出 */ @RestController public class StreamOutputController { //V1 通过ChatModel实现stream实现流式输出 @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @GetMapping(value = "/stream/chatflux1") public Flux<String> chatflux(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return deepseekChatModel.stream(question); } @GetMapping(value = "/stream/chatflux2") public Flux<String> chatflux2(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return qwenChatModel.stream(question); } }通过ChatClient实现stream实现流式输出
配置类LLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description ChatModel+ChatClient+多模型共存 */ @Configuration public class SaaLLMConfig { // 模型名称常量定义 private final String DEEPSEEK_MODEL = "deepseek-v3"; private final String QWEN_MODEL = "qwen-plus"; @Bean(name = "deepseek") public ChatModel deepSeek() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build() ) .build(); } @Bean(name = "qwen") public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder() .withModel(QWEN_MODEL) .build() ) .build(); } @Bean(name = "deepseekChatClient") public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel deepSeek) { return ChatClient.builder(deepSeek) .defaultOptions(ChatOptions.builder() .model(DEEPSEEK_MODEL) .build()) .build(); } @Bean(name = "qwenChatClient") public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen) { return ChatClient.builder(qwen) .defaultOptions(ChatOptions.builder() .model(QWEN_MODEL) .build()) .build(); } } controller第2版
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; /** * @Description 流式输出 */ @RestController public class StreamOutputController { //V1 通过ChatModel实现stream实现流式输出 @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; //V2 通过ChatClient实现stream实现流式输出 @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; @GetMapping(value = "/stream/chatflux1") public Flux<String> chatflux(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return deepseekChatModel.stream(question); } @GetMapping(value = "/stream/chatflux2") public Flux<String> chatflux2(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return qwenChatModel.stream(question); } @GetMapping(value = "/stream/chatflux3") public Flux<String> chatflux3(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return deepseekChatClient.prompt(question).stream().content(); } @GetMapping(value = "/stream/chatflux4") public Flux<String> chatflux4(@RequestParam(name = "question",defaultValue = "你是谁") String question) { return qwenChatClient.prompt(question).stream().content(); } }5.4 新增前端代码trytry
前端效果


Flux<T>本质提一嘴

Flux是SpringWebFlux中的一个核心组件,属于响应式编程模型的一部分。它主要用于处理异步、非阻塞的流式数据,能够高效地处理高并发场景。Flux可以生成和处理一系列的事件或数据如流式输出等。
看类注释和类所在的jar包我们就明白:
SAA中的流式输出是通过ReactorStreams技术实现的和SpringWebFlux的底层实现是一样的技术。
具体执行流程:
ReactorStreams会订阅数据源,当有数据时,ReactorStreams以分块流的方式发送给客户端用户。
SSE
index.html
<!DOCTYPE html> <html> <head> <title>SSE流式chat</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; } #messageInput { width: 90%; padding: 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 10px; } button { padding: 10px 20px; font-size: 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #0056b3; } #messages { margin-top: 20px; padding: 15px; background-color: #f9f9f9; border: 1px solid #ddd; border-radius: 8px; max-height: 300px; overflow-y: auto; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } #messages div { padding: 8px 0; border-bottom: 1px solid #eee; font-size: 14px; color: #333; } #messages div:last-child { border-bottom: none; } </style> </head> <body> <textarea rows="4" cols="50" placeholder="请输入你的问题..."></textarea><br> <button onclick="sendMsg()">发送提问</button> <div></div> <script> function sendMsg() { // 获取用户输入的消息 const message = document.getElementById('messageInput').value; if (message == "") return false; //1 客户端使用 JavaScript 的 EventSource 对象连接到服务器上的一个特定端点(URL) const eventSource = new EventSource('stream/chatflux2?question=' + message); //2 监听消息事件 eventSource.onmessage = function (event) { // 获取流式返回的数据 const data = event.data; // 将接收到的数据展示到页面上 const messagesDiv = document.getElementById('messages'); messagesDiv.innerHTML += event.data; }; //3 监听错误事件 eventSource.onerror = function (error) { console.error('EventSource 发生错误:', error); eventSource.close(); // 关闭连接 }; } </script> </body> </html> 存放位置

测试
http://localhost:8004/index.html

六、提示词Prompt
6.1 DeepSeek提示词样例
https://api-docs.deepseek.com/zh-cn/prompt-library/

6.2 是什么?

先从最简单的API调用说起


可以近似的理解
Prompt > Message > String简单的字符串
API 概览

再从源码Prompt说起
String
最初的Prompt只是简单的文本字符串提问。
Message
enum MessageType

上述也称为,Prompt 中的四大角色(Role)。
Prompt

6.3 Prompt中的四大角色(Role)
system
设定AI行为边界/角色/定位。指导AI的行为和响应方式,设置AI如何解释和回复输入的。
user
用户原始提问输入。代表用户的输入他们向AI提出的问题、命令或陈述。
assistant
AI返回的响应信息,定义为”助手角色”消息。用它可以确保上下文能够连贯的交互。
记忆对话,积累回答。

tool
桥接外部服务,可以进行函数调用如,支付/数据查询等操作,类似调用第3方util工具类,后面章节详细介绍。
6.4 开发步骤
新建子模块Module

改pom.xml文件
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>springAI-05chat-Prompt</artifactId> <packaging>jar</packaging> <name>springAI-05chat-Prompt</name> <url>http://maven.apache.org</url> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-openai--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.34</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> </project> 编写yml文件
server.port=8005 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-05Prompt # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api} 主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa05PromptApplication { public static void main(String[] args) { SpringApplication.run(Saa05PromptApplication.class,args); } } 业务类
配置类LLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description ChatModel+ChatClient+多模型共存 */ @Configuration public class SaaLLMConfig { // 模型名称常量定义 private final String DEEPSEEK_MODEL = "deepseek-v3"; private final String QWEN_MODEL = "qwen-plus"; @Bean(name = "deepseek") public ChatModel deepSeek() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build() ) .build(); } @Bean(name = "qwen") public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder() .withModel(QWEN_MODEL) .build() ) .build(); } @Bean(name = "deepseekChatClient") public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel deepSeek) { return ChatClient.builder(deepSeek) .defaultOptions(ChatOptions.builder() .model(DEEPSEEK_MODEL) .build()) .build(); } @Bean(name = "qwenChatClient") public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen) { return ChatClient.builder(qwen) .defaultOptions(ChatOptions.builder() .model(QWEN_MODEL) .build()) .build(); } }controller第1版(system和user)
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class PromptController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; // http://localhost:8005/prompt/chat?question=火锅介绍下 @GetMapping("/prompt/chat") public Flux<String> chat(String question) { return deepseekChatClient.prompt() // AI 能力边界 .system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告") .user(question) .stream() .content(); } }测试:

controller第2版(通过ChatModel实现)
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class PromptController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; // http://localhost:8005/prompt/chat?question=火锅介绍下 @GetMapping("/prompt/chat") public Flux<String> chat(String question) { return deepseekChatClient.prompt() // AI 能力边界 .system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告") .user(question) .stream() .content(); } @GetMapping("/prompt/chat2") public Flux<ChatResponse> chat2(String question) { // 用户消息 UserMessage userMessage = new UserMessage(question); // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,每个故事控制在300字以内"); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt); } @GetMapping("/prompt/chat3") public Flux<String> chat3(String question) { // 用户消息 UserMessage userMessage = new UserMessage(question); // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,每个故事控制在300字以内"); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt) .map(response -> response.getResults().get(0).getOutput().getText()); } } controller第3版(assistant)
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class PromptController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; // http://localhost:8005/prompt/chat?question=火锅介绍下 @GetMapping("/prompt/chat") public Flux<String> chat(String question) { return deepseekChatClient.prompt() // AI 能力边界 .system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告") .user(question) .stream() .content(); } @GetMapping("/prompt/chat2") public Flux<ChatResponse> chat2(String question) { // 用户消息 UserMessage userMessage = new UserMessage(question); // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,每个故事控制在300字以内"); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt); } @GetMapping("/prompt/chat3") public Flux<String> chat3(String question) { // 用户消息 UserMessage userMessage = new UserMessage(question); // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,每个故事控制在300字以内"); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt) .map(response -> response.getResults().get(0).getOutput().getText()); } @GetMapping("/prompt/chat4") public String chat4(String question) { AssistantMessage assistantMessage = deepseekChatClient.prompt() .user(question) .call() .chatResponse() .getResult() .getOutput(); return assistantMessage.getText(); } }controller第4版(tool)
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.ToolResponseMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import java.util.List; /** * @auther [email protected] * @create 2025-07-25 21:25 * @Description 知识出处,https://java2ai.com/docs/1.0.0.2/tutorials/basics/prompt/?spm=5176.29160081.0.0.2856aa5cdeol7a */ @RestController public class PromptController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; // http://localhost:8005/prompt/chat?question=火锅介绍下 @GetMapping("/prompt/chat") public Flux<String> chat(String question) { return deepseekChatClient.prompt() // AI 能力边界 .system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告") .user(question) .stream() .content(); } /** * http://localhost:8005/prompt/chat2?question=葫芦娃 * @param question * @return */ @GetMapping("/prompt/chat2") public Flux<ChatResponse> chat2(String question) { // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手,每个故事控制在300字以内"); // 用户消息 UserMessage userMessage = new UserMessage(question); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt); } /** * http://localhost:8005/prompt/chat3?question=葫芦娃 * @param question * @return */ @GetMapping("/prompt/chat3") public Flux<String> chat3(String question) { // 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个讲故事的助手," + "每个故事控制在600字以内且以HTML格式返回"); // 用户消息 UserMessage userMessage = new UserMessage(question); Prompt prompt = new Prompt(userMessage, systemMessage); return deepseekChatModel.stream(prompt) .map(response -> response.getResults().get(0).getOutput().getText()); } /** * http://localhost:8005/prompt/chat4?question=葫芦娃 * @param question * @return */ @GetMapping("/prompt/chat4") public String chat4(String question) { AssistantMessage assistantMessage = deepseekChatClient.prompt() .user(question) .call() .chatResponse() .getResult() .getOutput(); return assistantMessage.getText(); } /** * http://localhost:8005/prompt/chat5?city=北京 * 近似理解Tool后面章节讲解...... * @param city * @return */ @GetMapping("/prompt/chat5") public String chat5(String city) { String answer = deepseekChatClient.prompt() .user(city + "未来3天天气情况如何?") .call() .chatResponse() .getResult() .getOutput() .getText(); ToolResponseMessage toolResponseMessage = new ToolResponseMessage( List.of(new ToolResponseMessage.ToolResponse("1","获得天气",city) ) ); String toolResponse = toolResponseMessage.getText(); String result = answer + toolResponse; return result; } }七、提示词Prompt Template
7.1 Prompt演化历程
简单纯字符串提问问题
最初的Prompt只是简单的文本字符串。
多角色消息
将消息分为不同角色(如用户、助手、系统等),设置功能边界,增强交互的复杂性和上下文感知能力。
占位符(Prompt Template)
引入占位符(如{占位符变量名})以动态插入内容。
7.2 提示词模板是什么

7.3 开发步骤
新建子模块Module

改pom.xml
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-06PromptTemplate</artifactId> <packaging>jar</packaging> <name>SAA-06PromptTemplate</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.38</version> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml文件
server.port=8006 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-06PromptTemplate # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api}主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa06PromptTemplateApplication { public static void main(String[] args) { SpringApplication.run(Saa06PromptTemplateApplication.class, args); } } 业务类
配置类LLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { // 模型名称常量定义 private final String DEEPSEEK_MODEL = "deepseek-v3"; private final String QWEN_MODEL = "qwen-plus"; @Bean(name = "deepseek") public ChatModel deepSeek() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build() ) .build(); } @Bean(name = "qwen") public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder() .withModel(QWEN_MODEL) .build() ) .build(); } @Bean(name = "deepseekChatClient") public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel deepSeek) { return ChatClient.builder(deepSeek) .defaultOptions(ChatOptions.builder() .model(DEEPSEEK_MODEL) .build()) .build(); } @Bean(name = "qwenChatClient") public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen) { return ChatClient.builder(qwen) .defaultOptions(ChatOptions.builder() .model(QWEN_MODEL) .build()) .build(); } }重点步骤
PromptTemplate基本使用
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; @RestController public class PromptTemplateController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * @Description: PromptTemplate基本使用,使用占位符设置模版 PromptTemplate * 测试地址 * http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200 */ @GetMapping("/prompttemplate/chat") public Flux<String> chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate = new PromptTemplate("" + "讲一个关于{topic}的故事" + "并以{output_format}格式输出," + "字数在{wordCount}左右"); // PromptTempate -> Prompt Prompt prompt = promptTemplate.create(Map.of( "topic", topic, "output_format",output_format, "wordCount",wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } }PromptTemplate读取模版文件实现模版功能
/src/main/resources路径下新建:prompttemplate/atguigu-template.txt
讲一个关于{topic}的故事,并以{output_format}格式输出。

package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; @RestController public class PromptTemplateController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; @Value("classpath:/prompttemplate/atguigu-template.txt") private org.springframework.core.io.Resource userTemplate; /** * @Description: PromptTemplate基本使用,使用占位符设置模版 PromptTemplate * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200 */ @GetMapping("/prompttemplate/chat") public Flux<String> chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate = new PromptTemplate("" + "讲一个关于{topic}的故事" + "并以{output_format}格式输出," + "字数在{wordCount}左右"); // PromptTempate -> Prompt Prompt prompt = promptTemplate.create(Map.of( "topic", topic, "output_format",output_format, "wordCount",wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } /** * @Description: PromptTemplate读取模版文件实现模版功能 * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat2?topic=java&output_format=html */ @GetMapping("/prompttemplate/chat2") public String chat2(String topic,String output_format) { PromptTemplate promptTemplate = new PromptTemplate(userTemplate); Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", output_format)); return deepseekChatClient.prompt(prompt).call().content(); } }PromptTemplate多角色设定
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; /** * @auther [email protected] * @create 2025-07-26 16:25 * @Description TODO */ @RestController public class PromptTemplateController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; @Value("classpath:/prompttemplate/atguigu-template.txt") private org.springframework.core.io.Resource userTemplate; /** * @Description: PromptTemplate基本使用,使用占位符设置模版 PromptTemplate * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200 */ @GetMapping("/prompttemplate/chat") public Flux<String> chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate = new PromptTemplate("" + "讲一个关于{topic}的故事" + "并以{output_format}格式输出," + "字数在{wordCount}左右"); // PromptTempate -> Prompt Prompt prompt = promptTemplate.create(Map.of( "topic", topic, "output_format",output_format, "wordCount",wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } /** * @Description: PromptTemplate读取模版文件实现模版功能 * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat2?topic=java&output_format=html */ @GetMapping("/prompttemplate/chat2") public String chat2(String topic,String output_format) { PromptTemplate promptTemplate = new PromptTemplate(userTemplate); Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", output_format)); return deepseekChatClient.prompt(prompt).call().content(); } /** * @Auther: [email protected] * @Description: * 系统消息(SystemMessage):设定AI的行为规则和功能边界(xxx助手/什么格式返回/字数控制多少)。 * 用户消息(UserMessage):用户的提问/主题 * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=知识产权法 * * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=夫妻肺片 */ @GetMapping("/prompttemplate/chat3") public String chat3(String sysTopic, String userTopic) { // 1.SystemPromptTemplate SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是{systemTopic}助手,只回答{systemTopic}其它无可奉告,以HTML格式的结果。"); Message sysMessage = systemPromptTemplate.createMessage(Map.of("systemTopic", sysTopic)); // 2.PromptTemplate PromptTemplate userPromptTemplate = new PromptTemplate("解释一下{userTopic}"); Message userMessage = userPromptTemplate.createMessage(Map.of("userTopic", userTopic)); // 3.组合【关键】 多个 Message -> Prompt Prompt prompt = new Prompt(List.of(sysMessage, userMessage)); // 4.调用 LLM return deepseekChatClient.prompt(prompt).call().content(); } }

PromptTemplate人物设定
通过ChatModel实现
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; @RestController public class PromptTemplateController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; @Value("classpath:/prompttemplate/atguigu-template.txt") private org.springframework.core.io.Resource userTemplate; /** * @Description: PromptTemplate基本使用,使用占位符设置模版 PromptTemplate * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200 */ @GetMapping("/prompttemplate/chat") public Flux<String> chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate = new PromptTemplate("" + "讲一个关于{topic}的故事" + "并以{output_format}格式输出," + "字数在{wordCount}左右"); // PromptTempate -> Prompt Prompt prompt = promptTemplate.create(Map.of( "topic", topic, "output_format",output_format, "wordCount",wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } /** * @Description: PromptTemplate读取模版文件实现模版功能 * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat2?topic=java&output_format=html */ @GetMapping("/prompttemplate/chat2") public String chat2(String topic,String output_format) { PromptTemplate promptTemplate = new PromptTemplate(userTemplate); Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", output_format)); return deepseekChatClient.prompt(prompt).call().content(); } /** * @Auther: [email protected] * @Description: * 系统消息(SystemMessage):设定AI的行为规则和功能边界(xxx助手/什么格式返回/字数控制多少)。 * 用户消息(UserMessage):用户的提问/主题 * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=知识产权法 * * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=夫妻肺片 */ @GetMapping("/prompttemplate/chat3") public String chat3(String sysTopic, String userTopic) { // 1.SystemPromptTemplate SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是{systemTopic}助手,只回答{systemTopic}其它无可奉告,以HTML格式的结果。"); Message sysMessage = systemPromptTemplate.createMessage(Map.of("systemTopic", sysTopic)); // 2.PromptTemplate PromptTemplate userPromptTemplate = new PromptTemplate("解释一下{userTopic}"); Message userMessage = userPromptTemplate.createMessage(Map.of("userTopic", userTopic)); // 3.组合【关键】 多个 Message -> Prompt Prompt prompt = new Prompt(List.of(sysMessage, userMessage)); // 4.调用 LLM return deepseekChatClient.prompt(prompt).call().content(); } /** * @Description: 人物角色设定,通过SystemMessage来实现人物设定,本案例用ChatModel实现 * 设定AI为”医疗专家”时,仅回答医学相关问题 * 设定AI为编程助手”时,专注于技术问题解答 * @Auther: [email protected] * http://localhost:8006/prompttemplate/chat4?question=牡丹花 */ @GetMapping("/prompttemplate/chat4") public String chat4(String question) { //1 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个Java编程助手,拒绝回答非技术问题。"); //2 用户消息 UserMessage userMessage = new UserMessage(question); //3 系统消息+用户消息=完整提示词 //Prompt prompt = new Prompt(systemMessage, userMessage); Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); //4 调用LLM String result = deepseekChatModel.call(prompt).getResult().getOutput().getText(); System.out.println(result); return result; } }通过ChatClient实现
package com.atguigu.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; @RestController public class PromptTemplateController { @Resource(name = "deepseek") private ChatModel deepseekChatModel; @Resource(name = "qwen") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; @Value("classpath:/prompttemplate/atguigu-template.txt") private org.springframework.core.io.Resource userTemplate; /** * @Description: PromptTemplate基本使用,使用占位符设置模版 PromptTemplate * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat?topic=java&output_format=html&wordCount=200 */ @GetMapping("/prompttemplate/chat") public Flux<String> chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate = new PromptTemplate("" + "讲一个关于{topic}的故事" + "并以{output_format}格式输出," + "字数在{wordCount}左右"); // PromptTempate -> Prompt Prompt prompt = promptTemplate.create(Map.of( "topic", topic, "output_format",output_format, "wordCount",wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } /** * @Description: PromptTemplate读取模版文件实现模版功能 * @Auther: [email protected] * 测试地址 * http://localhost:8006/prompttemplate/chat2?topic=java&output_format=html */ @GetMapping("/prompttemplate/chat2") public String chat2(String topic,String output_format) { PromptTemplate promptTemplate = new PromptTemplate(userTemplate); Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", output_format)); return deepseekChatClient.prompt(prompt).call().content(); } /** * @Auther: [email protected] * @Description: * 系统消息(SystemMessage):设定AI的行为规则和功能边界(xxx助手/什么格式返回/字数控制多少)。 * 用户消息(UserMessage):用户的提问/主题 * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=知识产权法 * * http://localhost:8006/prompttemplate/chat3?sysTopic=法律&userTopic=夫妻肺片 */ @GetMapping("/prompttemplate/chat3") public String chat3(String sysTopic, String userTopic) { // 1.SystemPromptTemplate SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是{systemTopic}助手,只回答{systemTopic}其它无可奉告,以HTML格式的结果。"); Message sysMessage = systemPromptTemplate.createMessage(Map.of("systemTopic", sysTopic)); // 2.PromptTemplate PromptTemplate userPromptTemplate = new PromptTemplate("解释一下{userTopic}"); Message userMessage = userPromptTemplate.createMessage(Map.of("userTopic", userTopic)); // 3.组合【关键】 多个 Message -> Prompt Prompt prompt = new Prompt(List.of(sysMessage, userMessage)); // 4.调用 LLM return deepseekChatClient.prompt(prompt).call().content(); } /** * @Description: 人物角色设定,通过SystemMessage来实现人物设定,本案例用ChatModel实现 * 设定AI为”医疗专家”时,仅回答医学相关问题 * 设定AI为编程助手”时,专注于技术问题解答 * @Auther: [email protected] * http://localhost:8006/prompttemplate/chat4?question=牡丹花 */ @GetMapping("/prompttemplate/chat4") public String chat4(String question) { //1 系统消息 SystemMessage systemMessage = new SystemMessage("你是一个Java编程助手,拒绝回答非技术问题。"); //2 用户消息 UserMessage userMessage = new UserMessage(question); //3 系统消息+用户消息=完整提示词 //Prompt prompt = new Prompt(systemMessage, userMessage); Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); //4 调用LLM String result = deepseekChatModel.call(prompt).getResult().getOutput().getText(); System.out.println(result); return result; } /** * @Description: 人物角色设定,通过SystemMessage来实现人物设定,本案例用ChatClient实现 * 设定AI为”医疗专家”时,仅回答医学相关问题 * 设定AI为编程助手”时,专注于技术问题解答 * @Auther: [email protected] * http://localhost:8006/prompttemplate/chat5?question=火锅 */ @GetMapping("/prompttemplate/chat5") public Flux<String> chat5(String question) { return deepseekChatClient.prompt() .system("你是一个Java编程助手,拒绝回答非技术问题。") .user(question) .stream() .content(); } }八、格式化输出(Structured Output)
8.1 是什么?

8.2 开发步骤
假设我们期望将模型输出转换为Record记录类结构体,不再是传统的String。
新建子模块Module

改pom.xml
<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>com.atguigu.study</groupId> <artifactId>SpringAiAlibaba-atguiguV1</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SAA-07StructuredOutput</artifactId> <packaging>jar</packaging> <name>SAA-07StructuredOutput</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <compilerArgs> <arg>-parameters</arg> </compilerArgs> <source>17</source> <target>17</target> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> 编写yml文件
server.port=8006 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-06PromptTemplate # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api}主启动类
package com.atguigu.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Saa07StructuredOutputApplication { public static void main(String[] args) { SpringApplication.run(Saa07StructuredOutputApplication.class, args); } }业务类
配置类LLMConfig
package com.atguigu.study.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { // 模型名称常量定义 private final String DEEPSEEK_MODEL = "deepseek-v3"; private final String QWEN_MODEL = "qwen-plus"; @Bean(name = "deepseek") public ChatModel deepSeek() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build() ) .build(); } @Bean(name = "qwen") public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi(DashScopeApi.builder() .apiKey(System.getenv("aliQwen-api")) .build()) .defaultOptions( DashScopeChatOptions.builder() .withModel(QWEN_MODEL) .build() ) .build(); } @Bean(name = "deepseekChatClient") public ChatClient deepseekChatClient(@Qualifier("deepseek") ChatModel deepSeek) { return ChatClient.builder(deepSeek) .defaultOptions(ChatOptions.builder() .model(DEEPSEEK_MODEL) .build()) .build(); } @Bean(name = "qwenChatClient") public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen) { return ChatClient.builder(qwen) .defaultOptions(ChatOptions.builder() .model(QWEN_MODEL) .build()) .build(); } }新建记录类StudentRecord
package com.atguigu.study.records; /** * @Description jdk14后的新特性,记录类替代lombok */ public record StudentRecord(String id,String sname,String major,String email) { } controllerV1
package com.atguigu.study.controller; import com.atguigu.study.records.StudentRecord; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.function.Consumer; @RestController public class StructuredOutputController { @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * http://localhost:8007/structuredoutput/chat?sname=李四&[email protected] * @param sname * @return */ @GetMapping("/structuredoutput/chat") public StudentRecord chat(String sname,String email) { return qwenChatClient.prompt() .user(new Consumer<ChatClient.PromptUserSpec>() { @Override public void accept(ChatClient.PromptUserSpec promptUserSpec) { promptUserSpec.text("学号1001,我叫{sname},大学专业是计算机科学与技术,邮箱{email}") .param("sname",sname) .param("email",email); } }).call().entity(StudentRecord.class); } }
controllerV2
package com.atguigu.study.controller; import com.atguigu.study.records.StudentRecord; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.function.Consumer; @RestController public class StructuredOutputController { @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * http://localhost:8007/structuredoutput/chat?sname=李四&[email protected] * @param sname * @return */ @GetMapping("/structuredoutput/chat") public StudentRecord chat(String sname,String email) { return qwenChatClient.prompt() .user(new Consumer<ChatClient.PromptUserSpec>() { @Override public void accept(ChatClient.PromptUserSpec promptUserSpec) { promptUserSpec.text("学号1001,我叫{sname},大学专业是计算机科学与技术,邮箱{email}") .param("sname",sname) .param("email",email); } }).call().entity(StudentRecord.class); } /** * http://localhost:8007/structuredoutput/chat2?sname=孙伟&[email protected] * @param sname * @return */ @GetMapping("/structuredoutput/chat2") public StudentRecord chat2(@RequestParam(name = "sname") String sname, @RequestParam(name = "email") String email) { String" 学号1002,我叫{sname},大学专业是软件工程,邮箱{email} """; return qwenChatClient.prompt() .user(promptUserSpec -> promptUserSpec.text(stringTemplate) .param("sname",sname) .param("email",email)) .call() .entity(StudentRecord.class); } }