Structured Output Converter

截至 2024 年 5 月 2 日,旧的 AudioContentImageContentVideoContentFileContent 类已弃用,取而代之的是新的 AudioMediaContentImageMediaContentVideoMediaContentFileMediaContent 实现。后者是前者的即插即用替代品,并提供相同的功能。更改的原因主要是命名,因为没有进行任何解析,但也与 Spring MimeType 包保持一致,带来了一些改进的功能。

As of 02.05.2024 the old OutputParser, BeanOutputParser, ListOutputParser and MapOutputParser classes are deprecated in favor of the new StructuredOutputConverter, BeanOutputConverter, ListOutputConverter and MapOutputConverter implementations. the latter are drop-in replacements for the former ones and provide the same functionality. The reason for the change was primarily naming, as there isn’t any parsing being done, but also have aligned with the Spring org.springframework.core.convert.converter package bringing in some improved functionality.

LLM 产生结构化输出的能力对于依赖可靠解析输出值的下游应用程序至关重要。开发人员希望快速将 AI 模型的结果转换为数据类型,例如 JSON、XML 或 Java 类,以便传递给其他应用程序函数和方法。

The ability of LLMs to produce structured outputs is important for downstream applications that rely on reliably parsing output values. Developers want to quickly turn results from an AI model into data types, such as JSON, XML or Java classes, that can be passed to other application functions and methods.

Spring AI OutputParsers 有助于将 LLM 输出转换为结构化格式。如下图所示,这种方法围绕 LLM 文本补全端点运行:

The Spring AI Structured Output Converters help to convert the LLM output into a structured format. As shown in the following diagram, this approach operates around the LLM text completion endpoint: image::structured-output-architecture.jpg[]

从大型语言模型(LLM)使用通用完成API生成结构化输出需要仔细处理输入和输出。结构化输出转换器在LLM调用之前和之后都扮演着关键角色,确保实现所需的输出结构。

Generating structured outputs from Large Language Models (LLMs) using generic completion APIs requires careful handling of inputs and outputs. The structured output converter plays a crucial role before and after the LLM call, ensuring the desired output structure is achieved.

在LLM调用之前,转换器将格式说明附加到提示中,为模型提供明确的指导,以生成所需的输出结构。这些说明充当蓝图,塑造模型的响应以符合指定的格式。

Before the LLM call, the converter appends format instructions to the prompt, providing explicit guidance to the models on generating the desired output structure. These instructions act as a blueprint, shaping the model’s response to conform to the specified format.

在LLM调用之后,转换器获取模型的输出文本并将其转换为结构化类型的实例。此转换过程涉及解析原始文本输出并将其映射到相应的结构化数据表示,例如JSON、XML或领域特定的数据结构。

After the LLM call, the converter takes the model’s output text and transforms it into instances of the structured type. This conversion process involves parsing the raw text output and mapping it to the corresponding structured data representation, such as JSON, XML, or domain-specific data structures.

StructuredOutputConverter 是尽力将模型输出转换为结构化输出。AI模型不保证会按要求返回结构化输出。模型可能无法理解提示或无法按要求生成结构化输出。考虑实施验证机制以确保模型输出符合预期。

The StructuredOutputConverter is a best effort to convert the model output into a structured output. The AI Model is not guaranteed to return the structured output as requested. The model may not understand the prompt or be unable to generate the structured output as requested. Consider implementing a validation mechanism to ensure the model output is as expected.

StructuredOutputConverter 不用于LLM Tool Calling ,因为此功能默认提供结构化输出。

The StructuredOutputConverter is not used for LLM Tool Calling, as this feature inherently provides structured outputs by default.

Structured Output API

StructuredOutputConverter 接口允许您获取结构化输出,例如将输出映射到Java类或从基于文本的AI模型输出映射到值的数组。接口定义是:

The StructuredOutputConverter interface allows you to obtain structured output, such as mapping the output to a Java class or an array of values from the text-based AI Model output. The interface definition is:

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

}

它结合了Spring Converter<String, T> 接口和 FormatProvider 接口

It combines the Spring Converter<String, T> interface and the FormatProvider interface

public interface FormatProvider {
	String getFormat();
}

下图显示了使用结构化输出API时的数据流。

The following diagram shows the data flow when using the structured output API.

