이 시리즈의 이전 게시물에서 다루었습니다.상담원에게 JSON이 필요한 이유(Tool Calling에서 MCP 데이터),JSON Schema⟧가 인프라가 된 이유(Function Calling 및 MCP 진화), 그리고Gemini 특정 구조화된 출력 구성(Gemini API 가이드).
이 기사는 다음과 같이 축소됩니다.사용하는 모델 API에 관계없이 "JSON로 응답해 주세요"에서 "출력이 이 JSON 스키마⟧와 일치해야 합니다"로 어떻게 이동합니까?? 공급업체는 2024~2026년에 Structured Outputs⟧ / JSON Schema⟧ 모드와 같은 이름으로 이에 집중했습니다. Tool Calling을 사용한 동일한 아이디어, 약간 다른 필드 이름, 스키마 하위 집합 및 경계입니다.
4개 레벨, 각 레벨은 이전 레벨보다 엄격합니다.
팀은 일반적으로 모델에서 JSON을 얻기 위해 네 가지 접근 방식을 사용합니다. 신뢰성은 크기에 따라 다릅니다.
| 수준 | 접근하다 | 당신이 통제하는 것 | 전형적인 실패 |
|---|---|---|---|
| L0 | 프롬프트 전용: "출력 JSON" | 소프트 제약조건 | ```json fences, prose, single quotes, trailing commas |
| L1 | 프롬프트 + 퓨샷 JSON 예 | 예를 들어 형태를 정하세요. 엄격한 규칙은 없습니다. | 필드 이름 드리프트, 필드 누락, 혼합 유형 |
| L2 | JSON Mode⟧ (response_format: json_object, etc.) | 출력은 유효해야 합니다 JSON | Parses, but price may be a string |
| L3 | 구조화된 출력+ JSON 스키마⟧ | 필드, 유형, 열거형, 필수 | 의미적 환각, 잘림, 무시된 키워드 |
For production extraction, classification, or form filling, aim for L3. L0–L1 suit exploration; L2 when shape varies and you only need JSON.parse. L3 is the contract programs can consume directly.
JSON Schema⟧가 제어하는 것과 제어하지 않는 것
JSON 스키마⟧필드, 유형, 필수 키, 열거형, 범위, 배열 항목 형태 등 문서 구조를 설명합니다. 공급업체 구조화된 출력s⟧ 해당 스키마를 프롬프트에 붙여넣는 것이 아니라 생성으로 컴파일합니다.
Schema can enforce: syntax shape (object / array / string / integer), required, enum, minimum / maximum, additionalProperties: false, nested objects and arrays.
Schema cannot enforce business correctness. Example: “total_cents must equal sum of line items” — assert that in code after Schema validation. Schema also does not fact-check: a well-typed fabricated invoice number is still hallucination.
Tool inputSchema uses the same language; Structured Output constrains the final reply, Tool Calling constrains tool arguments. See 데이터 흐름 가이드.
제한된 디코딩: 스키마가 프롬프트보다 나은 이유
프롬프트는 준수 가능성을 높일 뿐입니다. 구조화된 출력 용도제한된 디코딩: 각 토큰에서 디코더는 JSON 구문을 위반하거나 스키마를 위반하는 토큰을 억제합니다.
일반적으로 정규식 제거 마크다운 펜스 없이 구문 분석 가능하고 모양이 올바른 JSON을 얻을 수 있습니다. 구현은 다르지만(FSM, 문법, 로짓 마스크) 개발자 계약은 동일합니다.프롬프트뿐만 아니라 API에도 스키마를 전달합니다..
제한된 디코딩 보장구조, 아니다의미론. 항상 동일한 스키마로 다시 검증하고 프로덕션에 비즈니스 규칙을 추가하세요.
OpenAI, Gemini, Anthropic 비교
동일한 개념, 다른 필드 이름. 예: 하나의 송장 개체를 추출합니다.
| 공급업체 | JSON 모드⟧ | 구조화된 출력 / 스키마 | 메모 |
|---|---|---|---|
| 오픈AI | response_format: { type: "json_object" } | response_format: { type: "json_schema", json_schema: { name, schema, strict: true } } | strict: true rejects undeclared fields; works with Pydantic model_json_schema() |
| 구글 제미니 | responseMimeType: "application/json" | Above + responseJsonSchema or SDK response_schema | 보다우월 Gemini API 기사 |
| 인류 | 프롬프트 + 구문 분석 | output_format (Claude structured output) or schema in Messages API | 필드는 SDK를 통해 발전합니다. 스키마를 플랫하게 유지 |
When migrating vendors, keep the Schema itself standard JSON Schema⟧ (type, properties, required, enum); SDKs only wrap the request. Do not mix OpenAPI 3.0 uppercase types (OBJECT) with JSON Schema⟧ lowercase (object).
좋은 스키마 작성: Pydantic에서 프로덕션까지
Recommended flow: define types in Pydantic / Zod → export JSON Schema⟧ → tune → send to API. Put semantics in description — it enters model context and disambiguates “qty = pieces vs boxes”; type: integer alone cannot.
from pydantic import BaseModel, Field
class LineItem(BaseModel):
name: str = Field(description="Product name")
qty: int = Field(description="Quantity, positive integer", ge=1)
unit_price_cents: int = Field(description="Unit price in cents", ge=0)
class Invoice(BaseModel):
vendor: str
currency: str = Field(description="ISO 4217, e.g. CNY")
items: list[LineItem]
total_cents: int
schema = Invoice.model_json_schema()
# In production add additionalProperties: false
실제 규칙:
- Prefer object root over root-level array;
{ "items": [...] }is more stable on some APIs. - Start with type / properties / required / enum, then add
additionalProperties, min/max; do not dump full Draft 2020-12 — some keywords are ignored. - 계속 얕게 중첩하세요; 순환 참조는 거부됩니다. 스키마를 평면화합니다.
- 분할 스키마와 프롬프트: 스키마 = 모양; 프롬프트 = 의미(“아래 텍스트에서 송장 추출…”).
OpenAI 구조화된 출력s⟧ 예
Chat Completions supports json_schema response format since 2024. With strict: true, output should only contain Schema fields:
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
class Invoice(BaseModel):
vendor: str
total_cents: int
response = client.chat.completions.create(
model="gpt-4o-2024-08-06",
messages=[
{"role": "user", "content": "Extract invoice: Acme sold 2 keyboards for 398 CNY."}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "invoice",
"strict": True,
"schema": Invoice.model_json_schema(),
},
},
)
data = response.choices[0].message.content # JSON string
import json
invoice = json.loads(data)
Gemini uses response_mime_type + response_json_schema — see the dedicated Gemini API article. For Anthropic, check current SDK structured output docs — same idea, official field names.
프로덕션 파이프라인: 생성 → 구문 분석 → 검증 → 재시도
구조화된 출력은 “한 번의 API 호출만으로 완료되는” 것이 아닙니다. 다음 네 단계를 수정하세요.
- Generate: call model with Schema; log prompt, Schema version, raw
content. - Parse:
JSON.parse(or SDKparsed); on failure, retry whole response — no half-parse. - Schema validate: run same JSON Schema⟧ via AJV /
jsonschema/ Pydantic; retry or degrade on failure. - 비즈니스 검증: 사용자 정의 주장(총계, 외래 키); 실패 시 인간 또는 규칙 엔진.
개발 중에는 스키마와 2~3개의 긍정적/부정적 샘플을 저장소에 저장합니다. 구조 및 Diff를 위해 로컬에서 JSON Toolbox⟧를 사용합니다. REST 계약 테스트와 동일한 사고 방식, 소비자는 LLM입니다.
일반적인 함정: JSON 모드⟧가 켜져 있는 동안 "설명하고 JSON"를 요청합니다. 잘림(최대 토큰 증가 또는 작업 분할) API 프런트엔드 데모의 키; 프롬프트에서 스키마 버전이 드리프트됩니다.
Tool Calling과의 차이점
| 구조화된 출력 | 툴 콜링 / MCP | |
|---|---|---|
| 제약 | 사용자에게 보내는 최종 JSON 답변 | Tool argument JSON (inputSchema) |
| 부작용 | 없음 — 데이터만 | Host / MCP 서버 실행 |
| 일반적인 사용 | 양식 추출, 분류, 작성, 상담원 전달 | 인벤토리, 파일, 외부 API |
| 실패 시 | 재시도 또는 사람 | 공구 메시지 오류 → 모델에게 다시 질문하세요 |
전체 에이전트 루프는 다음과 같습니다.구조화된 출력 의도 추출 → 도구 호출 행위 → 구조화된 출력 또는 사용자를 위한 산문 요약. 구조화된 출력을 사용하여 '결제 API가 호출되었습니다'라고 가장하지 마세요. 모델이 이를 호출하지 않았습니다.
FAQ
프롬프트에 "JSON을 출력해 주세요"라고 입력하면 충분합니까?
아니요. 프롬프트는 규정 준수 확률을 높일 뿐입니다. 마크다운 울타리, 후행 쉼표 및 필드 드리프트가 여전히 발생합니다. 프로덕션에서는 최소한 JSON 모드⟧를 활성화하세요. 이상적으로는 API Structured Output 채널을 통해 JSON Schema⟧를 전달하여 디코딩 시 불법 토큰을 제외시키는 것이 좋습니다.
JSON 모드⟧와 구조화된 출력의 차이점은 무엇인가요?
JSON Mode⟧는 유효한 JSON 텍스트만 보장하며 필드 이름, 유형 또는 필수 키는 보장하지 않습니다. 구조화된 출력은 JSON Schema⟧를 추가하고 생성 중에 토큰을 필터링합니다. 형태가 안정화되므로 저장하거나 다음 홉으로 직접 전달할 수 있습니다.
OpenAI, Gemini, Anthropic 구성 필드가 동일합니까?
같은 개념, 다른 이름. OpenAI: json_schema 및 strict를 사용한 response_format; Gemini: responseMimeType + responseJsonSchema; Anthropic: output_format 또는 도구의 구조화된 출력입니다. 스키마 표준을 유지하십시오. SDK는 요청을 래핑합니다.
구조화된 출력이 Tool Calling을 대체할 수 있나요?
아니요. 구조화된 출력은 최종 JSON 응답을 제한합니다. 도구 호출은 도구 인수 JSON을 제한하고 호스트가 도구를 실행하도록 요구합니다. 추출/분류/채우기에는 전자를 사용하세요. 후자는 인벤토리, 파일, MCP용입니다. 전체 에이전트 체인은 종종 두 가지를 모두 사용합니다.
여전히 모델 출력을 검증해야 합니까?
예. 제한된 디코딩은 구문 오류와 유형 드리프트를 줄이지 만 의미론적 정확성(유효한 유형, 조작된 값)은 줄이지 않습니다. 프로덕션 환경에서 동일한 JSON Schema⟧를 다시 실행하세요. 실패 시 재시도, 성능 저하 또는 검토를 수행합니다.
스키마와 샘플 출력을 로컬에서 어떻게 검증합니까?
JSON Schema⟧ 및 몇 가지 모델 출력 샘플을 JSON 파일로 저장하세요. 구문 및 구조 확인을 위해 브라우저에서 JSON 도구 상자⟧를 사용하세요. 아무것도 업로드되지 않습니다.
요약
AI에서 JSON Schema⟧와 일치하는 JSON을 얻으려면 순서가 중요합니다.먼저 스키마를 정의하고 JSON Mode⟧ / Structured Output을 활성화한 다음 프롬프트를 작성하세요.. 프롬프트 = 의미론; 스키마 = 모양; Pydantic / Zod는 작가 친화적인 프런트입니다. 공급업체 API는 스키마 채널을 노출합니다.
실제 송장 하나를 실행하거나 전체 지원 기록을 실행합니다. 스키마 → API 호출 → 출력을 유효성 검사기에 붙여넣습니다. 일치하면 데이터베이스나 다음 에이전트로 연결하세요. 도구 인수는 여전히 Tool Calling / MCP를 거칩니다. 하나의 API로 병합하지 마세요.