Spring AI Alibaba(上)

目录

[1. 介绍 Spring AI Alibaba](#1. 介绍 Spring AI Alibaba)

[2. 多模态](#2. 多模态)

[3. 文生图(同步调用)](#3. 文生图(同步调用))

[4. 语音合成(实时合成)](#4. 语音合成(实时合成))

[4.1 语音合成的概念](#4.1 语音合成的概念)

[4.2 实战(单向流式调用)](#4.2 实战(单向流式调用))

[5. 语音识别](#5. 语音识别)

[5.1 语音识别概念](#5.1 语音识别概念)

[5.2 实战(非流式调用)](#5.2 实战(非流式调用))

[5.3 实战( 双向流式调用:基于回调 (识别传入麦克风的声音) )](#5.3 实战( 双向流式调用:基于回调 (识别传入麦克风的声音) ))

[6. 视频生成与编辑](#6. 视频生成与编辑)

[6.1 视频生成与编辑 概念](#6.1 视频生成与编辑 概念)

[6.2 万相3.0 系列 相关](#6.2 万相3.0 系列 相关)

[6.3 文生视频(同步/异步)](#6.3 文生视频(同步/异步))

[6.4 图生视频](#6.4 图生视频)

[6.4.1 基于首帧生成](#6.4.1 基于首帧生成)

[6.4.2 基于首尾帧生成](#6.4.2 基于首尾帧生成)

[6.5 全模态参考生视频](#6.5 全模态参考生视频)


1. 介绍 Spring AI Alibaba

Spring AI Alibaba是基于Spring AI 研发,用更高层级API帮助开发者简化AI应用开发。开发者通过dashscope调用阿里百炼云平台上的大模型。

阿里百炼云平台是一个饭店,dashscope就像店里面的厨师,用户在前台点菜,厨师做完菜你才能拿到。意思是:dashscope是阿里百炼的引擎,用户调用的模型都是它在返回给你。

dashscope sdk 和 open ai一样都是独立的,dashscope 不依赖、不兼容 open ai,它也不只在spring boot框架中使用,在什么地方都可以,别的语言中也可以使用。强调一点:

复制代码
spring-ai-alibaba-starter-dashscope 是原生dashscope sdk的高级封装,但它是已经开源了,社区维护版本更新速度相对于原生sdk来说它会慢得多,就导致许多官方新鲜玩意它没有。下面都用目前最新的 dashscope-sdk-java 2.22.26

2. 多模态

多模态指的是 模型 同时理解和处理文本、图像、音频和其他数据格式信息的能力。Spring AI官方提供了Message API,它提供了多模态大语言模型(LLM)所需的所有抽象。

多模态就像人的感官。人有 听、说、看、写等感知,模型包含两种及以上感官能力的就叫多模态,包含全部感官的就叫"全模态"

多模态示例:用户可通过 文字描述 生成音频,可通过文字(或加上图像、视频给AI读取作为参考)生成图像、视频,示例很多总之就是感官之间的相互搭配。下面代码示例:"给AI图片,通过文字叫它告诉我图片的内容 (视觉理解)"

controller层代码: 巨蛇神/JavaEE进阶 - 码云 - 开源中国

pom.xml代码: 巨蛇神/JavaEE进阶 - 码云 - 开源中国

application.yml配置: 巨蛇神/JavaEE进阶 - 码云 - 开源中国

java 复制代码
    private final ChatClient chatClient;
    //构造注入
    public MultiModelController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @RequestMapping("/image")
    public String image(String message) throws URISyntaxException, MalformedURLException {
        String url = "https://img0.baidu.com/it/u=783667476,75278391&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889";
//        String url = "https://img.shetu66.com/2023/07/19/1689756946086335.jpg";
//        String url="https://pic.rmb.bdstatic.com/bjh/news/116367f183dd830ed1fdb3feec901d48.png";
        //传入图片URL来解析图片,不会被 MediaType 影响;图片中会包含数据格式标识,模型会根据标识解析。
        //但是传入base64、byte[]、本地 Resource,MediaType 就很重要的是!
        List<Media> medias = List.of(new Media(MediaType.IMAGE_JPEG,new URI(url).toURL().toURI()));
        //定义用户提示词
        UserMessage user = UserMessage.builder()
                .text(message)
                .media(medias)
                .build();
        //调用大模型
        String content = chatClient.prompt(new Prompt(user))
                .call()
                .content();
        return content;
    }

3. 文生图(同步调用)

阿里百炼云平台提供自研的千问(Qwen)系列和万象(Wan)系列模型,可进行文生图。目前 wan2.7-image-pro 和 wan2.7-image 及 wan2.6系列无法使用 spring-ai-alibaba-starter-dashscope 依赖使用。(spring-ai-alibaba-starter-dashscope是对原生 dashscope-sdk-java的封装,此时可能模型更新太快封装速度没跟上)

只能通过原生 dashscope-sdk-java 使用最新的万象 "wan2.7-image-pro"。

同、异步调用官方都有示例:万相-图像生成与编辑2.7 API参考 - 阿里云百炼


所需依赖: 巨蛇神/JavaEE进阶 - 码云 - 开源中国

所需配置: dashscope-sdk-java 不是spring框架的产物,不能在配置文件中配置api-key和模型等。

下面生成的图片虽然一点也不像韩立,但可以传几张图片给模型作为参考再生成;

温馨提醒:wan2.7-image-pro 文生图代码稍微修改就能图生图。在构建参数时,组图生成参数:parameters.enable_sequential(bool,默认为 false)。仅wan2.7-image-pro和wan2.7-image支持,Qwen系列不可用。开启组图模式时,thinking_mode和color_palette参数不可用。

更多参数细节:万相-图像生成与编辑2.7 API参考 - 阿里云百炼


若用 spring-ai-alibaba-starter-dashscope依赖生图非常简单,还可以在yml文件中直接配置模型相关参数,详细可见:

controller层:巨蛇神/JavaEE进阶 - 码云 - 开源中国

配置文件:巨蛇神/JavaEE进阶 - 码云 - 开源中国

所需依赖:

java 复制代码
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!-- 必需 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
    </dependencies>

    <!-- spring-ai-alibaba-starter-dashscope 1.0.0.2以后bom换成了spring-ai-extensions-bom -->
    <dependencyManagement>
        <dependencies>
            <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>

4. 语音合成(实时合成)

4.1 语音合成的概念

语音合成指的是Text To Speech(TTS),也就是文本转语音。TTS生成的音频只有一个音色,也是就是只有一个人在说话。但是音频合成可以一同营造周围环境的声音,比如,"两个人正在说话,旁边有鸟在鸣叫、大爷在唱歌"

  1. 目前阿里百炼语音合成主要有三款系列模型
  2. Qwen-Audio-TTS / CosyVoice / Qwen 系列模型
  3. Qwen-Audio-TTS/CosyVoice 系列模型使用同一模型名称同时支持 WebSocket 和 HTTP 两种接入方式;Qwen 系列模型通过模型名称区分,带 -realtime 后缀的为 WebSocket 接入,不带后缀的为 HTTP 接入。
  4. CosyVoice 系列模型还支持通过 AOQ 协议接入;
  5. 各模型系列的各个版本都有对应可使用的音色,版本甚至系列之间的音色混用。
  6. 目前几乎只有Qwen-Audio-TTS / CosyVoice 支持使用声音复刻 和使用声音设计

注:

  • 使用声音复刻:当已有目标人物的录音素材,希望在合成中还原该音色时。
  • 使用声音设计:当没有录音素材,希望根据文字描述从零创建全新音色时。

4.2 实战(单向流式调用)

使用模型:qwen-audio-3.1-tts-flash

需添依赖:

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dashscope-sdk-java</artifactId>
    <version>2.22.26</version>
</dependency>
java 复制代码
package com.jusheshen.alibaba.controller;

import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class AudioTTSController {
    // 模型
    private static String model = "qwen-audio-3.0-tts-flash";
    // 音色
    private static String voice = "longanhuan_v3.6";
    //音频存储的文件路径
    public static String audioFileLoad = System.getProperty("user.dir")+"/audio.mp3";
    //File
    private static final File file = new File(audioFileLoad);

    public static void streamAudioDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);

        // 实现回调接口ResultCallback
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                if (result.getAudioFrame() != null) {
                    // 此处实现保存音频数据到本地的逻辑
                    try(OutputStream output = new FileOutputStream(file,true)){//每帧都会重新打开 OutputStream ,设置为true前面的帧才不会被覆盖
                        //获取音频字节数组,并写入文件
                        output.write(result.getAudioFrame().array());
                        //刷新缓冲区,数据真正写入文件
                        output.flush();
                    }catch (FileNotFoundException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(TimeUtils.getTimestamp() + " 收到音频");
                }
                // 获取输出信息,包含事件类型和原始文本
                if (result.getOutput() != null && result.getOutput().has("type")) {
                    System.out.println("事件类型: " + result.getOutput().get("type").getAsString()
                            + ", 原始文本: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " 收到Complete,语音合成结束");
                latch.countDown();
            }

            @Override
            public void onError(Exception e) {
                System.out.println("出现异常:" + e.toString());
                latch.countDown();
            }
        };

        // 请求参数
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
                        // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model("qwen-audio-3.1-tts-flash") // 模型
                        .voice("loongstella_v3.1") // 音色
                        .build();
        // 第二个参数"callback"传入回调即启用异步模式
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // 非阻塞调用,立即返回null(实际结果通过回调接口异步传递),在回调接口的onEvent方法中实时获取二进制音频
        try {
            synthesizer.call("今天天气怎么样?");
            // 等待合成完成
            latch.await();
            // 等待播放线程全部播放完
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 任务结束后关闭websocket连接
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        // 首次发送文本时需建立 WebSocket 连接,因此首包延迟会包含连接建立的耗时
        System.out.println(
                "[Metric] requestId为:"
                        + synthesizer.getLastRequestId()
                        + ",首包延迟(毫秒)为:"
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // 以下为华北2(北京)地域的配置,调用时请将"{WorkspaceId}"替换为真实的业务空间ID,各地域的配置不同。
//        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        //关闭进程
        System.exit(0);
    }
}

SpeechSynthesizer是语音合成的一个重要类,想进一步了解它可到:Qwen-Audio-TTS Java SDK - 阿里云百炼

更多示例在:alibabacloud-bailian-speech-demo/samplesGitHub


上面代码的解析补充,但其实也没补充些啥:

5. 语音识别

5.1 语音识别概念

语音识别指的是Speech To Text(STT),也就是语音转文本,与语音合成相反。

  1. 模型有如下功能:
  2. 实时识别 和 非实时识别
  3. 处理专业术语。多个领域可能出现相同的术语,在系统提示词中描述你的领域背景,模型在每次请求时自适应。
  4. 说话人声分离。归类哪些人说了哪些话。

一个模型可能有多个功能,但也可能你想要的功能们在一个模型上没有,比如"实时人声分离"目前没有一个模型同时拥有。(现:2026/10/1)

阿里云百炼官方:语音识别概述 - 阿里云百炼

5.2 实战(非流式调用)

所需依赖与语音生成一样。

notice :不同模型在RecognitionParam param手动配置参数方面可能不完全一致,例如下面的"qwen-audio-3.1-asr-flash-streaming" 的参数需要手动写入将要识别音频的采样率。

java 复制代码
    @RequestMapping("/stt1")
    //非流式调用:提交单个语音实时转写任务,通过传入本地文件的方式同步阻塞地拿到转写结果。
    public void stt1(){
        Gson gson = new Gson();
        // 创建Recognition实例
        Recognition recognizer = new Recognition();
        // 创建RecognitionParam
        RecognitionParam param =
                RecognitionParam.builder()
                        .model("qwen-audio-3.1-asr-flash-streaming")
                        // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
                        // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .format("mp3")
                        .sampleRate(22050)
                        //.parameter("language_hints", new String[]{"zh"})
                        .build();
        File file = new File(System.getProperty("user.dir")+"/audio.mp3");
        try {
            String callStr = recognizer.call(param, file);
            //call返回
            System.out.println("识别结果:" +callStr);
            JsonObject jsonObject = gson.fromJson(callStr, JsonObject.class);
            if (jsonObject.has("sentences")) {
                JsonArray sentences = jsonObject.get("sentences").getAsJsonArray();
                //取出 sentences数组的最后一个元素,并把它转成对象
                JsonObject elementJson = sentences.get(sentences.size()-1).getAsJsonObject();
                //取出 "text" 键对应的值,并转成字符串
                String text = elementJson.get("text").getAsString();
                //打印
                System.out.println("最终识别的完整结果展示:"+text);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 任务结束后关闭 WebSocket 连接
            recognizer.getDuplexApi().close(1000, "bye");
        }
        System.out.println(
                "[Metric] requestId: "
                        + recognizer.getLastRequestId()
                        + ", first package delay ms: "
                        + recognizer.getFirstPackageDelay()
                        + ", last package delay ms: "
                        + recognizer.getLastPackageDelay());
        System.exit(0);
    }

上述代码进一步解析:

5.3 实战( 双向流式调用:基于回调 (识别传入麦克风的声音) )

以下是官方示例Qwen-Audio-ASR-Stream 系列模型的 "双向流式调用:基于回调" 的识别传入麦克风的声音代码,这个代码比较多,代码逻辑比较复杂,请看Gitee

AudioTranscription.stt2():巨蛇神/JavaEE进阶 - 码云 - 开源中国

utils(RealtimeRecognitionTask):巨蛇神/JavaEE进阶 - 码云 - 开源中国

前端:巨蛇神/JavaEE进阶 - 码云 - 开源中国

notice:官方示例 Qwen-Audio-ASR-Streaming实时语音识别 没有实现如何返回给前端的代码,我的代码中简单实现了,但比较粗糙,看我下面的解析后,大家可以自行丰富或修改。

目前粗糙项目存在的问题:

  1. 前端显示的文本间没有明确分隔:

  2. 前端/后端 处理不够好导致没有识别内容的文本也发送(其实是我懒得做了......)

  3. stt2()中的部分代码是官方示例的代码,他给的代码可能是为了让人好理解,要说资源利用这块,还是开发者自己操心。

6. 视频生成与编辑

6.1 视频生成与编辑 概念

视频生成 与 视频编辑 是两个大类,就像 文生图 与 语音合成 ;除此之外,Wan3.0系列模型还有一个 视频延长 (可向前、向后或前后延长视频)。

视频生成包含:

  • 文生视频
  • 图生视频 (其中图生视频分为 基于首尾帧 和基于首帧生成)
  • 参考生视频

以上都支持传入音频;下面解释这三种生成的含义
视频编辑就叫视频编辑,没有子集

视频编辑比如可以: "修改背景风格"、"添加视频元素"、"删除元素"和"修改元素"等改变视频内容的能力


  1. 文生视频:直接通过提示词生成视频。详细:文生视频 - 阿里云百炼

  2. 图片生成---基于首尾帧:提供两张图片,一张作为视频首帧,另一张作为视频尾帧,视频中间的内容通过提示词描述生成。详细:图生视频-基于首尾帧 - 阿里云百炼

  3. 图片生成---基于首帧:提供一张图片作为视频首帧,视频的内容通过提示词描述生成。详细:万相-图生视频-基于首帧 - 阿里云百炼

  4. 参考生视频:可通过传入多张图片 + 多个视频 + 多个音频 资源根据提示词相互作用生成,也可只使用单种资源生成。相对于前几种,这个组合生成方式比较多,详细:参考生视频 - 阿里云百炼


阿里目前最新的视频生成模型支持 全模态输入 生成视频,它就是 万相3.0 ( wan3.0-video / wan3.0-video-prime**),** 使用这个模型你可以使用 视频生成和编辑 的全部功能(nb,不用来回切换模型了)。

**注意!注意!**要用万相3.0系列模型 需dashscope-java-sdk 版本在2.22.31或以上;Wan 3.0包含更多type参数,这些参数有些不能随意搭配,例如:first_frame/last_frame 与reference_image/reference_video /reference_audio/file/link 类型互斥,不能在同一请求中混用。

官方给出了详细的素材组合(参数组合):万相3.0-视频生成API参考 - 阿里云百炼

需要注意:一次请求 type参数的值只能file、link二选一,且只能最多一个,就不能多个file 或 多个link。综上,type共有7种值,详细参数说明可看官方 万相3.0-视频生成API参考 - 阿里云百炼(官方有请求/响应包示例)

6.2 万相3.0 系列 相关

  • Wan3.0系列模型都只有30秒免费额度,生成失败的话不算入。
  • 要用Wan3.0系列模型 需dashscope-java-sdk 版本在2.22.31或以上。
  • 不允许生成含有真人脸的视频,模型在生成视频时会检测生成的视频是否含有,若含有就不会返回视频。
  • 一次只能输出一个视频。

6.3 文生视频(同步/异步)

代码老简单了:巨蛇神/JavaEE进阶 - Gitee.com

6.4 图生视频

6.4.1 基于首帧生成

基于首帧可以通过 图像URL、本地文件路径、Base64编码字符串 三种方式构建media生成视频。

代码非常简单:巨蛇神/JavaEE进阶 - 码云 - 开源中国

由于first_frame与reference_audio不能共存,所以要想视频出现想要的音效只能通过提示词描述,这也是官方在文档中明确提到的。

我的代码只展示了同步调用的代码示例,但异步调用+轮询等待的操作应该与上面的文生视频一模一样,因为这个模型没有第二个asyncCall方法。

Wan3.0系列模型不能像 Wan2.5/Wan2.7系列模型 一样通过指定视频特效生成含有该特效的视频。

6.4.2 基于首尾帧生成

基于首尾帧 只需在 基于首帧 生成视频的代码上额外添加几行代码:

notice:首尾帧的两张图片资源必须要分开放到两个Media。其实不仅是图片资源要分放Media,一个Media仅代表一个资源,例如下面要讲的 参考生视频,传入的每种的每个 视频、图片和音频 资源都独享一个Media。

6.5 全模态参考生视频

万相3.0系列模型,可参考图片/视频/音频生成视频,也可参考文件或网页生成。

参考文件或网页生成视频type分别为file 与 link,这两个非常好用。如传入ppt URL,让模型根据ppt生成视频。

对于参考生视频 图像 的传入支持以下1,2,3;视频 和 音频 传入仅支持1,2;参考文件传入也仅支持1,2可同样支持官方示例解说:官方上传了

  1. 公网URL
  2. 临时URL
  3. Base64 编码字符串

阿里官方给出的示例很详细:万相3.0-视频生成 - 阿里云百炼。

暂时停止......

相关推荐
Latchh1 小时前
视频封面为什么会黑屏或截到旧帧
图像处理·人工智能·计算机视觉·音视频
小马哥crazymxm1 小时前
Arxiv论文周选 (2026-W35)
论文阅读·人工智能·计算机视觉·目标跟踪
shaibdoio2 小时前
瞬维AI落地经验:AI Agent工具调用准确率怎么提
数据库·人工智能·oracle
imDwAaY2 小时前
Spring Boot的核心配置文件一览及其加载顺序
java·spring boot·后端
一缕82年的清风2 小时前
当 AI 承包了 90% 的代码,架构师那致命的 10% 到底在控什么?
人工智能·vibecoding
头发够用的程序员2 小时前
TensorRT 自定义算子插件实战(三):手搓 2×2 最大池化 customMaxpool
人工智能·pytorch·python·深度学习·神经网络·边缘计算·jetson
zhangx1234_2 小时前
javaEE 多线程1
java·linux·服务器
lisw052 小时前
人工智能网络:结构、动力学与经济学!
人工智能
@不误正业2 小时前
技术线05_端侧小模型不可靠先检查你的Agent架构
人工智能·架构·agent·端侧模型·4b