structured output api

FormatProvider 向AI模型提供特定的格式指南,使其能够生成文本输出,这些输出可以使用 Converter 转换为指定的目标类型 T 。以下是此类格式说明的示例:

The FormatProvider supplies specific formatting guidelines to the AI Model, enabling it to produce text outputs that can be converted into the designated target type T using the Converter. Here is an example of such formatting instructions:

  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 附加到用户输入的末尾,如下所示:

The format instructions are most often appended to the end of the user input using the PromptTemplate like this:

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

Converter<String, T>负责将模型的输出文本转换为指定类型 T 的实例。

The Converter<String, T> is responsible to transform output text from the model into instances of the specified type T.

Available Converters

目前,Spring AI提供 AbstractConversionServiceOutputConverterAbstractMessageOutputConverterBeanOutputConverterMapOutputConverterListOutputConverter 实现:

Currently, Spring AI provides AbstractConversionServiceOutputConverter, AbstractMessageOutputConverter, BeanOutputConverter, MapOutputConverter and ListOutputConverter implementations:

structured output hierarchy4
  • AbstractConversionServiceOutputConverter&lt;T&gt; - 提供预配置的 GenericConversionService ,用于将LLM输出转换为所需格式。未提供默认的 FormatProvider 实现。

  • AbstractConversionServiceOutputConverter<T> - Offers a pre-configured GenericConversionService for transforming LLM output into the desired format. No default FormatProvider implementation is provided.

  • AbstractMessageOutputConverter&lt;T&gt; - 提供预配置的 MessageConverter ,用于将LLM输出转换为所需格式。未提供默认的 FormatProvider 实现。

  • AbstractMessageOutputConverter<T> - Supplies a pre-configured MessageConverter for converting LLM output into the desired format. No default FormatProvider implementation is provided.

  • BeanOutputConverter&lt;T&gt; - 配置了指定的Java类(例如Bean)或 ParameterizedTypeReference ,此转换器采用 FormatProvider 实现,指导AI模型生成符合 DRAFT_2020_12JSON Schema (源自指定的Java类)的JSON响应。随后,它利用 ObjectMapper 将JSON输出反序列化为目标类的Java对象实例。

  • BeanOutputConverter<T> - Configured with a designated Java class (e.g., Bean) or a ParameterizedTypeReference, this converter employs a FormatProvider implementation that directs the AI Model to produce a JSON response compliant with a DRAFT_2020_12, JSON Schema derived from the specified Java class. Subsequently, it utilizes an ObjectMapper to deserialize the JSON output into a Java object instance of the target class.

  • MapOutputConverter - 扩展了 AbstractMessageOutputConverter 的功能,其 FormatProvider 实现指导AI模型生成符合RFC8259的JSON响应。此外,它还包含一个转换器实现,该实现利用提供的 MessageConverter 将JSON有效负载转换为 java.util.Map&lt;String, Object&gt; 实例。

  • MapOutputConverter - Extends the functionality of AbstractMessageOutputConverter with a FormatProvider implementation that guides the AI Model to generate an RFC8259 compliant JSON response. Additionally, it incorporates a converter implementation that utilizes the provided MessageConverter to translate the JSON payload into a java.util.Map<String, Object> instance.

  • ListOutputConverter - 扩展了 AbstractConversionServiceOutputConverter 并包含一个 FormatProvider 实现,专为逗号分隔列表输出定制。转换器实现采用提供的 ConversionService 将模型文本输出转换为 java.util.List

  • ListOutputConverter - Extends the AbstractConversionServiceOutputConverter and includes a FormatProvider implementation tailored for comma-delimited list output. The converter implementation employs the provided ConversionService to transform the model text output into a java.util.List.

Using Converters

以下部分提供了如何使用可用转换器生成结构化输出的指南。

The following sections provide guides how to use the available converters to generate structured outputs.

Bean Output Converter

以下示例展示了如何使用 BeanOutputConverter 生成演员的电影作品。

The following example shows how to use BeanOutputConverter to generate the filmography for an actor.

目标记录代表演员的电影作品:

The target record representing actor’s filmography:

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

以下是使用高级流畅的 ChatClient API 应用 BeanOutputConverter 的方法:

Here is how to apply the BeanOutputConverter using the high-level, fluent ChatClient API:

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:

