【2025最新最全】SpringAI Alibaba + 阿里云百炼 详细教程(上)

【2025最新最全】SpringAI Alibaba + 阿里云百炼 详细教程(上)

目录

一、SpringAI Alibaba理论概述

1.1 SSA为什么会出现?

1.2 是什么?

1.3 能干嘛?

1.4 SpringAI VS SpringAI Alibaba VS LangChain4J

二、HelloWord案例

2.1 阿里云百炼平台入口官网

接入阿里百炼平台的通义模型

大模型调用三件套

(1)获得Api-key

(2)获得模型名

(3)获得baseUrl开发地址

2.2 创建父工程

父工程

使用 bom 管理依赖版本

2.3 开发五步骤

创建Module

改pom.xml

编写yml

创建主启动类

业务类

ApiKey不可以明文需配置进环境变量

修改环境变量

K-V键值对设置

重启IDEA

配置类SaaLLMConfig

方式一

方式二

对话模型(Chat Model)

创建controller

测试

三、Ollama私有化部署和对接本地大模型

3.1 Ollama本地大模型部署

安装ollama

安装通义千问大模型

3.2 微服务对接本地大模型

创建Module

修改pom.xml

编写yml

创建主启动类

创建controller

四、ChatClient VS ChatModel

4.1 二者区别?

4.2 编码案例

创建Module

修改pom.xml

编写yml文件

主启动类

业务类第1版(只有ChatModel)

新建配置类SaaLLMConfig

创建controller

业务类第2版(只有ChatClient)

新建ChatClientController

业务类第3版(ChatModel + ChatClient混合使用)

修改配置类SaaLLMConfig

新建ChatClientControllerV2

二者对比

五、Server-SentEvents(SSE) 实现Stream流式输出及多模型共存

5.1 Response Streaming流式输出

是什么?

SpringAI Alibaba流式输出有两种

5.2 SSE(Server-Sent Events)服务器发送事件

Server-SentEvents(SSE)服务器发送事件实现流式输出

一句话

SSE适用场景

5.3 开发步骤

新建子模块Module

改POM

编写yml配置文件

主启动类

业务类

通过ChatModel实现stream实现流式输出

配置类LLMConfig

controller第1版

通过ChatClient实现stream实现流式输出

配置类LLMConfig

controller第2版

5.4 新增前端代码trytry

前端效果

Flux 本质提一嘴

SSE

index.html

存放位置

测试

六、提示词Prompt

6.1 DeepSeek提示词样例

6.2 是什么?

先从最简单的API调用说起

API 概览

再从源码Prompt说起

String

Message

Prompt

6.3 Prompt中的四大角色(Role)

system

user

assistant

tool

6.4 开发步骤

新建子模块Module

改pom.xml文件

编写yml文件

主启动类

业务类

配置类LLMConfig

controller第1版(system和user)

controller第2版(通过ChatModel实现)

controller第3版(assistant)

controller第4版(tool)

七、提示词Prompt Template

7.1 Prompt演化历程

简单纯字符串提问问题

多角色消息

占位符(Prompt Template)

7.2 提示词模板是什么

7.3 开发步骤

新建子模块Module

改pom.xml

编写yml文件

主启动类

业务类

配置类LLMConfig

重点步骤

PromptTemplate基本使用

PromptTemplate读取模版文件实现模版功能

PromptTemplate多角色设定

PromptTemplate人物设定

通过ChatModel实现

通过ChatClient实现

八、格式化输出(Structured Output)

8.1 是什么?

8.2 开发步骤

新建子模块Module

改pom.xml

编写yml文件

主启动类

业务类

配置类LLMConfig

新建记录类StudentRecord

controllerV1

controllerV2



一、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

参考我另一篇文章:https://blog.ZEEKLOG.net/qq_38196449/article/details/146884435?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228b2b864d7f97bbdfa5369a00459532a0%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8b2b864d7f97bbdfa5369a00459532a0&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-146884435-null-null.nonecase&utm_term=Ollama&spm=1018.2226.3001.4450https://blog.ZEEKLOG.net/qq_38196449/article/details/146884435?ops_request_misc=%257B%2522request%255Fid%2522%253A%25228b2b864d7f97bbdfa5369a00459532a0%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=8b2b864d7f97bbdfa5369a00459532a0&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-146884435-null-null.nonecase&utm_term=Ollama&spm=1018.2226.3001.4450

