网站建设衤金手指花总十四,写作网站vir,中国建设银行网站首页joy,家用电脑可以做网站服务器一、概论通义千问VL模型支持对视频内容进行理解#xff0c;文件形式包括图像列表#xff08;视频帧#xff09;或视频文件。视频抽帧说明通义千问VL 模型通过从视频中提取帧序列进行内容分析#xff0c;抽帧的频率决定了模型分析的精细度#xff0c;不同 SDK 抽帧频率不同…一、概论通义千问VL模型支持对视频内容进行理解文件形式包括图像列表视频帧或视频文件。视频抽帧说明通义千问VL 模型通过从视频中提取帧序列进行内容分析抽帧的频率决定了模型分析的精细度不同 SDK 抽帧频率不同使用DashScope SDK可通过fps参数来控制抽帧间隔每隔 fps1秒抽取一帧该参数范围为(0.1, 10)且默认值为2.0。建议为高速运动场景设置较高fps为静态或长视频设置较低fps。使用OpenAI兼容SDK采用固定频率抽帧每0.5秒1帧不支持自定义。简单解释以上的内容通义千问 VL 模型能 “看懂” 视频内容你可以给它传视频文件也可以传由视频画面帧组成的图片列表不过模型并不是逐帧看完整个视频而是抽取部分画面来分析抽帧的密集程度频率会影响分析的细致度不同使用方式的抽帧规则不一样公式用 DashScope SDK 时能自己调抽帧间隔靠 fps 参数—— 比如 fps 设 2就是每 二分之一 秒抽 1 帧参数能设 0.1 到 10 之间默认 2.0。如果视频里画面动得快比如球赛、车流就把 fps 设高一点抽帧更密看得更细如果是静态画面多的视频比如讲座、慢镜头或很长的视频就把 fps 设低一点抽帧疏一点省资源。用 OpenAI 兼容 SDK 时抽帧频率是固定的 —— 每 0.5 秒抽 1 帧没法自己调整。二、代码实现我们新增出一个接口出来专门来处理 视频 类型的数据第一步新增视频请求实体类VideoRequestimport com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.exception.UploadFileException; import gzj.spring.ai.Request.VideoRequest; import gzj.spring.ai.Service.VideoService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/multimodal/video) RequiredArgsConstructor CrossOrigin // 跨域支持生产环境建议限定域名 public class VideoController { private final VideoService videoService; RequestMapping(/simple) public String simpleVideoCall(RequestBody VideoRequest request) throws ApiException, NoApiKeyException, UploadFileException { return videoService.simpleVideoCall(request); } }第二步新增视频服务接口VideoServiceimport com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.exception.UploadFileException; import gzj.spring.ai.Request.VideoRequest; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; public interface VideoService { /** * 视频理解-普通调用非流式 * param request 视频请求参数 * return 视频理解结果文本 */ String simpleVideoCall(VideoRequest request) throws ApiException, NoApiKeyException, UploadFileException; /** * 视频理解-流式调用SSE推送 * param request 视频请求参数 * return SseEmitter 用于前端接收流式结果 */ SseEmitter streamVideoCall(VideoRequest request); }第三步新增视频服务实现类VideoServiceImplimport com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation; import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam; import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; import com.alibaba.dashscope.common.MultiModalMessage; import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.exception.UploadFileException; import gzj.spring.ai.Request.VideoRequest; import gzj.spring.ai.Service.VideoService; import io.reactivex.Flowable; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.util.*; import static com.alibaba.cloud.ai.graph.utils.TryConsumer.log; Service public class VideoServiceImpl implements VideoService { Value(${spring.ai.dashscope.api-key}) private String apiKey; /** * 构建视频请求参数封装video fps */ private MapString, Object buildVideoParams(VideoRequest request) { MapString, Object videoParams new HashMap(2); videoParams.put(video, request.getVideoUrl()); videoParams.put(fps, request.getFps()); log.info(视频抽帧配置fps{} → 每隔{}秒抽取一帧, request.getFps(), 1/request.getFps()); return videoParams; } /** * 视频理解-普通调用非流式 */ Override public String simpleVideoCall(VideoRequest request) throws ApiException, NoApiKeyException, UploadFileException { MultiModalConversation conv new MultiModalConversation(); // 1. 构建用户消息视频参数 提问文本 MultiModalMessage userMessage MultiModalMessage.builder() .role(Role.USER.getValue()) .content(Arrays.asList( buildVideoParams(request), // 视频fps参数 Collections.singletonMap(text, request.getQuestion()) // 提问文本 )).build(); // 2. 构建API请求参数 MultiModalConversationParam param MultiModalConversationParam.builder() .apiKey(apiKey) .model(qwen3-vl-plus) // 仅qwen3-vl-plus支持视频理解 .messages(Arrays.asList(userMessage)) .build(); // 3. 同步调用API MultiModalConversationResult result conv.call(param); // 4. 解析返回结果 ListMapString, Object content result.getOutput().getChoices().get(0).getMessage().getContent(); if (content ! null !content.isEmpty()) { return content.get(0).get(text).toString(); } return 未获取到视频理解结果; } /** * 视频理解-流式调用SSE推送 */ Override public SseEmitter streamVideoCall(VideoRequest request) { // 设置超时时间60秒视频处理耗时可能更长 SseEmitter emitter new SseEmitter(60000L); new Thread(() - { MultiModalConversation conv new MultiModalConversation(); try { // 1. 构建用户消息 MultiModalMessage userMessage MultiModalMessage.builder() .role(Role.USER.getValue()) .content(Arrays.asList( buildVideoParams(request), Collections.singletonMap(text, request.getQuestion()) )).build(); // 2. 构建流式请求参数 MultiModalConversationParam param MultiModalConversationParam.builder() .apiKey(apiKey) .model(qwen3-vl-plus) .messages(Arrays.asList(userMessage)) .incrementalOutput(true) // 增量输出流式 .build(); // 3. 流式调用API FlowableMultiModalConversationResult resultFlow conv.streamCall(param); resultFlow.blockingForEach(item - { try { ListMapString, Object content item.getOutput().getChoices().get(0).getMessage().getContent(); if (content ! null !content.isEmpty()) { String text content.get(0).get(text).toString(); // 推送流式数据到前端 emitter.send(SseEmitter.event().data(text)); } } catch (Exception e) { log.error(视频流式推送失败, e); handleEmitterError(emitter, 流式推送失败 e.getMessage()); } }); // 流式结束标记 emitter.send(SseEmitter.event().name(complete).data(视频理解流结束)); emitter.complete(); } catch (ApiException | NoApiKeyException | UploadFileException e) { log.error(视频流式调用API失败, e); handleEmitterError(emitter, API调用失败 e.getMessage()); } catch (Exception e) { log.error(视频流式调用未知异常, e); handleEmitterError(emitter, 系统异常 e.getMessage()); } }).start(); return emitter; } /** * 工具方法统一处理SSE发射器异常 */ private void handleEmitterError(SseEmitter emitter, String errorMsg) { try { emitter.send(SseEmitter.event().name(error).data(errorMsg)); emitter.completeWithError(new RuntimeException(errorMsg)); } catch (Exception e) { log.error(处理发射器异常失败, e); } } }总结以下是本次新增通义千问 VL 视频理解接口相关代码的核心总结一、代码新增模块与结构整体延续原有多模态服务的分层设计新增 4 个核心模块保持代码风格统一请求实体类VideoRequest封装视频理解所需参数包含videoUrl视频链接、fps抽帧频率、question提问文本通过NotNull/DecimalMin/DecimalMax做参数校验限定 fps 范围 0.1~10默认 2.0避免无效请求。服务接口VideoService定义两类调用方式 ——simpleVideoCall非流式、streamVideoCallSSE 流式与原有多模态接口设计一致。服务实现类VideoServiceImpl复用MultiModalConversation客户端仅调整消息内容为video fps参数 Map模型固定为qwen3-vl-plus仅该模型支持视频理解流式调用超时设为 60 秒适配视频抽帧 / 解析的耗时比图片的 30 秒更长复用统一的 SSE 异常处理方法handleEmitterError保证错误信息标准化推送。控制器VideoController暴露/api/multimodal/video/simple接口接收VideoRequest参数调用服务层完成视频理解请求。二、代码层面关键注意事项模型限制代码中硬绑定qwen3-vl-plus模型不可替换为其他模型如 qwen-vl 等不支持视频输入限制videoUrl仅支持视频文件的直接 HTTPS 链接如.mp4 格式非网页链接如抖音 / B 站的网页 URL异常兼容捕获ApiException/NoApiKeyException等通义千问 SDK 异常与原有多模态异常处理逻辑一致。三、核心设计亮点参数规范化fps 参数添加清晰注释每隔1/fps秒抽取一帧配合校验注解降低使用错误率逻辑复用性沿用原有多模态服务的客户端、异常处理、流式推送逻辑减少重复代码适配视频特性针对视频处理耗时更长的特点调整流式调用超时时间兼顾性能与稳定性。三、效果演示大家注意视频在线的视频一定要是mp4结尾的如果是别的格式的视频可能无法解析加密如果觉得这份修改实用、总结清晰别忘了动动小手点个赞再关注一下呀 后续还会分享更多 AI 接口封装、代码优化的干货技巧一起解锁更多好用的功能少踩坑多提效 你的支持就是我更新的最大动力咱们下次分享再见呀