AI相关概念

Java+AI所需要学的技术栈
spring AI解决了什么问题,有什么用
集成各厂商的大模型AI
核心特性
大模型调用能力(ai应用开发的基础)
提示工程
会话记忆
RAG检索增强生成
工具调用
MCP模型上下文协议
快速体验一下SpringAI-alibaba和通义千问
Spring AI与通义千问的首次对话_哔哩哔哩_bilibili
SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识_springai 入门-CSDN博客
本地代码:
起步
创建springboot 3项目, 并且记得去获取你用的平台的api-key
安装依赖
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud.ai/spring-ai-alibaba-bom -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.0.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 引入DashScope依赖-->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
</dependencies>
添加配置文件(resources/application.yml)
spring:
application:
name: SpringAIByItLaoqi
ai:
dashscope:
api-key: sk-40f10179676c42f494a1ed7c74e2c8f3
chat:
options:
model: qwen-plus
使用Spring AI的基本流程
创建出ChatClient对象, 并且指定ChatClient要使用的模型
使用ChatClient对象, chatClient对象.prompt().system(“系统默认给ai的提示词”).user(message).stream().content()
ChatClient的注入
Spring AI会默认注入给含DashScopeChatModel,ChatClient.Builder这两种类型的对象给方法中
//第一种通过DashScopeChatModel模型来创建
@Configuration
public class ChatClientConfig {
@Bean
public ChatClient init(DashScopeChatModel dashScopeChatModel){
return ChatClient.builder(dashScopeChatModel)
.defaultSystem("你是一个热心、可爱的智能助手")
.defaultAdvisors(new SimpleLoggerAdvisor())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
}
//第二种注入方式
@Bean
public ChatClient init2(ChatClient.Builder chatClientBuilder){
return chatClientBuilder.build();
}
"""
这种方式是最基础也是最简单的方式,使用的模型就是application.yml配置文件里面设置的模型
"""
第一次对话
@RestController
@RequestMapping("/api")
public class ChatClientController {
@Resource
private ChatClient chatClient;
@GetMapping(value = "/chat")
public Flux<String> chat(String message) {
//提示词规范
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt();
if(message==null||message.length()==0){
message = "你好";
}
return prompt.user(message).stream().content();
}
}
提示词工程
设置这一次用户的提示词: user(“”)
设置这一次系统的提示词: system(“”)
设置全局的系统提示词, 需要在创建ChatClient对象的时候设置
设置默认提示词保持输出口吻一致
@RestController
@RequestMapping("/api")
public class ChatClientController {
@Resource
private ChatClient chatClient;
private final PromptTemplate zhihuStyleTemplate = new PromptTemplate("""
你是一位知乎高赞答主,请按以下规范用中文回答:
- 先给出1-2句话的明确结论(置顶)。
- 然后分点阐述,每点包含:观点->逻辑链例证或数据。
- 如存在争议,简要列出不同观点及适用场景。
- 最后给出可执行建议或检查清单。
- 风格:专业、克制、结构清晰,避免堆砌术语。
- 允许使用 Markdown 标题(##)与列表,但避免过度装饰。
- 禁止杜撰事实;不确定处请标注假设或推测。
- 回答必须围绕用户问题展开,不进行无关延伸。
用户问题:{question}
""");
@GetMapping(value = "/answer")
public Flux<String> zhihuAnswer(String question,
@RequestParam(required = false) String model) {
if(!StringUtils.hasText(question)){
question = "人究竟是什么样的一种存在呢";
}
String system = zhihuStyleTemplate.render(Map.of("question", question));
//提示词规范
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt()
.system(system)
.user(question);
//切换模型
String effectiveModel = StringUtils.hasText(model) ? model : "qwen-plus";
DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(effectiveModel).build();
prompt = prompt.options(options);
return prompt.stream().content();
}
}
切换模型
第一种: 在创建ChatClient的时候设置模型
@Configuration public class ChatClientConfig { @Bean public ChatClient init(DashScopeChatModel dashScopeChatModel){ //切换模型 DashScopeChatOptions options = new DashScopeChatOptions(); options.setModel("qwen-plus"); dashScopeChatModel.setDashScopeChatOptions(options); return ChatClient.builder(dashScopeChatModel) .defaultSystem("你是一个热心、可爱的智能助手") .defaultAdvisors(new SimpleLoggerAdvisor()) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); } }第二种: 在原有ChatClient对象的基础上切换要使用的模型
//提示词规范 ChatClient.ChatClientRequestSpec prompt = chatClient.prompt() .system(system) .user(question); //切换模型 String effectiveModel = StringUtils.hasText(model) ? model : "qwen-plus"; DashScopeChatOptions options = DashScopeChatOptions.builder() .withModel(effectiveModel).build(); prompt = prompt.options(options);//装载到提示词工程 return prompt.stream().content();
结构化抽取成json数据
//结构化抽取的提示词模板
private final PromptTemplate extractStyleTemplate = new PromptTemplate("""
你是一名物流信息抽取助手。请从用户提供的中文物流状态描述中提取以下字段,并以 JSON 返回:
- orderId:订单号
- currentLocation:当前位置
- estimatedArrival:预计到达时间
- courierName:快递员姓名
- courierPhone:快递员联系方式
输出要求:
只返回一个 JSON 对象,不要附加任何解释或额外文本;
若缺失某字段,请给出合理的空值或最可能的估计;
时间字段使用可解析字符串(如 IS0-8601 或"明晚”这类自然语言)。
""");
@GetMapping("/extract")
public ExtractInfo extract(String text){
if (!StringUtils.hasText(text)){
text = "订单号为110739793978279的快递件," +
" 目前还在广州市增城区, 预计下周一送达, " +
"本此快递由王小明派送, 王小明的联系方式为110";
}
String system = String.valueOf(extractStyleTemplate);
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt()
.system(system)
.user(text);
return prompt.call().entity(ExtractInfo.class);//抽取成ExtractInfo对象
}
ExtractInfo.java
@Data
public class ExtractInfo {
@JsonProperty("orderId")
@JsonPropertyDescription("订单号")
private Long orderId;
@JsonProperty("currentLocation")
@JsonPropertyDescription("当前位置")
private String currentLocation;
@JsonProperty("estimatedArrival")
@JsonPropertyDescription("预计到达时间: 参照现在的时间推断,时区是中国上海")
private Date estimatedArrival;
@JsonProperty("courierName")
@JsonPropertyDescription("快递员姓名")
private String courierName;
@JsonProperty("courierPhone")
@JsonPropertyDescription("快递员联系方式")
private String courierPhone;
}
默认全局提示, 日志功能 会话记忆
ChatClient的全局系统默认提示词:
private MessageWindowChatMemory chatMemory =
MessageWindowChatMemory.builder().build();//会话记忆
@Bean
public ChatClient init(DashScopeChatModel dashScopeChatModel){
return ChatClient.builder(dashScopeChatModel)
.defaultSystem("你是一个热心、可爱的智能助手")//全局系统默认提示词
.defaultAdvisors(new SimpleLoggerAdvisor())//简单日志功能
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())//开启绘画记忆
.build();
}
Spring AI核心组件
结构化输出
第一种就是前文提到的, 抽取成单个对象
第二种,抽取成一个list
@GetMapping("/extract")
public ExtractInfo extract(String text){
if (!StringUtils.hasText(text)){
text = "订单号为110739793978279的快递件," +
" 目前还在广州市增城区, 预计下周一送达, " +
"本此快递由王小明派送, 王小明的联系方式为110";
}
String system = String.valueOf(extractStyleTemplate);
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt()
.system(system)
.user(text);
return prompt.call().entity(ExtractInfo.class);
}
流式响应
采用的是SSE协议实现流式响应,
后端响应头头可写MediaType.TEXT_EVENT_STREAM_VALUE可不写,最好写一下; 返回类型必须是Flux<?>
前端使用EventSource来接收
后端实现
import org.springframework.http.MediaType;
@GetMapping(value = "/testSSE", produces = MediaType.TEXT_EVENT_STREAM_VALUE+";charset=UTF-8")//charset一般在配置文件就配置好
public Flux<String> sendTimeStream(){
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt();
return prompt.user("生成一段长笑话").stream().content();
}
前端实现
<template>
<view class="container">
<button type="default" @click="testSSE">点击测试SSE</button>
{{text}}
</view>
</template>
<script setup>
import {ref} from 'vue';
const text = ref('');
const testSSE = ()=>{
const eventSource = new EventSource("http://localhost:8080/api/testSSE");
eventSource.onmessage = (event)=>{
console.log("接收到的内容:"+event.data);
text.value += event.data;
}
eventSource.onerror = (err)=>{
console.error("error:"+err);
eventSource.close();//必须手动关闭,当服务端没有消息的时候就会报错,利用这个报错手动关闭链接
}
}
</script>
提示词模板
第一种, 前文提到过的, 用PromptTemplate对象渲染
第二种, 在user()中使用lambda表达式渲染
//
@GetMapping("/promptTemplate")
public Flux<String> promptTemplate(@RequestParam(required = false, defaultValue = "乐哥学编程") String username){
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt();
return prompt.user(u->u.text("根据{username}这个用户名, 写一首简短的诗")
.param("username", username)).stream().content();
}
//param()可以有多个;param(Map.of(k1,v1...))这样也可以
多厂商多模型使用
就是创建多种模型的ChatClient bean,提供给其他容器使用
ChatClientConfig.java
private MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder().build();
@Bean
public ChatClient qwenplusChatClient(DashScopeChatModel dashScopeChatModel){
DashScopeChatOptions options = new DashScopeChatOptions();
options.setModel("qwen-plus");
dashScopeChatModel.setDashScopeChatOptions(options);
return ChatClient.builder(dashScopeChatModel)
.defaultSystem("你是一个热心、可爱的智能助手")
.defaultAdvisors(new SimpleLoggerAdvisor())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
@Bean
public ChatClient qwen3maxChatClient(DashScopeChatModel dashScopeChatModel){
DashScopeChatOptions options = new DashScopeChatOptions();
options.setModel("qwen3-max");
dashScopeChatModel.setDashScopeChatOptions(options);
return ChatClient.builder(dashScopeChatModel)
.defaultSystem("你是一个热心、可爱的智能助手")
.defaultAdvisors(new SimpleLoggerAdvisor())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
ChatClientController.java
@Resource(name = "qwenplusChatClient")
private ChatClient chatClient;
@Resource
private ChatClient qwen3maxChatClient;
会话保持(会话记忆)
第一种,是最基本的开启会话记忆
第二种,进阶了通过ChatMemory.CONVERSATION_ID 来区分用户
@GetMapping("/memory")
public Flux<String> memory(@RequestParam(required = false, defaultValue = "我是乐哥学编程") String question, String userid){
ChatClient.ChatClientRequestSpec prompt = chatClient.prompt();
return prompt.user(question)
.advisors(advisorSpec -> {//区分用户
advisorSpec.param(ChatMemory.CONVERSATION_ID, userid);
}).stream().content();
}
工具调用
有些功能比如,获取当前系统时间,获取不存在于模型数据中的人的信息。必须使用工具
控制器
@GetMapping("/tool")
public Flux<String> tool(String question){
return chatClient.prompt()
.user(question)
.tools(new MyDateTime())//告诉ai要调用哪些我的工具
.stream().content();
}
MyDateTime.java
public class MyDateTime {
@Tool(description = "获取当前时间")
public String getNowTime(){
return LocalDateTime.now().toString();
}
@Tool(description = "获取用户的生日,入参是用户的名字,出参是年月日 如 1992年08月01日")
public String getLisiBirthday(String username){
if ("张三".equals(username)) {
return "一九九九年一月一日";
}else if ("李四".equals(username)) {
return "2000年09月01日";
}else {
return "unknown";
}
}
}
MCP集成
RAG集成
稍后了解
Mono.just
Flux.just