SpringAI 结构化输出 04:输出转换器

高层API .entity(…) 底层基于 StructuredOutputConverter(结构化输出转换器)抽象实现。大多数业务应用不会直接使用该底层API。 当遇到下面几种场景,才需要直接使用这套底层API:

  1. 需要解析内置转换器无法处理的输出(例如被Markdown代码块包裹的JSON);
  2. 需要处理非JSON格式,如YAML、CSV;
  3. 需要直接对接底层的 ChatModel API。

Spring AI 的结构化输出转换器负责把大模型文本输出转换为结构化对象。如下图所示,该组件围绕大模型文本补全接口工作:

转换器会在调用大模型之前、之后分别执行逻辑

  • 调用前:向提示词追加格式要求指令,引导模型生成目标结构的输出;
  • 调用后:解析模型返回的文本,映射为Java结构化类型实例。

⚠️注意: StructuredOutputConverter 属于尽力而为的转换组件,并不能保证大模型一定返回符合要求的结构化内容。建议搭配模式校验(schema validation)一起使用,保障输出符合预期。 大模型工具调用(Tool Calling)不使用该转换器,工具调用本身原生就输出结构化数据。

结构化输出 API

StructuredOutputConverter 接口,用于将大模型文本输出映射为Java类、数组等结构化对象。接口定义:

该接口同时继承Spring的 Converter<String, T> 接口与 FormatProvider 接口:

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

FormatProvider 和 Converter<String, T> 的作用分别是:

  • FormatProvider:提供格式约束提示文本,告诉大模型应该输出什么样式;
  • Converter<String, T>:将原始模型输出字符串,转换为目标泛型类型 T

示例:格式指令文本示例(会被追加到Prompt)

格式指令一般通过 PromptTemplate 占位符追加到用户输入末尾:

getJsonSchema() 的作用

该默认方法是Spring AI 2.0新增。它是桥梁,让自定义转换器可以兼容 useProviderStructuredOutput()(厂商原生结构化输出)与 validateSchema()(模式校验自修复)能力。

  • 如果实现该方法并返回Schema(一般委托给BeanOutputConverter),那么两个开关均可正常生效;
  • 如果保留默认空实现,则这两个能力对当前转换器不生效。

内置转换器列表

Spring AI内置实现: AbstractConversionServiceOutputConverterAbstractMessageOutputConverterBeanOutputConverterMapOutputConverterListOutputConverter,如下图:

  • AbstractConversionServiceOutputConverter 内置预配置的GenericConversionService,用于将大模型输出转为目标对象;不提供默认的FormatProvider实现。
  • AbstractMessageOutputConverter 内置预配置的MessageConverter;不提供默认FormatProvider实现。
  • BeanOutputConverter【最常用】 构造时传入Java实体类/Record或者ParameterizedTypeReference; 内部FormatProvider生成基于Java类推导的DRAFT_2020_12规范JSON Schema,引导模型输出对应JSON; 使用JsonMapper把返回JSON反序列化为Java对象。
  • MapOutputConverter 继承AbstractMessageOutputConverter; 提供格式指令让模型输出标准JSON,再通过MessageConverter把JSON解析为Map<String,Object>
  • ListOutputConverter 继承AbstractConversionServiceOutputConverter; 适配逗号分隔列表输出场景,通过ConversionService将文本转为Java List集合。

转换器的使用方式

下面示例分别演示高层ChatClient流式API、底层ChatModel原生API两种用法。

BeanOutputConverter 实体输出转换器

下面的例子展示了如何使用BeanOutputConverter

定义目标Record:

✅高层ChatClient写法(日常业务首选)

✅底层ChatModel原始API写法

生成Schema时的属性顺序

BeanOutputConverter支持注解@JsonPropertyOrder,手动指定JSON Schema字段输出顺序,不受Java类/Record内字段定义顺序影响。

提示: Record与普通Java Bean都支持该注解。

泛型Bean类型

使用ParameterizedTypeReference处理复杂泛型,例如List

高层API:

底层API:

MapOutputConverter Map输出转换器

下面的代码片段演示如何使用 MapOutputConverter,将模型输出转换为 Map 内部存放的数字列表。

高层API:

底层API:

ListOutputConverter List输出转换器

下面的代码片段演示如何使用 ListOutputConverter,将模型输出转换为冰淇淋口味的列表。

高层API:

底层API:

自定义转换器

内置BeanOutputConverter比较严格,要求返回内容纯粹可直接解析的JSON。但大模型经常会把JSON包裹在Markdown代码块内:

这种情况原生转换器会抛出异常。可以自定义宽松转换器,剔除markdown代码围栏,提取JSON再交给原转换器解析

示例:LenientJsonOutputConverter宽松JSON转换器:

使用自定义转换器,直接传入.entity()

注意: 该实现把getJsonSchema()委托给底层Bean转换器,所以 validateSchema()useProviderStructuredOutput() 两个可靠性开关依旧可以正常工作。

非JSON格式处理

如果需要处理YAML、CSV等非JSON格式,需要完整实现StructuredOutputConverter接口:

  1. 自行编写getFormat()返回格式提示词;
  2. 自行编写convert()解析逻辑;
  3. getJsonSchema()保持默认空实现。

⚠️注意: 此时厂商原生结构化输出、Schema校验重试两个能力会失效,仅使用提示词驱动模式。

原文:Output Converters