인기 있는 LLM 제공업체(OpenAI, Gemini, Anthropic, Mistral 및 AWS Bedrock) 간의 구조화된 출력 비교

조금 다른 API는 특별한 접근이 필요합니다.

Page content

다음은 구조화된 출력을 지원하는 주요 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_objectJSON 형식을 강제합니다(정확한 스키마는 아님) — 클라이언트 측에서 검증합니다.


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)

유용한 링크