353-Spring AI Alibaba ARK 多模型示例

本示例将展示如何使用 Spring AI Alibaba 集成 ARK 多模型能力,实现图像理解、视频内容分析等多模态 AI 功能。ARK 是字节跳动提供的大模型服务平台,支持多种 AI 模型能力。
1. 示例目标
我们将创建一个包含多个核心功能的 Web 应用:
- 图像理解 (
/api/image):通过上传图片或提供图片 URL,让 AI 模型识别并描述图片内容。 - 流式图像理解 (
/api/stream/image):以流式方式返回图像理解结果。 - 视频内容分析 (
/api/video):从视频中提取关键帧,让 AI 模型分析并描述视频内容。
2. 技术栈与核心依赖
- Spring Boot 3.x
- Spring AI Alibaba (用于对接字节跳动 ARK 大模型)
- OpenAI 兼容接口 (ARK 提供与 OpenAI 兼容的 API 接口)
- JavaCV (用于视频处理和帧提取)
- Maven (项目构建工具)
在 pom.xml 中,你需要引入以下核心依赖:
<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Open AI 兼容接口 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><!-- JavaCV 用于视频处理 --><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.9</version></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>3. 项目配置
在 src/main/resources/application.yml 文件中,配置你的 ARK API Key 和模型信息。
spring:ai:openai:# API Key Configurationapi-key: ${ARK_API_KEY:your-api-key}# Ark LLM API Base URLbase-url: https://ark.cn-beijing.volces.com/api/chat:options:# Model ID, replace with actual access point IDmodel: ${ARK_MODEL_ID:your-model-id}# Chat API path, consistent with OpenAI interfacecompletions-path: /v3/chat/completionsserver:port: 7878logging:level:org:springframework:ai:chat:client:advisor: DEBUG重要提示:请将 ARK_API_KEY 和 ARK_MODEL_ID 环境变量设置为你从 ARK 平台获取的有效 API Key 和模型 ID。你也可以直接将其写在配置文件中,但这不推荐用于生产环境。
4. 准备资源文件
在 src/main/resources 目录下创建以下文件结构:
src/main/resources/
└── multimodel/├── dog_and_girl.jpeg└── video.mp44.1 multimodel/dog_and_girl.jpeg
这是用于图像理解的示例图片,包含一只狗和一个小女孩。
4.2 multimodel/video.mp4
这是用于视频内容分析的示例视频。系统会自动提取视频帧并进行分析。
5. 编写 Java 代码
5.1 ArkMultiModelApplication.java
主应用程序类,用于启动 Spring Boot 应用。
package com.alibaba.cloud.ai.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** ARK Multi-Model Application* 支持聊天、图像生成、文本向量等多种模型能力* * @author brian xiadong*/
@SpringBootApplication
public class ArkMultiModelApplication {public static void main(String[] args) {SpringApplication.run(ArkMultiModelApplication.class, args);}
}5.2 MultiModelController.java
实现多模型功能的核心控制器类。
package com.alibaba.cloud.ai.example.controller;import com.alibaba.cloud.ai.example.controller.helper.FrameExtraHelper;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.content.Media;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.*;import java.net.URI;
import java.util.List;/*** ark Multi-Model REST Controller* 提供聊天、图片生成、文本向量等多个模型能力的API接口* */
@RestController
@RequestMapping("/api")
public class MultiModelController {private static final String DEFAULT_PROMPT = "这些是什么?";private static final String DEFAULT_VIDEO_PROMPT = "这是一组从视频中提取的图片帧,请描述此视频中的内容。";@Autowiredprivate ChatModel chatModel;private ChatClient openAiChatClient;public MultiModelController(ChatModel chatModel) {this.chatModel = chatModel;// 构造时,可以设置 ChatClient 的参数this.openAiChatClient = ChatClient.builder(chatModel)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().build()).build())// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(OpenAiChatOptions.builder().topP(0.7).build()).build();}@GetMapping("/image")public String image(@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT)String prompt) throws Exception {List<Media> mediaList = List.of(new Media(MimeTypeUtils.IMAGE_PNG,new URI("https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg").toURL().toURI()));UserMessage message = UserMessage.builder().text(prompt).media(mediaList).build();ChatResponse response = openAiChatClient.prompt(new Prompt(message)).call().chatResponse();return response.getResult().getOutput().getText();}@GetMapping("/stream/image")public String streamImage(@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT)String prompt) {UserMessage message = UserMessage.builder().text(prompt).media(new Media(MimeTypeUtils.IMAGE_JPEG,new ClassPathResource("multimodel/dog_and_girl.jpeg"))).build();List<ChatResponse> response = openAiChatClient.prompt(new Prompt(message)).stream().chatResponse().collectList().block();StringBuilder result = new StringBuilder();if (response != null) {for (ChatResponse chatResponse : response) {String outputContent = chatResponse.getResult().getOutput().getText();result.append(outputContent);}}return result.toString();}@GetMapping("/video")public String video(@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_VIDEO_PROMPT)String prompt) {List<Media> mediaList = FrameExtraHelper.createMediaList(10);UserMessage message = UserMessage.builder().text(prompt).media(mediaList).build();ChatResponse response = openAiChatClient.prompt(new Prompt(message)).call().chatResponse();return response.getResult().getOutput().getText();}
}5.3 FrameExtraHelper.java
视频帧提取辅助类,用于从视频中提取关键帧。
package com.alibaba.cloud.ai.example.controller.helper;import jakarta.annotation.PreDestroy;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.ai.content.Media;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.PathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.MimeType;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;import static org.bytedeco.javacpp.Loader.deleteDirectory;@Component
public final class FrameExtraHelper implements ApplicationRunner {private FrameExtraHelper() {}private static final Map<String, List<String>> IMAGE_CACHE = new ConcurrentHashMap<>();private static final File videoUrl = new File("spring-ai-alibaba-multi-model-example/dashscope-multi-model/src/main/resources/multimodel/video.mp4");private static final String framePath = "spring-ai-alibaba-multi-model-example/dashscope-multi-model/src/main/resources/multimodel/frame/";private static final Logger log = LoggerFactory.getLogger(FrameExtraHelper.class);public static void getVideoPic() {List<String> strList = new ArrayList<>();File dir = new File(framePath);if (!dir.exists()) {dir.mkdirs();}try (FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoUrl.getPath());Java2DFrameConverter converter = new Java2DFrameConverter()) {ff.start();ff.setFormat("mp4");int length = ff.getLengthInFrames();Frame frame;for (int i = 1; i < length; i++) {frame = ff.grabFrame();if (frame.image == null) {continue;}BufferedImage image = converter.getBufferedImage(frame); ;String path = framePath + i + ".png";File picFile = new File(path);ImageIO.write(image, "png", picFile);strList.add(path);}IMAGE_CACHE.put("img", strList);ff.stop();}catch (Exception e) {log.error(e.getMessage());}}@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("Starting to extract video frames");getVideoPic();log.info("Extracting video frames is complete");}@PreDestroypublic void destroy() {try {deleteDirectory(new File(framePath));}catch (IOException e) {log.error(e.getMessage());}log.info("Delete temporary files...");}public static List<String> getFrameList() {assert IMAGE_CACHE.get("img") != null;return IMAGE_CACHE.get("img");}public static List<Media> createMediaList(int numberOfImages) {List<String> imgList = IMAGE_CACHE.get("img");int totalFrames = imgList.size();int interval = Math.max(totalFrames / numberOfImages, 1);return IntStream.range(0, numberOfImages).mapToObj(i -> imgList.get(i * interval)).map(image -> new Media(MimeType.valueOf("image/png"),new PathResource(image))).collect(Collectors.toList());}
}6. 运行与测试
- 启动应用:运行你的 Spring Boot 主程序。
- 使用浏览器或 API 工具(如 Postman, curl)进行测试。
测试 1:图像理解
访问以下 URL,让 AI 模型分析图片内容。
http://localhost:7878/api/image?prompt=请描述这张图片中的内容预期响应:
这张图片中有一个小女孩和一只狗。女孩看起来很年轻,可能正在与狗互动。狗似乎是一只金毛寻回犬或类似的品种,毛发呈金黄色。他们可能在一个户外环境中,背景看起来有些模糊,但可能是草地或公园。整体氛围看起来很温馨,展现了人与宠物之间的亲密关系。
测试 2:流式图像理解
访问以下 URL,以流式方式获取图像理解结果。
http://localhost:7878/api/stream/image?prompt=图片中有哪些动物和人物?预期响应:
图片中有一只狗和一个小女孩。狗看起来是一只金毛寻回犬,毛发呈金黄色。小女孩看起来年龄很小,可能只有几岁。他们似乎在一个户外环境中,女孩可能正在与狗互动或玩耍。图片整体呈现出一种温馨的氛围,展现了儿童与宠物之间的友好关系。
测试 3:视频内容分析
访问以下 URL,让 AI 模型分析视频内容。
http://localhost:7878/api/video?prompt=请描述这个视频中发生了什么预期响应:
根据视频帧分析,这个视频似乎记录了一个户外场景。视频中可能包含人物活动或自然景观。由于只提取了部分关键帧,无法提供完整的视频内容描述,但从这些帧可以看出视频可能涉及一些动态场景或事件。如果需要更详细的分析,建议提供更具体的视频内容或增加提取的帧数。
7. 实现思路与扩展建议
实现思路
本示例的核心思想是"多模态 AI 能力集成"。通过 Spring AI Alibaba 框架,我们能够轻松集成 ARK 平台提供的多模态 AI 能力,包括:
- 图像理解:通过将图像作为输入,让 AI 模型识别并描述图像内容。
- 视频分析:通过提取视频关键帧,让 AI 模型理解视频内容。
- 流式响应:支持流式返回 AI 模型的分析结果,提升用户体验。
扩展建议
- 多图像处理:扩展系统以支持同时处理多张图像,实现更复杂的图像理解场景。
- 视频摘要生成:基于视频内容分析,自动生成视频摘要或关键信息提取。
- 音频集成:添加音频处理能力,实现视频中的语音识别和情感分析。
- 实时视频流分析:集成实时视频流处理能力,适用于监控、直播等场景。
- 多模型对比:集成多个 AI 模型,对比不同模型在相同任务上的表现。
- 自定义模型微调:基于特定领域数据,对 ARK 模型进行微调,提升特定场景的识别准确率。
