收藏本站,收获最前沿的人工智能与编程资讯!!

Spring AI ​结构化输出转换器 Structured Output Converter

技术文档 29℃ 0

结构化输出转换器

大语言模型(LLM)生成结构化输出的能力,对于依赖可靠解析输出值的下游应用至关重要。开发者希望快速将AI模型的返回结果转换为JSON、XML或Java类等数据类型,以便传递给应用的其他函数和方法使用。

Spring AI 结构化输出转换器可帮助将LLM输出转换为结构化格式。如下图所示,该方案围绕LLM文本补全接口运行:

结构化输出转换器架构

使用通用补全API从大语言模型(LLM)生成结构化输出,需要谨慎处理输入和输出。结构化输出转换器在LLM调用前后均发挥关键作用,确保生成符合预期的输出结构。

在调用LLM之前,转换器会将格式指令追加到提示词中,为模型提供明确的生成指导。这些指令相当于蓝图,引导模型的响应符合指定格式。

随着越来越多的AI模型原生支持结构化输出,你可以通过 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 使用原生结构化输出功能。该方案直接将生成的JSON Schema用于模型的原生结构化输出API,无需在提示词中添加前置格式指令,结果更可靠。

在调用LLM之后,转换器接收模型的输出文本,并将其转换为结构化类型的实例。该转换过程包括解析原始文本输出,并将其映射为对应的结构化数据表示,如JSON、XML或领域专用数据结构。

注意:结构化输出转换器会尽最大努力将模型输出转换为结构化格式,但不保证AI模型一定按要求返回结构化输出。模型可能无法理解提示词,或无法按要求生成结构化输出。建议实现校验机制,确保模型输出符合预期。

注意:结构化输出转换器不适用于LLM工具调用,因为该功能默认已原生提供结构化输出。

结构化输出API

StructuredOutputConverter 接口支持从基于文本的AI模型输出中获取结构化结果,例如将输出映射为Java类或值数组。接口定义如下:

public interface StructuredOutputConverterextends Converter, FormatProvider {

}

它组合了Spring的 Converter 接口和 FormatProvider 接口:

public interface FormatProvider {
	String getFormat();
}

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

FormatProvider 为AI模型提供具体的格式指南,使其生成的文本输出可通过 Converter 转换为指定的目标类型T。以下是格式指令示例:

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

StructuredOutputConverter outputConverter = ...
String userInputTemplate = """
    ... user text input ....
    {format}
    """; // 包含"format"占位符的用户输入
Prompt prompt = new Prompt(
        PromptTemplate.builder()
					.template(this.userInputTemplate)
					.variables(Map.of(..., "format", this.outputConverter.getFormat())) // 用转换器的格式替换占位符
					.build().createMessage()
);

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

可用转换器

目前,Spring AI提供以下实现类:AbstractConversionServiceOutputConverterAbstractMessageOutputConverterBeanOutputConverterMapOutputConverterListOutputConverter

结构化输出类层级结构

  • AbstractConversionServiceOutputConverter:提供预配置的 GenericConversionService,用于将LLM输出转换为目标格式。无默认的 FormatProvider 实现。

  • AbstractMessageOutputConverter:提供预配置的 MessageConverter,用于将LLM输出转换为目标格式。无默认的 FormatProvider 实现。

  • BeanOutputConverter:配置指定的Java类(如Bean)或 ParameterizedTypeReference,使用 FormatProvider 指导AI模型生成符合DRAFT_2020_12标准、基于指定Java类生成的JSON Schema的JSON响应。随后通过 ObjectMapper 将JSON反序列化为目标类的Java对象实例。

  • MapOutputConverter:继承 AbstractMessageOutputConverter,实现 FormatProvider 指导模型生成符合RFC8259标准的JSON响应,并通过 MessageConverter 将JSON负载转换为 java.util.Map 实例。

  • ListOutputConverter:继承 AbstractConversionServiceOutputConverter,实现适配逗号分隔列表输出的 FormatProvider,并通过 ConversionService 将模型文本输出转换为 java.util.List

转换器使用方法

以下章节介绍如何使用现有转换器生成结构化输出。

Bean输出转换器

以下示例展示如何使用 BeanOutputConverter 生成演员的影视作品列表。

表示演员影视作品的目标记录类:

record ActorsFilms(String actor, Listmovies) {
}

使用高阶流式 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:

BeanOutputConverterbeanOutputConverter =
    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());

生成Schema的属性排序

BeanOutputConverter 支持通过 @JsonPropertyOrder 注解自定义生成JSON Schema中的属性顺序,可指定属性在Schema中的显示顺序,不受类或记录中声明顺序的影响。

示例:为 ActorsFilms 记录指定属性顺序:

@JsonPropertyOrder({"actor", "movies"})
record ActorsFilms(String actor, Listmovies) {}

该注解同时支持记录类和普通Java类。

泛型Bean类型

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

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

或直接使用底层 ChatModel API:

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

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();
ListactorsFilms = this.outputConverter.convert(this.generation.getOutput().getText());

Map输出转换器

以下代码展示如何使用 MapOutputConverter 将模型输出转换为Map中的数字列表:

Mapresult = 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>() {});

或直接使用底层 ChatModel API:

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();
Mapresult = this.mapOutputConverter.convert(this.generation.getOutput().getText());

List输出转换器

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

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

或直接使用底层 ChatModel API:

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();
Listlist = this.listOutputConverter.convert(this.generation.getOutput().getText());

原生结构化输出

许多现代AI模型现已原生支持结构化输出,相比基于提示词的格式化方式,结果更可靠。Spring AI通过原生结构化输出功能支持该能力。

使用原生结构化输出时,BeanOutputConverter 生成的JSON Schema会直接发送到模型的结构化输出API,无需在提示词中添加格式指令。该方案具备以下优势:

  • 更高可靠性:模型保证输出符合Schema规范

  • 更简洁的提示词:无需追加格式指令

  • 更好的性能:模型可内部优化结构化输出

使用原生结构化输出

启用原生结构化输出,使用 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 参数:

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.BuilderdefaultAdvisors() 全局设置:

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

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

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

  • OpenAI:GPT-4o及更高版本(支持JSON Schema)

  • Anthropic:Claude 3.5 Sonnet及更高版本

  • Vertex AI Gemini:Gemini 1.5 Pro及更高版本

  • Mistral AI:Mistral Small及更高版本(支持JSON Schema)

部分AI模型(如OpenAI)不支持顶层对象数组的原生输出。这种情况下,可使用Spring AI默认的结构化输出转换(不启用原生结构化输出顾问)。

内置JSON模式

部分AI模型提供专用配置选项生成结构化(通常为JSON)输出:

  • OpenAI结构化输出:确保模型生成严格符合JSON Schema的响应。可选择 JSON_OBJECT(保证生成合法JSON)或 JSON_SCHEMA(保证匹配指定Schema),配置项:spring.ai.openai.chat.options.responseFormat

  • Azure OpenAI:提供 spring.ai.azure.openai.chat.options.responseFormat 指定输出格式。设置为 { "type": "json_object" } 启用JSON模式

  • Ollama:提供 spring.ai.ollama.chat.options.format 指定响应格式,目前仅支持 json

  • Mistral AI:提供 spring.ai.mistralai.chat.options.responseFormat 指定输出格式。json_object 启用JSON模式;json_schema 启用原生结构化输出,保证匹配指定Schema

相关推荐