or using the low-level ChatModel API directly:

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(
    new PromptTemplate(this.template, Map.of("actor", this.actor, "format", this.format)).create()).getResult();

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

Property Ordering in Generated Schema

BeanOutputConverter 通过 @JsonPropertyOrder 注解支持在生成的 JSON 模式中自定义属性排序。此注解允许您指定属性在模式中出现的精确顺序,无论它们在类或记录中的声明顺序如何。

The BeanOutputConverter supports custom property ordering in the generated JSON schema through the @JsonPropertyOrder annotation. This annotation allows you to specify the exact sequence in which properties should appear in the schema, regardless of their declaration order in the class or record.

例如,要确保 ActorsFilms 记录中属性的特定顺序:

For example, to ensure specific ordering of properties in the ActorsFilms record:

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

此注解适用于记录和常规 Java 类。

This annotation works with both records and regular Java classes.

Generic Bean Types

使用 ParameterizedTypeReference 构造函数指定更复杂的目标类结构。例如,表示演员列表及其电影作品:

Use the ParameterizedTypeReference constructor to specify a more complex target class structure. For example, to represent a list of actors and their filmographies:

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:

or using the low-level ChatModel API directly:

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 = new PromptTemplate(this.template, Map.of("format", this.format)).create();

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

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

Map Output Converter

以下代码片段展示了如何使用 MapOutputConverter 将模型输出转换为映射中的数字列表。

The following snippet shows how to use MapOutputConverter to convert the model output to a list of numbers in a map.

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:

or using the low-level ChatModel API directly:

MapOutputConverter mapOutputConverter = new MapOutputConverter();

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

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

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

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

List Output Converter

以下代码片段展示了如何使用 ListOutputConverter 将模型输出转换为冰淇淋口味列表。

The following snippet shows how to use ListOutputConverter to convert the model output into a list of ice cream flavors.

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

or using the low-level ChatModel API directly:

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

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

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

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

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

Supported AI Models

以下 AI 模型已测试支持列表、映射和 Bean 结构化输出。

The following AI Models have been tested to support List, Map and Bean structured outputs.

Model

Integration Tests / Samples

OpenAI

OpenAiChatModelIT

Anthropic Claude 3

AnthropicChatModelIT.java

Azure OpenAI

AzureOpenAiChatModelIT.java

Mistral AI

MistralAiChatModelIT.java

Ollama

OllamaChatModelIT.java

Vertex AI Gemini

VertexAiGeminiChatModelIT.java

Built-in JSON mode

一些 AI 模型提供专用配置选项来生成结构化(通常是 JSON)输出。

Some AI Models provide dedicated configuration options to generate structured (usually JSON) output.

  • OpenAI Structured Outputs 可以确保您的模型生成严格符合您提供的 JSON 模式的响应。您可以选择 JSON_OBJECT ,它保证模型生成的消息是有效的 JSON,或者选择 JSON_SCHEMA 并提供一个模式,它保证模型将生成一个与您提供的模式匹配的响应( spring.ai.openai.chat.options.responseFormat 选项)。

  • OpenAI Structured Outputs can ensure your model generates responses conforming strictly to your provided JSON Schema. You can choose between the JSON_OBJECT that guarantees the message the model generates is valid JSON or JSON_SCHEMA with a supplied schema that guarantees the model will generate a response that matches your supplied schema (spring.ai.openai.chat.options.responseFormat option).

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

  • Azure OpenAI - provides a spring.ai.azure.openai.chat.options.responseFormat options specifying the format that the model must output. Setting to { "type": "json_object" } enables JSON mode, which guarantees the message the model generates is valid JSON.

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

  • Ollama - provides a spring.ai.ollama.chat.options.format option to specify the format to return a response in. Currently, the only accepted value is json.

  • Mistral AI - 提供 spring.ai.mistralai.chat.options.responseFormat 选项,用于指定返回响应的格式。将其设置为 { "type": "json_object" } 启用 JSON 模式,这保证模型生成的消息是有效的 JSON。

  • Mistral AI - provides a spring.ai.mistralai.chat.options.responseFormat option to specify the format to return a response in. Setting it to { "type": "json_object" } enables JSON mode, which guarantees the message the model generates is valid JSON.