第一章 ChatModel
Spring AI Alibaba官网: https://java2ai.com/docs/versions
第一节 父工程
整体项目目录结构

|
STEP1: 创建普通的Maven项目
注意:不要用SpringInintilizer去创建
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>
<groupId>com.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
<spring-boot.version>3.5.5</spring-boot.version>
<spring-ai.version>1.0.0</spring-ai.version>
<SpringAIAlibaba.version>1.0.0.2</SpringAIAlibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${SpringAIAlibaba.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>
第二节 子工程
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>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<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获取
 |
②Model获取
 |
 |
③URL获取

|
④免费额度用完即停

|
- 位置: resources/application.properties
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_HelloWorld
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;
@GetMapping("/dochat")
public ResponseResult<String> doChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
String resContent = dashScopeChatModel.call(msg);
return ResponseResult.ok(resContent);
}
@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: 配置环境变量
STEP3: 下载deepseek
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
<?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>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<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
- Ollama端口: 11434
- model的名称: 自己去
ollama list 查看
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_Ollama
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 {
@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模型
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
 有两个默认实现(①远程模型 ②本地模型) |
 这个Default其实是null来的 |

|
@Autowired
private DashScopeChatModel dashScopeChatModel;
private ChatClient chatClient;
第三节 通过构造方法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
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
- 位置: 任意Controller中;
- 如何用: 直接
@Autowired即可
@Autowired
private ChatClient dashScopechatClientv2;
第四章 多模型共存
第一节 ChatModel配置多模型共存
STEP1: 创建子Model
STEP2: 修改子pom.xml 【同第一章】
STEP3: applicant.properties
- 位置: resourcesapplicant/properties 【删除两行】
- 分析: 因为是多个模型, 所以不在配置文件指定
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_HelloWorld
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
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
public ChatClient deepSeekV3_2ChatModel(@Qualifier("deepSeekV3_2") ChatModel deepSeekV3_2ChatModel) {
return ChatClient.builder(deepSeekV3_2ChatModel)
.defaultOptions(ChatOptions.builder().model(DEEPSEEKV3_2).build())
.build();
}
@Bean
public ChatClient deepSeekR1ChatModel(@Qualifier("deepSeekR1") ChatModel deepSeekR1ChatModel) {
return ChatClient.builder(deepSeekR1ChatModel)
.defaultOptions(ChatOptions.builder().model(DEEPSEEKVR1).build())
.build();
}
}
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
第一节 四大提示词角色

| 角色 |
作用 |
通俗解释 |
| 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 |
- 灵活程度: Prompt > Message > String 简单的文本字符串提问
- 一般本文示例
@GetMapping("/chat/string")
public String chatWithString(String msg) {
String result = chatModel.call(msg);
return result;
}
@GetMapping("/chat/message")
public String chatWithMessage(String msg) {
UserMessage userMessage = new UserMessage(msg);
String response = chatModel.call(userMessage);
return response;
}
- 注意清洗:
response.getResult().getOutput().getText();
@GetMapping("/chat/prompt")
public String chatWithPrompt(String msg) {
UserMessage userMessage = new UserMessage(msg);
Prompt prompt = new Prompt(userMessage);
ChatResponse response = chatModel.call(prompt);
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);
Prompt prompt = new Prompt(userMessage, systemMessage);
Flux<ChatResponse> response = chatModel.stream(prompt);
return response.map(res -> res.getResults().get(0).getOutput().getText());
}
第三章 ChatClient传入三种提问
- 说明:
- 可以传入system角色约束, 可以可以不传递不是必须; 但是user()是必须的
- 如下是ChatMode和ChatClient的对比; 个人比较喜欢CahtClient,
- 对比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>类型

@GetMapping("/string/call")
public String stringCall(@RequestParam(value = "message") String msg) {
return chatClient.prompt()
.user(msg)
.call()
.content();
}
@GetMapping("/message/call")
public String messageCall(@RequestParam(value = "message") String msg) {
UserMessage userMessage = new UserMessage(msg);
String userMessageText = userMessage.getText();
return chatClient.prompt()
.user(userMessageText)
.call()
.content();
}
@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()
.system(systemMessage.getText())
.user(userMessage.getText())
.call()
.content();
}
@GetMapping("/string/stream")
public Flux<String> stringStream(@RequestParam(value = "message") String msg) {
return chatClient.prompt()
.user(msg)
.stream()
.content();
}
@GetMapping("/message/stream")
public Flux<String> messageStream(@RequestParam(value = "message") String msg) {
UserMessage userMessage = new UserMessage(msg);
String userMessageText = userMessage.getText();
return chatClient.prompt()
.user(userMessageText)
.stream()
.content();
}
@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);
return chatClient.prompt()
.system(systemMessage.getText())
.user(userMessage.getText())
.stream()
.content();
}
第四章 提示词模板
- 介绍: 提示词模板是一个预先设计好的文本框架,其中包含占位符,可以通过填充具体内容来动态生成最终提示词。
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;
@GetMapping("/resume")
public Flux<String> resumeGenerate(String name, String email, String post, String salary) {
PromptTemplate promptTemplate = new PromptTemplate("这是应聘的简历你,你无需在意是否规范,直接给我返回html格式的简历即可" +
",包括名称{name},邮箱{email},岗位{post},以及期望薪资{salary}");
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;
public record StudentRecord(String sname, String sid, String semail, String smajor) {}
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;
@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

<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
STEP3: applictaoin.properties
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_HelloWorld
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
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那个是内存的, 重启服务就没了;
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;
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository() {
return RedisChatMemoryRepository.builder()
.host(host)
.port(port)
.build();
}
}
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 {
@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) {
MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(redisChatMemoryRepository)
.maxMessages(10)
.build();
return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder().model(QWENPLUS).build())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
.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();
}
@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
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
STEP3: