> ## Documentation Index
> Fetch the complete documentation index at: https://javaai.pig4cloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 结构化输出转换器

<Note>
  截至 2024 年 2 月 5 日，旧的 `OutputParser`、`BeanOutputParser`、`ListOutputParser` 和 `MapOutputParser` 类已被弃用，取而代之的是新的 `StructuredOutputConverter`、`BeanOutputConverter`、`ListOutputConverter` 和 `MapOutputConverter` 实现。
  后者是前者的直接替代品，提供相同的功能。更改的原因主要是命名，因为没有进行任何解析，同时也与 Spring `org.springframework.core.convert.converter` 包保持一致，带来了一些改进的功能。
</Note>

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

Spring AI `Structured Output Converters` 帮助将 LLM 输出转换为结构化格式。
如下图所示，这种方法围绕 LLM 文本完成端点运行：

<Frame caption="结构化输出转换器架构">
  <img src="https://mintcdn.com/pig/uheqNCvodstPwu2o/images/structured-output-architecture.jpg?fit=max&auto=format&n=uheqNCvodstPwu2o&q=85&s=bdb29f5552452a4e6ed4c99b60813b84" width="900" data-path="images/structured-output-architecture.jpg" />
</Frame>

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

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

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

<Tip>
  `StructuredOutputConverter` 是尽最大努力将模型输出转换为结构化输出。
  AI 模型不能保证按照请求返回结构化输出。
  模型可能不理解提示或无法生成请求的结构化输出。
  考虑实现验证机制以确保模型输出符合预期。
</Tip>

<Tip>
  `StructuredOutputConverter` 不用于 LLM [工具调用](/spring4ai/api/tools)，因为此功能默认提供结构化输出。
</Tip>

## 结构化输出 API

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

```java theme={"system"}
public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {

}
```

它结合了 Spring [Converter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/converter/Converter.html) 接口和 FormatProvider 接口

```java theme={"system"}
public interface FormatProvider {
	String getFormat();
}
```

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

<Frame caption="结构化输出 API">
  <img src="https://mintcdn.com/pig/uheqNCvodstPwu2o/images/structured-output-api.jpg?fit=max&auto=format&n=uheqNCvodstPwu2o&q=85&s=0a076f8d679aec0e1cc72a4899637b0e" width="900" data-path="images/structured-output-api.jpg" />
</Frame>

`FormatProvider` 为 AI 模型提供特定的格式指南，使其能够生成可以转换为指定目标类型 `T` 的文本输出。以下是此类格式指令的示例：

```
您的响应应该是 JSON 格式。
JSON 的数据结构应该匹配这个 Java 类：java.util.HashMap
不要包含任何解释，只提供符合此格式的 RFC8259 兼容 JSON 响应，不要有任何偏差。
```

