인기 있는 LLM 제공업체(OpenAI, Gemini, Anthropic, Mistral 및 AWS Bedrock) 간의 구조화된 출력 비교
조금 다른 API는 특별한 접근이 필요합니다.
다음은 구조화된 출력을 지원하는 주요 LLM 제공업체 간의 비교 및 최소한의 Python 예제입니다.
이미 Ollama에 호스팅된 LLM에서 구조화된 출력을 요청하는 방법에 대해 살펴보았습니다. 여기서는 다른 제공업체에서 동일한 요청을 어떻게 할 수 있는지 검토합니다.
TL;DR 매트릭스
제공업체 | 네이티브 “JSON 모드” | JSON 스키마 강제 적용 | 일반적인 조절 수단 | 참고 사항 |
---|---|---|---|---|
OpenAI | 예 | 예 (첫 번째 등급) | response_format={"type":"json_schema", ...} |
Responses API 또는 Chat Completions을 통해 작동; 또한 함수 호출이 가능합니다. |
Google Gemini | 예 | 예 (첫 번째 등급) | response_schema= + response_mime_type="application/json" |
스키마가 설정되면 엄격히 검증된 JSON을 반환합니다. |
Anthropic (Claude) | 간접적 | 예 (JSON 스키마를 사용한 Tool Use을 통해) | tools=[{input_schema=...}] + tool_choice |
모델이 “사용자”가 정의한 스키마에 맞는 도구를 호출하도록 강제; 스키마 형식의 인수를 제공합니다. |
Mistral | 예 | 부분적 (JSON만; 서버 측 스키마 없음) | response_format={"type":"json_object"} |
JSON을 보장하지만, 클라이언트 측에서 스키마에 맞게 검증해야 합니다. |
AWS Bedrock (플랫폼) | 모델에 따라 다름 | 예 (Tool/Converse 스키마를 통해) | toolConfig.tools[].toolSpec.inputSchema |
Bedrock의 Converse API는 도구 입력이 JSON 스키마에 따라 검증됩니다. |
LLM 구조화된 출력 - 일반 정보
LLM 구조화된 출력은 대규모 언어 모델(LLMs)이 자유로운 텍스트 대신 사전 정의된 구조나 형식에 엄격히 따르는 응답을 생성할 수 있는 능력을 의미합니다. 이 구조화된 출력은 JSON, XML, 테이블, 템플릿과 같은 형식으로 제공되어 데이터가 기계가 읽을 수 있고 일관되며, 소프트웨어에 의해 다양한 애플리케이션에서 쉽게 파싱될 수 있습니다.
구조화된 출력은 전통적인 LLM 출력과 달리 일반적으로 개방형 자연어 텍스트를 생성합니다. 대신, JSON 객체와 정의된 키 및 값 유형, 또는 출력에 특정 클래스(예: 다중 선택 답변, 감정 클래스, 또는 데이터베이스 행 형식)를 사용하여 스키마 또는 형식을 강제합니다. 이 접근법은 신뢰성 향상, 오류 및 환상 감소, 데이터베이스, API 또는 워크플로우와 같은 시스템에 통합을 간소화합니다.
LLM에서 구조화된 출력을 생성하는 것은 일반적으로 다음과 같은 기술을 사용합니다:
- 모델이 원하는 형식으로 출력을 생성하도록 안내하기 위해 상세한 프롬프트 지시문을 지정합니다.
- Python에서 사용하는 Pydantic과 같은 검증 및 파싱 도구를 사용하여 출력이 스키마와 일치하도록 보장합니다.
- 때로는 문법 또는 유한 상태 기계 기반의 디코딩 제약을 강제하여 토큰 수준에서 형식에 대한 준수를 보장합니다.
구조화된 LLM 출력의 이점은 다음과 같습니다:
- 기계가 읽을 수 있고 통합이 쉬운 점.
- 변동성 및 오류 감소.
- 일관된 데이터 형식이 필요한 작업에 대한 예측 가능성 및 검증 가능성 향상.
도전 과제는 효과적인 스키마 설계, 복잡한 중첩 데이터 처리, 자유로운 텍스트 생성에 비해 추론 능력의 잠재적 한계입니다.
종합적으로, 구조화된 출력은 인간이 읽을 수 있는 텍스트보다 정확하고 형식화된 데이터가 필요한 애플리케이션에서 LLM을 더 유용하게 만듭니다.
구조화된 출력 Python 예제
모든 코드 조각은 JSON 형식의 이벤트 정보를 추출합니다: {title, date, location}
. 키/모델은 원하는 대로 변경할 수 있습니다.
1) OpenAI — JSON 스키마 (엄격)
from openai import OpenAI
import json
client = OpenAI()
schema = {
"name": "Event",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string", "description": "YYYY-MM-DD"},
"location": {"type": "string"}
},
"required": ["title", "date", "location"],
"additionalProperties": False
},
"strict": True
}
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "user", "content": "Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}
],
response_format={"type": "json_schema", "json_schema": schema},
)
data = json.loads(resp.choices[0].message.content)
print(data)
OpenAI의 구조화된 출력 기능은 서버 측에서 이 스키마를 강제합니다.
2) Google Gemini — 응답 스키마 + JSON MIME
import google.generativeai as genai
from google.genai import types
# API 키로 구성
# genai.configure(api_key="your-api-key")
schema = types.Schema(
type=types.Type.OBJECT,
properties={
"title": types.Schema(type=types.Type.STRING),
"date": types.Schema(type=types.Type.STRING),
"location": types.Schema(type=types.Type.STRING),
},
required=["title", "date", "location"],
additional_properties=False,
)
resp = genai.generate_content(
model="gemini-2.0-flash",
contents="Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'",
generation_config=genai.GenerationConfig(
response_mime_type="application/json",
response_schema=schema,
),
)
print(resp.text) # 이미 스키마에 따라 유효한 JSON
Gemini는 response_schema
에 따라 엄격한 JSON을 반환합니다.
3) Anthropic (Claude) — JSON 스키마와 함께 Tool Use
from anthropic import Anthropic
import json
client = Anthropic()
tool = {
"name": "extract_event",
"description": "이벤트 세부 정보를 반환합니다.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string"},
"location": {"type": "string"}
},
"required": ["title", "date", "location"],
"additionalProperties": False
}
}
msg = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
tools=[tool],
tool_choice={"type": "tool", "name": "extract_event"}, # 스키마 강제
messages=[{"role": "user", "content":
"Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
)
# Claude는 도구를 "호출"합니다; 스키마에 맞는 인수를 가져옵니다.
tool_use = next(b for b in msg.content if b.type == "tool_use")
print(json.dumps(tool_use.input, indent=2))
Claude에는 일반적인 “JSON 모드” 전환 스위치가 없습니다. 대신, Tool Use와 **input_schema
**를 사용하여 검증된, 스키마 형식의 인수를 제공할 수 있습니다(사용을 강제할 수 있습니다).
4) Mistral — JSON 모드 (클라이언트 측 검증)
from mistralai import Mistral
import json
client = Mistral()
resp = client.chat.complete(
model="mistral-large-latest",
messages=[{"role":"user","content":
"Return JSON with keys title, date (YYYY-MM-DD), location for: "
"'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}],
response_format={"type": "json_object"} # 유효한 JSON 보장
)
data = json.loads(resp.choices[0].message.content)
print(data)
# 팁: `data`를 로컬에서 Pydantic/JSON 스키마에 따라 검증합니다.
Mistral의 json_object
는 JSON 형식을 강제합니다(정확한 스키마는 아님) — 클라이언트 측에서 검증합니다.
5) AWS Bedrock — Converse API Tool 스키마 (모델 무관)
import boto3, json
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
tools = [{
"toolSpec": {
"name": "extract_event",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"title": {"type": "string"},
"date": {"type": "string"},
"location": {"type": "string"}
},
"required": ["title","date","location"],
"additionalProperties": False
}
}
}
}]
resp = bedrock.converse(
modelId=model_id,
toolConfig={"tools": tools},
toolChoice={"tool": {"name": "extract_event"}}, # 스키마 강제
messages=[{"role":"user","content":[{"text":
"Extract the event from: 'PyData Sydney is on 2025-11-03 at Darling Harbour.'"}]}],
)
# toolUse 내용 추출
tool_use = next(
c["toolUse"] for c in resp["output"]["message"]["content"] if "toolUse" in c
)
print(json.dumps(tool_use["input"], indent=2))
Bedrock은 도구 입력을 JSON 스키마에 따라 검증하고, 많은 호스팅된 모델(예: Claude)은 Converse를 통해 지원합니다.
실용적인 지침 및 검증
- 서버 측에서 가장 강력한 보장이 필요한 경우: OpenAI 구조화된 출력 또는 Gemini 응답 스키마.
- Claude/Bedrock을 이미 사용하는 경우: JSON 스키마를 사용한 Tool을 정의하고 사용을 강제하고, 도구의 인수를 유형화된 객체로 읽습니다.
- Mistral을 사용하는 경우:
json_object
를 활성화하고 로컬에서 검증(예: Pydantic을 사용하여).
검증 패턴 (모두에 적용 가능)
from pydantic import BaseModel, ValidationError
class Event(BaseModel):
title: str
date: str
location: str
try:
event = Event.model_validate(data) # `data`는 어떤 제공업체에서든 가져온 값
except ValidationError as e:
# 처리 / 재시도 / 모델에 오류를 보고하여 수정 요청
print(e)
유용한 링크
- https://platform.openai.com/docs/guides/structured-outputs
- https://ai.google.dev/gemini-api/docs/structured-output
- https://docs.mistral.ai/capabilities/structured-output/json_mode/
- https://aws.amazon.com/blogs/machine-learning/structured-data-response-with-amazon-bedrock-prompt-engineering-and-tool-use
- https://github.com/aws-samples/anthropic-on-aws/blob/main/complex-schema-tool-use/README.md
- https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-anthropic-claude-messages.html
- Ollama에 호스팅된 LLM에서 구조화된 출력을 요청하는 방법
- Python Cheatsheet
- AWS SAM + AWS SQS + Python PowerTools
- AWS lambda 성능 비교: JavaScript vs Python vs Golang