安装通义千问大模型

ollama run qwen:4b

3.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); } }

Read more

旧电脑 Win7 复活计划:编译与运行 llama.cpp (Qwen3版)

旧电脑 Win7 复活计划:编译与运行 llama.cpp (Qwen3版)

🦕 旧电脑 Win7 复活计划:编译与运行 llama.cpp (Qwen3版) 这份指南专为不支持新版软件的 Windows 7 设计,通过本地编译实现大模型运行。 手动编译可以获得最好的性能,不想自己手动编译 可以直接使用下面编译好的bin文件,同时包含下面用到的相关软件和替换文件httplib.h 链接:https://pan.quark.cn/s/2c5f627c93d7 提取码:cSJh 📋 0. 软件版本清单 请务必确保使用以下特定版本,以保证在 Win7 下的兼容性: 软件名称文件名 (根据截图)作用备注编译环境w64devkit-x64-2.5.0.7z.exe提供 GCC 编译器核心工具构建工具cmake-3.31.10-windows-x86_64.msi生成编译配置必须安装到默认路径源码工具Git_for_Windows_(64bit)_v2.45.

AI绘画+电商:用图片和视频驱动未来电商

过去三年里,AI绘画从实验室走向大众,从简单模仿到艺术创作。如今,这项技术正悄然改变着一个万亿美元级的行业——电子商务。当AI绘画遇上电商,一场深刻的视觉革命正在拉开帷幕。 视觉冲击力:电商转化的第一道门槛 在电商平台上,消费者无法触摸实物,视觉呈现成为购买决策的关键因素。研究表明: * 高质量产品图能将转化率提升30-50% * 视频展示的商品比仅用图片的商品多获得157%的点击率 * 87%的线上消费者认为产品图片是购物决策的重要因素 然而,高质量视觉内容的制作传统上面临三大挑战:成本高、周期长、创意匮乏。专业摄影、模特拍摄、后期修图,每个环节都需要大量时间和资金投入,对小企业和新兴品牌尤为不友好。 AI绘画技术:视觉内容的民主化革命 AI绘画技术的突破性进展正在改变这一局面。以Midjourney、Stable Diffusion、DALL-E 3为代表的一批AI绘画工具,让高质量视觉内容的创作变得前所未有地简单和高效。 四大核心应用场景: 1. 产品视觉优化与扩展 * 一键生成专业级产品展示图 * 自动扩展产品使用场景(如咖啡机在不同厨房环境中的

用 C# 扩展 Dynamics 365 Copilot:自定义插件与场景

Dynamics 365 Copilot 作为基于 AI 的智能助手,为企业用户提供了自动化流程、智能分析和自然语言交互的能力,但通用功能往往无法满足特定行业或企业的定制化需求。本文将详细介绍如何通过 C# 编写自定义插件,扩展 Dynamics 365 Copilot 的能力,并结合实际业务场景实现定制化 AI 交互。 一、核心基础:Dynamics 365 Copilot 扩展架构 Dynamics 365 Copilot 的扩展主要依赖于 Power Platform 插件框架 和 Copilot Studio 的自定义连接器,核心技术栈包括: * C# (.NET Framework 4.8 或 .NET 6+):编写业务逻辑插件 * Dynamics 365 SDK:

智能创作与优化新时代:【ChatGPT-4o】在【数学建模】、【AI绘画】、【海报设计】与【论文优化】中的创新应用

智能创作与优化新时代:【ChatGPT-4o】在【数学建模】、【AI绘画】、【海报设计】与【论文优化】中的创新应用

目录 1. 引言 什么是ChatGPT4o? 背景与发展历史 2.chatgpt4o数学建模 常见的数学建模专业术语及其简要说明 一个具体的代码例子 问题描述 代码实现  代码说明 运行结果 3.chatgpt4o在论文 1.例如生成基于标签的推荐系统模型及算法研究  1. 摘要 2. 引言 3. 文献综述 4. 模型与算法 5. 实验与分析 6. 结论与展望 7. 参考文献 案例背景 2.具体应用场景 1. 摘要优化 原稿: ChatGPT优化后的版本: 优化点: 2. 引言部分的结构优化 原稿: ChatGPT优化后的版本: 优化点: 3. 方法部分的细化与完善 原稿: ChatGPT优化后的版本: 4. 结论的增强