SpringAI-it老齐

AI相关概念

学 AI,看这个视频就够了!最全程序员 AI 指南:AI核心概念、实用AI工具、AI编程技巧、AI开发技术

Java+AI所需要学的技术栈

spring AI解决了什么问题,有什么用

炸裂,Spring AI让Java再次伟大!还有人没用过吗?

Java开发AI项目,太爽了!LangChain4j实战教程保姆级 | 代码开源

  • 集成各厂商的大模型AI

  • 核心特性

    • 大模型调用能力(ai应用开发的基础)

    • 提示工程

    • 会话记忆

    • RAG检索增强生成

    • 工具调用

    • MCP模型上下文协议

快速体验一下SpringAI-alibaba和通义千问

Spring AI与通义千问的首次对话_哔哩哔哩_bilibili

SpringAI 大模型应用开发篇-SpringAI 项目的新手入门知识_springai 入门-CSDN博客

本地代码:

起步

  1. 创建springboot 3项目, 并且记得去获取你用的平台的api-key

  2. 安装依赖

<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>
  1. 添加配置文件(resources/application.yml)

spring:
  application:
    name: SpringAIByItLaoqi
  ai:
    dashscope:
      api-key: sk-40f10179676c42f494a1ed7c74e2c8f3
      chat:
        options:
          model: qwen-plus

使用Spring AI的基本流程

  1. 创建出ChatClient对象, 并且指定ChatClient要使用的模型

  2. 使用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

Spring-Boot王鹤 2026-03-23
博客分类 + 标签 + 归档 高效管理方案(Java/Vue 笔记专属) 2026-03-22