使用 Spring AI 从图片中提取结构化数据

1、概览

本文将带你了解如何使用 Spring AI 通过 OpenAI 聊天模型从图像中提取结构化数据。

OpenAI 聊天模型可以分析上传的图像并返回相关信息。它还能返回结构化的输出结果,这些输出结果可以很容易地通过管道输送到其他应用程序,以进行进一步操作。

举例说明,我们要创建一个 Web 服务,接收客户端上传的图像并将其发送给 OpenAI,以计算图像中彩色汽车的数量。Web 服务会以 JSON 格式返回颜色数量。

2、Spring Boot 配置

在 Maven pom.xml 中添加以下 Spring Boot Start WebSpring AI Model 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 文件中,必须要提供用于验证 OpenAI API 的 API 密钥(spring.ai.openai.api-key)和能够执行图像分析的聊天模型(spring.ai.openai.chat.options.model)的配置。

有多种支持图像分析的 模型,如 gpt-4o-minigpt-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 以注册 ChatClient 等 Bean。

3、简单的 Web 应用

完成所有配置后,我们要创建一个 Web 服务,让用户上传图像,并将其传递给 OpenAI,让 AI 计算图中彩色汽车的数量。

3.1、REST Controller

在这个 REST Controller 中,只接受一个图像文件和要统计的颜色作为请求参数:

@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");
        }
    }
}

如果成功执行,返回 ResponseEntity<CarCount>

3.2、POJO

如果我们想让聊天模型返回结构化输出,就需要在向 OpenAI 发出的 HTTP 请求中将输出格式定义为 JSON schema

在 Spring AI 中,通过定义 POJO 类可以大大简化这一定义。

定义两个 POJO 类来存储颜色及其对应的计数。CarCount 存储每种颜色的汽车数量列表和总数量,即列表中数量的总和:

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

    // 构造函数、Getter、Setter 略
}

CarColorCount 存储颜色名称和相应的计数:

public class CarColorCount {
    private String color;
    private int count;

    // 构造函数、Getter、Setter 略
}

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);
    }
}

在这个 service 中,我们向 OpenAI 提交 system 提示和 user 提示。

system 提示为聊天模型行为提供指导。其中包含一系列避免意外行为的指令,例如计算用户未指定的颜色。这能确保聊天模型返回更确定的回复。

user 提示会向聊天模型提供必要的数据以供处理。在本例中,我们向其传递了两个输入。第一个是作为文本输入的颜色。另一个是作为媒体输入的图片。这需要上传文件的 InputStreamMIME 类型,我们可以从 Content Type 中推导出媒体的 MIME 类型。

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

4、测试

使用 Postman 向服务发起请求。

这里为聊天模型指定了三种不同的颜色(蓝色、黄色和绿色),用于统计:

Postman

在本例中,我们使用以下照片进行测试:

各种颜色的车辆

服务的 JSON 响应如下:

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

响应显示了我们在请求中指定的每种颜色的汽车数量。以及上述所有颜色的汽车总数。JSON schema 与我们在 CarCountCarColorCount 中的 POJO 类定义一致。

5、总结

本文介绍了如何从 OpenAI 聊天模型中提取结构化输出。

我们创建了一个 Web 服务,接受上传的图片,将其传递给 OpenAI 聊天模型进行图片分析,并返回包含相关信息的结构化输出。


Ref:https://www.baeldung.com/spring-ai-extract-data-from-images