格式指令通常使用 [PromptTemplate](/spring4ai/api/prompt#prompttemplate) 附加到用户输入的末尾，如下所示：

```java theme={"system"}
StructuredOutputConverter outputConverter = ...
String userInputTemplate = """
    ... 用户文本输入 ....
    {format}
    """; // 带有 "format" 占位符的用户输入。
Prompt prompt = new Prompt(
   new PromptTemplate(
		   this.userInputTemplate,
      Map.of(..., "format", outputConverter.getFormat()) // 用转换器的格式替换 "format" 占位符。
   ).createMessage());
```

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

### 可用的转换器

目前，Spring AI 提供了 `AbstractConversionServiceOutputConverter`、`AbstractMessageOutputConverter`、`BeanOutputConverter`、`MapOutputConverter` 和 `ListOutputConverter` 实现：

<Frame caption="结构化输出类层次结构">
  <img src="https://mintcdn.com/pig/uheqNCvodstPwu2o/images/structured-output-hierarchy4.jpg?fit=max&auto=format&n=uheqNCvodstPwu2o&q=85&s=bd167471acac3ec9059d8cb5edbb8af6" width="900" data-path="images/structured-output-hierarchy4.jpg" />
</Frame>

<AccordionGroup>
  <Accordion title="AbstractConversionServiceOutputConverter<T>" icon="cogs">
    提供预配置的 [GenericConversionService](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/support/GenericConversionService.html) 用于将 LLM 输出转换为所需格式。不提供默认的 `FormatProvider` 实现。
  </Accordion>

  <Accordion title="AbstractMessageOutputConverter<T>" icon="envelope">
    提供预配置的 [MessageConverter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/support/converter/MessageConverter.html) 用于将 LLM 输出转换为所需格式。不提供默认的 `FormatProvider` 实现。
  </Accordion>

  <Accordion title="BeanOutputConverter<T>" icon="mug-hot">
    配置有指定的 Java 类（例如，Bean）或 [ParameterizedTypeReference](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/ParameterizedTypeReference.html)，此转换器使用 `FormatProvider` 实现，指导 AI 模型生成符合从指定 Java 类派生的 `DRAFT_2020_12`、`JSON Schema` 的 JSON 响应。随后，它使用 `ObjectMapper` 将 JSON 输出反序列化为目标类的 Java 对象实例。
  </Accordion>

  <Accordion title="MapOutputConverter" icon="map">
    扩展 `AbstractMessageOutputConverter` 的功能，提供 `FormatProvider` 实现，指导 AI 模型生成符合 RFC8259 的 JSON 响应。此外，它还包含一个转换器实现，使用提供的 `MessageConverter` 将 JSON 负载转换为 `java.util.Map<String, Object>` 实例。
  </Accordion>

  <Accordion title="ListOutputConverter" icon="list">
    扩展 `AbstractConversionServiceOutputConverter` 并包含一个 `FormatProvider` 实现，专为逗号分隔的列表输出而设计。转换器实现使用提供的 `ConversionService` 将模型文本输出转换为 `java.util.List`。
  </Accordion>
</AccordionGroup>

## 使用转换器

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

### Bean 输出转换器

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

表示演员电影作品的目标记录：

```java theme={"system"}
record ActorsFilms(String actor, List<String> movies) {
}
```

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

```java theme={"system"}
ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
        .user(u -> u.text("为 {actor} 生成 5 部电影的电影作品。")
                    .param("actor", "Tom Hanks"))
        .call()
        .entity(ActorsFilms.class);
```

或直接使用低级 `ChatModel` API：

```java theme={"system"}
BeanOutputConverter<ActorsFilms> beanOutputConverter =
    new BeanOutputConverter<>(ActorsFilms.class);

String format = this.beanOutputConverter.getFormat();

String actor = "Tom Hanks";

String template = """
        为 {actor} 生成 5 部电影的电影作品。
        {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());
```

### 生成模式中的属性排序

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

例如，要确保 `ActorsFilms` 记录中的属性特定排序：

```java theme={"system"}
@JsonPropertyOrder({"actor", "movies"})
record ActorsFilms(String actor, List<String> movies) {}
```

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

#### 泛型 Bean 类型

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

```java theme={"system"}
List<ActorsFilms> actorsFilms = ChatClient.create(chatModel).prompt()
        .user("为 Tom Hanks 和 Bill Murray 生成 5 部电影的电影作品。")
        .call()
        .entity(new ParameterizedTypeReference<List<ActorsFilms>>() {});
```

或直接使用低级 `ChatModel` API：

```java theme={"system"}
BeanOutputConverter<List<ActorsFilms>> outputConverter = new BeanOutputConverter<>(
        new ParameterizedTypeReference<List<ActorsFilms>>() { });

String format = this.outputConverter.getFormat();
String template = """
        为 Tom Hanks 和 Bill Murray 生成 5 部电影的电影作品。
        {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 输出转换器

以下代码片段显示如何使用 `MapOutputConverter` 将模型输出转换为地图中的数字列表。

```java theme={"system"}
Map<String, Object> result = ChatClient.create(chatModel).prompt()
        .user(u -> u.text("为我提供一个 {subject}")
                    .param("subject", "一个从 1 到 9 的数字数组，键名为 'numbers'"))
        .call()
        .entity(new ParameterizedTypeReference<Map<String, Object>>() {});
```

或直接使用低级 `ChatModel` API：

```java theme={"system"}
MapOutputConverter mapOutputConverter = new MapOutputConverter();

String format = this.mapOutputConverter.getFormat();
String template = """
        为我提供一个 {subject}
        {format}
        """;

Prompt prompt = new PromptTemplate(this.template,
        Map.of("subject", "一个从 1 到 9 的数字数组，键名为 'numbers'", "format", this.format)).create();

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

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

### List 输出转换器

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

```java theme={"system"}
List<String> flavors = ChatClient.create(chatModel).prompt()
                .user(u -> u.text("列出五种 {subject}")
                            .param("subject", "冰淇淋口味"))
                .call()
                .entity(new ListOutputConverter(new DefaultConversionService()));
```

或直接使用低级 `ChatModel API`：

```java theme={"system"}
ListOutputConverter listOutputConverter = new ListOutputConverter(new DefaultConversionService());

String format = this.listOutputConverter.getFormat();
String template = """
        列出五种 {subject}
        {format}
        """;

Prompt prompt = new PromptTemplate(this.template,
        Map.of("subject", "冰淇淋口味", "format", this.format)).create();

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

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

## 支持的 AI 模型

以下 AI 模型已经过测试，支持 List、Map 和 Bean 结构化输出。

<CardGroup cols={2}>
  <Card title="OpenAI" icon="openai" href="/spring4ai/api/chat/openai-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelIT.java)
  </Card>

  <Card title="Anthropic Claude 3" icon="cat" href="/spring4ai/api/chat/anthropic-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatModelIT.java)
  </Card>

  <Card title="Azure OpenAI" icon="microsoft" href="/spring4ai/api/chat/azure-openai-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatModelIT.java)
  </Card>

  <Card title="Mistral AI" icon="cloud" href="/spring4ai/api/chat/mistralai-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiChatModelIT.java)
  </Card>

  <Card title="Ollama" icon="cube" href="/spring4ai/api/chat/ollama-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelIT.java)
  </Card>

  <Card title="Vertex AI Gemini" icon="google" href="/spring4ai/api/chat/vertexai-gemini-chat">
    [查看测试](https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModelIT.java)
  </Card>
</CardGroup>

## 内置 JSON 模式

一些 AI 模型提供专门的配置选项来生成结构化（通常是 JSON）输出。

<AccordionGroup>
  <Accordion title="OpenAI" icon="openai">
    [OpenAI 结构化输出](/spring4ai/api/chat/openai-chat#structured-outputs) 可以确保您的模型生成严格符合您提供的 JSON Schema 的响应。您可以选择 `JSON_OBJECT`，它保证模型生成的消息是有效的 JSON，或者选择 `JSON_SCHEMA` 并提供模式，保证模型将生成与您提供的模式匹配的响应（`spring.ai.openai.chat.options.responseFormat` 选项）。
  </Accordion>

  <Accordion title="Azure OpenAI" icon="microsoft">
    [Azure OpenAI](/spring4ai/api/chat/azure-openai-chat) 提供 `spring.ai.azure.openai.chat.options.responseFormat` 选项，指定模型必须输出的格式。设置为 `{ "type": "json_object" }` 启用 JSON 模式，它保证模型生成的消息是有效的 JSON。
  </Accordion>

  <Accordion title="Ollama" icon="cube">
    [Ollama](/spring4ai/api/chat/ollama-chat) 提供 `spring.ai.ollama.chat.options.format` 选项来指定返回响应的格式。目前，唯一接受的值是 `json`。
  </Accordion>

  <Accordion title="Mistral AI" icon="cloud">
    [Mistral AI](/spring4ai/api/chat/mistralai-chat) 提供 `spring.ai.mistralai.chat.options.responseFormat` 选项来指定返回响应的格式。设置为 `{ "type": "json_object" }` 启用 JSON 模式，它保证模型生成的消息是有效的 JSON。
  </Accordion>
</AccordionGroup>

<Card title="文档有误？请协助编辑" icon="pencil" href="https://github.com/javaai-pig4cloud-com/javaai-docs/edit/main/spring-ai/api/structured-output-converter.mdx">
  发现文档问题？点击此处直接在 GitHub 上编辑并提交 PR，帮助我们改进文档！
</Card>
