【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

OpenClaw本地部署接入飞书机器人完全安装指南

OpenClaw本地部署接入飞书机器人完全安装指南

作者:网心 2026-3-10 在 Windows 系统上从头开始部署 OpenClaw,并将其配置为可以接入飞书的智能机器人。我们将以实战中遇到的问题为鉴,确保安装过程顺畅无误。 第一章:准备工作与环境检查 在正式开始安装前,请确保您的电脑满足以下基础条件,并理解我们将要使用的关键命令。 1. 系统要求 操作系统: Windows 10 或 Windows 11 (需使用管理员权限运行 PowerShell)。 网络环境: 能够正常访问 GitHub 和 npm 仓库。如果您在网络受限的环境中,可能需要提前准备代理或镜像配置。 2. 核心命令解释 在整个安装过程中,有两个核心命令您需要理解: 一键安装命令:iwr -useb https://openclaw.ai/install.ps1 | iex iwr:Invoke-WebRequest 的别名,用于从指定网址下载文件。

Magic API:低代码接口开发平台完全指南

Magic API:低代码接口开发平台完全指南

Magic API:低代码接口开发平台完全指南 🌟 你好,我是 励志成为糕手 ! 🌌 在代码的宇宙中,我是那个追逐优雅与性能的星际旅人。 ✨ 每一行代码都是我种下的星光,在逻辑的土壤里生长成璀璨的银河; 🛠️ 每一个算法都是我绘制的星图,指引着数据流动的最短路径; 🔍 每一次调试都是星际对话,用耐心和智慧解开宇宙的谜题。 🚀 准备好开始我们的星际编码之旅了吗? 目录 * Magic API:低代码接口开发平台完全指南 * 摘要 * 1. Magic API概述与核心概念 * 1.1 什么是Magic API * 1.2 Magic API的核心特性 * 1.3 Magic API的设计理念 * 2. Magic API架构设计与组件分析 * 2.1 整体架构概览 * 2.2 API引擎工作原理 * 2.3 脚本引擎与SQL执行机制 * 3. Magic API核心功能实现

基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的跌倒检测系统(千问+DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)

基于YOLOv8/YOLOv10/YOLOv11/YOLOv12与SpringBoot的跌倒检测系统(千问+DeepSeek智能分析+web交互界面+前后端分离+YOLO数据)

项目摘要 本项目旨在设计并实现一个高效、智能且用户友好的基于多版本YOLO深度学习模型与SpringBoot Web框架的实时跌倒检测系统。随着全球老龄化社会的加速到来,老年人在日常生活中发生跌倒的风险日益增高,及时、准确地检测跌倒事件对于保障其生命安全与健康具有重大社会意义。传统监控或穿戴式设备存在隐私侵扰、用户体验不佳或漏报率高等局限。因此,本项目融合了当前前沿的计算机视觉技术与现代Web开发架构,构建了一个集智能分析、实时监控、数据管理与远程交互于一体的综合性解决方案。 系统的核心检测引擎采用了性能卓越的YOLO系列目标检测算法,并创新性地集成了YOLOv8、YOLOv10、YOLOv11及YOLOv12四种最新版本模型,为用户提供了灵活、可对比的算法选择,以适应不同的精度与速度需求。模型在精心标注的自定义数据集上进行训练与验证,该数据集包含 ‘fallen’(已跌倒)、‘falling’(正在跌倒)和‘stand’(站立/正常) 三个关键类别,共计3,888张图像(训练集3,594张,验证集294张),确保了系统对跌倒过程动态的精确识别能力。 系统后端采用SpringB

科哥OCR WebUI太香了!紫蓝渐变界面操作丝滑又高效

科哥OCR WebUI太香了!紫蓝渐变界面操作丝滑又高效 1. 这不是又一个OCR工具,而是一次体验升级 你有没有过这样的经历: 花半小时配环境、改路径、调参数,终于跑通一个OCR模型,结果打开网页——灰扑扑的Gradio默认界面,按钮挤在角落,上传框像上世纪的网页表单,检测完还得手动翻JSON看坐标? 科哥做的这个cv_resnet18_ocr-detection WebUI,彻底改写了这个剧本。 它没有堆砌炫技功能,却把“好用”刻进了每一处交互细节:紫蓝渐变的标题栏不刺眼、悬停有微光反馈、检测进度条带实时耗时显示、结果文本一键全选复制、坐标数据自动格式化为可读结构……这不是给工程师看的调试面板,而是为真实使用者设计的工作台。 更关键的是——它足够轻量。 不用GPU也能跑,CPU上单图检测3秒出结果;模型基于ResNet18轻量架构,显存占用低,连GTX 1060都能稳稳扛住批量处理;所有功能模块(单图/批量/训练/导出)都封装在同一个Web界面里,无需切终端、不用记命令,点几下就完成从前要写脚本才能做的事。 这篇文章不讲模型结构、