目录

0 前言

1 Spring AI介绍

1.1 简介

1.2 名词解释

1.3 环境需求

2 接入DeepSeek

2.1 DeepSeek API Keys

2.2 引入依赖

2.3 配置文件

2.4 编写接口

2.5 测试

3 核心API

3.1 ChatClient

3.1.1 基本使用

3.1.2 角色预设

3.1.3 结构化输出

3.1.4 SSE协议

(1)SSE协议

(2)实现SSE协议

3.1.5 流式输出

(1)流式编程

(2)流式输出

3.1.6 日志打印

(1)Advisor

(2)全局日志打印

(3)对话日志打印

3.2 ChatModel

3.2.1 call()

3.2.2 角色预设

3.2.3 流式输出

3.3 ChatClient VS ChatModel

4 Spring AI Alibaba

4.1 快速上手

4.1.1 获取API key

4.1.2 引入依赖

4.1.3 配置文件

4.1.4 代码实现

4.2 多模态

4.2.1 引入依赖

4.2.2 配置文件

4.2.3 代码实现


0 前言

        自从Transformer诞生以来,LLM(Large Language Model)大模型技术就开始蓬勃发展,从ChatGPT系列到国内的DeepSeek、通义千问系列,大模型技术不断迭代,一次次突破性能指标上限。但是,大模型的落地应用成为行业难题,诸如智能体、RAG等技术不断解决落地难和应用难的问题。

        繁多的技术规范不统一,学习起来极为困难,而Spring AI、LangChain等技术正是统一大模型应用落地的框架,也是传统开发编程融入人工智能发展的新机遇。

1 Spring AI介绍

1.1 简介

        2025年5月20日,Spring AI官方宣布1.0 GA (General Availability)版本正式发布,这是Spring官方推出的首个稳定版人工智能(AI)集成框架。旨在帮助Java/Spring开发者更便捷地在企业级应用中集成AI能力(如大语言模型、机器学习、向量数据库、图像生成等)。它的发布标志着Spring生态正式进入AI时代,为Java开发者提供了标准化的AI开发工具链,AI技术正式进入Spring生态的核心工具链。

        因为是Spring官网开源的AI框架,因此和SpringBoot家族的项目天然的兼容。并且框架封装集成了多个流行LLM的厂商的接口开发规范,因此开发人员学习一个框架便可自由替换LLM从而学会其它LLM的接入和使用。

1.2 名词解释

        Token(词元):大模型所能理解的最小语义单元,一个词、一个数字、一个字母和一个标点符号都可以算作是一个Token。但是由于词的划分通常是由分词器处理,不同分词器的划分词的方式不同,因此可能一句话的Tokens计算结果不一定完全一样。

        提示词:用户/系统提供给大模型的文本/指令,简而言之就是告诉大模型你要它干什么。由于提示词的优劣影响大模型的输出指令,因此提示词的设计也有学问。从专业角度分类,提示词分为系统提示词和用户提示词。

        系统提示词:大模型应用系统内嵌,全局共享,定义了大模型的角色、行为、知识库边界等规范。比如“你是一名精通编程的开发者”,这句提示词就定义了大模型的角色,从而用户每次输入的提示词都会携带系统提示词,输出和编程开发相关的内容。

        用户提示词:由用户直接输入,比如“请帮我总结这篇文章的思想”。

1.3 环境需求

        SpringBoot最低需求:3.2版本以上(Spring AI强制要求),推荐3.4版本以上。

        JDK最低需求:JDK 17以上(SpringBoot 3.x版本要求),推荐JDK 21以上。

2 接入DeepSeek

2.1 DeepSeek API Keys

        我们选择接入国内著名的LLM DeepSeek,更方便学习使用。DeepSeek的网页版和客户端是免费使用的,但是DeepSeek的API需要收费,因此需要首先开通DeepSeek API:

DeepSeek官网https://www.deepseek.com/

        点击右上角API开放平台,进入后点击充值按钮(需要先实名)。由于是学习阶段,几块钱足够使用了:

        然后创建API keys(记得复制保存):

