Spring AI Alibaba 多模态开发:集成视觉理解与视频分析能力
导读:多模态是当前大模型最热的能力扩展方向------不只是"聊天",而是真正能"看图"、"看视频"、"生图"。本文基于 Spring AI Alibaba 1.1 版,系统讲解多模态架构设计、图文混合 API 调用、视频理解以及文生图能力,并结合三个真实业务场景给出完整代码示例。
一、多模态的价值与应用场景
在和客户聊需求时,有一句话我说了很多次:"文本对话只是 AI 能力的冰山一角。"一旦模型能理解图像和视频,能处理的业务场景会呈指数级扩大:
传统文本 AI 能做的:
问答 / 摘要 / 翻译 / 代码生成 ...
多模态 AI 能做的:
电商商品识别 → 自动生成详情页文案
监控视频分析 → 异常行为检测告警
医疗影像描述 → 辅助医生初步筛查
产品图片质检 → 自动化缺陷识别
证件 OCR → 信息结构化提取
...
通义千问视觉系列(qwen-vl)是国内开放多模态能力最完善的模型之一,Spring AI Alibaba 1.1 版对其做了完整的 API 适配。
二、多模态架构基础
2.1 核心概念:Media 对象
在 Spring AI 的多模态设计中,Media 是承载非文本内容(图片、音频、视频)的核心对象:
java
// Media 对象的核心字段
public class Media {
MimeType mimeType; // 内容类型,如 IMAGE_JPEG、VIDEO_MP4
Object data; // 实际数据:URL 字符串、byte[]、或 Resource
}
UserMessage 支持携带多个 Media 附件,实现图文混合输入:
java
// 文字 + 图片 = 多模态消息
UserMessage multimodalMessage = UserMessage.builder()
.text("请描述这张图片的内容")
.media(new Media(MimeTypeUtils.IMAGE_JPEG, imageResource))
.build();
2.2 多模态处理流程
输入阶段 处理阶段 输出阶段
+----------+ +-------------+ +---------+
| 文本消息 | | | | |
+----------+ -------> | qwen-vl-max | -------> | 文本 |
| 图片/URL | | | | 回复 |
+----------+ (组装成 +-------------+ | |
| 视频文件 | UserMessage) | +---------+
+----------+ | Spring AI
| 自动序列化
| (URL/Base64)
2.3 MIME 类型速查
+----------------+----------------------------------+
| 类型 | Spring AI MimeType |
+----------------+----------------------------------+
| JPEG 图片 | MimeTypeUtils.IMAGE_JPEG |
| PNG 图片 | MimeTypeUtils.IMAGE_PNG |
| GIF 图片 | MimeTypeUtils.IMAGE_GIF |
| WEBP 图片 | MimeType.valueOf("image/webp") |
| MP4 视频 | MimeType.valueOf("video/mp4") |
| AVI 视频 | MimeType.valueOf("video/avi") |
| PDF 文档 | MimeTypeUtils.APPLICATION_PDF |
+----------------+----------------------------------+
三、依赖与配置
3.1 Maven 依赖
xml
<dependencies>
<!-- Spring AI Alibaba DashScope(含多模态支持) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 文件上传支持 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
</dependencies>
3.2 YAML 配置
yaml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
options:
# 多模态任务默认使用 qwen-vl-max
model: qwen-vl-max
max-tokens: 2048
# 文件上传大小限制(视频文件可能较大)
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
四、视觉理解:图片分析实战
4.1 图片 URL 模式(推荐)
通过 URL 传图是最简单的方式,适合图片已经上传至 CDN 或公网可访问的场景:
java
@Service
@RequiredArgsConstructor
@Slf4j
public class VisionService {
private final ChatClient chatClient;
/**
* 图片 URL 模式:分析公网图片
*
* @param imageUrl 图片的公网 URL
* @param question 关于图片的问题
*/
public String analyzeImageByUrl(String imageUrl, String question) {
log.info("分析图片:{}", imageUrl);
// 构建携带图片 URL 的 UserMessage
UserMessage userMessage = UserMessage.builder()
.text(question)
.media(new Media(
MimeTypeUtils.IMAGE_JPEG,
new URL(imageUrl) // 直接传 URL 对象
))
.build();
return chatClient.prompt()
.messages(userMessage)
.options(DashScopeChatOptions.builder()
.withModel("qwen-vl-max")
.build())
.call()
.content();
}
/**
* 多图对比分析:同时传入多张图片
*
* @param imageUrls 图片 URL 列表
* @param question 关于这些图片的问题
*/
public String compareImages(List<String> imageUrls, String question) {
// 构建多 Media 附件的 UserMessage
List<Media> mediaList = imageUrls.stream()
.map(url -> {
try {
return new Media(MimeTypeUtils.IMAGE_JPEG, new URL(url));
} catch (Exception e) {
throw new RuntimeException("无效的图片 URL: " + url, e);
}
})
.toList();
// UserMessage 构建器支持传入 Media 列表
UserMessage message = UserMessage.builder()
.text(question)
.media(mediaList.toArray(new Media[0]))
.build();
return chatClient.prompt()
.messages(message)
.call()
.content();
}
}
4.2 本地文件模式(Base64 编码)
当图片存储在本地或无法提供公网 URL 时,可以读取文件并以 Base64 形式传输:
java
/**
* 本地文件模式:读取本地图片进行分析
*
* @param imageFile 上传的图片文件
* @param question 分析问题
*/
public String analyzeLocalImage(MultipartFile imageFile, String question) throws IOException {
// 获取文件 MimeType
String contentType = imageFile.getContentType();
MimeType mimeType = MimeType.valueOf(
contentType != null ? contentType : "image/jpeg");
// 读取文件内容为 byte[]
byte[] imageBytes = imageFile.getBytes();
// 构建 Media 对象(传 byte[] 时,Spring AI 自动 Base64 编码)
Media imageMedia = new Media(mimeType, imageBytes);
UserMessage message = UserMessage.builder()
.text(question)
.media(imageMedia)
.build();
return chatClient.prompt()
.messages(message)
.call()
.content();
}
4.3 REST 接口封装
java
@RestController
@RequestMapping("/api/vision")
@RequiredArgsConstructor
public class VisionController {
private final VisionService visionService;
/**
* URL 图片分析接口
*/
@GetMapping("/analyze")
public ResponseEntity<Map<String, String>> analyzeByUrl(
@RequestParam String imageUrl,
@RequestParam(defaultValue = "请详细描述图片内容") String question) {
try {
String result = visionService.analyzeImageByUrl(imageUrl, question);
return ResponseEntity.ok(Map.of("result", result));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(Map.of("error", e.getMessage()));
}
}
/**
* 本地图片上传分析接口
*/
@PostMapping("/upload-analyze")
public ResponseEntity<Map<String, String>> analyzeUpload(
@RequestParam("file") MultipartFile file,
@RequestParam(defaultValue = "请详细描述图片内容") String question) {
try {
String result = visionService.analyzeLocalImage(file, question);
return ResponseEntity.ok(Map.of("result", result));
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(Map.of("error", "图片分析失败:" + e.getMessage()));
}
}
}
五、视频分析:时序理解与 VideoQA
5.1 视频分析架构
视频分析比图片复杂,qwen-vl 处理视频的方式是对视频进行帧采样,然后将关键帧序列连同问题一起传入模型进行时序理解。
原始视频文件
|
| (上传至 OSS/视频服务)
v
视频 URL(公网可访问)
|
v
Spring AI Media 对象 (video/mp4)
|
v
qwen-vl 模型(内部进行帧采样与时序分析)
|
v
视频内容描述 / 问题回答
5.2 视频分析代码实现
java
@Service
@RequiredArgsConstructor
@Slf4j
public class VideoAnalysisService {
private final ChatClient chatClient;
/**
* 视频内容理解:通过 URL 分析视频
*
* @param videoUrl 视频的公网 URL(需先上传至 OSS)
* @param question 关于视频的问题
*/
public String analyzeVideo(String videoUrl, String question) {
log.info("开始分析视频:{}", videoUrl);
long startTime = System.currentTimeMillis();
try {
Media videoMedia = new Media(
MimeType.valueOf("video/mp4"),
new URL(videoUrl)
);
UserMessage message = UserMessage.builder()
.text(question)
.media(videoMedia)
.build();
String result = chatClient.prompt()
.messages(message)
.options(DashScopeChatOptions.builder()
// 视频理解使用专用模型
.withModel("qwen-vl-max")
// 视频分析通常需要更多输出空间
.withMaxTokens(4096)
.build())
.call()
.content();
log.info("视频分析完成,耗时:{}ms", System.currentTimeMillis() - startTime);
return result;
} catch (Exception e) {
log.error("视频分析失败:{}", e.getMessage(), e);
throw new RuntimeException("视频分析失败", e);
}
}
/**
* 监控视频异常检测
* 专门针对安防监控场景优化的 Prompt
*/
public String detectAnomalies(String videoUrl) {
String prompt = """
请分析这段监控视频,重点关注以下异常行为:
1. 人员聚集或打架斗殴
2. 可疑物品遗留
3. 未授权区域入侵
4. 异常跌倒或昏厥事件
5. 火灾烟雾迹象
请以结构化方式输出:
- 是否检测到异常:是/否
- 异常类型(如有):
- 发生时间段(大约):
- 建议处理方式:
""";
return analyzeVideo(videoUrl, prompt);
}
/**
* 教学视频内容摘要
*/
public String summarizeVideo(String videoUrl) {
String prompt = """
请观看这段教学视频,并提供:
1. 视频主题(一句话概括)
2. 主要知识点(3-5个要点)
3. 视频结构(时间线摘要)
4. 适合人群建议
""";
return analyzeVideo(videoUrl, prompt);
}
}
六、文生图:wanx 模型集成
通义千问 wanx 模型提供文生图能力,Spring AI Alibaba 通过 ImageClient 进行集成(注意:图像生成使用的是 ImageModel,不是 ChatModel)。
6.1 文生图配置
yaml
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
image:
options:
# 文生图模型
model: wanx-v1
# 图片尺寸:1024x1024 / 720x1280 / 1280x720
size: 1024x1024
# 生成数量(1-4张)
n: 1
# 图片质量:standard / hd
style: <auto>
6.2 文生图实现
java
@Service
@RequiredArgsConstructor
@Slf4j
public class ImageGenerationService {
// 注入 ImageModel(与 ChatModel 不同的接口)
private final ImageModel imageModel;
/**
* 文生图:根据文字描述生成图片
*
* @param prompt 图片描述 Prompt
* @return 生成图片的 URL 列表
*/
public List<String> generateImages(String prompt) {
log.info("文生图请求:{}", prompt);
ImageOptions options = DashScopeImageOptions.builder()
.withModel("wanx-v1")
.withN(1)
.withWidth(1024)
.withHeight(1024)
.build();
ImagePrompt imagePrompt = new ImagePrompt(prompt, options);
ImageResponse response = imageModel.call(imagePrompt);
List<String> imageUrls = response.getResults().stream()
.map(result -> result.getOutput().getUrl())
.filter(url -> url != null && !url.isEmpty())
.toList();
log.info("生成了 {} 张图片", imageUrls.size());
return imageUrls;
}
/**
* 商品图生成:针对电商场景优化的 Prompt 工程
*
* @param productName 商品名称
* @param style 风格描述(如:简约、奢华、科技感)
* @param background 背景描述(如:白色背景、户外场景)
*/
public List<String> generateProductImage(
String productName, String style, String background) {
// 电商图片专用 Prompt 模板
String enhancedPrompt = String.format(
"%s,%s风格,%s,产品摄影,高清质感,专业布光," +
"细节清晰,适合电商展示,8K超高清",
productName, style, background
);
return generateImages(enhancedPrompt);
}
}
七、三大业务场景实战
场景一:电商商品图文描述
java
@Service
@RequiredArgsConstructor
public class EcommerceVisionService {
private final VisionService visionService;
private final ImageGenerationService imageGenerationService;
/**
* 上传商品图片,自动生成详情页文案
*/
public Map<String, Object> processProductImage(String imageUrl, String productCategory) {
String prompt = String.format("""
这是一张%s类目的商品图片,请分析并生成以下内容:
1. 【商品标题】(30字以内,包含核心卖点)
2. 【卖点提炼】(5个核心卖点,每条不超过15字)
3. 【商品描述】(200字左右,突出品质和使用场景)
4. 【规格参数】(从图片可识别的尺寸、颜色、材质等)
5. 【适用人群】(目标消费者描述)
请以 JSON 格式输出。
""", productCategory);
String rawResult = visionService.analyzeImageByUrl(imageUrl, prompt);
Map<String, Object> result = new HashMap<>();
result.put("imageUrl", imageUrl);
result.put("rawAnalysis", rawResult);
result.put("category", productCategory);
result.put("timestamp", LocalDateTime.now().toString());
return result;
}
}
场景二:医疗影像初筛(辅助)
java
/**
* 医疗影像描述辅助(注意:仅作参考,不替代专业医学诊断)
*/
public String describeMedicalImage(String imageUrl, String imageType) {
String prompt = String.format("""
这是一张%s医学影像图,请以专业但通俗的方式描述:
1. 图像基本信息(检查类型、扫描部位)
2. 图像质量评估
3. 可见的主要解剖结构
4. 明显可见的影像特征描述
重要提示:以上描述仅为影像特征的客观描述,
不构成医学诊断建议,请以专业医生判断为准。
""", imageType);
return visionService.analyzeImageByUrl(imageUrl, prompt);
}
场景三:文档图片 OCR 与结构化提取
java
/**
* 证件/票据 OCR 信息提取
*/
public Map<String, String> extractDocumentInfo(MultipartFile documentImage,
String documentType) throws IOException {
String prompt = String.format("""
这是一张%s图片,请提取其中所有可见的文字信息,
并以 JSON 格式输出,key 为字段名称,value 为字段值。
对于无法识别的字段,value 设为 null。
只输出 JSON,不要其他解释文字。
""", documentType);
String jsonResult = visionService.analyzeLocalImage(documentImage, prompt);
// 解析 JSON 结果
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(
jsonResult.replaceAll("```json|```", "").trim(),
new TypeReference<>() {});
} catch (JsonProcessingException e) {
log.warn("JSON 解析失败,返回原始文本");
return Map.of("rawText", jsonResult);
}
}
八、多模态 Prompt 工程技巧
8.1 图像分析 Prompt 最佳实践
❌ 差的 Prompt:
"描述这张图"
✅ 好的 Prompt:
"这是一张[场景类型]图片,请从以下维度进行分析:
1. [维度1]:具体说明
2. [维度2]:具体说明
请以结构化方式输出结果。"
关键原则:
- 明确任务:告诉模型你期望的输出格式;
- 提供上下文:说明图片类型、场景背景;
- 分维度提问:复杂图片按维度拆解;
- 指定专业视角:如"以产品经理视角"、"以安防工程师视角"。
8.2 文生图 Prompt 优化技巧
文生图的 Prompt 质量直接决定生成效果,以下是提升图片质量的关键词组合:
基础结构:主体描述 + 风格修饰 + 技术参数 + 质量标签
示例:
"[主体] 一只在樱花树下打盹的橘猫
[风格] 日本动漫风格,吉卜力工作室风格
[场景] 春天下午,阳光明媚
[技术] 8K超高清,专业插画,细节丰富
[质量] 最佳质量,杰作"
效果对比:
"一只猫" → 效果普通
"在樱花树下打盹的橘猫,日本动漫风格,8K超高清,最佳质量" → 效果明显更好
九、常见问题与注意事项
Q:图片大小有限制吗?
DashScope API 对单张图片的限制通常是 20MB,建议超大图片先压缩再传输:
java
// 使用 ImageIO 压缩图片
private byte[] compressImage(byte[] originalBytes, float quality) throws IOException {
BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(originalBytes));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality); // 0.0~1.0
writer.setOutput(ImageIO.createImageOutputStream(outputStream));
writer.write(null, new IIOImage(originalImage, null, null), param);
return outputStream.toByteArray();
}
Q:视频长度有限制吗?
目前 qwen-vl 对视频长度有一定限制,通常建议控制在 5 分钟以内。超长视频建议先切片处理。
Q:Base64 还是 URL,哪个更推荐?
- 开发测试:URL 更方便,直接用图床链接;
- 生产环境:先上传到 OSS,再传 URL,更稳定且节省带宽;
- 隐私数据:Base64 适合不便公开的敏感图片,但会增加请求体积。
十、总结
Spring AI Alibaba 的多模态能力让 Java 应用从"纯文本 AI"升级到了"全感知 AI"。核心要点回顾:
- Media 对象是多模态的核心,封装了 MimeType + 内容数据;
- 图片输入支持 URL 和 Base64 两种模式,URL 模式更推荐;
- 视频分析通过帧采样实现时序理解,适合监控、教育等场景;
- 文生图 使用
ImageModel接口(非 ChatModel),通过 wanx 模型实现; - Prompt 工程对多模态质量影响极大,要明确维度、指定格式。
下一篇将进入多模型路由策略,探讨如何在通义千问、DeepSeek、OpenAI 之间实现动态选择、Fallback 降级和成本优化。
参考资料