4.Spring Ai Alibaba

4.Spring Ai Alibaba

明廷盛 嘻嘻😁

第一章 ChatModel

Spring AI Alibaba官网: https://java2ai.com/docs/versions

第一节 父工程

整体项目目录结构
image.png|375

STEP1: 创建普通的Maven项目

注意:不要用SpringInintilizer去创建

STEP2: 修改pom.xml

  • 位置: ~/pom.xml 【共计8处修改】
<?xml version="1.0" encoding="UTF-8"?>
<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.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging> <!--1-->

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--版本要对应 SpringBoot/SpringAI/SpringAIAlibaba-->
<java.version>21</java.version> <!--2-->
<spring-boot.version>3.5.5</spring-boot.version> <!--3-->
<spring-ai.version>1.0.0</spring-ai.version> <!--4-->
<SpringAIAlibaba.version>1.0.0.2</SpringAIAlibaba.version> <!--5-->
</properties>

<!--6.三个依赖-->
<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 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.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>

</dependencies>
</dependencyManagement>
<!--7.build插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
<!--8.用Alibaba的仓库,原来的镜像没有SpringAIAlibaba-->
<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>

第二节 子工程

STEP1: 创建子model

STEP2: 修改子pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>SAA01_HelloWorld</artifactId>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</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>21</source>
<target>21</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>

STEP3: application.properties

①Key获取
image.png|350
②Model获取
image.png|275

③URL获取
image.png|275
④免费额度用完即停
image.png
  • 位置: resources/application.properties
server.port=8080

#大模型对话中文乱码UTF8编码处理
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA01_HelloWorld

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=qwen-plus

STEP4: SpringBoot的启动类

  • 位置: java/com/mts/SAA01HelloWorldApplication.java
package com.mts;

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

STEP5: 配置类注入ChatModel

  • 位置: java/com/mts/config/SaaLLMConfig.java
  • 注意: 注意包! 注意包! 注意包! 看清楚是SpringAi的还是DasScope的
package com.mts.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 {

@Value("${spring.ai.dashscope.api-key}")
private String apiKey;

@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder().apiKey(apiKey).build();
}
}

STEP6: 编写Controller进行测试

  • 说明: 我这里前后端传递做了封装, 简单测试就返回String即可
  • 位置: java/com/mts/Controller/ChatHelloController.java
package com.mts.Controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.mts.common.response.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/hello")
public class ChatHelloController {

@Autowired
private DashScopeChatModel dashScopeChatModel; // ChatModel的实现类

/**
* call: 非流式返回
*
* @param msg
* @return
*/
@GetMapping("/dochat")
public ResponseResult<String> doChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
String resContent = dashScopeChatModel.call(msg);
return ResponseResult.ok(resContent);
}

/** stream: 流式返回
*
* @param msg
* @return
*/
@GetMapping("/streamchat")
public Flux<String> streamChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
Flux<String> resStream = dashScopeChatModel.stream(msg);
return resStream;
}
}

第二章 调用本地模型Ollama

第一节 Ollama本地大模型部署

STPE1: 下载Ollama软件

官网: https://ollama.com/download/windows

STEP2: 配置环境变量

image-20250306201749040

STEP3: 下载deepseek

  • 直接cmd, 输入会自动下载
image-20250306201323644|450
ollama pull deepseek-r1:1.5b

第二节 Ollama常用命令

指令 作用
ollama pull xxx 拉取模型
ollama ps 后台正在运行的实例
ollama list 查看下载的本地模型
ollama run xxx 运行xxx模型在后台
ollama stop xxx 停止xxx模型在运行

第三节 对接本地大模型

STEP1: 创建子model

STEP2: 修改子pom.xml

  • 位置:~/pom.xml 【共计修改1处】
<?xml version="1.0" encoding="UTF-8"?>
<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.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>SAA02_Ollama</artifactId>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</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>

<!--!!!!!!!!修改1处: 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>21</source>
<target>21</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>

STEP3: application.properties

  1. Ollama端口: 11434
  2. model的名称: 自己去ollama list 查看
server.port=8080

#中文UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA01_Ollama

# ====ollama Config =============
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=deepseek-r1:1.5b

