LLM에게 JSON을 요청합니다. 대부분의 경우 JSON을 받습니다. 하지만 운영(production) 환경에서는 그 "대부분"이 문제가 됩니다. 제약 디코딩(Constrained Decoding)은 샘플링(sampling) 직전에 로짓(logit)을 직접 조정해 "대부분"을 "항상"으로 바꿔 줍니다.
유형: Build
언어: Python
선수 강의: Phase 5 · 17 (Chatbots), Phase 5 · 19 (Subword Tokenization)
예상 시간: 약 60분
학습 목표
프롬프팅(Prompting), 벤더 네이티브 구조화 출력 API(native structured output API), 제약 디코딩(constrained decoding)의 차이를 설명할 수 있습니다.
로짓 프로세서(logit processor)가 문법(grammar)에 맞지 않는 토큰(token)을 마스킹(masking)하는 방식을 이해합니다.
유한 상태 기계(Finite-State Machine; FSM), 문맥 자유 문법(Context-Free Grammar; CFG), JSON 스키마(JSON Schema) 기반 디코딩의 트레이드오프(tradeoff)를 파악합니다.
필드 순서(field order), 널 허용 필드(nullable field), 스키마 검증(schema validation) 같은 운영 단계의 스키마 설계(schema design) 기준을 적용합니다.
문제
분류기(classifier)가 LLM에게 이렇게 프롬프트를 보냅니다. "Return one of {positive, negative, neutral}." 그런데 모델은 이렇게 응답합니다. "The sentiment is positive — this review is overwhelmingly favorable because the customer explicitly states that they ...". 파서(parser)는 크래시(crash)합니다. 분류기의 F1 점수는 0.0이 됩니다.
자유 형식 생성(free-form generation)은 계약(contract)이 아닙니다. 그저 제안(suggestion)일 뿐입니다. 운영 시스템에는 계약이 필요합니다.
2026년 현재 세 가지 계층(layer)이 존재합니다.
프롬프팅(Prompting). 정중히 부탁합니다. "Return only the JSON object." 프론티어 모델(frontier model)에서는 약 80% 정도 작동하고, 더 작은 모델에서는 그보다 낮아집니다.
벤더 네이티브 구조화 출력 API. OpenAI response_format, Anthropic tool use, Gemini JSON mode가 여기에 속합니다. 지원되는 스키마에서는 안정적이지만 벤더에 종속(vendor-locked)됩니다.
제약 디코딩(Constrained Decoding). 모든 생성 단계마다 로짓을 수정해 모델이 유효하지 않은 토큰을 아예 낼 수 없도록 만듭니다. 구조적으로 100% 유효(valid)합니다. 어떤 로컬 모델에도 적용할 수 있습니다.
이번 강의는 세 방식 모두에 대한 직관을 만들고, 각각을 언제 꺼내 쓸지 그 기준을 정합니다.
사전 테스트
2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요
1.LLM에게 'Return only the JSON object'라고 프롬프팅(prompting)하는 것만으로는 운영 환경에서 유효한 JSON을 보장할 수 없는 이유는 무엇인가요?
2.제약 디코딩(constrained decoding)에서 로짓 프로세서(logit processor)는 어떤 역할을 하나요?
0/2 답변 완료
개념
제약 디코딩의 동작 방식. 각 생성 단계에서 LLM은 전체 어휘(vocabulary), 대략 100k 토큰 위의 로짓 벡터(logit vector)를 만듭니다. *로짓 프로세서(logit processor)*는 모델과 샘플러(sampler) 사이에 자리 잡습니다. 현재 목표 문법(target grammar) 안의 위치를 기준으로 어떤 토큰이 유효한지 계산합니다. 목표 문법은 JSON Schema, 정규표현식(regex), 문맥 자유 문법(context-free grammar) 등이 될 수 있습니다. 그런 다음 유효하지 않은 토큰의 로짓을 모두 음의 무한대(negative infinity)로 설정합니다. 남은 로짓 위의 소프트맥스(softmax)는 유효한 다음 토큰(valid continuation)에만 확률 질량(probability mass)을 둡니다.
2026년 현재 주요 구현(implementation)은 다음과 같습니다.
Outlines. JSON Schema 또는 정규표현식을 유한 상태 기계(finite-state machine)로 컴파일합니다. 모든 토큰에 대해 O(1) 시간 안에 유효한 다음 토큰을 조회할 수 있습니다. FSM 기반이므로 재귀적(recursive) 스키마는 평탄화(flatten)해야 합니다.
XGrammar / llguidance. 문맥 자유 문법 엔진(CFG engine)입니다. 재귀적인 JSON Schema도 처리합니다. 디코딩 오버헤드(overhead)가 거의 없습니다. OpenAI는 2025년 구조화 출력 구현에서 llguidance를 공식적으로 언급(credit)했습니다.
vLLM guided decoding. Outlines, XGrammar, lm-format-enforcer 백엔드(backend)를 통해 guided_json, guided_regex, guided_choice, guided_grammar를 기본 기능으로 제공합니다.
Instructor. 파이단틱(Pydantic) 기반 래퍼(wrapper)이며 어떤 LLM 위에서도 동작합니다. 검증(validation)에 실패하면 재시도(retry)합니다. 여러 프로바이더(cross-provider)에서 쓸 수 있지만 로짓을 직접 수정하지는 않습니다. 대신 재시도와 구조화 출력을 의식한 프롬프트(structured-output-aware prompt)에 의존합니다.
직관과 어긋나는 결과
제약 디코딩은 제약이 없는 생성보다 오히려 더 빠를 때가 많습니다. 이유는 두 가지입니다. 첫째, 다음 토큰 후보 공간이 줄어듭니다. 둘째, 영리한 구현은 강제된 토큰(forced token)의 생성을 아예 건너뜁니다. 예를 들어 {"name": " 같은 스캐폴딩(scaffolding)은 모든 바이트가 이미 결정되어 있기 때문입니다.
비용이 큰 함정
필드 순서(field order)는 중요합니다. answer를 reasoning보다 앞에 두면 모델은 생각하기도 전에 답에 약속(commit)해 버립니다. JSON은 유효합니다. 그러나 정답이 틀립니다. 어떤 검증으로도 이를 잡지 못합니다.
작동 방식이 다릅니다. Instructor는 로짓을 건드리지 않습니다. 대신 스키마를 프롬프트에 포함시키고, 출력을 파싱한 뒤, 검증에 실패하면 재시도합니다(기본값 3회). 어떤 프로바이더와도 함께 동작합니다. 재시도는 지연(latency)과 비용을 더합니다. 여러 프로바이더 사이의 이식성(portability)이 핵심 강점입니다.
서버 측 제약 디코딩(server-side constrained decoding)입니다. 지원되는 스키마에 대해서는 Outlines와 비슷한 수준의 신뢰성을 제공합니다. 로컬 모델을 관리할 필요가 없습니다. 대신 벤더에 묶입니다.
함정들(Pitfalls)
재귀적 스키마(Recursive schemas). Outlines는 재귀를 고정 깊이로 평탄화합니다. 트리 구조 출력, 예를 들어 중첩 댓글이나 추상 구문 트리(Abstract Syntax Tree; AST)에는 XGrammar 또는 llguidance 같은 CFG 기반 엔진이 필요합니다.
거대한 enum. 10,000개 옵션의 enum은 컴파일이 느리거나 타임아웃이 날 수 있습니다. 이때는 리트리버(retriever) 방식으로 바꿉니다. 먼저 상위 k개 후보를 예측하고, 그 후보들로 제약을 겁니다.
너무 엄격한 문법(Grammar too strict).date: "YYYY-MM-DD" 정규표현식을 강제하면 모델은 누락된 날짜에 "unknown"을 출력할 수 없습니다. 모델은 이를 보완하느라 날짜를 지어냅니다. null이나 센티넬(sentinel) 값을 허용하도록 만듭니다.
성급한 약속(Premature commitment). 위에서 본 필드 순서 함정을 참고합니다. 항상 추론(reasoning)을 먼저 둡니다.
스키마 없는 벤더 JSON 모드. 순수 JSON 모드는 유효한 JSON 문법만 보장하며, 사용 사례(use case)에 맞는 유효한 출력은 보장하지 않습니다. 항상 전체 스키마를 제공합니다.
사용해보기
2026년 스택(stack) 선택 기준은 다음과 같습니다.
상황
선택
OpenAI/Anthropic/Google 모델, 단순한 스키마
벤더 네이티브 구조화 출력
어떤 프로바이더든, 파이단틱 워크플로우, 재시도 허용
Instructor
로컬 모델, 100% 유효성 필요, 평탄한 스키마
Outlines (FSM)
로컬 모델, 재귀적 스키마
XGrammar 또는 llguidance
자체 호스팅 추론 서버
vLLM guided decoding
재시도가 허용되는 배치 처리(batch processing)
Instructor + 가장 저렴한 모델
산출물 만들기
outputs/skill-structured-output-picker.md로 저장합니다.
---
name: structured-output-picker
description: Choose a structured output approach, schema design, and validation plan.
version: 1.0.0
phase: 5
lesson: 20
tags: [nlp, llm, structured-output]
---
Given a use case (provider, latency budget, schema complexity, failure tolerance), output:
Guide the student in Korean.
1. Mechanism. Native vendor structured output, Instructor retries, Outlines FSM, or XGrammar CFG. One-sentence reason.
2. Schema design. Field order (reasoning first, answer last), nullable fields for "unknown", enum vs regex, required fields.
3. Failure strategy. Max retries, fallback model, graceful `null` handling, out-of-distribution refusal.
4. Validation plan. Schema compliance rate (target 100%), semantic validity (LLM-judge), field-coverage rate, latency p50/p99.
Refuse any design that puts `answer` or `decision` before reasoning fields. Refuse to use bare JSON mode without a schema. Flag recursive schemas behind an FSM-only library.
이 스킬(skill)은 프로바이더, 지연 예산(latency budget), 스키마 복잡도, 실패 허용도(failure tolerance)를 입력으로 받아 작동 방식(mechanism), 스키마 설계, 실패 전략, 검증 계획을 골라 줍니다. answer나 decision이 추론 필드보다 앞서는 설계를 거부하고, 스키마가 없는 맨 JSON 모드(bare JSON mode)를 쓰지 못하게 하며, 재귀적 스키마가 FSM 전용 라이브러리 뒤에 숨어 있으면 표시(flag)합니다.
연습문제
쉬움. 제약 디코딩 없이 작은 오픈 가중치 모델(small open-weights model), 예를 들어 Llama-3.2-3B에 Review(sentiment, confidence, evidence_span)을 요청합니다. 100개 리뷰에서 유효한 JSON으로 파싱되는 비율을 측정합니다.
중간. 같은 말뭉치(corpus)에 Outlines JSON 모드를 적용합니다. 준수율(compliance rate), 지연, 의미 정확도(semantic accuracy)를 비교합니다.
어려움. 전화번호 정규표현식 \d{3}-\d{3}-\d{4}를 위한 정규표현식 제약 디코더(regex-constrained decoder)를 처음부터 구현합니다. 1000개 샘플에서 유효하지 않은 출력이 0개임을 검증합니다.