
本示例将展示如何使用 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 Configuration
api-key: ${ARK_API_KEY:your-api-key}
# Ark LLM API Base URL
base-url: https://ark.cn-beijing.volces.com/api/
chat:
options:
# Model ID, replace with actual access point ID
model: ${ARK_MODEL_ID:your-model-id}
# Chat API path, consistent with OpenAI interface
completions-path: /v3/chat/completions
server:
port: 7878
logging:
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.mp4
4.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 = "这是一组从视频中提取的图片帧,请描述此视频中的内容。";
@Autowired
private 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());
}
}
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("Starting to extract video frames");
getVideoPic();
log.info("Extracting video frames is complete");
}
@PreDestroy
public 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 模型进行微调,提升特定场景的识别准确率。