STEP4: SpringBoot的启动类

STEP5: 编写Controller进行测试

  • 位置: java/com/mts/Controller/ChatHelloController.java
  • 注意: SpringAIAlibaba有dashscopeChatModel和ollamaChatModel两个ChatModel(接口)实现, 分别对应 远程调用 和 本地调用, so要用Qualifier去指定
package com.mts.controller;

import com.mts.common.response.ResponseResult;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/ollama")
public class OllamaController {

// SpringAIAlibaba有dashscopeChatModel和ollamaChatModel两个ChatModel(接口)实现
// 分别对应 远程调用 和 本地调用 // so要用Qualifier去指定
@Autowired
@Qualifier("ollamaChatModel")
private ChatModel chatModel;

@GetMapping("/ollama/chat")
public ResponseResult<String> chat(@RequestParam(name = "message") String msg) {
String result = chatModel.call(msg);
return ResponseResult.ok(result);
}

@GetMapping("/ollama/streamchat")
public Flux<String> streamchat(@RequestParam(name = "message", defaultValue = "你是谁") String msg) {
return chatModel.stream(msg);
}

}

STEP6: cmd运行Ollama模型

  • 注意: 要cmd运行了对应的模型, 才能有回复
ollama run deepseek-r1:1.5b

第三章 ChatClient

第一节 ChatClient 🆚 ChatModel

ChatModel ChatClient
注入形式 自动注入 手动注入且依赖ChatModel,使用自动配置的 ChatClient.Builder
调用方式 直接调用call()或者stream()方法 链式调用,支持同步和反应式(Reactive)编程模型,自动封装提示词和解析响应
结构化输出 需手动解析响应文本 支持自动映射为java对象
适用场景 实现简单功能和场景 快速开发复杂功能的场景,如企业级智能XXX问答系统
功能扩展 偏弱 强,支持聊天记忆(Chat Memory)/工具Tool/函数调用(Function Calling)/RAG等等

第二节 如何获取ChatClient

image.png|500
有两个默认实现(①远程模型 ②本地模型)
image.png|300
这个Default其实是null来的
image.png|900
@Autowired
private DashScopeChatModel dashScopeChatModel; // ChatModel的实现类

private ChatClient chatClient; // 报错!!!! 如图3

第三节 通过构造方法Builder注入

  • 分析: 可以看到ChatClient不是@Autowired注入的, 而是构造方法中build出来的!
package com.mts.Controller;

import com.mts.common.response.ResponseResult;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/chatclient")
public class ChatClientController {


private ChatClient chatClient;

// !!!! 构造方法注入;
public ChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
}

// 非流输出
@GetMapping("/dochat")
public ResponseResult<String> doChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
String resContent = chatClient.prompt().user(msg).call().content();
return ResponseResult.ok(resContent);
}

// 流输出
@GetMapping("/streamchat")
public Flux<String> streamChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
Flux<String> resContent = chatClient.prompt().user(msg).stream().content();
return resContent;
}

}

第四节 通过配置类注入ChatClient

  • 易混: 你第一章/第一节/STEP5不也写的”配置类注入ChatModel”吗? 那不一样? ChatChatModel也需要注入?
  • 回答: 不不不! 那里至少注入apikey, 让DashScopChatModel拿个apikey; 而这里是用配置类build一个ChatClient然后注入Bean
  • 位置: java/com/mts/config/SaaLLMConfig.java 【共计4处修改】
package com.mts.config;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

@Value("${spring.ai.dashscope.api-key}")
private String apiKey;

@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder().apiKey(apiKey).build();
}

@Bean // 1
public ChatClient chatClient(ChatModel chatModel) { // 2
return ChatClient.builder(chatModel).build(); // 3
} // 4
}


  • 位置: 任意Controller中;
  • 如何用: 直接@Autowired即可
@Autowired
private ChatClient dashScopechatClientv2;

第四章 多模型共存

第一节 ChatModel配置多模型共存

STEP1: 创建子Model

STEP2: 修改子pom.xml 【同第一章】

STEP3: applicant.properties

  • 位置: resourcesapplicant/properties 【删除两行】
  • 分析: 因为是多个模型, 所以不在配置文件指定
