此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3spring-doc.cadn.net.cn

结构化输出转换器

LLMs的生成结构化输出的能力对下游依赖于可靠解析输出值的应用非常重要。 开发人员希望能够快速地将AI模型的结果转换为JSON、XML或Java类等数据类型,以便能够传递给其他应用程序的功能和方法。spring-doc.cadn.net.cn

Spring AI Structured Output Converters帮助将LLM输出转换为结构化格式。 如以下图表所示,此方法围绕LLM文本完成端点进行操作:spring-doc.cadn.net.cn

Structured Output Converter Architecture

使用通用完成API从大型语言模型(LLMs)生成结构化输出需要仔细处理输入和输出。结构化输出转换器在调用LLM之前和之后扮演着关键角色,确保达到所需的输出结构。spring-doc.cadn.net.cn

在调用LLM之前,转换应用会在提示中附加格式说明,明确指导模型生成所需的输出结构。这些说明充当蓝图,使模型的响应符合指定的格式要求。spring-doc.cadn.net.cn

随着更多的AI模型原生支持结构化输出,您可以利用Native Structured Output功能与AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT。这种方法直接使用生成的JSON模式与模型的原生结构化输出API,从而消除预提示格式化指令的需求,并提供更可靠的结果。spring-doc.cadn.net.cn

在调用LLM之后,转换器会将模型的输出文本转换为特定结构化的实例。这个转换过程涉及解析原始文本输出,并将其映射到相应的结构化数据表示形式,例如JSON、XML或特定领域的数据结构。spring-doc.cadn.net.cn

StructuredOutputConverter 是一种尽力而为的方法,将模型输出转换为结构化输出。spring-doc.cadn.net.cn

AI 模型不保证会返回如您所请求的结构化输出。
模型可能无法理解提示或无法生成如您所请求的结构化输出。spring-doc.cadn.net.cn

考虑实现一个验证机制以确保模型输出符合预期。spring-doc.cadn.net.cn

StructuredOutputConverter 不用于大语言模型 工具调用,因为该特性默认提供了结构化的输出。spring-doc.cadn.net.cn

结构化输出API

StructuredOutputConverter 接口允许您获取结构化输出,例如将模型输出映射到 Java 类或文本基础 AI 模型输出的值数组。 接口定义如下:spring-doc.cadn.net.cn

public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {

}

它结合了Spring Converter<String, T> 接口和 FormatProvider 接口spring-doc.cadn.net.cn

public interface FormatProvider {
	String getFormat();
}

使用结构化输出API时的数据流如下所示的图表。spring-doc.cadn.net.cn

Structured Output API

The FormatProvider 向 AI 模型提供特定的格式化指南,使模型能够生成可以使用 Converter 转换为指定目标类型 T 的文本输出。以下是一个此类格式说明的例子:spring-doc.cadn.net.cn

  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.

提示格式说明通常会使用PromptTemplate添加到用户输入的末尾,就像这样:spring-doc.cadn.net.cn

    StructuredOutputConverter outputConverter = ...
    String userInputTemplate = """
        ... user text input ....
        {format}
        """; // user input with a "format" placeholder.
    Prompt prompt = new Prompt(
            PromptTemplate.builder()
						.template(this.userInputTemplate)
						.variables(Map.of(..., "format", this.outputConverter.getFormat())) // replace the "format" placeholder with the converter's format.
						.build().createMessage()
    );

The Converter<String, T> 负责将模型的输出文本转换为指定类型 T 的实例。spring-doc.cadn.net.cn

可用转换器

目前,Spring AI 提供了AbstractConversionServiceOutputConverterAbstractMessageOutputConverterBeanOutputConverterMapOutputConverterListOutputConverter 实现:spring-doc.cadn.net.cn

