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 = "这是一组从视频中提取的图片帧,请描述此视频中的内容。"; @Autowired private 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> */@Componentpublic 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 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/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 = "这是一组从视频中提取的图片帧,请描述此视频中的内容。"; @Autowired private 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 图片处理功能测试
测试步骤:
- 发送 GET 请求至
/api/image
,携带提示文本参数。检查响应结果是否符合预期。测试结果:假设请求 URL 为 http://localhost:8080/api/image?prompt=这是一张什么照片?
,响应结果如下:
{ "result": "这是一张在海滩上拍摄的照片,照片中有一个人和一只狗。"}
6.2 视频帧提取功能测试
测试步骤:
- 调用
FrameExtraHelper.createMediaList(10)
方法,提取 10 帧图片。检查返回的 Media
列表是否正确。测试结果:成功返回 10 个 Media
对象,每个对象包含一张视频帧图片。
7. 总结
本文详细介绍了 Spring AI Alibaba ARK Multi-Model
的实现原理、架构设计以及关键参数的配置和使用方法。通过具体的代码示例和测试验证,我们验证了项目的功能正确性和稳定性。希望本文能为读者理解和应用 Spring AI 和 Alibaba ARK 平台提供有价值的参考。
8. 参考资料
以上就是本次技术博客的全部内容,感谢阅读!如果有任何问题或建议,请随时留言交流。