server.port=8080

#?????????UTF8????
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA01_HelloWorld

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
# spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
# spring.ai.dashscope.chat.options.model=qwen-plus

STEP4: SpringBoot启动类

STEP5: 配置类注入多个ChatModel

  • 位置: java/com/mts/config/SaaLLMConfig.java
package com.mts.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.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
// 模型名称
private final String DEEPSEEKV3_2 = "deepseek-v3.2";
private final String DEEPSEEKVR1 = "deepseek-r1";

@Bean
public ChatModel deepSeekV3_2() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKV3_2).build())
.build();
}

@Bean
public ChatModel deepSeekR1() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKVR1).build())
.build();
}

}

STEP6: 编写Controller进行测试

  • 位置: java/src/main/java/com/mts/controller/MulChatModelController.java
package com.mts.controller;

import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/mulchatmodel")
public class MulChatModelController {

@Autowired
@Qualifier("deepSeekV3_2")
private ChatModel deepSeekV3_2;

@Autowired
@Qualifier("deepSeekR1")
private ChatModel deepSeekR1;

@GetMapping("/v3stream")
public Flux<String> v3modelChat(@RequestParam(value = "message") String msg) {return deepSeekV3_2.stream(msg);}

@GetMapping("/r1stream")
public Flux<String> r1modelChat(@RequestParam(value = "message") String msg) {return deepSeekR1.stream(msg);}
}

第二节 ChatClient配置多模型共存

STEP5: 配置类注入多个ChatChatClient

  • 位置: 同上【共11行修改】
  • 注意: 要现有ChatModel再有ChatClient
package com.mts.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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
// 模型名称
private final String DEEPSEEKV3_2 = "deepseek-v3.2";
private final String DEEPSEEKVR1 = "deepseek-r1";

@Bean
public ChatModel deepSeekV3_2() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKV3_2).build())
.build();
}

@Bean
public ChatModel deepSeekR1() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKVR1).build())
.build();
}

@Bean // 1
public ChatClient deepSeekV3_2ChatModel(@Qualifier("deepSeekV3_2") ChatModel deepSeekV3_2ChatModel) { // 2
return ChatClient.builder(deepSeekV3_2ChatModel) // 3
.defaultOptions(ChatOptions.builder().model(DEEPSEEKV3_2).build()) // 4
.build(); // 5
} // 6

@Bean
public ChatClient deepSeekR1ChatModel(@Qualifier("deepSeekR1") ChatModel deepSeekR1ChatModel) { // 7
return ChatClient.builder(deepSeekR1ChatModel) // 8
.defaultOptions(ChatOptions.builder().model(DEEPSEEKVR1).build()) // 9
.build(); // 10
} // 11

}

STEP6: 编写Controller进行测试

package com.mts.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/mulchatclient")
public class MulChatClientController {

@Autowired
@Qualifier("deepSeekV3_2ChatModel")
private ChatClient deepSeekV3_2ChatModel;

@Autowired
@Qualifier("deepSeekR1ChatModel")
private ChatClient deepSeekR1ChatModel;

@GetMapping("/v3stream")
public Flux<String> v3modelChat(@RequestParam(value = "message") String msg) {
return deepSeekV3_2ChatModel.prompt().user(msg).stream().content();
}

@GetMapping("/r1stream")
public Flux<String> r1modelChat(@RequestParam(value = "message") String msg) {
return deepSeekR1ChatModel.prompt().user(msg).stream().content();
}
}

第五章 提示词

官方文档: https://java2ai.com/docs/frameworks/agent-framework/tutorials/messages

第一节 四大提示词角色

image.png|575

角色 作用 通俗解释
USER 代表用户的输入,包括问题、命令或陈述,是AI响应的基础 用户的输入
ASSISTANT AI对用户输入的响应,维持对话流程,可能包含函数工具调用请求 ai的回复
SYSTEM 在对话开始前设定AI的身份、行为准则、输出风格或知识边界,用户不可见但深刻影响AI响应 ai知识边界
TOOL 响应工具调用助手消息返回附加信息,用于执行计算、获取数据等特定功能 ai可以调用哪些方法?

第二节 ChatModel传入三种提问

