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-4o
和gpt-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 聊天模型进行图像分析,并以结构化形式返回相关信息。
欢迎私信加入讨论组领取源码和资料