
本示例演示了如何使用 Spring AI Alibaba Starter 与 Dashscope 的多模态服务,实现图像识别、视频分析和音频处理功能。
1. 案例目标
我们将创建一个包含多个核心功能的 Web 应用:
- 图像识别 (
/dashscope/multi/image):通过 URL 或二进制数据提供图像,让 AI 识别并描述图像内容。 - 视频分析 (
/dashscope/multi/video):提取视频帧并让 AI 分析视频内容。 - 音频处理 (
/dashscope/multi/audio):提供音频文件,让 AI 分析音频内容。 - 流式图像识别 (
/dashscope/multi/stream/image):以流式方式获取图像识别结果。
2. 技术栈与核心依赖
- Spring Boot 3.x
- Spring AI Alibaba (用于对接阿里云 DashScope 通义大模型)
- JavaCV (用于视频帧提取)
- Maven (项目构建工具)
在 pom.xml 中,你需要引入以下核心依赖:
<dependencies>
<!-- Spring Boot Web 用于构建 RESTful API -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba 核心启动器,集成 DashScope -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<!-- JavaCV 用于视频帧提取 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
</dependencies>
3. 项目配置
在 src/main/resources/application.yml 文件中,配置你的 DashScope API Key。
server:
port: 10013
spring:
application:
name: spring-ai-alibaba-dashscope-multi-model-example-application
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 建议使用环境变量,更安全
重要提示 :请将 AI_DASHSCOPE_API_KEY 环境变量设置为你从阿里云获取的有效 API Key。你也可以直接将其写在配置文件中,但这不推荐用于生产环境。
4. 初始化客户端
在控制器中,我们需要初始化一个 Spring AI Alibaba 的 ChatClient 对象实例:
@RestController
@RequestMapping("/dashscope/multi")
public class MultiModelController {
private final ChatClient dashScopeChatClient;
public MultiModelController(ChatModel chatModel) {
this.dashScopeChatClient = ChatClient.builder(chatModel).build();
}
// 其他方法...
}
5. 实现多模型功能
5.1 图像识别功能
实现基于 URL 的图像识别功能:
@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).metadata(new HashMap<>()).build();
message.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.IMAGE);
ChatResponse response = dashScopeChatClient
.prompt(new Prompt(message,
DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build()))
.call()
.chatResponse();
return response.getResult().getOutput().getText();
}
5.2 视频分析功能
实现视频分析功能,首先需要提取视频帧:
@Component
public final class FrameExtraHelper implements ApplicationRunner {
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/";
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());
}
}
// 其他方法...
}
然后实现视频分析接口:
@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).metadata(new HashMap<>()).build();
message.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.VIDEO);
ChatResponse response = dashScopeChatClient
.prompt(new Prompt(message,
DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build()))
.call()
.chatResponse();
return response.getResult().getOutput().getText();
}
5.3 音频处理功能
实现音频处理功能:
@GetMapping("/audio")
public String audio(
@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_AUDIO_PROMPT) String prompt) {
Media media = new Media(MediaType.parseMediaType("audio/mpeg"),
URI.create("https://dashscope.oss-cn-beijing.aliyuncs.com/audios/welcome.mp3"));
UserMessage message =
UserMessage.builder().text(prompt).media(media).metadata(new HashMap<>()).build();
message.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.AUDIO);
ChatResponse response = dashScopeChatClient
.prompt(new Prompt(message,
DashScopeChatOptions.builder().withModel("qwen-audio-turbo-latest").withMultiModel(true).build()))
.call()
.chatResponse();
return response.getResult().getOutput().getText();
}
5.4 二进制图像识别功能
实现基于二进制数据的图像识别功能:
@GetMapping("/image/bin")
public String imagesBinary(
@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) throws Exception {
List<Media> mediaList = List.of(new Media(MimeTypeUtils.IMAGE_JPEG,
resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg")));
UserMessage message =
UserMessage.builder().text(prompt).media(mediaList).metadata(new HashMap<>()).build();
message.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.IMAGE);
ChatResponse response = dashScopeChatClient
.prompt(new Prompt(message,
DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build()))
.call()
.chatResponse();
return response.getResult().getOutput().getText();
}
5.5 流式图像识别功能
实现流式图像识别功能:
@GetMapping("/stream/image")
public String streamImage(
@RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt) {
List<Media> mediaList = List.of(new Media(MimeTypeUtils.IMAGE_JPEG,
resourceLoader.getResource("classpath:/multimodel/dog_and_girl.jpeg")));
UserMessage message = UserMessage.builder().text(prompt).media(mediaList).metadata(new HashMap<>()).build();
message.getMetadata().put(DashScopeApiConstants.MESSAGE_FORMAT, MessageFormat.IMAGE);
List<ChatResponse> response = dashScopeChatClient
.prompt(new Prompt(message,
DashScopeChatOptions.builder().withModel(DEFAULT_MODEL).withMultiModel(true).build()))
.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();
}
6. 运行与测试
- 启动应用 :运行 Spring Boot 主程序
DashScopeMultiModelApplication。 - 等待视频帧提取完成:应用启动后会自动提取视频帧,请等待日志显示"Extracting video frames is complete"。
- 使用浏览器或 API 工具(如 Postman, curl)进行测试。
测试 1:图像识别
访问以下 URL,测试图像识别功能:
http://localhost:10013/dashscope/multi/image
预期响应:
这是一张在海滩上拍摄的照片,照片中有一位女士和一只狗。女士坐在沙滩上,微笑着与狗互动,狗伸出前爪与她握手。背景是大海和天空,阳光洒在她们身上,营造出温暖和谐的氛围。
测试 2:视频分析
访问以下 URL,测试视频分析功能:
http://localhost:10013/dashscope/multi/video
预期响应:
这组图片展示了一位女士坐在沙滩上,背对着镜头,面向大海。她穿着一件粉色的连衣裙,头发自然垂下。背景是广阔的海洋和蓝天白云,天气晴朗,海面平静。这位女士似乎在享受宁静的海滩时光,可能是在思考或放松。整个场景给人一种宁静和平静的感觉。
测试 3:音频处理
访问以下 URL,测试音频处理功能:
http://localhost:10013/dashscope/multi/audio
预期响应:
这是一个欢迎音频,内容为"欢迎使用阿里云通义千问大模型服务"。
测试 4:二进制图像识别
访问以下 URL,测试二进制图像识别功能:
http://localhost:10013/dashscope/multi/image/bin
预期响应:与测试 1 相同,因为使用的是同一张图片。
测试 5:流式图像识别
访问以下 URL,测试流式图像识别功能:
http://localhost:10013/dashscope/multi/stream/image
预期响应:与测试 1 相同,但响应是以流式方式获取的。
7. 实现思路与扩展建议
实现思路
本案例的核心思想是**"多模态数据处理"**。我们通过 Spring AI Alibaba 提供的统一接口,处理不同类型的媒体数据(图像、视频、音频),并利用 DashScope 的多模态能力进行分析和理解。这使得:
- 接口统一:不同类型的媒体数据可以通过相同的 API 接口进行处理,简化了开发流程。
- 功能强大:利用大模型的多模态能力,可以实现对图像、视频、音频内容的深度理解。
- 易于扩展:基于 Spring Boot 的架构,可以方便地添加新的功能模块。
扩展建议
- 批量处理:扩展接口以支持批量处理多个图像或视频文件。
- 自定义模型:根据业务需求,选择不同的 DashScope 模型进行处理。
- 结果缓存:对于相同的输入,可以增加缓存层,避免重复调用 API,提升性能。
- 异步处理:对于耗时的视频处理,可以实现异步任务队列,提升用户体验。
- 安全性增强:添加文件上传大小限制、类型验证等安全措施。
- 集成更多模态:扩展支持更多类型的媒体数据,如 3D 模型、AR/VR 内容等。