传入 .call返回类型 .stream返回类型 其他
String 直接传入 String Flux<String>
Message String->Message String Flux<String>
Prompt String->Message->Prompt ChatResponse Flux<ChatResponse> 需要清洗为String
image.png
  • 灵活程度: Prompt > Message > String 简单的文本字符串提问
  • 一般本文示例
// 1. 传入普通String
@GetMapping("/chat/string")
public String chatWithString(String msg) {
String result = chatModel.call(msg); // STEP1:直接传入字符串
return result;
}
// 2. 传入Message
@GetMapping("/chat/message")
public String chatWithMessage(String msg) {
UserMessage userMessage = new UserMessage(msg); // STEP1: 将String转换为UserMessage
String response = chatModel.call(userMessage); // STEP2:调用ChatModel
return response;
}
  • 注意清洗: response.getResult().getOutput().getText();
// 3. 传入Prompt
@GetMapping("/chat/prompt")
public String chatWithPrompt(String msg) {
UserMessage userMessage = new UserMessage(msg); // STEP1: 将String包装为UserMessage
Prompt prompt = new Prompt(userMessage); // STEP2: 创建Prompt
ChatResponse response = chatModel.call(prompt); // STEP3: 调用ChatModel
return response.getResult().getOutput().getText();
}

@GetMapping("/chat/stream/string")
public Flux<String> chatStreamString(String msg) {
return chatModel.stream(msg)
}
@GetMapping("/chat/stream/message")
public Flux<String> chatStreamMessage(String msg) {
UserMessage userMessage = new UserMessage(msg);
Flux<String> response = chatModel.stream(userMessage);
return response;
}
  • 注意清洗: return response.map(res -> res.getResults().get(0).getOutput().getText());
@GetMapping("/chat/stream/prompt")
public Flux<String> chatStreamPrompt(String msg) {
SystemMessage systemMessage = new SystemMessage("你只能讲故事,其他的问题无可奉告"); // 系统角色
UserMessage userMessage = new UserMessage(msg); // STEP1: String->Message
Prompt prompt = new Prompt(userMessage, systemMessage); // STEP2: Message->Prompt
Flux<ChatResponse> response = chatModel.stream(prompt); // STEP3: Prompt放入ChatModel
return response.map(res -> res.getResults().get(0).getOutput().getText()); // 清洗数据 返回String流数据
}

第三章 ChatClient传入三种提问

  • 说明:
    1. 可以传入system角色约束, 可以可以不传递不是必须; 但是user()是必须的
    2. 如下是ChatMode和ChatClient的对比; 个人比较喜欢CahtClient,
    3. 对比ChatMode, ChatClient链式调用比较固定, 都必须写,结构统一比较好记;
chatClient.prompt().system().user().assistant().tools().messages().options().advisors().call().content();
.strea()
1. prompt(): 构建提示词[必须第一个]
2. system(): 四大角色(ai角色边界)
3. user(): 四大角色(用户发出消息)
4. assistant(): 四大角色(ai返回消息)
5. tools(): 四大角色(ai调用方法)
6. messages(): 批量添加消息
7. options(): 配置模型参数
8. advisors(): 输出处理建议器
9. call().content(): 返回String类型
stream().content(): 返回Flux<String>类型

image.png

// 1. 普通的String文本 - 非流式
// 测试端口: http://localhost:8080/chatclient/string/call?message=1+1等于多少?
@GetMapping("/string/call")
public String stringCall(@RequestParam(value = "message") String msg) {
return chatClient.prompt() // STEP1: 创建prompt构建器
.user(msg) // STEP2: 添加用户消息
.call() // STEP3: 调用call方法
.content(); // STEP4: 获取内容
}
// 2. Message对象 - 非流式
// 测试端口: http://localhost:8080/chatclient/message/call?message=ai的全称
@GetMapping("/message/call")
public String messageCall(@RequestParam(value = "message") String msg) {
UserMessage userMessage = new UserMessage(msg);
String userMessageText = userMessage.getText(); // 提取消息文本
return chatClient.prompt() // STEP1: 创建prompt构建器
.user(userMessageText) // STEP2: 添加用户消息
.call() // STEP3: 调用call方法
.content(); // STEP4: 获取内容
}
// 3. Prompt对象 - 非流式
// 测试端口: http://localhost:8080/chatclient/prompt/call?message=给我讲一个关于友谊的故事
@GetMapping("/prompt/call")
public String promptCall(@RequestParam(value = "message") String msg) {
SystemMessage systemMessage = new SystemMessage("你只能讲故事,其他的问题无可奉告");
UserMessage userMessage = new UserMessage(msg);
Prompt prompt = new Prompt(userMessage, systemMessage);

return chatClient.prompt() // STEP1: 创建prompt构建器
.system(systemMessage.getText()) // STEP2: 添加系统提示
.user(userMessage.getText()) // STEP3: 添加用户消息
.call() // STEP4: 调用call方法
.content(); // STEP5: 获取内容
}

