構造化出力コンバーター
構造化された出力を生成する LLM の機能は、出力値の確実な解析に依存する下流アプリケーションにとって重要です。開発者は、AI モデルからの結果を、他のアプリケーション関数やメソッドに渡すことができる JSON、XML、Java クラスなどのデータ型にすばやく変換したいと考えています。
Spring AI Structured Output Converters は、LLM 出力を構造化形式に変換できます。次の図に示すように、このアプローチは LLM テキスト補完エンドポイントを中心に動作します。

汎用補完 API を使用して大規模言語モデル (LLM) から構造化された出力を生成するには、入力と出力を慎重に処理する必要があります。構造化出力コンバーターは、LLM 呼び出しの前後で重要なロールを果たし、目的の出力構造が確実に実現されるようにします。
LLM 呼び出しの前に、コンバーターはプロンプトにフォーマット指示を追加し、モデルに目的の出力構造を生成するための明確なガイダンスを提供します。これらの指示は青写真として機能し、指定されたフォーマットに準拠するようにモデルのレスポンスを形成します。
構造化出力をネイティブにサポートする AI モデルが増えるにつれ、AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT とネイティブ構造化出力機能を組み合わせることでこの機能を活用できるようになります。このアプローチでは、生成された JSON スキーマをモデルのネイティブな構造化出力 API で直接使用するため、事前のフォーマット指示が不要になり、より信頼性の高い結果が得られます。 |
LLM 呼び出しの後、コンバーターはモデルの出力テキストを受け取り、それを構造化型のインスタンスに変換します。この変換プロセスでは、生のテキスト出力を解析し、JSON、XML、ドメイン固有のデータ構造などの対応する構造化データ表現にマッピングします。
StructuredOutputConverter は、モデル出力を構造化出力に変換するためのベストエフォートです。AI モデルがリクエストどおりに構造化出力を返すことは保証されません。モデルがプロンプトを理解しなかったり、リクエストどおりに構造化出力を生成できない場合があります。モデル出力が期待どおりであることを確認するために、検証メカニズムを実装することを検討してください。 |
StructuredOutputConverter は LLM ツール呼び出しでは使用されません。この機能は本質的にデフォルトで構造化された出力を提供するためです。 |
構造化出力 API
StructuredOutputConverter インターフェースを使用すると、出力を Java クラスにマッピングしたり、テキストベースの AI モデル出力から値の配列をマッピングしたりするなど、構造化された出力を取得できます。インターフェース定義は次のとおりです。
public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {
}Spring コンバーター < 文字列、T> (Javadoc) インターフェースと 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}
"""; // user input with a "format" placeholder.
Prompt prompt = new Prompt(
PromptTemplate.builder()
.template(this.userInputTemplate)
.variables(Map.of(..., "format", this.outputConverter.getFormat())) // replace the "format" placeholder with the converter's format.
.build().createMessage()
);Converter<String, T> は、モデルからの出力テキストを指定された型 T のインスタンスに変換するロールを担います。
利用可能なコンバーター
現在、Spring AI は AbstractConversionServiceOutputConverter、AbstractMessageOutputConverter、BeanOutputConverter、MapOutputConverter、ListOutputConverter 実装を提供しています。

AbstractConversionServiceOutputConverter<T>- LLM 出力を目的の形式に変換するための事前構成済みの GenericConversionService (Javadoc) を提供します。デフォルトのFormatProvider実装は提供されていません。AbstractMessageOutputConverter<T>- LLM 出力を目的の形式に変換するための事前構成済みの MessageConverter (Javadoc) を提供します。デフォルトのFormatProvider実装は提供されていません。BeanOutputConverter<T>- 指定された Java クラス (例: Bean) または ParameterizedTypeReference (Javadoc) で構成されたこのコンバーターは、指定された Java クラスから派生したDRAFT_2020_12、JSON Schemaに準拠した JSON レスポンスを生成するように AI モデルに指示するFormatProvider実装を使用します。その後、ObjectMapperを使用して JSON 出力をターゲットクラスの Java オブジェクトインスタンスに逆直列化します。MapOutputConverter- AI モデルが RFC8259 準拠の JSON レスポンスを生成するようにガイドするFormatProvider実装により、AbstractMessageOutputConverterの機能が拡張されます。さらに、提供されているMessageConverterを使用して JSON ペイロードをjava.util.Map<String, Object>インスタンスに変換するコンバーター実装も組み込まれています。ListOutputConverter-AbstractConversionServiceOutputConverterを拡張し、カンマ区切りリスト出力用にカスタマイズされたFormatProvider実装が含まれます。コンバーター実装は、提供されたConversionServiceを使用して、モデルテキスト出力をjava.util.Listに変換します。
コンバーターの使用
次のセクションでは、利用可能なコンバーターを使用して構造化された出力を生成する方法について説明します。
Bean 出力コンバーター
次の例は、BeanOutputConverter を使用して俳優のフィルモグラフィーを生成する方法を示しています。
俳優のフィルモグラフィーを表す対象レコード:
record ActorsFilms(String actor, List<String> movies) {
} 高レベルで流れるような ChatClient API を使用して BeanOutputConverter を適用する方法は次のとおりです。
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 を直接使用します。
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(
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());生成されたスキーマ内のプロパティの順序
BeanOutputConverter は、@JsonPropertyOrder アノテーションを通じて、生成された JSON スキーマ内のカスタムプロパティの順序付けをサポートします。このアノテーションを使用すると、クラスまたはレコード内の宣言順序に関係なく、スキーマ内でプロパティが表示される正確な順序を指定できます。
例: ActorsFilms レコード内のプロパティの特定の順序を確保するには:
@JsonPropertyOrder({"actor", "movies"})
record ActorsFilms(String actor, List<String> movies) {}このアノテーションは、レコードと通常の Java クラスの両方で機能します。
汎用 Bean 型
より複雑なターゲットクラス構造を指定するには、ParameterizedTypeReference コンストラクターを使用します。例: 俳優とそのフィルモグラフィーのリストを表すには、次のようにします。
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 を直接使用します。
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 = PromptTemplate.builder().template(this.template).variables(Map.of("format", this.format)).build().create();
Generation generation = chatModel.call(this.prompt).getResult();
List<ActorsFilms> actorsFilms = this.outputConverter.convert(this.generation.getOutput().getText());マップ出力コンバーター
次のスニペットは、MapOutputConverter を使用してモデル出力をマップ内の数値のリストに変換する方法を示しています。
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 を直接使用します。
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();
Map<String, Object> result = this.mapOutputConverter.convert(this.generation.getOutput().getText());リスト出力コンバーター
次のスニペットは、ListOutputConverter を使用してモデル出力をアイスクリームのフレーバーのリストに変換する方法を示しています。
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 を直接使用します。
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();
List<String> list = this.listOutputConverter.convert(this.generation.getOutput().getText());ネイティブ構造化出力
多くの最新の AI モデルは、プロンプトベースのフォーマットに比べて信頼性の高い結果を提供する構造化出力をネイティブでサポートしています。Spring AI は、ネイティブ構造化出力機能を通じてこれをサポートしています。
ネイティブ構造化出力を使用する場合、BeanOutputConverter によって生成された JSON スキーマはモデルの構造化出力 API に直接送信されるため、プロンプトでのフォーマット指示は不要になります。このアプローチにより、以下のメリットが得られます。
Higher reliability : モデルはスキーマに準拠した出力を保証する
Cleaner prompts : フォーマット指示を追加する必要はない
Better performance : モデルは内部的に構造化された出力を最適化できる
ネイティブ構造化出力の使用
ネイティブ構造化出力を有効にするには、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);You can also set this globally using defaultAdvisors() on the ChatClient.Builder:
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultAdvisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
.build();
}Supported Models for Native Structured Output
The following models currently support native structured output:
OpenAI : GPT-4o and later models with JSON Schema support
Anthropic : Claude 3.5 Sonnet and later models
Vertex AI Gemini : Gemini 1.5 Pro and later models
| Some AI models, such as OpenAI, don’t support arrays of objects natively at the top level. In such cases, you can use the Spring AI default structured output conversion (without the native structured output advisor). |
組み込み JSON モード
一部の AI モデルでは、構造化された (通常は JSON) 出力を生成するための専用の構成オプションが提供されます。
OpenAI 構造化出力を使用すると、モデルが、提供された JSON スキーマに厳密に準拠したレスポンスを生成することが保証されます。モデルが生成するメッセージが有効な JSON であることを保証する
JSON_OBJECTか、モデルが提供されたスキーマに一致するレスポンスを生成することを保証する提供されたスキーマ付きのJSON_SCHEMA(spring.ai.openai.chat.options.responseFormatオプション) のいずれかを選択できます。Azure OpenAI - モデルが出力する必要がある形式を指定する
spring.ai.azure.openai.chat.options.responseFormatオプションを提供します。{ "type": "json_object" }に設定すると JSON モードが有効になり、モデルが生成するメッセージが有効な JSON であることが保証されます。Ollama - レスポンスを返す形式を指定するための
spring.ai.ollama.chat.options.formatオプションを提供します。現在受け入れられる値はjsonのみです。Mistral AI - レスポンスを返す形式を指定するための
spring.ai.mistralai.chat.options.responseFormatオプションを提供します。これを{ "type": "json_object" }に設定すると JSON モードが有効になり、モデルが生成するメッセージが有効な JSON であることが保証されます。