Structured Output Class Hierarchy
  • AbstractConversionServiceOutputConverter<T> - 提供了一个预配置的GenericConversionService,用于将LLM输出转换为所需的格式。没有默认的FormatProvider实现。spring-doc.cadn.net.cn

  • AbstractMessageOutputConverter<T> - 供应一个预配置的 MessageConverter,用于将大语言模型(LLM)输出转换为所需的格式。没有默认的 FormatProvider 实现。spring-doc.cadn.net.cn

  • BeanOutputConverter<T> - 配置为指定的Java类(例如,Bean)或ParameterizedTypeReference,此转换器使用一个FormatProvider实现,该实现指示AI模型生成符合指定Java类派生的DRAFT_2020_12JSON Schema的JSON响应。随后,它利用一个JsonMapper将JSON输出反序列化为目标类的Java对象实例。spring-doc.cadn.net.cn

  • MapOutputConverter - 扩展了 AbstractMessageOutputConverter 的功能,通过一个 FormatProvider 实现来指导 AI 模型生成符合 RFC8259 规范的 JSON 响应。此外,它还包含了一个转换器实现,使用提供的 MessageConverter 将 JSON 载荷转换为 java.util.Map<String, Object> 实例。spring-doc.cadn.net.cn

  • ListOutputConverter - 扩展了 AbstractConversionServiceOutputConverter 并包含了一个针对逗号分隔列表输出的 FormatProvider 实现。转换器实现使用提供的 ConversionService 将模型文本输出转换为 java.util.Listspring-doc.cadn.net.cn

使用转换器

以下部分提供了如何使用可用的转换器生成结构化输出的指南。spring-doc.cadn.net.cn

Bean Output Converter

以下示例展示了如何使用BeanOutputConverter为演员生成电影作品列表。spring-doc.cadn.net.cn

代表演员电影作品记录的目标条目:spring-doc.cadn.net.cn

record ActorsFilms(String actor, List<String> movies) {
}

这里是如何使用高阶、流畅的ChatClientAPI 应用 BeanOutputConverter:spring-doc.cadn.net.cn

ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
        .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
                    .param("actor", "Tom Hanks"))
        .call()
        .entity(ActorsFilms.class);

or using the low-level ChatModel API directly:spring-doc.cadn.net.cn

BeanOutputConverter<ActorsFilms> beanOutputConverter =
    new BeanOutputConverter<>(ActorsFilms.class);

String format = this.beanOutputConverter.getFormat();

String actor = "Tom Hanks";

String template = """
        Generate the filmography of 5 movies for {actor}.
        {format}
        """;

Generation generation = chatModel.call(
    PromptTemplate.builder().template(this.template).variables(Map.of("actor", this.actor, "format", this.format)).build().create()).getResult();

ActorsFilms actorsFilms = this.beanOutputConverter.convert(this.generation.getOutput().getText());

生成模式中的属性排序

The BeanOutputConverter 支持通过 @JsonPropertyOrder 注解在生成的 JSON 模式中自定义属性排序。 此注解允许您指定模式中属性的确切顺序,而不论这些属性在其类或记录中的声明顺序如何。spring-doc.cadn.net.cn

例如,确保ActorsFilms记录中的属性具有特定的排序顺序:spring-doc.cadn.net.cn

@JsonPropertyOrder({"actor", "movies"})
record ActorsFilms(String actor, List<String> movies) {}

此注解可以与记录类和普通的Java类一起使用。spring-doc.cadn.net.cn

通用Bean类型

使用ParameterizedTypeReference构造函数来指定更复杂的靶标类结构。 例如,表示演员及其电影作品列表:spring-doc.cadn.net.cn

List<ActorsFilms> actorsFilms = ChatClient.create(chatModel).prompt()
        .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
        .call()
        .entity(new ParameterizedTypeReference<List<ActorsFilms>>() {});

or using the low-level ChatModel API directly:spring-doc.cadn.net.cn

BeanOutputConverter<List<ActorsFilms>> outputConverter = new BeanOutputConverter<>(
        new ParameterizedTypeReference<List<ActorsFilms>>() { });

String format = this.outputConverter.getFormat();
String template = """
        Generate the filmography of 5 movies for Tom Hanks and Bill Murray.
        {format}
        """;

Prompt prompt = PromptTemplate.builder().template(this.template).variables(Map.of("format", this.format)).build().create();

Generation generation = chatModel.call(this.prompt).getResult();

List<ActorsFilms> actorsFilms = this.outputConverter.convert(this.generation.getOutput().getText());

Map 输出转换器

以下示例展示了如何使用MapOutputConverter将模型输出转换为数字列表映射。spring-doc.cadn.net.cn