2.2 引入依赖

        Spring AI提供了一个bom,该bom负责依赖的版本兼容管理,可以让开发人员直接引入相关依赖,不用手动管理版本:

    <!-- Spring AI 相关依赖版本管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-M6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

        由于Spring AI便于大模型的管理,设计了Open AI以及兼容风格的依赖,提供统一的接口管理不同的LLM。该依赖兼容DeepSeek,因此需要引入如下依赖:

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>

2.3 配置文件

        配置文件需要定义相关客户端的配置项,比如访问地址、api key(上述申请的DeepSeek API Keys)和相关模型选择:

spring:
  application:
    name: spring-ai-deepseek
  ai:
    openai:
      # api key需要去LLM官网申请
      api-key: 你的api key
      # LLM的API调用官网
      base-url: https://api.deepseek.com
      # 聊天属性的设置
      chat:
        options:
          # 模型
          model: deepseek-chat
          # 输出随机值([0,2]之间,值越高越随机)
          temperature: 0.7

        deepseek-chat模型对接DeepSeek官网最新版本的无深度思考功能的模型。

        注意:temperature选项通常不要和topP选项同时使用。topP是采样温度的一种替代方法,称为核采样 (nucleus sampling),模型会考虑具有 top_p 概率质量的 token 的结果。因此,0.1 表示仅考虑构成前 10% 概率质量的 token。通常建议修改此项或 temperature,但不要同时修改两者,否则会导致大模型输出超出预期范围。

2.4 编写接口

        通过注入OpenAiChatModel对象,该对象将配置文件的ai相关配置设置到属性中,从而可以通过该对象的call方法进行发送消息等待回复结果:

@RestController
@RequestMapping("/deepseek")
public class DeepSeekController {
    /**
     * 通过配置文件注册的大模型客户端对象
     */
    @Autowired
    private OpenAiChatModel openAiChatModel;

    @GetMapping("/chat")
    public String generate(String message) {
        return openAiChatModel.call(message);
    }
}

2.5 测试

        由于接口是等DeepSeek完全回复结束再返回响应结果,因此这个等待的过程比较慢:

3 核心API

        Spring AI提供了两种核心的API:ChatClient和ChatModel。ChatModel是上述演示的,更底层,直接与大模型交互。ChatClient是对ChatModel的进一步封装,更加简便。

3.1 ChatClient

        ChatClient是Spring AI封装的更高级的API,将复杂的交互流程封装为简洁的使用方式,采用与大模型通信的Fluent API(链式调用的思想,更容易理解调用流程),支持同步和反应式(Reactive)编程。

3.1.1 基本使用

        基本使用方式如下:

@RestController
@RequestMapping("/chatclient")
public class ChatClientController {
    /**
     * ChatClient聊天客户端
     */
    private final ChatClient chatClient;

    /**
     * 通过构造方法完成对象注入
     *
     * @param chatClientBuilder chatClient对象建造器
     */
    public ChatClientController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/call")
    public String generation(String userInput) {
        return this.chatClient.prompt()
                //用户输入的提示词
                .user(userInput)
                //请求大模型
                .call()
                //返回文本
                .content();
    }
}

        发送消息结果如下:

3.1.2 角色预设

        除了简单的使用方式,ChatClient还支持角色预设,在建造对象的时候,使用defaultSystem()方法可以添加系统提示词,为大模型预设角色。该提示词全局生效,每次的用户提示词都会结合系统提示词进行回复

    /**
     * 通过构造方法完成对象注入
     *
     * @param chatClientBuilder chatClient对象建造器
     */
    public ChatClientController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
                .defaultSystem("你是一个精通Java的工程师,专注于Java领域的前沿技术。")
                .build();
    }

        运行结果如下:

3.1.3 结构化输出

        如果想要大模型的输出结构化,可以使用entity()来将大模型的输出转为自定义结构的输出:

    /**
     * record关键词(JDK16新特性):定义学习清单实体类
     * @param time 学习周期
     * @param knowledge 技术
     */
    record StudyList(String time, List<String> knowledge) {}

    @GetMapping("/entity")
    public String entity(String userInput) {
        StudyList studyList =this.chatClient.prompt()
                //用户输入的提示词
                .user(userInput)
                //请求大模型
                .call()
                //返回文本,并将输出转为StudyList实体
                .entity(StudyList.class);
        return studyList.toString();
    }

        结果如下:

3.1.4 SSE协议

        上述等待输出的方式都是等大模型将所以回复生成后再统一返回,而大模型的输出需要一定的时间,用户等待时间过长不太友好。现在普遍的AI平台都是采用流式输出的方式,即数据流,每生成一个字就从服务器推送到客户端进行显示。

        要实现流式输出,首先需要了解一下SSE协议。

(1)SSE协议

        由于http协议的请求响应模式,客户端向服务器发送请求,服务器向客户端返回响应,而服务器无法主动向客户端发送请求。有时候需要服务器主动将数据推送到客户端,SSE协议核心思想正是如此。

        SSE(Server-Sent Events,服务器发送事件)协议是基于http协议设计的轻量级实时通信协议,浏览器通过EventSource API接收处理实时传输的事件。

        其具有如下特点

1.单向通信,只能由服务器向浏览器单向推送,浏览器无法向服务器发送。

2.自动重连:如果浏览器和服务器的连接断开,浏览器会尝试自动重连。

        开启SSE协议,需要在请求头设置:

Content-Type: text/event-stream;charset=utf-8
Connection: keep-alive

        SSE协议数据格式如下:

data: 消息内容(可多行传输)
id: 事件ID(用于断线重连时定位)
event: 自定义事件类型(如message、end等)
retry: 重连时间(毫秒)

        该数据格式遵循如下规则:

1.一条消息中,data字段是必须存在的,其它字段是可选字段。

2.消息与消息之间以/n/n分隔,一条消息中可以有多行,每行以/n分隔。

        比如:

// 消息1
data: 消息内容\n\n

// 消息2
retry: 5000\n
data: 消息内容\n\n

// 消息3
event: 自定义消息名称\n
data: 消息内容\n\n
(2)实现SSE协议

        下面四段代码分别实现了:发送消息、发送自定义事件的消息、指定重试时间、发送结束事件(也是自定义事件)的消息。这些消息的内容是当前时间:

@RestController
@RequestMapping("/sse")
public class SSEController {

    @RequestMapping("/data")
    public void data(HttpServletResponse response) throws IOException, InterruptedException {
        // 声明SSE事件流
        response.setContentType("text/event-stream;charset=utf-8");
        PrintWriter writer = response.getWriter();
        String s = "";
        // 5个message(只有data)
        for (int i = 0; i < 5; i++) {
            s = "data: " + new Date() + "\n\n";
            writer.write(s);
            writer.flush();
            Thread.sleep(1000);
        }
    }

    @RequestMapping("/event")
    public void event(HttpServletResponse response) throws IOException, InterruptedException {
        // 声明SSE事件流
        response.setContentType("text/event-stream;charset=utf-8");
        PrintWriter writer = response.getWriter();
        String s = "";
        // 自定义事件的message
        for (int i = 0; i < 5; i++) {
            s = "event: MyEvent\n" + "data: " + new Date() + "\n\n";
            writer.write(s);
            writer.flush();
            Thread.sleep(1000);
        }

    }

    @RequestMapping("/retry")
    public void retry(HttpServletResponse response) throws IOException, InterruptedException {
        // 声明SSE事件流
        response.setContentType("text/event-stream;charset=utf-8");
        PrintWriter writer = response.getWriter();
        String s = "";
        // 重试功能(5s重试)
        s = "retry: 5000\n" + "data: " + new Date() + "\n\n";
        writer.write(s);
        writer.flush();
    }

    @RequestMapping("/end")
    public void end(HttpServletResponse response) throws IOException, InterruptedException {
        // 声明SSE事件流
        response.setContentType("text/event-stream;charset=utf-8");
        PrintWriter writer = response.getWriter();
        String s = "";
        // 5个message(只有data)
        for (int i = 0; i < 5; i++) {
            s = "data: " + new Date() + "\n\n";
            writer.write(s);
            writer.flush();
            Thread.sleep(1000);
        }
        // 自定义事件实现数据流结束功能
        s = "event: end\n" + "data: " + "end-message\n\n";
        writer.write(s);
        writer.flush();
    }
}

        对应的前端代码如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>SSE</title>
