获取最新的快照版本,请使用 Spring AI 1.1.3spring-doc.cadn.net.cn

结构化输出转换器

截至2024年5月2日,旧的 OutputParserBeanOutputParserListOutputParserMapOutputParser 类已被弃用,取而代之的是新的 StructuredOutputConverterBeanOutputConverterListOutputConverterMapOutputConverter 实现。 后者可直接替换前者,并提供相同的功能。此次变更的主要原因是命名问题,因为原先并未进行任何解析操作,同时也为了与 Spring org.springframework.core.convert.converter 包保持一致,从而引入了一些改进的功能。

LLM生成结构化输出的能力对于依赖于可靠解析输出值的下游应用至关重要。 开发者希望快速将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

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

在LLM调用之前,转换器会向提示中追加格式指令,为模型提供明确的指导以生成期望的输出结构。这些指令充当蓝图,塑造模型的响应,使其符合指定的格式。spring-doc.cadn.net.cn

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

数字StructuredOutputConverter表示尽最大努力将模型输出转换为结构化输出。 AI模型不保证按请求返回结构化输出。 模型可能无法理解提示或无法按请求生成结构化输出。 请考虑实现验证机制以确保模型输出符合预期。
数字StructuredOutputConverter未用于LLM工具调用,因为此功能默认内置提供结构化输出。

结构化输出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

数字 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()
    );

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

可用转换器

目前,Spring AI提供了 AbstractConversionServiceOutputConverter, AbstractMessageOutputConverter, BeanOutputConverter, MapOutputConverterListOutputConverter 种实现方式:spring-doc.cadn.net.cn

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

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

  • BeanOutputConverter<T> - 通过指定的Java类(例如,Bean)或参数化类型参考进行配置,此转换器使用FormatProvider实现,该实现指导AI模型生成遵循DRAFT_2020_12、从指定Java类派生的JSON Schema的JSON响应。随后,它使用ObjectMapper将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 输出转换器

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

表示演员影视作品的目标记录:spring-doc.cadn.net.cn

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

如何使用高级流畅的 ChatClient API 应用 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);

或使用低级别的 ChatModel API 直接进行操作: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());

生成架构中的属性排序

在生成的JSON模式中,BeanOutputConverter 支持通过 @JsonPropertyOrder 注解来自定义属性排序。 此注解使您能够指定属性在架构中出现的确切顺序,而不受其在类或记录中的声明顺序影响。spring-doc.cadn.net.cn

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

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

此注解适用于记录(records)和常规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>>() {});

或使用低级别的 ChatModel API 直接进行操作: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>>() {});

或使用低级别的 ChatModel API 直接进行操作: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());

列表输出转换器

以下代码片段展示了如何使用ListOutputConverter将模型输出转换为冰淇淋口味的列表。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()));

或者直接使用低级别的ChatModel API: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());

内置JSON模式

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

  • OpenAI 结构化输出能确保您的模型生成严格遵循所提供JSON Schema的响应。您可以在以下选项中选择:使用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。spring-doc.cadn.net.cn