当前位置: 首页 > news >正文

Spring AI应用系列——基于ARK实现多模态模型应用

ARK 在这里指的是阿里云推出的 AIGC 研发平台 ARK,是阿里云面向开发者和企业用户打造的一站式 AIGC(AI Generated Content,人工智能生成内容)开发平台。

1. 引言

本文将深入探讨 ARK Multi-Model 的实现原理、架构设计以及关键参数的配置和使用方法。通过具体的代码示例和测试验证,我们将全面理解如何利用 Spring AI 和 Alibaba ARK 平台构建一个多模态模型应用。

2. 项目概述

Spring AI Alibaba ARK Multi-Model Example 是一个基于 Spring AI 和 Alibaba ARK 平台的多模态模型应用示例。它集成了聊天、图片生成、文本向量等多种模型能力,并通过 REST 接口提供服务。

3. 项目架构

项目的核心组件包括:

  • Spring Boot:作为基础框架,提供便捷的 Web 开发支持。
  • Spring AI:封装了多种 AI 模型的调用接口,简化了模型集成的复杂度。
  • Alibaba ARK:阿里云推出的一站式 AIGC 开发平台,提供了大模型 API 服务。
4. 核心功能模块
4.1 图片处理功能

MultiModelController.java

/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.alibaba.cloud.ai.example.controller;import com.alibaba.cloud.ai.example.controller.helper.FrameExtraHelper;
import org.apache.catalina.User;
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.InMemoryChatMemory;
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.image.Image;
import org.springframework.ai.model.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;
import java.util.Map;/*** ark Multi-Model REST Controller* 提供聊天、图片生成、文本向量等多个模型能力的API接口** @author brian xiadong*/
@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 的参数// {@link org.springframework.ai.chat.client.ChatClient};this.openAiChatClient = ChatClient.builder(chatModel)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))// 实现 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()));UserMessage message = new UserMessage(prompt, mediaList);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 = new UserMessage(prompt,new Media(MimeTypeUtils.IMAGE_JPEG,new ClassPathResource("multimodel/dog_and_girl.jpeg")));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 = new UserMessage(prompt, mediaList);ChatResponse response = openAiChatClient.prompt(new Prompt(message)).call().chatResponse();return response.getResult().getOutput().getText();}
}

功能解析

  • 参数说明
    • @RequestParam(value = "prompt", required = false, defaultValue = DEFAULT_PROMPT) String prompt:用户输入的提示文本,默认值为“这些是什么?”。
    • mediaList:包含图片资源的媒体列表,这里使用了一个固定 URL 的图片资源。
  • 实现逻辑
    • 创建 UserMessage 对象,包含提示文本和媒体列表。
    • 通过 openAiChatClient.prompt() 方法发送请求,并获取响应结果。
4.2 视频帧提取功能

FrameExtraHelper.java

/** Copyright 2024 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/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.model.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;/*** @author yuluo* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>*/@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());}}

功能解析

  • 参数说明
    • int numberOfImages:需要提取的图片帧数量。
  • 实现逻辑
    • IMAGE_CACHE 中获取所有图片帧路径。
    • 根据 numberOfImages 计算间隔,均匀选择图片帧。
    • 将选中的图片帧转换为 Media 对象并返回。
5. 参数配置与使用
5.1 application.yml 配置

application.yml

spring:ai:openai:# API Key Configuration。api-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: 8080logging:level:org:springframework:ai:chat:client:advisor: DEBUG

配置解析

  • api-key:ARK 平台的 API 密钥,用于身份验证。
  • base-url:ARK LLM API 的基础 URL。
  • model:使用的模型 ID。
  • completions-path:Chat API 的路径,与 OpenAI 接口保持一致。
5.2 ChatClient 构造参数
@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 的参数// {@link org.springframework.ai.chat.client.ChatClient};this.openAiChatClient = ChatClient.builder(chatModel)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(OpenAiChatOptions.builder().topP(0.7).build()).build();}}

参数解析

  • MessageChatMemoryAdvisor:实现 Chat Memory 的 Advisor,用于管理对话上下文。
  • SimpleLoggerAdvisor:实现 Logger 的 Advisor,用于日志记录。
  • OpenAiChatOptions:设置 ChatModel 的选项参数,如 topP(采样策略)。
6. 测试验证

为了验证功能的正确性,我们进行以下测试:

6.1 图片处理功能测试

测试步骤

  1. 发送 GET 请求至 /api/image,携带提示文本参数。
  2. 检查响应结果是否符合预期。

测试结果
假设请求 URL 为 http://localhost:8080/api/image?prompt=这是一张什么照片?,响应结果如下:

{"result": "这是一张在海滩上拍摄的照片,照片中有一个人和一只狗。"
}
6.2 视频帧提取功能测试

测试步骤

  1. 调用 FrameExtraHelper.createMediaList(10) 方法,提取 10 帧图片。
  2. 检查返回的 Media 列表是否正确。

测试结果
成功返回 10 个 Media 对象,每个对象包含一张视频帧图片。

7. 总结

本文详细介绍了 Spring AI Alibaba ARK Multi-Model 的实现原理、架构设计以及关键参数的配置和使用方法。通过具体的代码示例和测试验证,我们验证了项目的功能正确性和稳定性。希望本文能为读者理解和应用 Spring AI 和 Alibaba ARK 平台提供有价值的参考。

8. 参考资料
  • Spring AI 官方文档
  • Alibaba ARK 平台文档

以上就是本次技术博客的全部内容,感谢阅读!如果有任何问题或建议,请随时留言交流。

相关文章:

  • OpenHarmony - 小型系统内核(LiteOS-A)(十七)标准库
  • 游戏性能测试
  • 【Linux庖丁解牛】—环境变量!
  • C#规避内存泄漏的编码方法
  • 嵌入式软件--stm32 DAY 5 USART串口通讯(上)
  • linux中sigint和sigterm的区别
  • CSS:选择器-基本选择器
  • 虚实结合赋能嵌入式教育:基于嵌入式仿真实验教学平台的智能门禁系统实验深度解析
  • 通义灵码全面接入Qwen3:AI编程进入智能体时代,PAI云上部署实战解析
  • 文章记单词 | 第51篇(六级)
  • CKESC STONE 80A-L 电调专业测评
  • VARIAN安捷伦真空泵维修清洁保养操作SOP换油操作流程内部转子图文并茂内部培训手侧
  • 客户联络中心如何进行能力建设?
  • Elastic Security 8.18 和 9.0 中的新功能
  • 2025年Jetpack Compose集成网络请求库的完整实施方案
  • SQL Server数据库提权的几种方法——提权教程
  • Oracle 10g DG 状态检查和恢复同步
  • python安装和环境配置,开发方法简要步骤。
  • 慧星云支持 Qwen3:开启智算新生态,共筑高效 AI 未来
  • RPG4.设置角色输入
  • “五一”假期首日国铁郑州局迎大客流,预计发送旅客逾95万人次
  • 南京106亿元成交19宗涉宅地块:建邺区地块楼面单价重回4.5万元
  • 新型算法助力听障人士听得更清晰
  • 郭向阳任广东省公安厅分管日常工作副厅长(正厅级)
  • 郭继孚被撤销全国政协委员资格,此前为北京交通发展研究院长
  • 辽宁省全力开展辽阳一饭店火灾事故救援处置工作