</head>
<body>
<div id="sse"></div>
<script>
    let eventSource = new EventSource("/sse/data");
    eventSource.onmessage = function(event){
        document.getElementById("sse").innerHTML = event.data;
    }
<!--    eventSource.addEventListener("MyEvent", (event)=>{-->
<!--        document.getElementById("sse").innerHTML = event.data;-->
<!--    });-->
<!--    eventSource.addEventListener('end', function() {-->
<!--        eventSource.close();-->
<!--    });-->
</script>
</body>
</html>

        想监听哪种事件,就需要修改EventSource监听的接口url,同时自定义事件需要添加事件监听对应名称的自定义事件。

        注意:由于浏览器的自动重连,即使消息发送完毕,也会重新连接后重新发送消息。想要消息发送结束就终止发送,需要自定义end事件,并在前端监听,如果接收到end事件,就主动调用事件监听器的关闭函数。

        访问前端页面,可以显示消息:

3.1.5 流式输出

        3.1.4部分已经了解了SSE协议,Spring 5开始支持WebFlux,这可以更高效的实现SSE协议。

(1)流式编程

        Flux是流式编程的核心API,也是WebFlux的核心。Flux类似物流中心的传送带,不用等包裹一起打包运输到分类装置,而是由传送带一个一个包裹像水流一样传输。并且,其支持中途修改数据。

        FLux的流程如下:

1.创建FLux并设置数据源。

2.使用操作符处理数据。

3.订阅FLux来触发数据流动并消费数据。

    public static void main(String[] args) throws InterruptedException {
        //创建Flux
        Flux<String> flux = Flux.just("aaa", "bbb", "ccc", "ddd", "eee", "fff")
                .delayElements(Duration.ofSeconds(1));
        //处理数据
        flux.map(String::toUpperCase)
                // 订阅数据
                .subscribe(System.out::println);
        //防止程序处理完之前结束
        Thread.sleep(7000);
    }

        FLux创建方式有多种,上述just()是创建了包含7个元素的Flux流,并每延迟1s输出一个数据。

        处理数据采用map()将小写字母转为大写字母。

        不对flux流进行订阅subscribe,就不会触发数据流动。

        由于Flux默认是异步执行方式,启动非阻塞线程执行,因此在主线程需要sleep防止Flux数据流还未处理结束程序就结束。

        常见的操作符如下:

操作符 作用 作用示例 示例含义
map() 元素一对一映射 map(String::toUpperCase) 元素小写字母转大写
filter() 条件过滤 filter(str -> str.length > 10) 过滤字符串长度小于等于10的元素
take() 限制元素数量 take(5) 获取前5个元素
merge() 合并Flux(无顺序)

Flux.merge(Flux.just("aaa"), Flux.just("bbb"))

合并两个Flux流,如果订阅合并后的Flux流,相当于同时订阅合并前的两个Flux,因此输出顺序不确定。
concat() 按顺序拼接多个Flux

Flux.concat(Flux.just("aaa"), Flux.just("bbb"))

拼接两个Flux流,订阅拼接后的Flux流,相当于先订阅第一个Flux流,待其输出结束后再订阅第二个Flux流。
delayElements() 元素延迟发射

delayElements(Duration.ofSeconds(1))

Flux中的元素每延迟1秒流动1个
(2)流式输出

        在ChatClient中,如果想要开启流式输出,使用stream()方法可以生成流式输出Flux<String>流,效果就是每生成一个数据流就由服务器推送到客户端(这里必须设置编码类型,否则中文就会乱码):

    @GetMapping(value = "/stream", produces = "text/html;charset=utf-8")
    public Flux<String> stream(String userInput) {
        return this.chatClient.prompt()
                //用户输入的提示词
                .user(userInput)
                //请求大模型
                .stream()
                //返回文本
                .content();
    }

3.1.6 日志打印

        如果需要打印与大模型交互的日志信息,就需要用到SimpleLoggerAdvisor(这是Spring AI内置的Advisor之一)。

(1)Advisor

        Spring AI内置的Advisor是用户请求和大模型之间的中间件组件,主要用于对用户请求和响应进行拦截、过滤和增强。

        比如,用户请求发送大模型前,可以对提示词进行敏感词过滤。

        Advisor借助AOP思想实现,以链式结构运行,如果有多个advisor,执行顺序如下图所示:

(2)全局日志打印

        使用defaultAdvisors()在注册Bean时配置Advisor,这种方式配置的Advisor会在所有使用该chatClient的对话中都执行相同的Advisor:

    public ChatClientController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
                //系统提示词
                .defaultSystem("你是一个精通Java的工程师,专注于Java领域的前沿技术。")
                //系统日志打印(每个使用chatClient都会打印)
                .defaultAdvisors(new SimpleLoggerAdvisor())
                .build();
    }
(3)对话日志打印

        使用advisors()可以为当前对话配置Advisor,则这种方式只会对该接口发起的对话执行该Advisor:

    @GetMapping("/advisor")
    public String advisor(String userInput) {
        return this.chatClient.prompt()
                //用户输入的提示词
                .user(userInput)
                //对话日志打印(只有当前对话会打印日志)
                .advisors(new SimpleLoggerAdvisor())
                //请求大模型
                .call()
                //返回文本
                .content();
    }

        注意:如果同时配置defaultAdvisors()和advisors()为相同的Advisor,则advisors()配置的优先执行,defaultAdvisors()配置的后执行。即对话配置优先级更高。

        还需配置日志级别,才能看到打印的日志:

logging:
  level:
    org.springframework.ai.chat.client.advisor: debug

3.2 ChatModel

        在接入DeepSeek的案例中,我们已经使用了ChatModel,ChatModel更接近与大模型的直接交互,核心方法是call。

3.2.1 call()

        在ChatModel的call方法实现中主要有两种:

    default String call(String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    default String call(Message... messages) {
        Prompt prompt = new Prompt(Arrays.asList(messages));
        Generation generation = this.call(prompt).getResult();
        return generation != null ? generation.getOutput().getText() : "";
    }

    ChatResponse call(Prompt prompt);

        String call(String message):接收String类型的提示词,对提示词封装为Prompt对象,然后调用 ChatResponse call(Prompt prompt)方法将提示词发送给大模型并等待响应结果,获取结果后会手动将结果转为String类型。

        ChatResponse call(Prompt prompt):接收提示词对象Prompt,返回响应ChatResponse,该方法更加底层。

    @GetMapping("/callByPrompt")
    public String callByPrompt(String message) {
        ChatResponse response = openAiChatModel.call(new Prompt(message));
        return response.getResult().getOutput().getText();
    }

3.2.2 角色预设

        ChatModel的角色预设更加复杂,需要自定义系统提示词对象SystemMessage和用户提示词对象UserMessage,并将两个提示词合并为Prompt:

    @GetMapping(value = "/role")
    public String role(String message) {
        SystemMessage systemMsg = new SystemMessage("你是一名专业的美食家,会制作各种菜肴");
        UserMessage userMsg = new UserMessage(message);
        Prompt prompt = new Prompt(List.of(systemMsg, userMsg));
        ChatResponse response = openAiChatModel.call(prompt);
        return response.getResult().getOutput().getText();
    }

3.2.3 流式输出

        流式输出使用ChatModel提供的stream方法:

    @GetMapping(value = "/stream", produces = "text/html;charset=utf-8")
    public Flux<String> callByStream(String message) {
        Flux<ChatResponse> response = openAiChatModel.stream(new Prompt(message));
        return response.map(x->x.getResult().getOutput().getText());
    }

        可以看到,每生成一个字,就会在浏览器进行显示:

3.3 ChatClient VS ChatModel

        观察ChatClient的构建方法,发现其需要传入ChatModel对象,因此ChatClient对ChatModel进行了进一步的封装和增强:

        ChatClient和ChatModel的区别如下:

对比方面 ChatModel ChatClient
交互方式 手动构建Prompt对象、增强请求、解析响应 链式调用,自动封装请求和解析响应
输出 只能手动解析输出文本 支持结构化输出(entity())
扩展 依赖外部组件 内置Advisor对象,支持上下文记忆、RAG技术等
场景 依赖模型本身对模型进行微调等场景 快速构建AI应用的场景

4 Spring AI Alibaba

        Spring AI Alibaba 定位开源 AI Agent 开发框架,提供从 Agent 构建到 workflow 编排、RAG 检索、模型适配等能力,帮助开发者轻松构建生成式 AI 应用。并且该框架由于也是阿里系的,与Spring Cloud Alibaba等组件集成也比较方便。

        框架提供了如下的功能:

  • 提供多种大模型服务对接能力,包括 OpenAI、Ollama、阿里云 Qwen 等
  • 支持的模型类型包括聊天、文生图、音频转录、文生语音等
  • 支持同步和流式 API,在保持应用层 API 不变的情况下支持灵活切换底层模型服务,支持特定模型的定制化能力(参数传递)
  • 支持 Structured Output,即将 AI 模型输出映射到 POJOs
  • 支持矢量数据库存储与检索
  • 支持函数调用 Function Calling
  • 支持构建 AI Agent 所需要的工具调用和对话内存记忆能力
  • 支持 RAG 开发模式,包括离线文档处理如 DocumentReader、Splitter、Embedding、VectorStore 等,支持 Retrieve 检索

4.1 快速上手

4.1.1 获取API key

        首先需要前往阿里云百炼平台获取API key:

阿里云百炼https://bailian.console.aliyun.com        登录后开通大模型服务,在左侧点击密钥管理:

        然后创建API key并保存。

4.1.2 引入依赖

        需要引入的核心依赖是spring-ai-alibaba-starter:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter</artifactId>
            <version>1.0.0-M6.1</version>
        </dependency>
    </dependencies>

4.1.3 配置文件

        在配置文件中添加api-key的配置项:

server:
  port: 8081
spring:
  application:
    name: spring-ai-alibaba
  ai:
    dashscope:
      api-key: 百炼平台的API key
logging:
  pattern:
    console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

4.1.4 代码实现

        由于百炼平台已经部署好了大模型,因此我们可以直接调用。调用方式同Spring AI(Spring AI Alibaba是基于Spring AI实现的):

@RequestMapping("/ai/alibaba")
@RestController
public class AlibabaController {
    private static final String DEFAULT_PROMPT = "你是一个精通AI算法的工程师,请结合角色进行回答";
    private final ChatClient chatClient;

    public AlibabaController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
                .defaultSystem(DEFAULT_PROMPT)
                .defaultAdvisors(new SimpleLoggerAdvisor())
                .build();
    }

    @GetMapping("/chat")
    public String chat(String message) {
        return chatClient.prompt(message).call().content();
    }
}

        ChatClient用法和Spring AI一致,不再赘述。

