🔍 TL;DR
JSON 모드는 JSON 구문을 강제하지만 스키마 준수는 보장하지 않습니다 — 필드 누락, 잘못된 타입, 유효하지 않은 enum 값은 프롬프트 수정이 필요합니다. 세 가지 기법으로 이 격차를 해소할 수 있습니다: (1) 프롬프트에 JSON 템플릿으로 스키마를 직접 임베딩하기, (2) 유효한 출력 예시 하나 포함하기, (3) 타입·포맷·null 처리를 다루는 필드별 지시사항 추가하기. 배포 전 20개 케이스 테스트 세트에서 95% 이상의 통과율을 목표로 하십시오. API 강제 없는 자유형 프롬프트에는 JSON 대신 YAML을 사용하십시오 — 모델이 구문 오류를 더 적게 생성합니다.
프롬프트 설계가 구조화 출력 신뢰도를 결정합니다
📍 In One Sentence
구조화 출력 신뢰도는 파싱 가능하고, 모든 필수 필드를 포함하며, 올바른 데이터 타입을 사용하고, 유효한 enum 값을 가진 모델 응답의 비율입니다 — JSON 모드는 이 네 가지 중 첫 번째만 보장합니다.
💬 In Plain Terms
JSON 모드를 맞춤법 검사기라고 생각하십시오: 구문 오류는 잡지만 의미 오류는 잡지 못합니다. 문서는 맞춤법 검사를 통과하고도 여전히 틀릴 수 있습니다. JSON 모드에만 의존하는 프롬프트는 맞춤법 검사를 통과한 문서와 같습니다 — 구조적으로 유효하지만 잠재적으로 불완전하거나 잘못된 타입일 수 있습니다.
JSON 모드와 tool_use API는 파싱 가능한 JSON을 강제하지만 필드 완성도, 올바른 데이터 타입, 유효한 enum 값은 보장하지 않습니다 — 이러한 실패는 API 변경이 아닌 프롬프트 수준의 수정이 필요합니다. 가장 흔한 구조화 출력 실패는 구문상 유효한 JSON 내부에서 발생합니다: 모델이 선택적으로 처리하여 누락된 필수 필드, ISO 8601 대신 상대적 문자열("지난 화요일")로 포맷된 날짜, 오타나 약어가 있는 enum 값, null 대신 빈 문자열을 반환하는 nullable 필드.
세 가지 프롬프트 수준 개입이 신뢰도 격차를 일관되게 해소합니다. 스키마 임베딩은 출력 구조를 명확하게 합니다. 유효한 출력 예시 하나가 포맷 모호성을 제거합니다. 필드 수준 지시사항이 타입 및 null 처리 오류를 없앱니다. 이 세 가지를 합치면 네이티브 JSON 모드 사용 여부에 관계없이 GPT-5.6, Claude Sonnet 5, Gemini 2.5 Pro에서 구조화 출력 신뢰도를 95% 이상으로 높입니다.
| 실패 유형 | 프롬프트에서의 원인 | 프롬프트 수정 |
|---|---|---|
| 필수 필드 누락 | 모델이 자연어 설명에서 필드가 선택적이라고 추론함 | 각 필수 필드를 명시적으로 표시: "title REQUIRED" 또는 필수 필드를 별도로 나열 |
| 잘못된 데이터 타입 | 타입 어노테이션 없는 모호한 필드명 | 프롬프트에 타입 어노테이션 추가: "amount (integer, not string)" |
| 유효하지 않은 enum 값 | enum이 완전히 나열되지 않아 모델이 그럴듯한 값을 만들어냄 | 모든 enum 값을 명시적으로 나열: "status: one of 'active', 'inactive', 'pending'" |
| null vs 빈 문자열 혼동 | null과 ""를 구분하는 지시사항 없음 | "알 수 없으면 null을 반환하십시오. 알 수 없는 값에 빈 문자열을 절대 반환하지 마십시오." 추가 |
| 선언되지 않은 추가 필드 | 모델이 스키마에 없는 유용한 컨텍스트를 추가함 | "지정된 필드만 반환하십시오. 스키마에 나열되지 않은 필드를 추가하지 마십시오." 추가 |
🔍 JSON 모드만으로는 충분하지 않습니다
API 강제 JSON 모드를 사용할 때도 스키마-인-프롬프트, 필드 지시사항, 출력 예시가 필요합니다. JSON 모드와 프롬프트 스키마 설계는 상호 보완적이며 대안이 아닙니다. JSON 모드는 구문 실패를 방지하고 프롬프트 설계는 준수 실패를 방지합니다.
프롬프트에 스키마를 직접 임베딩하기
예상 출력 스키마를 자연어 설명이 아닌 JSON 템플릿으로 프롬프트에 직접 임베딩하십시오. 생성 전에 구조를 확인한 모델은 산문 설명만 받은 모델보다 필드 누락과 타입 오류를 더 적게 만들어냅니다.
스키마-인-프롬프트는 출력에서 예상하는 정확한 포맷을 사용합니다: 필드명, 중첩 깊이, 값 플레이스홀더. 스키마 템플릿은 작업 지시 다음, 예시 앞에 배치하십시오. 예상 타입을 전달하는 플레이스홀더 값을 사용하십시오: `"amount": 0`은 정수를, `"amount": 0.00`은 부동소수점을, `"created_at": "YYYY-MM-DDTHH:MM:SSZ"`는 예상하는 ISO 8601 포맷을 전달합니다.
🔍 TypeScript 스타일 타입 어노테이션 사용
JSON 모드를 사용할 수 없는 프롬프트에서는 스키마 템플릿 내부에 TypeScript 스타일 타입 어노테이션을 주석으로 추가하십시오: `"amount": 0 // float, USD, 소수점 2자리`. 이렇게 하면 별도의 필드 지시사항 섹션 없이도 스키마 구조 내에 타입 정보를 제공할 수 있습니다.
🔍 필드 순서가 중요합니다
스키마 템플릿에서 필수 필드를 먼저, 선택적 필드를 다음에, nullable 필드를 마지막에 나열하십시오. 모델은 무엇을 포함할지 결정할 때 앞에 나온 요소에 더 많은 가중치를 부여합니다 — 첫 번째로 나열된 nullable 필드는 모델이 값에 대해 불확실할 때 나중에 나열된 필수 필드보다 누락될 가능성이 높습니다.
❌ 자연어 설명만 사용
다음 텍스트에서 주문 세부 정보를 추출하여 JSON으로 반환하십시오. 주문 ID, 고객명, 총 금액, 주문 항목, 주문 상태를 포함하십시오. 텍스트: {{text}}
✅ JSON 템플릿으로 스키마 임베딩
다음 텍스트에서 주문 세부 정보를 추출하여 다음 스키마와 정확히 일치하는 JSON으로 반환하십시오: { "order_id": "string", "customer_name": "string", "total_amount": 0.00, "status": "string", "items": [ { "name": "string", "quantity": 0, "unit_price": 0.00 } ] } 유효한 JSON만 반환하십시오. JSON 객체 외부에 텍스트를 포함하지 마십시오. 텍스트: {{text}}
유효한 출력 예시 하나 제시하기
프롬프트에 구체적이고 실제적인 출력 예시 하나를 추가하면 스키마만 있는 프롬프트보다 구조화 출력 신뢰도가 5–8 퍼센트 포인트 높아집니다. 예시는 모델에게 예상하는 정확한 포맷, 필드 순서, 값 스타일, 따옴표 방식을 보여줍니다 — 스키마 정의만으로는 없앨 수 없는 모호성을 줄입니다.
스키마 템플릿 다음에 예시를 배치하고 명확하게 라벨을 붙이십시오("출력 예시:" 또는 "유효한 응답 예시:"). 실제적인 플레이스홀더 값을 사용하십시오 — "foo", "bar", "example"이 아닌 — 모델이 값 스타일에서 학습하기 때문입니다. 날짜에 ISO 8601을 사용한다면 ISO 8601 날짜를 보여주십시오. 가격에 소수점 두 자리가 있다면 `13`이 아닌 `12.99`를 보여주십시오.
🔍 예시 하나로 보통 충분합니다
두 번째 예시는 입력 조건에 따라 데이터 구조가 의미 있게 달라지는 경우에만 가치가 있습니다 — 예를 들어 제품 유형에 따라 특정 필드가 조건부로 존재하는 경우. 두 예시를 초과하면 대부분의 구조화 출력 작업에서 프롬프트 길이 비용이 신뢰도 이득을 초과합니다.
⚠️ 사소한 플레이스홀더 값을 피하십시오
"foo", "bar", "test" 또는 `0`을 플레이스홀더로 사용하는 예시는 모델에게 이것들이 유효한 값이라고 가르칩니다. 실제 데이터를 대표하는 값을 사용하십시오 — 실제 제품명, 실제적인 평점, 실제 날짜 문자열.
❌ 스키마만 — 출력 예시 없음
아래 리뷰에서 제품 세부 정보를 추출하여 이 스키마의 JSON으로 반환하십시오: { "product_name": "string", "rating": 0, "sentiment": "string", "key_features": ["string"] } 리뷰: {{review}}
✅ 스키마 + 실제적인 출력 예시 하나
아래 리뷰에서 제품 세부 정보를 추출하여 이 스키마의 JSON으로 반환하십시오: { "product_name": "string", "rating": 0, "sentiment": "string", "key_features": ["string"] } 출력 예시: { "product_name": "WH-1000XM5 헤드폰", "rating": 4, "sentiment": "positive", "key_features": ["노이즈 캔슬링", "30시간 배터리", "편안한 착용감"] } 리뷰: {{review}}
고위험 출력을 위한 필드 수준 지시사항 작성
필드 정확성이 중요한 프로덕션 프롬프트에서는 필수 필드마다 데이터 타입, 예상 포맷, null 처리, 해당되는 경우 허용 enum 값을 포함하는 지시사항을 하나씩 추가하십시오. 필드 수준 지시사항은 타입 오류를 유발하는 모호성을 없앱니다 — "amount"라는 필드는 명시적인 타입 지시사항 없이는 문자열, 정수, 부동소수점 모두가 될 수 있습니다.
필드 지시사항은 스키마 템플릿 다음, 예시 앞의 별도 섹션에 넣으십시오. 섹션에 "필드 요구사항:" 또는 "스키마 규칙:"이라는 라벨을 붙이십시오. 각 지시사항은 한 문장으로 유지하십시오.
| 필드 타입 | 지시사항 패턴 | 예시 지시사항 |
|---|---|---|
| 문자열 | 포맷, 최대 길이, 금지 문자 | "title (string, 최대 100자, HTML 태그 없음)" |
| 숫자 | 정수 vs 부동소수점, 정밀도, 단위 | "price (float, 소수점 정확히 2자리, USD, 통화 기호 없음)" |
| 날짜 | 포맷, 타임존 | "created_at (string, ISO 8601: YYYY-MM-DDTHH:MM:SSZ, UTC 타임존)" |
| Enum | 모든 허용 값을 정확히 나열 | "status (string, 정확히 다음 중 하나: 'active', 'inactive', 'pending')" |
| Boolean | true/false만 — yes/no/1/0 거부 | "is_verified (boolean, true 또는 false만 — 1/0이나 yes/no 불가)" |
| Nullable | null vs 빈 문자열 vs 생략 시 처리 | "description (string 또는 null — 알 수 없으면 null, 빈 것으로 알려진 경우 빈 문자열)" |
| 배열 | 최소/최대 항목, 항목 타입, 빈 배열 처리 | "tags (문자열 배열, 0–5개 항목, 없으면 [] 반환 — null 절대 불가)" |
🔍 필드 지시사항 추가 시점
다음 경우에 필드 지시사항을 추가하십시오: (1) 특정 포맷 요구사항이 있는 필드(ISO 날짜, 통화 정밀도), (2) enum 필드, (3) nullable이며 null/빈 문자열 구분이 중요한 필드, (4) 테스트 세트에서 해당 필드가 10% 이상 실패하는 경우. "title"이나 "name"처럼 단순하고 명확한 문자열 필드에는 필드 지시사항을 건너뛰십시오.
❌ 스키마만 — 필드 지시사항 없음
다음 필드가 있는 JSON을 반환하십시오: { "invoice_id": ..., "amount": ..., "due_date": ..., "status": ..., "line_items": [...] }
✅ 스키마 + 필드 수준 지시사항
다음 필드가 있는 JSON을 반환하십시오: { "invoice_id": "string", "amount": 0.00, "due_date": "YYYY-MM-DD", "status": "string", "line_items": [{"description": "string", "quantity": 0, "unit_price": 0.00}] } 필드 요구사항: - invoice_id: string, 형식 INV-XXXXXX (예: INV-004821) - amount: float, 소수점 2자리, 세금 포함 USD 합계 - due_date: string, ISO 8601 날짜 (YYYY-MM-DD), datetime 아님 - status: string, 정확히 다음 중 하나: 'paid', 'unpaid', 'overdue', 'cancelled' - line_items: 객체 배열, 1개 이상, 항목 없으면 [] 반환 - 결정할 수 없는 필드는 null 반환
API에는 JSON, 프롬프트에는 YAML, 테이블 데이터에는 CSV 선택
출력이 JSON 강제를 사용할 수 있는 API나 데이터베이스로 들어가는 경우 JSON을 사용하십시오. API 강제 없는 자유형 프롬프트에는 YAML을 사용하십시오 — 닫는 중괄호, 이스케이프 시퀀스, 후행 쉼표 인식이 필요 없기 때문에 모델이 YAML에서 구문 오류를 더 적게 생성합니다. CSV는 단순 테이블 데이터에만 사용하십시오.
자유형(API 강제 없음) 프롬프팅에서 JSON과 YAML의 신뢰도 차이는 구문 복잡성에서 비롯됩니다. JSON은 모든 문자열을 따옴표로 감싸고, 모든 객체를 중괄호로 닫고, 모든 쉼표를 올바르게 처리해야 합니다. YAML은 대신 들여쓰기를 사용합니다 — 모델이 더 일관되게 처리합니다. 트레이드오프: YAML 출력은 JSON을 필요로 하는 다운스트림 시스템에 공급하기 전 변환이 필요합니다.
- 다운스트림 시스템에 JSON 파서가 있고 API 강제를 사용할 수 있으면 JSON을 사용하십시오 — 강제가 구문 오류를 완전히 제거합니다.
- API 강제 없이 생성하고 팀에서 다운스트림 처리 전 JSON으로 변환한다면 YAML을 사용하십시오.
- 단순 테이블 데이터에만 CSV를 사용하십시오 — 셀에 중첩 객체나 배열이 필요한 순간 JSON이나 YAML로 전환하십시오.
- 사람이 읽기 쉬운 출력에만 Markdown 테이블을 사용하십시오 — 추가 도구 없이는 기계 판독 불가입니다.
| 포맷 | API 강제 없는 신뢰도 | 최적 용도 | 피해야 할 경우 |
|---|---|---|---|
| JSON | 스키마-인-프롬프트로 80–85% | API, 데이터베이스, 타입 안전 소비자 | API 강제 없이 복잡한 중첩이 포함된 경우 |
| YAML | 스키마-인-프롬프트로 88–92% | 사람이 읽기 쉬운 출력, 설정 스타일 데이터, API 강제 없는 경우 | 다운스트림 시스템이 변환 단계 없이 JSON을 요구하는 경우 |
| XML | 스키마-인-프롬프트로 85–90% | 문서 변환, 레거시 시스템 통합 | 단순 키-값 데이터 (XML이 불필요하게 장황해짐) |
| CSV | 단순 데이터에서 95%+ | 테이블 데이터, 스프레드시트 내보내기, 데이터 파이프라인 | 데이터에 중첩 또는 계층적 구조가 있는 경우 |
| Markdown 테이블 | 단순 테이블에서 높음 | 보고서, 문서, 사람이 읽기 쉬운 테이블 출력 | 기계 판독 가능한 다운스트림 처리가 필요한 경우 |
⚠️ YAML-JSON 변환 비용
프롬프트 신뢰도를 위해 YAML을 사용하고 다운스트림 처리에 JSON이 필요하다면 파이프라인에 변환 단계를 추가하십시오. Python의 yaml.safe_load()와 Node.js의 js-yaml이 한 줄로 처리합니다. 팀 전체에 YAML을 적용하기 전 이를 아키텍처에 반영하십시오.
잘못된 출력을 모델 스스로 수정하게 하기
구조화 출력 프롬프트가 유효성 검사에 실패하면 원래 지시사항, 잘못된 출력, 구체적인 유효성 검사 오류를 포함하는 수정 프롬프트를 전송하십시오. 모델은 전체 프롬프트 재작성 없이 60–75%의 경우에 자신의 잘못된 응답에서 유효한 출력을 복구합니다.
수정 프롬프트에는 세 가지 필수 부분이 있습니다: (1) 출력이 어떻게 보여야 하는지에 대한 재설명(스키마 또는 포맷), (2) 모델이 반환한 그대로의 잘못된 출력, (3) 구체적인 유효성 검사 오류 — "필수 필드 'invoice_id' 누락", "amount가 문자열이지만 float이어야 함". 이 세 부분 구조는 모델에게 다른 실패를 가진 다른 응답을 재생성하는 대신 특정 문제를 수정하기에 충분한 컨텍스트를 제공합니다.
🔍 수정이 두 번 실패하면 기본 프롬프트를 수정하십시오
수정 프롬프트가 두 번째 시도에서도 유효한 출력을 생성하지 못하면 문제는 입력 데이터가 아닌 기본 프롬프트에 있습니다. 재시도를 중단하고 실패 패턴을 진단하십시오: 어떤 필드가 어떤 입력 조건에서 실패하는지. 소스에서 실패를 방지하기 위해 필드 지시사항이나 스키마 변경을 추가하십시오.
⚠️ 수정 프롬프트는 지연 시간과 비용을 추가합니다
각 수정 프롬프트는 해당 호출의 API 비용과 지연 시간을 두 배로 늘립니다. 수정 프롬프트는 엣지 케이스 실패(출력의 10% 미만)에만 사용하십시오. 구조화 출력 프롬프트가 10% 이상 실패한다면 프로덕션에 수정 루프를 구축하는 대신 기본 프롬프트를 수정하십시오.
❌ 모호한 재시도 — 오류 컨텍스트 없음
유효하지 않은 출력을 반환하셨습니다. 다시 시도하여 유효한 JSON을 반환하십시오. {{original_prompt}}
✅ 스키마, 출력, 구체적 오류가 포함된 수정 프롬프트
이전 응답이 유효성 검사에 실패했습니다. 아래 나열된 오류만 수정하여 올바른 JSON을 반환하십시오. 예상 스키마: { "invoice_id": "string", "amount": 0.00, "status": "string" } 이전 응답: { "invoice_id": null, "amount": "150.00", "status": "PAID" } 유효성 검사 오류: - invoice_id가 null이지만 필수 문자열 필드입니다 — 입력에서 추출하십시오. - amount가 문자열("150.00")이지만 float(150.00)이어야 합니다. - status는 소문자여야 합니다: 'PAID' 대신 'paid' 사용 수정된 JSON 객체만 반환하십시오.
배열, enum, nullable 필드를 위한 프롬프트 패턴
배열, enum, nullable 필드는 스키마-인-프롬프트만으로는 방지할 수 없는 구조화 출력 실패의 세 가지 가장 흔한 원인입니다. 각각은 프롬프트에서 특정 지시사항 패턴이 필요합니다.
| 데이터 타입 | 흔한 실패 | 방지하는 프롬프트 패턴 |
|---|---|---|
| 배열 (0개 항목) | 모델이 [] 대신 null 반환 | "항목이 없으면 빈 배열 []을 반환하십시오. 배열 필드에 null을 절대 반환하지 마십시오." |
| 배열 (1개 이상) | 항목이 하나만 발견되면 배열 대신 단일 객체 반환 | "항목이 하나뿐이더라도 항상 배열을 반환하십시오. 단일 항목은 래핑되어야 합니다: {...}" |
| Enum (2–5개 값) | 모델이 유사한 값을 약어로 쓰거나 만들어냄 | "status: 정확히 다음 중 하나: 'active', 'inactive', 'pending' — 약어나 변형 불가" |
| Enum (6개 이상 값) | 모델이 목록에 없는 값을 만들어냄 | 모든 값을 번호 목록으로 나열한 다음: "위 목록의 값만 사용하십시오. 값을 약어로 쓰거나 결합하지 마십시오." |
| Nullable 필드 | 모델이 null 대신 "" 반환하거나 필드를 완전히 생략 | "값을 알 수 없으면 null을 반환하십시오. 빈 것으로 알려진 경우에만 빈 문자열 ''을 반환하십시오. 항상 필드를 포함하십시오 — 생략하지 마십시오." |
| 정수 vs 부동소수점 | 정수가 필요한 곳에 부동소수점 반환, 또는 둘 다 문자열 | "score (integer — 소수점 없음, 예: 4.0이 아닌 4)" 또는 "price (float — 소수점 정확히 2자리, 예: 13이 아닌 12.99)" |
| 중첩 객체 | 모델이 중첩 객체를 플랫 키로 축소 (예: {"address": {"city": ...}} 대신 "address.city") | 스키마 템플릿에 적절한 들여쓰기로 완전한 중첩 구조를 보여주십시오. 중첩에 대한 자연어 설명은 빈번하게 플랫 키로 축소됩니다. |
⚠️ null vs undefined vs 생략
JSON에는 undefined 값이 없지만 모델은 때때로 있는 것처럼 동작합니다 — null을 반환하는 대신 값이 알 수 없다고 생각할 때 필드를 완전히 생략합니다. 다운스트림 코드가 obj.hasOwnProperty() 같은 검사를 사용한다면 생략된 필드는 null 필드와 다릅니다. "null이더라도 스키마의 모든 필드를 항상 포함하십시오."를 추가하십시오.
🔍 중첩된 enum은 더 구체적인 지시가 필요합니다
중첩 객체 안의 enum은 최상위 enum보다 오타가 나거나 약어로 쓰일 가능성이 높습니다. 중첩 객체 안에 enum이 있다면 일반 필드 규칙 섹션뿐만 아니라 스키마 템플릿에서 해당 필드가 나타나는 곳 가까이에 지시사항을 반복하십시오.
프롬프트의 구조화 출력 신뢰도 측정
구조화 출력 프롬프트를 프로덕션에 배포하기 전 20개 케이스 테스트 세트에서 95% 이상의 통과율을 목표로 하십시오. 95% 미만이면 프로덕션 실패가 충분히 자주 발생하여 다운스트림 수정 루프가 필요합니다 — 이는 실패하는 모든 호출에 지연 시간을 추가하고 API 비용을 두 배로 늘립니다.
전체가 아닌 필드 수준에서 신뢰도를 측정하십시오. 전체 통과율 95%이지만 enum 필드 하나에서 통과율 60%인 프롬프트는 알려진 프로덕션 실패 모드를 가진 프롬프트입니다. 필드 수준 측정은 어떤 지시사항을 추가하거나 강화해야 하는지 정확히 알려줍니다.
- 1모든 스키마 필드에 대한 통과/실패 기준을 정의하십시오. 각 필드에 대해: 타입이 올바른지, 필수 필드가 존재하는지, enum 값이 허용 목록에 있는지, 날짜 포맷이 필요한 패턴과 일치하는지. 시각적 검사가 아닌 프로그래밍적 검사로 작성하십시오. 이 단계가 테스트 오라클을 생성합니다.
- 220개 케이스 테스트 세트를 구축하십시오. 정상 경로 입력 10개(일반적이고 잘 형성된 데이터), 엣지 케이스 5개(선택적 필드 누락, 긴 텍스트, 특이한 값, 다국어 콘텐츠), 적대적 입력 5개(필드 값에 포함된 지시사항, 극단적인 날짜, 모호한 타입). 실제 데이터 도메인에서 현실적인 입력을 사용하십시오.
- 3temperature 0에서 실행하고 필드별 통과/실패를 기록하십시오. 결정론적이고 반복 가능한 결과를 위해 temperature 0에서 20개 케이스를 모두 실행하십시오. 전체 결과뿐만 아니라 각 테스트 케이스에서 각 필드가 통과하는지 실패하는지 기록하십시오. 필드 수준 실패 패턴이 어떤 지시사항이 누락되었는지 파악합니다.
- 4가장 낮은 통과율 필드를 수정하고 재테스트하십시오. 필드 지시사항 하나를 추가하거나 강화하십시오: 타입, 포맷, null 처리, enum 값. 20개 케이스를 모두 다시 실행하십시오. 타겟된 지시사항 추가 하나가 일반적으로 전체 통과율을 5–15 퍼센트 포인트 높입니다. 전체 통과율이 95% 이상에 도달할 때까지 반복하십시오.
- 5두 번째 모델로 프롬프트를 검증하십시오. 동일한 프롬프트로 두 번째 모델에서 전체 20개 케이스를 실행하십시오. GPT-5.6에서 95%+이지만 Claude Sonnet 5에서 70%인 프롬프트는 모델 종속적입니다. 두 모델 모두에서 통과할 만큼 명시적인 지시사항을 추가하거나, 어떤 모델로 검증되었는지 문서화하고 재테스트 없이 전환하지 마십시오.
🔍 temperature 0에서 테스트를 실행하십시오
결정론적이고 반복 가능한 결과를 얻으려면 temperature 0에서 구조화 출력 테스트 세트를 실행하십시오. temperature 0에서 통과하는 프롬프트는 설계상 신뢰할 수 있습니다 — 운이 좋은 것이 아닙니다. 프롬프트가 결정론적으로 95%+를 통과한 후에만 temperature를 높이고, 그 다음 신뢰도가 유지되는지 확인하기 위해 새 temperature에서 테스트 세트를 다시 실행하십시오.
🔍 다중 모델 비교를 위해 PromptQuorum 사용
PromptQuorum은 20개 케이스 테스트 세트를 GPT-5.6, Claude Sonnet 5, Gemini 2.5 Pro에서 동시에 실행하고 필드 수준 통과율을 나란히 보여줍니다. 이를 통해 세 번 대신 한 번의 실행으로 모델 종속 실패를 파악할 수 있습니다.
구조화 출력 프롬프트의 5가지 흔한 실수
구조화 출력 프롬프트의 5가지 가장 흔한 실수는 모두 동일한 증상 — 간헐적 또는 체계적 실패 — 을 만들어내지만 다른 수정이 필요합니다. 지시사항을 추가하기 전에 어떤 실수를 했는지 진단하면 시간을 절약할 수 있습니다.
❌ 스키마를 임베딩하는 대신 자연어로 설명
Why it hurts: 자연어 설명은 모호합니다 — "항목 목록"은 배열, 쉼표로 구분된 문자열, 또는 번호 목록을 의미할 수 있으며 "합계"는 문자열이나 부동소수점 모두가 될 수 있습니다.
Fix: 예상 스키마를 JSON 템플릿으로 프롬프트에 직접 임베딩하십시오. 템플릿은 산문 설명이 아닌 구조를 통해 필드명, 중첩 깊이, 값 타입을 보여줍니다.
❌ 누락되거나 알 수 없는 값 처리 방법을 지정하지 않음
Why it hurts: 모델은 알 수 없는 필드에 null을 반환하는 대신 그럴듯한 값을 만들어냅니다 — 날짜는 "unknown"이 되고, 금액은 0이 되고, 누락된 ID는 "N/A"가 됩니다 — 이 중 어느 것도 타입 유효성 검사를 통과하지 못합니다.
Fix: 모든 nullable 필드에 명시적인 null 처리를 추가하십시오: "입력에서 값을 결정할 수 없으면 null을 반환하십시오. 값을 추측하거나 만들어내지 마십시오. 빈 문자열을 반환하지 마십시오."
❌ 프롬프트를 개발한 모델에서만 테스트
Why it hurts: 구조화 출력 신뢰도는 모델마다 크게 다릅니다 — 스키마 제약에 대한 다른 지시사항 따르기 동작 때문에 GPT-5.6에서 95%인 프롬프트가 Claude Sonnet 5에서 70%로 실패할 수 있습니다.
Fix: 모델에 무관한 것으로 처리하기 전에 모든 구조화 출력 프롬프트를 최소 2개 모델로 실행하십시오. PromptQuorum 또는 직접 API 호출을 사용하여 여러 모델에서 프롬프트를 테스트하십시오.
❌ 정확히 같은 프롬프트로 실패한 출력을 재시도
Why it hurts: temperature 0에서 재시도되는 실패 프롬프트는 매번 같은 실패를 생성합니다. 더 높은 temperature에서는 다양하지만 여전히 실패하는 출력을 생성합니다 — 다른 오류, 같은 근본 원인.
Fix: 구체적인 유효성 검사 오류와 잘못된 출력이 포함된 수정 프롬프트를 사용하거나, 실패 패턴(어떤 필드, 어떤 입력 유형)을 진단하고 기본 프롬프트에 타겟된 필드 지시사항을 추가하십시오.
❌ JSON 모드를 완전한 구조화 출력 솔루션으로 취급
Why it hurts: JSON 모드는 파싱 불가 출력을 방지하지만 스키마 준수 실패는 막지 못합니다 — JSON 모드를 사용하는 모델은 여전히 누락된 필드, 잘못된 타입, 유효하지 않은 enum 값이 있는 유효한 JSON을 반환할 수 있으며, 이 모두가 다운스트림 유효성 검사에 실패합니다.
Fix: API 강제 JSON 모드를 사용할 때도 항상 스키마-인-프롬프트와 필드 지시사항을 포함하십시오. API 설정은 구조화 출력과 JSON 모드를 참조하십시오 — 이 가이드는 프롬프트 수준의 보완 내용을 다룹니다.
자주 묻는 질문
구조화 출력 프롬프팅에 관한 가장 흔한 질문들은 JSON 모드와 프롬프트 설계의 경계, 포함할 예시 수, 실패하는 프롬프트를 체계적으로 개선하는 방법을 다룹니다.
JSON 모드가 있으면 스키마-인-프롬프트가 불필요한가요?
아닙니다. JSON 모드는 파싱 가능한 JSON 구문을 강제하지만 스키마 준수는 보장하지 않습니다. JSON 모드를 사용하는 모델은 여전히 필수 필드가 누락되거나, 잘못된 데이터 타입을 사용하거나, 유효하지 않은 enum 값을 포함하는 유효한 JSON을 반환할 수 있습니다. 스키마-인-프롬프트와 필드 지시사항은 스키마 준수 실패를 다루고, JSON 모드는 파싱 불가 출력만 방지합니다. 두 접근 방식은 상호 보완적이며 대안이 아닙니다.
프롬프트에 출력 예시를 몇 개나 포함해야 하나요?
예시 하나로 보통 충분하며 가장 큰 신뢰도 향상을 가져옵니다. 두 번째 예시는 입력 조건에 따라 데이터 구조가 의미 있게 달라지는 경우에만 가치가 있습니다 — 예를 들어 입력 유형에 따라 특정 필드가 조건부로 필요한 경우. 두 예시를 초과하면 대부분의 구조화 출력 작업에서 프롬프트 길이 비용이 신뢰도 이득을 초과합니다.
API 강제 없이 구조화 출력에 JSON 또는 YAML을 사용해야 하나요?
API 강제 없이 생성하고 출력을 JSON을 필요로 하는 시스템에서 파싱할 필요가 없는 경우 YAML을 사용하십시오. 닫는 중괄호, 이스케이프 시퀀스, 후행 쉼표 추적이 필요 없기 때문에 모델이 YAML에서 구문 오류를 더 적게 생성합니다. 출력이 JSON을 필요로 하는 API, 데이터베이스, 다운스트림 시스템으로 직접 들어가는 경우 JSON을 사용하십시오. 포맷에 관계없이 항상 파싱하고 유효성을 검사하십시오.
구조화 출력 통과율이 70%인 프롬프트를 가장 빠르게 개선하는 방법은 무엇인가요?
전체가 아닌 필드 수준에서 테스트 세트를 실행하십시오. 개별 통과율이 가장 낮은 필드를 찾고, 타입, 포맷, null 처리를 다루는 명시적인 지시사항 하나를 추가한 다음 재실행하십시오. 타겟된 필드 지시사항 하나가 일반적으로 전체 통과율을 5–15 퍼센트 포인트 높입니다. 95% 이상에 도달할 때까지 반복하십시오.
네이티브 JSON 모드 없이 모델에서 신뢰할 수 있는 구조화 출력을 얻으려면 어떻게 해야 하나요?
프롬프트에 전체 JSON 스키마를 템플릿으로 임베딩하고, 유효한 출력 예시 하나를 포함하고, 필드 수준 지시사항을 추가하고, temperature 0에서 실행하십시오. 모든 출력을 파싱하고 유효성을 검사하며, 유효성 검사 실패에 대해 수정 프롬프트를 전송하십시오. 잘 설계된 프롬프트는 네이티브 JSON 모드 없이 temperature 0에서 대부분의 모델에서 85–92%의 신뢰도를 달성합니다.
구조화 출력 프롬프트의 올바른 테스트 세트 크기는 얼마인가요?
최소 20개: 정상 경로 입력 10개(일반적이고 잘 형성된 데이터), 엣지 케이스 5개(특이한 값, 선택적 필드 누락, 긴 입력), 적대적 입력 5개(모델을 오도할 수 있는 값, 필드 값에 포함된 지시사항, 모호한 타입). 이 크기는 과도한 설정 시간 없이 가장 흔한 실패 범주를 파악합니다.
수정 프롬프트를 사용해야 할 때와 기본 프롬프트를 수정해야 할 때는 언제인가요?
실패가 드물 때 — 출력의 10% 미만 — 그리고 특이한 엣지 케이스 입력으로 인한 경우 수정 프롬프트를 사용하십시오. 실패가 체계적인 경우 — 여러 테스트 케이스에서 동일한 필드가 누락되거나 동일한 타입 오류가 나타나는 경우 — 기본 프롬프트를 수정하십시오. 수정 프롬프트는 실패마다 지연 시간과 API 비용을 추가하며, 더 나은 기본 프롬프트는 실패를 완전히 방지합니다.
스키마에서 필드 순서가 구조화 출력 신뢰도에 영향을 미치나요?
예. 필수 필드를 먼저, 선택적 또는 nullable 필드를 마지막에 배치하십시오. 모델은 무엇을 포함할지 결정할 때 스키마의 앞 요소에 더 많은 가중치를 부여합니다. 첫 번째로 나열된 nullable 필드는 모델이 값에 대해 불확실할 때 나중에 나열된 필수 필드보다 생략될 가능성이 높습니다. 이 순서 효과는 GPT-5.6와 Claude Sonnet 5 모두에서 일관됩니다.
관련 자료
- 구조화 출력과 JSON 모드: 사용 시기 및 방법 — 모델 준수 테이블과 함께 GPT-5.6, Claude, Gemini를 위한 API 수준 JSON 모드 설정
- 구조화 출력을 위한 최적 도구 (2026) — 구조화 추출 워크플로를 위한 Instructor, Outlines, Pydantic AI, LangChain 비교
- 출력 제어 방법: 포맷, Temperature, 제약 디코딩 — 제약 디코딩 메커니즘, 구조화 작업을 위한 temperature와 top-p, stop 시퀀스
- 프롬프트 품질 평가 방법: 지표, 테스트, 체크리스트 — 20개 케이스 테스트 세트 구성, 이진 통과/실패 점수, LLM-as-judge 루브릭
- 여러 모델에서 프롬프트를 테스트하는 방법 — 모델별 실패를 찾기 위해 GPT-5.6, Claude Sonnet 5, Gemini 2.5 Pro에서 동일한 프롬프트 실행
- 제로샷 vs 퓨샷 프롬프팅 — 프롬프트에 예시를 추가할 시기와 다양한 작업 유형에 맞는 예시 수
출처
- OpenAI 구조화 출력 문서 — OpenAI API의 response_format 및 JSON 모드 기술 사양
- Anthropic tool use 문서 — Claude의 tool_use 파라미터가 API 수준에서 구조화 출력을 강제하는 방법
- Google Gemini GenerationConfig 문서 — 네이티브 JSON 출력을 위한 Gemini의 responseMimeType 설정
- BAML 벤치마크: 구조화 출력 정확도 트레이드오프 — 여러 모델에서 제약 생성과 비제약 생성 간의 신뢰도 차이에 대한 증거
- NIST AI 위험 관리 프레임워크 — 프로덕션 시스템에서 AI 출력 유효성 검사를 위한 거버넌스 원칙
