Structured Output은 LLM을 사용하는 개발자들의 생산성을 보다 더 높여주기 위한 기능으로, JSON, XML or Java 클래스와 같이 메서드의 반환값으로 특정 타입을 지원해줍니다.
Spring AI에서는 이러한 타입을 Converter를 통해서 제공해주는데요, 오늘은 Spring AI에서 제공해주는 Structured Output Converter에 대해 알아보려고 합니다.
Structured Output Converter
LLM 모델에 대한 질의 결과로 받는 값을 특정 타입(JSON, XML or Java Class)으로 변환해주는 기능으로, 다음 사진과 같이 LLM 질의 결과를 추출하기 전에 컨버터에 의해서 결과 값을 변환할 수 있게 해주는 기능입니다.
AI의 질의 결과 값을 내부 Converter에 의해 변환한다는 의미에서 보다시피, AI 모델의 질의 자체가 구조화 되어 들어오는 것이 아닌 점을 유의해야 합니다.
또한, Tool Caling에 대해서도 기본적으로 StructuredOutputConverter를 제공하지는 않는다고 하네요.
Structured Output API
사진을 보면, 가장 기본이 되는 클래스가 FormatProvider와 Converter<String, T>가 있는데요, Spring 문서에 의하면 FormatProvider는 AI 모델에 특정한 구조로 응답을 주도록 가이드해주는 역할을 하며, Converter는 T 타입을 Text 형태로 출력할 수 있도록 도와주는 클래스라고 합니다.
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.
Converter 종류
사진을 보면, AbstractMessageOutputConverter를 상속한 MapOutputConverter와 AbstractConversionServiceOutputConverter를 상속한 ListOutputConverter, 그리고 BeanOutputConvert가 있습니다.
AbstractMessageOutputConverter
- Spring Core에서 제공하는 GenericConversionService를 통해 결과 값을 변환하며, 아직 FormatProvider를 구현하지 않았기 때문에 이를 구현하여 사용해야 하는 추상 클래스입니다.
AbstractConversionServiceOutputConverter
- Spring Message에서 제공하는 MessageConverter를 통해 결과 값을 반환하며, 이 역시 아직 FormatProvider를 구현하지 않았기 때문에 이를 구현하여 사용해야 하는 추상 클래스입니다.
MapOutputConverter
- AbstractMessageOutputConverter를 구현한 클래스로, Map 형태의 결과 값을 추출하는 특징을 가지고 있고, 다음과 같이 System Prompt에 넣게 될 FormatProvider의 getFormat()을 구현하는 것을 확인할 수 있습니다.
public class MapOutputConverter extends AbstractMessageOutputConverter<Map<String, Object>> {
public MapOutputConverter() {
super(new MappingJackson2MessageConverter());
}
public Map<String, Object> convert(@NonNull String text) {
if (text.startsWith("```json") && text.endsWith("```")) {
text = text.substring(7, text.length() - 3);
}
Message<?> message = MessageBuilder.withPayload(text.getBytes(StandardCharsets.UTF_8)).build();
return (Map)this.getMessageConverter().fromMessage(message, HashMap.class);
}
public String getFormat() {
String raw = "Your response should be in JSON format.\nThe data structure for the JSON should match this Java class: %s\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\nRemove the ```json markdown surrounding the output including the trailing \"```\".\n";
return String.format(raw, HashMap.class.getName());
}
}
ListOutputConverter
- AbstractConversionServiceOutputConverter의 구현체로, List 형식의 결과 값을 추출하는 특징을 가지고 있고, 다음과 같이 System Prompt에 넣게 될 FormatProvider의 getFormat()을 구현하는 것을 확인할 수 있습니다.
public class ListOutputConverter extends AbstractConversionServiceOutputConverter<List<String>> {
public ListOutputConverter() {
this(new DefaultConversionService());
}
public ListOutputConverter(DefaultConversionService defaultConversionService) {
super(defaultConversionService);
}
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";
}
public List<String> convert(@NonNull String text) {
return (List)this.getConversionService().convert(text, List.class);
}
}
BeanOutputConverter
- 기본 StructuredOutputConverter의 구현체로, 기본적인 format 구현은 JSON을 포맷을 따르는데, DRAFT_2020_12라는 JSON 표준 포맷을 따른다고 합니다.
- 또한, 내부적으로 ObjectMapper를 사용하는 것을 확인할 수 있는데요, 이를 통해 원하는 Class Type으로 JSON 결과 값을 역직렬화할 수 있는 것을 알 수 있습니다.
- 저는 여기서 약간 헷갈렸던 포인트가, Bean이라는 네이밍인데요, Spring Bean을 등록해야 Bean이라는 이름을 붙일 수 있지 않을까 해서 검색해보니, Bean이라고 하는 네이밍은 Java 기반의 POJO도 될 수 있는데 너무 Spring Bean = Bean 이라고만 생각했던 것 같습니다.
public class BeanOutputConverter<T> implements StructuredOutputConverter<T> {
private final Logger logger;
private final Type type;
private final ObjectMapper objectMapper;
private String jsonSchema;
public BeanOutputConverter(Class<T> clazz) {
this(ParameterizedTypeReference.forType(clazz));
}
public BeanOutputConverter(Class<T> clazz, ObjectMapper objectMapper) {
this(ParameterizedTypeReference.forType(clazz), objectMapper);
}
public BeanOutputConverter(ParameterizedTypeReference<T> typeRef) {
this(typeRef.getType(), (ObjectMapper)null);
}
public BeanOutputConverter(ParameterizedTypeReference<T> typeRef, ObjectMapper objectMapper) {
this(typeRef.getType(), objectMapper);
}
public String getFormat() {
String template = "Your response should be in JSON format.\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\nDo not include markdown code blocks in your response.\nRemove the ```json markdown from the output.\nHere is the JSON Schema instance your output must adhere to:\n```%s```\n";
return String.format(template, this.jsonSchema);
}
}
예제 테스트
@SpringBootTest
class OutputConverterTest {
@Autowired
lateinit var chatClientBuilder: ChatClient.Builder
@DisplayName("OutputConverter를 사용한 예제")
@Test
fun outputConverterTest() {
val chatClient: ChatClient = chatClientBuilder.build()
val userInput = "오늘 날씨는 {city} 어때? 그리고 {city}의 내일 날씨는 어때? 답변은 JSON 형식으로 해줘."
val promptTemplate = PromptTemplate.builder()
.template(userInput)
.variables(mapOf("city" to "서울"))
.build().createMessage()
val prompt = Prompt(promptTemplate)
val response = chatClient.prompt(prompt)
.call()
.content()!!
println(response)
val outputConverter = BeanOutputConverter(WeatherInfo::class.java)
val weatherInfo = outputConverter.convert(response)
println(weatherInfo)
}
data class WeatherInfo(
val today: String?,
val tomorrow: String?
)
}
AI Model에서 지원하는 Converter
- Spring AI에서 제공하는 Converter 외에도, AI 모델에서 제공하는 List, Map, Class에 대한 Converter도 있는데요, 여기서는 중요하지 않은 부분이라 링크로 첨부하도록 하겠습니다.
Built-in JSON Mode에 대해
- 몇몇 AI 모델은 JSON 형식으로 출력하도록 제공하는데요, 다음 Spring AI 링크 에서 확인하실 수 있습니다.
Deprecated 목록
다음은 Structured Output API가 들어서면서 Deprecated 된 항목입니다. 이유는 너무 당연스럽게 해당 Parser에 해당하는 Converter가 나왔기 때문이라네요... ^^;
- OutputParser, BeanOutputParser, ListOutputParser, MapOutputParser
참고
https://docs.spring.io/spring-ai/reference/api/structured-output-converter.html
Structured Output Converter :: Spring AI Reference
As of 02.05.2024 the old OutputParser, BeanOutputParser, ListOutputParser and MapOutputParser classes are deprecated in favor of the new StructuredOutputConverter, BeanOutputConverter, ListOutputConverter and MapOutputConverter implementations. the latter
docs.spring.io
'Spring Framework > AI' 카테고리의 다른 글
Spring AI - Chat memory (0) | 2025.10.02 |
---|---|
Spring AI - Advisor API (0) | 2025.09.30 |
Spring AI - ChatClient API 살펴보기 (0) | 2025.09.21 |
Spring AI - Prompt와 Message (0) | 2025.09.21 |
Spring AI - Chat Client API 시작하기 (0) | 2025.09.20 |