Map<String, Object> result = ChatClient.create(chatModel).prompt()
        .user(u -> u.text("Provide me a List of {subject}")
                    .param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))
        .call()
        .entity(new ParameterizedTypeReference<Map<String, Object>>() {});

or using the low-level ChatModel API directly:spring-doc.cadn.net.cn

MapOutputConverter mapOutputConverter = new MapOutputConverter();

String format = this.mapOutputConverter.getFormat();
String template = """
        Provide me a List of {subject}
        {format}
        """;

Prompt prompt = PromptTemplate.builder().template(this.template)
.variables(Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", this.format)).build().create();

Generation generation = chatModel.call(this.prompt).getResult();

Map<String, Object> result = this.mapOutputConverter.convert(this.generation.getOutput().getText());

列表输出转换器

The following snippet shows how to use ListOutputConverter to convert the model output into a list of ice cream flavors.spring-doc.cadn.net.cn

List<String> flavors = ChatClient.create(chatModel).prompt()
                .user(u -> u.text("List five {subject}")
                            .param("subject", "ice cream flavors"))
                .call()
                .entity(new ListOutputConverter(new DefaultConversionService()));

or using the low-level ChatModel API directly:spring-doc.cadn.net.cn

ListOutputConverter listOutputConverter = new ListOutputConverter(new DefaultConversionService());

String format = this.listOutputConverter.getFormat();
String template = """
        List five {subject}
        {format}
        """;

Prompt prompt = PromptTemplate.builder().template(this.template).variables(Map.of("subject", "ice cream flavors", "format", this.format)).build().create();

Generation generation = this.chatModel.call(this.prompt).getResult();

List<String> list = this.listOutputConverter.convert(this.generation.getOutput().getText());

原生结构化输出

许多现代AI模型现在提供了原生支持结构化输出的功能,这比基于提示的格式化更能提供可靠的结果。Spring AI 通过 原生结构化输出 功能支持这一点。spring-doc.cadn.net.cn

当使用原生结构化输出时,BeanOutputConverter生成的JSON模式直接发送到模型的结构化输出API,从而在提示中无需指定格式说明。这种方法提供了:spring-doc.cadn.net.cn

使用原生结构化输出

要启用原生结构化输出,请使用AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT参数:spring-doc.cadn.net.cn

ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
    .advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorsFilms.class);

您也可以通过在ChatClient.Builder中设置defaultAdvisors()来全局配置此参数:spring-doc.cadn.net.cn

@Bean
ChatClient chatClient(ChatClient.Builder builder) {
    return builder
        .defaultAdvisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
        .build();
}

支持原生结构化输出的模型

当前以下模型支持原生结构化输出:spring-doc.cadn.net.cn

一些AI模型,例如OpenAI,不支持在顶层直接使用对象数组。在这种情况下,您可以使用Spring AI默认的结构化输出转换(无需使用原生结构化输出顾问)。

内置 JSON 模式

一些AI模型提供了专门的配置选项以生成结构化的(通常是JSON)输出。spring-doc.cadn.net.cn

  • OpenAI结构化输出可以确保您的模型生成的响应严格符合您提供的JSON模式。您可以选择JSON_OBJECT,该选项保证模型生成的消息是有效的JSON,或者选择带有自定义模式的JSON_SCHEMA,该选项保证模型将生成一个与您提供的模式匹配的响应(spring.ai.openai.chat.options.responseFormat选项)。spring-doc.cadn.net.cn

  • Azure OpenAI - 提供了spring.ai.azure.openai.chat.options.responseFormat选项,指定模型必须输出的格式。设置为{ "type": "json_object" }可以启用JSON模式,确保模型生成的消息是有效的JSON。spring-doc.cadn.net.cn

  • Ollama - 提供了一个spring.ai.ollama.chat.options.format选项,用于指定返回响应的格式。目前,唯一接受的值是jsonspring-doc.cadn.net.cn

  • Mistral AI - 提供一个 spring.ai.mistralai.chat.options.responseFormat 选项来指定返回响应的格式。将其设置为 { "type": "json_object" } 可启用 JSON 模式,这能确保模型生成的消息是有效的 JSON。此外,将其设置为 { "type": "json_schema" } 并提供一个自定义模式可以启用原生结构化输出支持,这能确保模型生成的响应符合您提供的模式。spring-doc.cadn.net.cn