구조화된 출력
구조화된 출력 (Structured Outputs)
모델 응답이 반드시 우리가 정의한 JSON 스키마를 따르게 만들면, 타입 안전하고 재사용 가능한 데이터 구조를 안정적으로 얻을 수 있어요. 구조화된 출력은 그 보장을 해 주는 기능이에요. 검증·재시도 로직 없이도 깔끔한 데이터를 받고 싶을 때 특히 유용해요.
두 가지 모드
Structured Outputs는 보장 수준과 요구사항이 서로 다른 두 모드를 제공해요.
Best-effort 모드 (strict: false)
기본값인 strict: false는 스키마를 하드하게 강제하지 않고 모델이 맞추려고 시도해요.
- 유효한 JSON이지만 스키마 준수를 보장하진 않아요 — 필드 타입이 다르거나 누락·초과 필드가 있는 JSON이 나올 수 있어요.
- 오류·형식 오류 가능 — 가끔 형식이 깨진 JSON이나 스키마 검증 실패로 400 오류가 날 수 있어요.
- 요구사항이 적어요 — 선택 필드 같은 유연한 스키마 제약이 가능해요.
- 모델 지원 범위가 넓어요 — Structured Outputs를 지원하는 모든 모델에서 쓸 수 있어요.
가끔 생기는 검증 오류를 재시도 로직으로 감당할 수 있는 상황에 어울려요.
Strict 모드 (strict: true)
제한된 디코딩(constrained decoding)으로 스키마 준수를 보장해요. 생산 환경에서는 이 모드를 쓰는 걸 권장해요. 단, 스키마 검증 오류 자체가 없으므로 별도 오류 처리가 필요 없어요.
구조화된 출력의 장점
- 타입 안전한 응답 — 잘못된 출력에 대한 검증·재시도 로직을 줄여요.
- 프로그램 방식 거절 감지 — 안전 기반 모델 거절을 코드로 감지할 수 있어요.
- 단순한 프롬프트 — 일관된 형식이 필요 없다 보니 프롬프트가 간단해져요.
SDK에서는 Pydantic(Python)과 Zod(TypeScript)로 스키마를 정의해 타입 안전성을 한 단계 더 높일 수 있어요.
텍스트에서 구조 뽑아내기
response_format에 json_schema 타입으로 스키마를 넘기면, 비구조화 텍스트에서 지정한 필드를 추출할 수 있어요.
from groq import Groq
import json
groq = Groq()
response = groq.chat.completions.create(
model="openai/gpt-oss-20b",
messages=[
{"role": "system", "content": "Extract product review information from the text."},
{"role": "user", "content": "I bought the UltraSound Headphones last week and I'm really impressed!"},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "product_review",
"strict": True,
"schema": {
"type": "object",
"properties": {
"product_name": {"type": "string"},
"rating": {"type": "number"},
"sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
"key_features": {"type": "array", "items": {"type": "string"}},
},
"required": ["product_name", "rating", "sentiment", "key_features"],
"additionalProperties": False,
},
},
},
)
result = json.loads(response.choices[0].message.content or "{}")
print(json.dumps(result, indent=2))
스키마 정의 시 additionalProperties: false와 required를 명시해야 strict 모드가 정확히 동작해요.
모드 선택
권장: 생산 애플리케이션에서는 가능하면 Strict 모드(strict: true), 모델 지원이 더 넓어야 하거나 개발 단계에서는 Best-effort 모드(strict: false)로 폴백하세요.
참고로 Structured Outputs는 현재 스트리밍과 도구 사용(tool use)과 함께 쓰면 지원되지 않아요.
JSON Object Mode
Structured Outputs의 json_schema 모드와 달리, JSON Object Mode는 스키마 강제 없이 유효한 JSON 문법만 보장해요. response_format을 {"type": "json_object"}로 설정하면 켜져요. 스키마 준수까지는 보장하지 않으므로, 가능하면 Structured Outputs를 쓰는 게 좋아요.
요구사항과 제약이 있어요.
- 프롬프트(시스템 메시지 또는 사용자 입력)에 JSON 지시를 명시해야 해요.
- 출력은 문법적으로 유효한 JSON이지만 의도한 스키마와는 다를 수 있어요.
- 스키마 준수를 원하면 검증 라이브러리 + 재시도 로직을 함께 써야 해요.
모범 사례
- 사용자 입력 처리 — 부적절하거나 호환되지 않는 입력에 대한 명시적 지시를 넣어요. 스키마 준수는 해도 의미적 정확성은 보장되지 않으니, 호환되지 않는 입력에 대한 폴백 응답(빈 필드, 오류 메시지)을 지정해 두면 환각을 줄일 수 있어요.
- 출력 품질 — 구조화된 출력은 스키마 준수에는 강하지만 의미적 정확성은 보장하지 않아요. 지속적인 오류가 있으면 지시를 다듬거나, 시스템 메시지에 예시를 추가하거나, 복잡한 작업을 분해해 보세요. 프롬프트 공학 가이드에서 최적화 기법을 확인할 수 있어요.
더 알아보기
- Text Generation — 채팅 완성 기본
- Prompting — 프롬프트 최적화
- Prometheus Metrics — 관측 지표