Spring AI系列之使用 Spring AI 图像识别

1. 概述

在本教程中,我们将探索如何使用 Spring AI 和 OpenAI 的聊天模型,从图像中提取结构化数据。

OpenAI 的聊天模型可以分析上传的图像并返回相关信息。它还可以返回结构化的输出,便于将结果传递给其他应用程序以进行进一步操作。

为了说明这一点,我们将创建一个 Web 服务,用于接收来自客户端的图像并将其发送给 OpenAI,以统计图像中有多少辆彩色汽车。该 Web 服务将以 JSON 格式返回每种颜色的数量。

2. Spring Boot 配置

我们需要在 Maven 的 pom.xml 文件中添加以下依赖项:Spring Boot Web 启动器 和 Spring AI OpenAI 模型依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>

在我们的 

Spring Boot application.yml 配置文件中,必须提供用于身份验证的 API 密钥(spring.ai.openai.api-key),以及一个支持图像分析的聊天模型(spring.ai.openai.chat.options.model)。

目前有多种模型支持图像分析,例如 gpt-4o-mini

gpt-4ogpt-4.5-preview。较大的模型如 gpt-4o 拥有更广泛的知识,但成本较高;而较小的模型如 gpt-4o-mini 成本更低、延迟更小。我们可以根据实际需求选择合适的模型。

在本教程中,我们将选择 gpt-4o 聊天模型进行演示:

spring:
  ai:
    openai:
      api-key: "<YOUR-API-KEY>"
      chat:
        options:
          model: "gpt-4o"

配置完成后,Spring Boot 会自动加载 OpenAiAutoConfiguration,在应用启动时注册相关的 Bean,例如我们稍后将创建的 ChatClient


3. 示例 Web 服务

完成所有配置后,接下来我们将创建一个 Web 服务,允许用户上传图像,并将图像传递给 OpenAI,用于统计图像中彩色汽车的数量。

3.1. REST 控制器

在这个 REST 控制器中,我们通过请求参数接收一个图像文件和需要统计的颜色列表:

@RestController
@RequestMapping("/image")
public class ImageController {
    @Autowired
    private CarCountService carCountService;

    @PostMapping("/car-count")
    public ResponseEntity<?> getCarCounts(@RequestParam("colors") String colors,
      @RequestParam("file") MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            var carCount = carCountService.getCarCount(inputStream, file.getContentType(), colors);
            return ResponseEntity.ok(carCount);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error uploading image");
        }
    }
}

为了获得成功的响应,我们期望服务以CarCount类型的 

ResponseEntity 作为响应返回。


3.2. POJO

如果我们希望聊天模型返回结构化的输出,就需要在发给OpenAI 的 HTTP 请求中,以JSON Schema的形式定义输出格式。而在Spring AI中,我们可以通过定义 POJO(普通 Java 对象)类来大大简化这一过程。

我们将定义两个 POJO 类,用于存储颜色及其对应的数量。CarCount 类用于存储每种颜色对应的车辆数量列表以及总数(即列表中各项数量的总和):

public class CarCount {
    private List<CarColorCount> carColorCounts;
    private int totalCount;

    // constructor, getters and setters
}
    #CarColorCount 用于存储颜色名称及其对应的数量:
    public class CarColorCount {
        private String color;
        private int count;
    
        // constructor, getters and setters
    }
    3.3. 服务类(Service)

    现在,我们来创建核心的 Spring 服务,用于将图像发送到 OpenAI 的 API 进行分析。在这个CarCountService中,我们注入一个 ChatClientBuilder,它用于构建与 OpenAI 通信的 ChatClient:

    @Service
    public class CarCountService {
        private final ChatClient chatClient;
    
        public CarCountService(ChatClient.Builder chatClientBuilder) {
            this.chatClient = chatClientBuilder.build();
        }
    
        public CarCount getCarCount(InputStream imageInputStream, String contentType, String colors) {
            return chatClient.prompt()
              .system(systemMessage -> systemMessage
                .text("Count the number of cars in different colors from the image")
                .text("User will provide the image and specify which colors to count in the user prompt")
                .text("Count colors that are specified in the user prompt only")
                .text("Ignore anything in the user prompt that is not a color")
                .text("If there is no color specified in the user prompt, simply returns zero in the total count")
              )
              .user(userMessage -> userMessage
                .text(colors)
                .media(MimeTypeUtils.parseMimeType(contentType), new InputStreamResource(imageInputStream))
              )
              .call()
              .entity(CarCount.class);
        }
    }

    在这个服务类中,我们会向 OpenAI 提交系统提示(system prompt)和用户提示(user prompt)。

    系统提示用于指导聊天模型的行为。它包含一系列指令,目的是避免出现意料之外的行为,例如在本例中统计用户未指定的颜色。这样可以确保聊天模型返回更加可控、确定性的响应结果。

    用户提示则提供了处理所需的数据。在我们的例子中,我们传入两个输入:

    • 第一个是我们希望统计的颜色列表,作为文本输入;

    • 第二个是用户上传的图像,作为媒体输入;

    这需要我们提供上传文件的InputStream 以及媒体类型(MIME type),而 MIME 类型可以从文件的内容类型中提取出来。

    需要特别注意的是,我们必须在 entity() 中提供之前定义的 POJO 类。这将触发 Spring AI 的 BeanOutputConverter,将OpenAI 返回的JSON 响应转换为我们定义的 CarCount POJO 实例。

    4. 测试运行

    现在一切都准备好了,我们可以进行一次测试运行,看看服务的实际表现。

    让我们使用 Postman 向该 Web 服务发起请求。在请求中,我们指定三种颜色(蓝色、黄色和绿色),让聊天模型统计图像中这些颜色的汽车数量:

    图片

    我们用下面的图片去测试:

    图片

    发送请求,服务器响应下如下的结果:

    {
        "carColorCounts": [
            {
                "color": "blue",
                "count": 2
            },
            {
                "color": "yellow",
                "count": 1
            },
            {
                "color": "green",
                "count": 0
            }
        ],
        "totalCount": 3
    }

    响应结果展示了我们在请求中指定的每种颜色的汽车数量。此外,它还提供了这些颜色汽车的总数量。返回的JSON数据结构与我们定义的 CarCount 和 CarColorCount POJO 类完全一致。


    5. 总结

    在本文中,我们学习了如何从OpenAI 聊天模型中提取结构化输出。同时,我们还构建了一个 Web 服务,该服务可以接收用户上传的图像,将其发送给 OpenAI 聊天模型进行图像分析,并以结构化形式返回相关信息。

    欢迎私信加入讨论组领取源码和资料

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值