Spring AI 1.x 系列【24】结构化输出 API

文章目录

  • [1. 前言](#1. 前言)
  • [2. 结构化输出转换器](#2. 结构化输出转换器)
    • [2.1 基本原理](#2.1 基本原理)
    • [2.2 核心 API](#2.2 核心 API)
      • [2.2.1 StructuredOutputConverter](#2.2.1 StructuredOutputConverter)
      • [2.2.2 BeanOutputConverter](#2.2.2 BeanOutputConverter)
      • [2.2.3 AbstractMessageOutputConverter](#2.2.3 AbstractMessageOutputConverter)
        • [2.2.3.1 MapOutputConverter](#2.2.3.1 MapOutputConverter)
      • [2.2.4 AbstractConversionServiceOutputConverter](#2.2.4 AbstractConversionServiceOutputConverter)
        • [2.2.4.1 ListOutputConverter](#2.2.4.1 ListOutputConverter)
    • [2.3 ChatClient 入口](#2.3 ChatClient 入口)
      • [2.3.1 entity()](#2.3.1 entity())
      • [2.3.2 responseEntity()](#2.3.2 responseEntity())
  • [3. 模型原生结构化输出](#3. 模型原生结构化输出)
    • [3.1 智谱 AI](#3.1 智谱 AI)
    • [3.2 支持原生结构化输出的模型](#3.2 支持原生结构化输出的模型)
    • [3.3 内置 JSON 模式](#3.3 内置 JSON 模式)

1. 前言

在之前的所有案例中,大模型返回的都是非结构化的文本,例如,用户提问 "介绍一下Spring AI" :

java 复制代码
Spring AI 是一个由 Spring 团队开发的全新框架,旨在为 Java 生态系统提供强大的人工智能集成能力。以下是 Spring AI 的主要特点和介绍:

## 核心概念

**Spring AI** 是一个用于简化 AI 应用开发的 Spring 框架,它提供了统一的编程模型来与各种 AI 模型和平台进行交互。

## 主要特性

### 1. **统一的 AI 编程模型**
- 提供一致的 API 来调用不同的 AI 服务(如 OpenAI、Azure OpenAI、Anthropic Claude 等)
- 统一的接口设计,简化了 AI 集成的复杂性

在某些场景中,需要结构化的输出,比如在合同关键信息提取任务中,合同金额、日期、甲方、乙方等信息,期望大模型能返回结构化的内容信息,比如:

java 复制代码
合同编号:HT-2026-001
甲方:北京XX科技有限公司
乙方:上海XX贸易有限公司
合同金额:1000000.50
签订日期:2026-03-15
关键条款:[保密条款:双方需对合同内容保密,保密期限为3年, 违约责任:任何一方违约需支付合同金额10%的违约金]
合同有效期:12个月

另外,还期望结构化的输出能转换为 POJO 对象,方便后续业务逻辑处理。

2. 结构化输出转换器

Spring AI 结构化输出转换器用于将 LLM 输出转换为结构化格式,方便开发者快速将 AI 模型的结果转换成可被其他应用函数和方法使用的数据类型,例如 JSONXMLJava 类。

2.1 基本原理

工作流程

结构化输出转换器在 LLM 调用前后都扮演关键角色,确保得到期望的输出结构:

  • 在调用 LLM 之前:转换器会向提示词追加格式指令,为模型提供明确的生成指导。这些指令相当于蓝图,让模型的响应符合指定格式。
  • 在调用 LLM 之后:转换器接收模型输出的文本,并将其转换为结构化类型的实例。这个转换过程包括解析原始文本输出,并映射到对应的结构化数据表示,例如 JSONXML 或领域特定数据结构。

注意事项

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

  • 结构化输出转换器不用于 LLM 工具调用(Tool Calling / Function Calling),因为该功能默认就提供结构化输出。

  • 随着越来越多 AI 模型原生支持结构化输出,你可以通过 AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT 使用原生结构化输出功能。

2.2 核心 API

2.2.1 StructuredOutputConverter

结构化输出的基础接口,允许你从基于文本的 AI 模型输出中获取结构化输出,例如将输出映射到 Java 类或值数组。继承了 SpringConverter<String, T> 接口和 FormatProvider 接口。

接口定义如下:

java 复制代码
public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {
    // Converter<String, T>: 将字符串转换为目标类型T
    // FormatProvider: 提供格式说明(用于生成提示词)
    String getFormat(); // 返回格式描述(如JSON Schema)
}

FormatProvider (格式提供者)向 AI 模型提供具体的格式规范,其输出可通过 Converter 转换为目标类型 T

接口定义如下:

java 复制代码
public interface FormatProvider {
    String getFormat();
}

格式指令示例如下:

java 复制代码
你的回复必须是 JSON 格式。
JSON 结构需匹配此类:java.util.HashMap
不要包含任何解释,仅返回符合 RFC8259 标准的 JSON,严格遵循格式。

格式指令通常使用 PromptTemplate 追加到用户输入末尾,如下所示:

java 复制代码
// 1. 定义带{format}占位符的用户提示词模板
String userInputTemplate = """
    ... 用户输入 ....  // 比如:"提取这份合同的甲方、乙方、金额信息"
    {format}  // 格式占位符,后续会替换成FormatProvider 
    """;

// 2. 组装完整提示词:用outputConverter.getFormat()填充{format}占位符
Prompt prompt = new Prompt(
        PromptTemplate.builder()
                    .template(this.userInputTemplate)  // 传入模板
                    .variables(Map.of(..., "format", this.outputConverter.getFormat()))  // 填充占位符:format=AI的格式指南
                    .build().createMessage()
);

Converter (转换器)负责将生成的文本可以通过 Converter 转换成指定的目标类型 T

接口定义如下:

java 复制代码
@FunctionalInterface
public interface Converter<S, T> {

	@Nullable
	T convert(S source);
}

Spring AI 目前提供以下实现:


类结构说明

  • AbstractConversionServiceOutputConverter<T>:提供预配置的 GenericConversionService,用于将 LLM 输出转为目标格式。无默认的 FormatProvider 实现。
  • AbstractMessageOutputConverter<T>:提供预配置的 MessageConverter,用于将 LLM 输出转为目标格式。无默认的 FormatProvider 实现。
  • BeanOutputConverter<T>:通过指定 Java 类或 ParameterizedTypeReference 配置。它使用 FormatProvider 指导模型输出符合 JSON Schema (DRAFT_2020_12)JSON,再通过 ObjectMapper 反序列化为 Java 对象。
  • MapOutputConverter:继承 AbstractMessageOutputConverter,指导模型输出 RFC8259 合规 JSON,并转换为 Map<String, Object>
  • ListOutputConverter:继承 AbstractConversionServiceOutputConverter,用于逗号分隔列表格式输出,并转换为 List

2.2.2 BeanOutputConverter

AI 返回的文本自动解析为指定的 Java Bean/Record

核心属性

java 复制代码
public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {

    /**
     * 目标转换类型(可以是普通Class、泛型、Record)
     */
    private final Type type;

    /**
     * Jackson 对象序列化/反序列化工具
     */
    private final ObjectMapper objectMapper;

    /**
     * 根据目标类型自动生成的 JSON Schema(用于提示 AI 按格式输出)
     */
    private String jsonSchema;

    /**
     * 模型返回文本清理器(处理 markdown、思考标签、空格等脏数据)
     */
    private final ResponseTextCleaner textCleaner;

构造方法

java 复制代码
    /**
     * 通过 Class 构建转换器
     */
    public BeanOutputConverter(Class<T> clazz) {
        this(clazz, null, null);
    }

    /**
     * 带自定义 ObjectMapper 的构造
     */
    public BeanOutputConverter(Class<T> clazz, ObjectMapper objectMapper) {
        this(clazz, objectMapper, null);
    }

    /**
     * 完整构造:Class + ObjectMapper + 文本清理器
     */
    public BeanOutputConverter(Class<T> clazz, ObjectMapper objectMapper, ResponseTextCleaner textCleaner) {
        this(ParameterizedTypeReference.forType(clazz), objectMapper, textCleaner);
    }

    /**
     * 通过 ParameterizedTypeReference 构建(支持泛型,如 List<Bean>)
     */
    public BeanOutputConverter(ParameterizedTypeReference<T> typeRef) {
        this(typeRef, null, null);
    }

    public BeanOutputConverter(ParameterizedTypeReference<T> typeRef, ObjectMapper objectMapper) {
        this(typeRef, objectMapper, null);
    }

    public BeanOutputConverter(ParameterizedTypeReference<T> typeRef, ObjectMapper objectMapper, ResponseTextCleaner textCleaner) {
        this(typeRef.getType(), objectMapper, textCleaner);
    }

    /**
     * 最终私有构造:初始化所有组件并自动生成 JSON Schema
     */
    private BeanOutputConverter(Type type, ObjectMapper objectMapper, ResponseTextCleaner textCleaner) {
        Objects.requireNonNull(type, "Type cannot be null;");
        this.type = type;
        // 使用传入的 ObjectMapper 或创建默认
        this.objectMapper = objectMapper != null ? objectMapper : getObjectMapper();
        // 使用传入的清理器 或 创建默认
        this.textCleaner = textCleaner != null ? textCleaner : createDefaultTextCleaner();
        // 自动生成 JSON Schema
        this.generateSchema();
    }

核心方法

  • 自动生成 JSON Schema
  • 自动清洗模型返回文本
  • 反序列化为目标对象
java 复制代码
    // ==================== 核心:创建默认文本清理器 ====================
    /**
     * 默认文本清理器组合:
     * 1. 空格清理
     * 2. <thinking> 思考标签清理
     * 3. Markdown 代码块 ```json ```清理
     * 4. 再次空格清理
     * 确保 AI 返回的文本变成纯 JSON
     */
    private static ResponseTextCleaner createDefaultTextCleaner() {
        return CompositeResponseTextCleaner.builder()
                .addCleaner(new WhitespaceCleaner())
                .addCleaner(new ThinkingTagCleaner())
                .addCleaner(new MarkdownCodeBlockCleaner())
                .addCleaner(new WhitespaceCleaner())
                .build();
    }

    // ==================== 核心:自动生成 JSON Schema ====================
    /**
     * 根据目标 Java 类型自动生成 JSON Schema(Draft 2020-12)
     * 支持:
     * - Jackson 注解(@JsonProperty、@JsonPropertyOrder、@JsonIgnore)
     * - Kotlin 数据类
     * - 所有字段默认必填
     */
    private void generateSchema() {
        // 集成 Jackson 注解模块
        JacksonModule jacksonModule = new JacksonModule(
                JacksonOption.RESPECT_JSONPROPERTY_REQUIRED,
                JacksonOption.RESPECT_JSONPROPERTY_ORDER
        );

        // 构建 Schema 生成器配置:Draft2020_12 + 纯JSON格式
        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
                        SchemaVersion.DRAFT_2020_12,
                        OptionPreset.PLAIN_JSON)
                .with(jacksonModule)
                .with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT); // 禁止额外属性

        // 所有字段默认必填
        configBuilder.forFields().withRequiredCheck(field -> true);

        // 若项目存在 Kotlin,则自动加入 Kotlin 模块支持
        if (KotlinDetector.isKotlinReflectPresent()) {
            configBuilder.with(new KotlinModule());
        }

        // 生成 JSON Schema
        SchemaGeneratorConfig config = configBuilder.build();
        SchemaGenerator generator = new SchemaGenerator(config);
        JsonNode jsonNode = generator.generateSchema(this.type);

        // 格式化输出 JSON Schema
        ObjectWriter objectWriter = this.objectMapper.writer(
                new DefaultPrettyPrinter()
                        .withObjectIndenter(
                                new DefaultIndenter().withLinefeed(System.lineSeparator())
                        )
        );

        try {
            this.jsonSchema = objectWriter.writeValueAsString(jsonNode);
        } catch (JsonProcessingException e) {
            logger.error("Could not pretty print json schema for type: {}", this.type);
            throw new RuntimeException("Could not generate JSON Schema for " + this.type, e);
        }
    }

    // ==================== 核心:将 AI 文本转为 Java Bean ====================
    /**
     * 转换入口:
     * 1. 清理 AI 返回的脏文本(markdown、thinking、空格)
     * 2. 使用 Jackson 反序列化为目标类型
     */
    @Override
    public T convert(@NonNull String text) {
        try {
            // 清理文本
            text = this.textCleaner.clean(text);
            // 反序列化 JSON → Java 对象
            return this.objectMapper.readValue(text, this.objectMapper.constructType(this.type));
        } catch (JsonProcessingException e) {
            logger.error(LoggingMarkers.SENSITIVE_DATA_MARKER,
                    "Could not parse text to target type: \"{}\" into {}",
                    text, this.type);
            throw new RuntimeException(e);
        }
    }

    // ==================== 创建默认 ObjectMapper ====================
    /**
     * 创建默认 ObjectMapper:
     * - 忽略未知属性(最关键,避免 AI 多返回字段导致解析失败)
     * - 自动加载可用模块(如 Kotlin、Java8 时间)
     */
    protected ObjectMapper getObjectMapper() {
        return JsonMapper.builder()
                .addModules(JacksonUtils.instantiateAvailableModules())
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .build();
    }

    // ==================== 生成给 AI 的格式提示指令 ====================
    /**
     * 生成给大模型的格式指令:
     * 1. 输出 JSON
     * 2. 不要解释
     * 3. 严格遵循 RFC8259
     * 4. 不要 markdown
     * 5. 提供 JSON Schema
     */
    @Override
    public String getFormat() {
        String template = """
                Your response should be in JSON format.
                Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
                Do not include markdown code blocks in your response.
                Remove the ```json markdown from the output.
                Here is the JSON Schema instance your output must adhere to:
                ```%s```
                """;
        return String.format(template, this.jsonSchema);
    }

    // ==================== 工具方法 ====================

    /**
     * 获取生成的 JSON Schema 字符串
     */
    public String getJsonSchema() {
        return this.jsonSchema;
    }

    /**
     * 将 JSON Schema 转为 Map(用于 Native Structured Output 原生模式)
     */
    public Map<String, Object> getJsonSchemaMap() {
        try {
            return this.objectMapper.readValue(this.jsonSchema, Map.class);
        } catch (JsonProcessingException e) {
            logger.error("Could not parse JSON Schema to Map", e);
            throw new IllegalStateException(e);
        }
    }
}

2.2.3 AbstractMessageOutputConverter

【基于消息转换器的结构化输出抽象转换器】,结构化输出转换器的另一种抽象实现,核心区别于 AbstractConversionServiceOutputConverter

  • 依赖 Spring Messaging 模块的 MessageConverter(消息转换器)而非 ConversionService(类型转换服务)
  • 专注于「消息体格式」的结构化转换(如 JSON/XML 格式的消息文本 ↔ Java 对象)
  • 更适配消息驱动的场景(如 Spring Cloud Stream、消息队列集成的 AI 应用)

核心属性

java 复制代码
    /**
     * 核心属性:Spring Messaging 消息转换器
     * 作用:
     * 1. 处理「消息文本(如JSON/XML)」与「Java对象」的双向转换
     * 2. 支持多格式解析(区别于仅处理类型转换的 ConversionService)
     * 3. 适配消息驱动场景(如从消息队列接收LLM输出的结构化消息)
     * 修饰符说明:
     * - 非final:允许子类在运行时替换/重置消息转换器(适配动态配置场景)
     * - private:仅当前类访问,子类通过getter获取
     */
    private MessageConverter messageConverter;

构造方法

java 复制代码
    /**
     * 构造器:注入消息转换器(依赖注入模式)
     * 设计意图:
     * - 强制子类初始化时指定消息转换器,避免空指针
     * - 支持传入自定义配置的MessageConverter(如定制JSON解析规则的MappingJackson2MessageConverter)
     *
     * @param messageConverter 消息转换器实例(不能为空,实际源码中会通过Assert.notNull校验)
     */
    public AbstractMessageOutputConverter(MessageConverter messageConverter) {
        // 初始化消息转换器,交由父类统一管理
        this.messageConverter = messageConverter;
    }

核心抽象方法

  • getFormat:生成 LLM 输出格式提示(如 JSON Schema/XML格式说明)
  • convert:将 LLM 返回的原始文本(如 JSON/XML 字符串)转换为目标类型 T
java 复制代码
    /**
     * Getter方法:给子类提供消息转换器的访问入口
     * 典型使用场景:
     * 子类实现convert()方法时,调用该方法获取转换器,完成「LLM输出文本 → 目标类型」的转换
     *
     * @return 初始化后的消息转换器实例
     */
    public MessageConverter getMessageConverter() {
        return this.messageConverter;
    }

    /**
     * 【必须由子类实现】生成LLM输出格式提示(如JSON Schema/XML格式说明)
     * 作用:告诉LLM需要返回的结构化格式(如"请返回符合以下JSON格式的文本:{...}")
     * 注:该方法由StructuredOutputConverter接口定义,父类仅声明抽象,子类需按需实现
     */
    @Override
    public abstract String getFormat();

    /**
     * 【必须由子类实现】执行结构化转换核心逻辑
     * 作用:将LLM返回的原始文本(如JSON/XML字符串)转换为目标类型T
     * 注:该方法由StructuredOutputConverter接口定义,父类仅声明抽象,子类需结合MessageConverter实现
     *
     * @param source LLM返回的原始结构化文本(如JSON字符串、XML字符串)
     * @return 转换后的目标类型实例(如ContractInfo、Book等)
     */
    @Override
    public abstract T convert(String source);
}
2.2.3.1 MapOutputConverter

【Map 输出转换器】将 AI 模型返回的 JSON 文本,自动清洗并转换为 Java Map<String, Object>

处理流程

  • 自动去除 Markdown 代码块
  • 使用 Spring MappingJackson2MessageConverterJSON 解析
  • 自动生成格式提示词,要求模型返回标准 JSON

源码如下

java 复制代码
public class MapOutputConverter extends AbstractMessageOutputConverter<Map<String, Object>> {

    /**
     * 默认构造方法
     * 直接使用 Spring 自带的 MappingJackson2MessageConverter 进行 JSON 转换
     */
    public MapOutputConverter() {
        super(new MappingJackson2MessageConverter());
    }

    /**
     * 核心转换方法:将 AI 返回的文本 → Map<String, Object>
     * @param text AI 返回的原始文本(可能带 Markdown)
     * @return 解析后的 Map 对象
     */
    @Override
    public Map<String, Object> convert(@NonNull String text) {
        // 1. 自动清理 AI 最常返回的 Markdown 代码块:```json ... ```
        if (text.startsWith("```json") && text.endsWith("```")) {
            // 去掉开头 ```json (7个字符) 和结尾 ```(3个字符)
            text = text.substring(7, text.length() - 3);
        }

        // 2. 构建 Spring Message 对象,用于适配 MessageConverter
        Message<?> message = MessageBuilder.withPayload(text.getBytes(StandardCharsets.UTF_8)).build();

        // 3. 使用 JSON 消息转换器将文本转为 HashMap
        return (Map) this.getMessageConverter().fromMessage(message, HashMap.class);
    }

    /**
     * 生成给 AI 模型的格式指令(Prompt)
     * 告诉模型:必须返回 JSON、不要解释、不要 Markdown
     */
    @Override
    public String getFormat() {
        String rawFormat = """
                Your response should be in JSON format.
                The data structure for the JSON should match this Java class: %s
                Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
                Remove the ```json markdown surrounding the output including the trailing "```".
                """;

        // 格式化指令,填入 HashMap 类名
        return String.format(rawFormat, HashMap.class.getName());
    }
}

2.2.4 AbstractConversionServiceOutputConverter

【结构化输出转换器抽象基类】封装 Spring 内置的默认类型转换服务(DefaultConversionService),用于将 LLM 输出转为目标格式,没有提供默认的 FormatProvider 实现。

核心职责:

  • 封装 Spring 内置的默认类型转换服务(DefaultConversionService),避免子类重复实现基础类型转换逻辑
  • 定义结构化输出转换器的通用骨架,遵循「模板方法模式」------ 通用逻辑封装在抽象父类,具体逻辑由子类实现
  • 统一管理类型转换规则,保证所有子类的类型转换行为一致(如 StringLocalDateStringBigDecimal 等)

核心属性和构造器

java 复制代码
/**
 * 【结构化输出转换器抽象基类】
 * @param <T> 泛型:指定转换器最终要转换的目标类型(如 ContractInfo、Book 等业务对象)
 * @author Mark Pollack
 * @author Christian Tzolov
 * @see StructuredOutputConverter 结构化输出转换器核心接口
 * @see DefaultConversionService Spring 内置默认类型转换服务
 * @see BeanOutputConverter 该抽象类的核心子类(JSON字符串转Java Bean)
 */
public abstract class AbstractConversionServiceOutputConverter<T> implements StructuredOutputConverter<T> {

    /**
     * 核心属性:Spring 内置的默认类型转换服务
     * 作用:
     * - 处理基础类型转换(String→int、String→double)
     * - 处理复杂类型转换(String→LocalDate、String→BigDecimal、String→枚举)
     * - 支持扩展自定义类型转换器(如 String→自定义业务枚举)
     * 修饰符说明:
     * - private:仅当前类可访问,子类通过 getter 方法获取
     * - final:一旦初始化不可修改,保证线程安全(Spring AI 多线程场景下无并发问题)
     */
    private final DefaultConversionService conversionService;

    /**
     * 构造器:注入类型转换服务(依赖注入模式)
     * 设计思路:通过构造器注入而非硬编码,保证灵活性------子类可传入自定义配置的 ConversionService
     *
     * @param conversionService Spring 默认类型转换服务实例,不能为空(实际使用时会通过 Assert.notNull 校验)
     */
    public AbstractConversionServiceOutputConverter(DefaultConversionService conversionService) {
        // 初始化类型转换服务,交给父类统一管理
        this.conversionService = conversionService;
    }
}

也声明了 getFormatconvert 方法:

java 复制代码
    /**
     * Getter 方法:给子类提供类型转换服务的访问入口
     * 设计思路:子类(如 BeanOutputConverter)在实现具体转换逻辑时,需要调用该方法获取 ConversionService,
     * 进而完成「中间数据(如 Map)→ 目标类型(如 ContractInfo)」的转换
     *
     * @return 初始化后的默认类型转换服务实例
     */
    public DefaultConversionService getConversionService() {
        return this.conversionService;
    }

    /**
     * 【必须由子类实现】结构化输出转换器核心接口方法:生成格式提示(如 JSON Schema)
     * 作用:告诉 LLM 需要返回的结构化格式(如 "你的输出必须符合以下JSON Schema:{...}")
     * 注:该方法由 StructuredOutputConverter 接口定义,父类未实现,需子类按需实现
     *
     * @return 格式提示字符串(如 JSON Schema、字段说明等)
     */
    @Override
    public abstract String getFormat();

    /**
     * 【必须由子类实现】结构化输出转换器核心接口方法:执行转换逻辑
     * 作用:将 LLM 返回的文本(如 JSON 字符串)转换为目标类型 T
     * 注:该方法由 StructuredOutputConverter 接口定义,父类未实现,需子类按需实现
     *
     * @param source LLM 返回的原始文本(如 JSON 字符串、CSV 字符串等)
     * @return 转换后的目标类型实例(如 ContractInfo、Book 等)
     */
    @Override
    public abstract T convert(String source);
2.2.4.1 ListOutputConverter

【List 输出转换器】将 AI 返回的【逗号分隔字符串】自动转换为 Java List<String>

核心特点

  • 不需要 JSON
  • 简单轻量,适合返回简单列表
  • 基于 Spring ConversionService 实现类型转换

源码如下:

java 复制代码
public class ListOutputConverter extends AbstractConversionServiceOutputConverter<List<String>> {

    /**
     * 默认构造:使用 Spring 内置默认类型转换服务
     */
    public ListOutputConverter() {
        this(new DefaultConversionService());
    }

    /**
     * 可传入自定义 ConversionService 的构造器
     */
    public ListOutputConverter(DefaultConversionService defaultConversionService) {
        super(defaultConversionService);
    }

    /**
     * 生成给 AI 的格式指令:
     * 要求返回【逗号分隔的简单列表】,不要多余文字、不要 JSON、不要解释
     */
    @Override
    public String getFormat() {
        return "Respond with only a list of comma-separated values, without any leading or trailing text.\nExample format: foo, bar, baz\n";
    }

    /**
     * 核心转换方法:
     * 将 AI 返回的文本 → 自动转为 List<String>
     */
    @Override
    public List<String> convert(@NonNull String text) {
        // 使用 Spring 类型转换服务,直接把字符串转成 List
        return (List) this.getConversionService().convert(text, List.class);
    }
}

2.3 ChatClient 入口

在使用时,Spring AI 提供了简单的入口:

2.3.1 entity()

AI 返回的文本动转换成你想要的 Java 对象。

三个重载方法:

  • entity(Class<T> type) :传入普通 Class 类型(最常用)
  • entity(ParameterizedTypeReference<T> type):传入 泛型类型(如 List<ActorsFilms>
  • entity(StructuredOutputConverter<T> structuredOutputConverter):传入自定义转换器

2.3.2 responseEntity()

entity() 返回转换后的对象,responseEntity() 则可以将【原始响应体 + 转换后的 Java 对象】都返回。

三个重载方法:

  • responseEntity(Class<T> type) :传入普通 Class 类型
  • responseEntity(ParameterizedTypeReference<T> type) :传入 泛型类型
  • responseEntity(StructuredOutputConverter<T> structuredOutputConverter) :传入自定义转换器

3. 模型原生结构化输出

大多数 AI 模型现已提供原生结构化输出支持,相比基于提示词的方式更可靠。

这种方式提供:

  • 更高可靠性:模型保证输出符合 Schema
  • 更简洁的提示词:无需追加格式指令
  • 更好性能:模型可内部优化结构化输出

Spring AI 也支持原生结构化输出功能,使用时,BeanOutputConverter 生成的 JSON Schema 会直接发送到模型的结构化输出 API,无需在提示词中加入格式指令。

3.1 智谱 AI

智谱 API 文档中,可以看到对话补全调用时,支持结构化输出参数 response_format

  • text:表示普通文本输出模式,模型返回自然语言文本
  • json_object:表示 JSON 输出模式,模型会返回有效的 JSON 格式数据,建议在提示词中明确说明需要 JSON 格式输出。


提示 :智谱 API 只支持配置响应格式,并不完全提供模型原生结构化输出,在 Spring AI 时,实际也是在提示词中直接添加的格式指令、输出 Schema

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

除此之外,很多大模型还支持通过 format 请求参数,直接传递 JSON Schema ,而不是在提示词中加入格式指令。

例如 Ollama

以下模型目前支持原生结构化输出:

  • OpenAIGPT-4o 及后续支持 JSON Schema 的模型
  • AnthropicClaude 3.5 Sonnet 及后续模型
  • Vertex AI GeminiGemini 1.5 Pro 及后续模型
  • Mistral AIMistral Small 及后续支持 JSON Schema 的模型

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

3.3 内置 JSON 模式

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

例如,OpenAiChatOptions 实现了 StructuredOutputChatOptions 接口:

java 复制代码
public class OpenAiChatOptions implements ToolCallingChatOptions, StructuredOutputChatOptions {

可以通过 spring.ai.openai.chat.options.responseFormat 属性配置 Open AI 的结构化输出,确保模型生成的响应严格符合您提供的 JSON Schema

yml 复制代码
spring:
  ai:
    openai:
      chat:
        options:
          response-format:
            type: JSON_SCHEMA
            name: MySchemaName
            schema: {"type":"object","properties":{"steps":{"type":"array","items":{"type":"object","properties":{"explanation":{"type":"string"},"output":{"type":"string"}},"required":["explanation","output"],"additionalProperties":false}},"final_answer":{"type":"string"}},"required":["steps","final_answer"],"additionalProperties":false}

response-format 可以选择:

  • JSON_OBJECT:保证模型生成的是有效 JSON
  • JSON_SCHEMA:配合提供的 schema,保证模型生成符合 schema 的响应

其他支持的模型:

  • Azure OpenAI:提供 spring.ai.azure.openai.chat.options.responseFormat 选项
  • Ollama:提供 spring.ai.ollama.chat.options.format 选项
  • Mistral AI:提供 spring.ai.mistralai.chat.options.responseFormat 选项
相关推荐
han_hanker2 小时前
springboot 不推荐使用@Autowired怎么处理
java·spring boot·后端
最初的↘那颗心2 小时前
LangChain4j入门:集成SpringBoot与核心概念全解析
java·spring boot·ai·大模型·langchain4j
计算机学姐2 小时前
基于SpringBoot的高校实验室预约管理系统
java·spring boot·后端·mysql·spring·信息可视化·tomcat
九转成圣2 小时前
实战记录:用 Java 拼接长图/网格图,我踩了哪些坑?
java·开发语言
武汉庞小锋2 小时前
opencode使用各大模型小结
人工智能
拾光向日葵2 小时前
南京林业大学2026年硕士研究生跨门类调剂政策详解
大数据·人工智能·物联网
lzhdim2 小时前
SQL 入门 9:SQL 高级子查询:ANY、EXISTS 与多位置应用
java·开发语言·数据库·sql·mysql
LSQ的测试日记2 小时前
深度学习_YOLO,卡尔曼滤波和
人工智能·深度学习·yolo
枫叶林FYL2 小时前
【自然语言处理 NLP】前沿架构与多模态 状态空间模型(Mamba/SSM)深度实现
人工智能·机器学习