4.2 多模态

        多模态是指模型能接收多种模态的数据的能力,比如图片、音频、文本等等。模型的多模态能力体现了综合能力,因为依靠多种模态的数据,更能全面分析、处理和解决用户问题。

        如果想要应用层实现多模态的AI应用,就需要使用有多模态能力的大模型。

4.2.1 引入依赖

        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>

        注意:该依赖与spring-ai-alibaba-starter包(版本更老)存在依赖冲突,因此需要排除spring-ai-alibaba-starter包。

4.2.2 配置文件

        需要在配置文件添加具有多模态功能的大模型,具体可以去百炼平台查询:

spring:
  application:
    name: spring-ai-alibaba
  ai:
    dashscope:
      api-key: 阿里云百炼平台api key
      chat:
        options:
          model: qwen-vl-max-latest #模型名称
          multi-model: true #是否启用多模型

4.2.3 代码实现

        下面使用如下图片进行测试:

        将图片转化为List<Media>对象,并与用户提示词拼接得到输入的数据,然后输入模型得到返回响应:

    @GetMapping("/image")
    public String image(String prompt) throws Exception {
        // 图片的网络路径
        String url = 图片路径;
        // 将图片网络路径转化并放到list中
        List<Media> mediaList = List.of(new Media(MimeTypeUtils.IMAGE_PNG, new URI(url).toURL().toURI()));
        // 用户提示词和图片拼接
        UserMessage message = UserMessage.builder().text(prompt).media(mediaList).build();
        ChatResponse response = chatClient
                .prompt(new Prompt(message))
                .call()
                .chatResponse();
        return response.getResult().getOutput().getText();
    }

        可以看到,模型能结合用户的输入文本和图片综合分析图片内容,结果如下:

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