Spring Ai超简单教程系列 - 04结构化输出

在网上找了一些Spring AI相关的教程,因为官方API更新较快的原因,大部分教程的内容都已过时。所以在参照官方参考文档学习的同时,沉淀每篇文章,为同样热爱学习的你,提供参考。

本系列教程参考Spring AI官方1.1.x版本文档:https://docs.spring.io/spring-ai/reference/1.1/index.html

结构化输出

对于AI应用开发,需要对调用大模型返回的内容进行反序列化,以便通过稳定的、结构化的方式读取返回结果。Spring AI提供了结构化输出转换器Structured Output Converters能够自动将大模型返回结果与实体对象进行映射,下图是官网提供的结构化输出转换器的执行过程图:

Spring AI底层会通过提示词向大模型提供明确的响应格式规范,引导模型生成符合要求的文本输出,使这些输出能够通过结构化输出转换器转换为指定目标对象,下面是格式规范说明的提示词示例:

text 复制代码
  Your response should be in JSON format.
  The data structure for the JSON should match this Java class: java.util.HashMap
  Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.

在实际调用过程中,Spring AI会根据目标对象生成具体的格式要求,生成的具体格式规范可以自行查阅Spring AI源码。

实体对象映射

我们模拟一个通过大模型查询书籍和作者信息的场景。

定义书籍对象:

java 复制代码
public record BookAuthor(
        /** 作者 **/
        String author,
        /** 书名 **/
        String book
) {
}

定义大模型查询接口,通过结构化输出转换器,自动实现大模型响应结果映射:

java 复制代码
@GetMapping("/ai/structured2entity")
BookAuthor structuredOutput2Entity(@RequestParam("book") String book) {
    return ChatClient.create(this.chatModel)
            .prompt()
            .user(up -> up.text("《{book}》这本书的作者是谁?").params(Map.of("book", book)))
            .call()
            .entity(BookAuthor.class);
}

效果

对象集合映射

模拟应用系统数据库结构设计的场景,通过指定应用系统,让大模型帮助我们完成数据库表结构设计,并输出数据表、表字段对象集合。

定义数据表实体对象:

java 复制代码
public record TableDefinition(
        /** 表名 **/
        String table,
        /** 表注释 **/
        String comment,
        /** 字段列表 **/
        List<TableColumnDefinition> columns
) {
}

定义表字段实体对象:

java 复制代码
public record TableColumnDefinition(
        /** 字段名 **/
        String column,
        /** 字段类型 **/
        String type,
        /** 字段注释 **/
        String comment,
        /** 是否主键 **/
        boolean isPrimaryKey
) {
}

定义大模型调用接口,将大模型输出内容映射为实体对象集合:

java 复制代码
@GetMapping("/ai/structured2list")
List<TableDefinition> structuredOutput2List(@RequestParam("topic") String topic) {
    return ChatClient.create(this.chatModel)
            .prompt()
            .user(up -> up.text("设计一套和{topic}相关的数据库表结构,包含必要的数据表及表字段").params(Map.of("topic", topic)))
            .system("你是一个数据建模领域的专家,擅长根据业务主题设计数据库表结构")
            .call()
            .entity(new ParameterizedTypeReference<List<TableDefinition>>() {});
}

效果