// 1. 普通的String文本 - 流式
// 测试端口: http://localhost:8080/chatclient/string/stream?message=你好,请做一个自我介绍
@GetMapping("/string/stream")
public Flux<String> stringStream(@RequestParam(value = "message") String msg) {
return chatClient.prompt() // STEP1: 创建prompt构建器
.user(msg) // STEP2: 添加用户消息(字符串)
.stream() // STEP3: 调用stream方法
.content(); // STEP4: 获取内容流
}
// 2. Message对象 - 流式(需要手动转换为字符串)
// 测试端口: http://localhost:8080/chatclient/message/stream?message=请用中文写一个关于大海
@GetMapping("/message/stream")
public Flux<String> messageStream(@RequestParam(value = "message") String msg) {
UserMessage userMessage = new UserMessage(msg); // STEP1: 创建UserMessage
String userMessageText = userMessage.getText(); // STEP2: 提取消息文本
return chatClient.prompt() // STEP3: 创建prompt构建器
.user(userMessageText) // STEP4: 添加用户消息(字符串形式)
.stream() // STEP5: 调用stream方法
.content(); // STEP6: 获取内容流
}
// 3. Prompt对象 - 流式
// 测试端口: http://localhost:8080/chatclient/prompt/stream?message=请讲一个关于勇敢的小兔子的
@GetMapping("/prompt/stream")
public Flux<String> promptStream(@RequestParam(value = "message") String msg) {
SystemMessage systemMessage = new SystemMessage("你只能讲故事,其他的问题无可奉告");
UserMessage userMessage = new UserMessage(msg);
Prompt prompt = new Prompt(userMessage, systemMessage); // STEP1: 创建Prompt对象

return chatClient.prompt() // STEP2: 创建prompt构建器
.system(systemMessage.getText()) // STEP3: 添加系统提示(字符串形式)【可选】
.user(userMessage.getText()) // STEP4: 添加用户消息(字符串形式)
.stream() // STEP5: 调用stream方法
.content(); // STEP6: 获取内容流
}

第四章 提示词模板

  • 介绍: 提示词模板是一个预先设计好的文本框架,其中包含占位符,可以通过填充具体内容来动态生成最终提示词。
package com.mts.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.Map;

@RestController
@RequestMapping("/prompt/template")
public class PromptTemplateController {

@Autowired
private ChatClient chatClient;

// http://localhost:8080/prompt/template/resume?name=张三&email=1434234234@qq.com&post=java算法&salary=12k
@GetMapping("/resume")
public Flux<String> resumeGenerate(String name, String email, String post, String salary) {
// STEP1: PromptTemplate先把格式告诉ai
PromptTemplate promptTemplate = new PromptTemplate("这是应聘的简历你,你无需在意是否规范,直接给我返回html格式的简历即可" +
",包括名称{name},邮箱{email},岗位{post},以及期望薪资{salary}");
// STEP2: 再告诉他你格式中的占位符{},具体是什么数据
Prompt prompt = promptTemplate.create(Map.of(
"name", name, "email", email,
"post", post, "salary", salary));
return chatClient.prompt(prompt).stream().content();
}
}

第五章 格式化输出

  • 介绍: 这是一个将AI回复自动转换为Java对象(推荐)/XML/JSON
  • 位置: com.mts.record.StudentRecord
package com.mts.record;

// Record = Class+Lombok的@Data
public record StudentRecord(String sname, String sid, String semail, String smajor) {}
  • 位置: 任意Controller
package com.mts.controller;

import com.mts.record.StudentRecord;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/structoutput")
public class StructuredOutputController {

@Autowired
private ChatClient chatClient;

// http://localhost:8080/structoutput/chat?sid=1001&sname=张三&email=1423412@qq.com&smajor=软件工程
@GetMapping("/chat")
public StudentRecord chat(String sid, String sname, String email, String smajor) {
return chatClient.prompt()
.user(promptUserSpec -> promptUserSpec.text("学号{sid},我叫{sname},大学专业是{smajor},邮箱{email}")
.param("sname", sname).param("sid", sid)
.param("email", email).param("smajor", smajor))
.call().entity(StudentRecord.class);
}

}

第六章 对话持久化

第一章 实现步骤

STEP1: 创建子Model

STEP2: 修改pom.xml

  • 位置: ~/pom.xml 【共计两个jar包修改, 9行修改】
  • 说明: ①Alibaba和SpringAi和Redis的jar包 ②Redis池; Jedis
    image.png
<!--spring-ai-alibaba memory-redis-->                                   <!--1-->
<dependency> <!--2-->
<groupId>com.alibaba.cloud.ai</groupId> <!--3-->
<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId> <!--4-->
</dependency> <!--5-->
<!--jedis--> <!--6-->
<dependency> <!--7-->
<groupId>redis.clients</groupId> <!--8-->
<artifactId>jedis</artifactId> <!--9-->

STEP3: applictaoin.properties

  • 说明: 配置redis信息
server.port=8080

# ======UTF-8===========
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA01_HelloWorld

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228

# ====Redis Config =====================
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.connect-timeout=3
spring.data.redis.timeout=2

STEP4: 配置类

  • 分析: Spring AI持久化的接口为ChatMemoryRepository, 其下有两个实现类, Memory那个是内存的, 重启服务就没了;
image.png|475 image.png|281
package com.mts.config;

import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedisMemoryConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private String port;

// 注册ChatMemoryRepository的Redis实现类
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository() {
return RedisChatMemoryRepository.builder()
.host(host)
.port(port)
.build();
}
}
  • 共计6处修改
package com.mts.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 com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

// apikey
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
//模型
private final String QWENPLUS = "qwen-plus";

@Bean
public ChatModel chatModel() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWENPLUS).build())
.build();
}

@Bean
public ChatClient chatClient(ChatModel chatModel, RedisChatMemoryRepository redisChatMemoryRepository) { // 6
MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder() // 1
.chatMemoryRepository(redisChatMemoryRepository) // 2
.maxMessages(10) // 最长留存上下文 // 3
.build(); // 4

return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder().model(QWENPLUS).build())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()) // 5
.build();
}
}

STEP5: 编写Controller进行测试

package com.mts.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;

@RestController
@RequestMapping("/memory")
public class MemoryController {

@Autowired
private ChatClient chatClient;

// 测试
@GetMapping("/streamchat")
public Flux<String> doChat(String message) {
return chatClient.prompt().user(message).stream().content();
}

/**
* @param message 用户本轮发来的消息
* @param contentId 用户所在对话的对话id
* @return
*/
@GetMapping("/withmemory")
public Flux<String> withMemory(String message, String contentId) {
return chatClient.prompt().user(message)
.advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID, contentId)) // 上下文
.stream().content();
}
}

第七章 向量数据库

第一节 下载Docker

参考教程: https://www.runoob.com/docker/windows-docker-install.html

STEP1: 下载

第二节 实现步骤

STEP1: 创建子Model

STEP2: 修改pom.xml

<!--spring-ai-alibaba dashscope-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- 添加 Redis 向量数据库依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

STEP3:

  • Title: 4.Spring Ai Alibaba
  • Author: 明廷盛
  • Created at : 2026-04-09 14:08:38
  • Updated at : 2026-04-08 16:42:00
  • Link: https://blog.20040424.xyz/2026/04/09/⚔️实战项目/2. 毕设/4.Spring Ai Alibaba/
  • License: All Rights Reserved © 明廷盛
On this page
4.Spring Ai Alibaba