Huny's Dev Blog
huny.dev

LLM 구조화된 출력(Structured Outputs)과 문법 기반 디코딩(Constrained Decoding) 해부: XGrammar의 Pushdown Automaton 로짓 마스킹과 Jump-Forward 최적화 원리

LLM 서빙에서 JSON Schema 및 Function Calling의 100% 정합성을 보장하는 문법 가이드 디코딩(Constrained Decoding)의 내부 원리를 분석한다. Context-Free Grammar 기반 Pushdown Automaton(PDA) 상태 전이, Vocab Trie 적응형 로짓 마스킹(-inf), 그리고 결정론적 구조 토큰을 한 번에 건너뛰는 Jump-Forward 최적화 메커니즘을 살펴보고, 로컬 Python 시뮬레이션 검증과 실무 서빙 체크리스트를 정리한다.

Hun Jang
Hun Jang Nov 10, 2026

대규모 언어 모델(LLM)의 생성 로짓을 문법 제약 엔진이 실시간으로 필터링하고 결정론적 토큰을 가속 전진(Jump-Forward)시키는 구조화된 출력(Constrained Decoding)의 아키텍처를 상징하는 추상 비주얼 아트워크다. 동심원 궤도와 다층 오토마톤 기하학, 그리고 100% 정합성을 향해 정렬되는 빛의 빔을 통해 고속 가이드 디코딩 생태계를 표현했다.

생성형 AI와 대규모 언어 모델(LLM)이 자연어 질의응답을 넘어 API 연동, 데이터베이스 쿼리 생성, 정형 리포트 출력, 그리고 도구 호출(Tool Calling/Function Calling)을 수행하는 자율형 에이전트의 중추로 자리 잡으면서 '출력 데이터의 구조적 정합성(Structural Validity)'은 시스템 안정성을 결정짓는 핵심 엔지니어링 과제가 되었다. 모델이 아무리 뛰어난 추론 능력을 갖추었더라도 반환된 JSON 문자열에 따옴표가 하나 누락되거나, 스키마에 정의되지 않은 임의의 키가 생성되거나, 불필요한 마크다운 코드 블록(json ... )과 부가 설명 문장이 섞여 들어오면 다운스트림 애플리케이션의 JSON 파서가 크래시되며 전체 워크플로가 즉시 중단된다. XGrammar GitHub Repository, vLLM Structured Outputs Documentation

결론부터 말하면 문법 기반 가이드 디코딩(Constrained Decoding / Grammar-Guided Generation)은 사후 파싱(Post-parsing)이나 재시도 프롬프트에 의존하지 않고, LLM의 토큰 생성 매 스텝마다 문맥 자유 문법(CFG) 기반의 푸시다운 오토마톤(PDA)을 통해 다음 허용 토큰 집합을 판별하고 비허용 토큰의 로짓(Logit)을 −∞-\infty로 강제 마스킹하여 100% 스키마 준수를 수학적으로 보장하는 차세대 추론 기술이다. 특히 최신 서빙 엔진(vLLM, SGLang, MLC-LLM)에 표준으로 채택된 XGrammar는 어휘 트리(Vocab Trie)와 적응형 비트마스크 캐시를 통해 마스크 생성 오버헤드를 마이크로초 단위로 단축했을 뿐만 아니라, 고정된 키 명칭이나 문법 구조 구간을 GPU 순방향 연산 없이 CPU가 즉시 건너뛰는 Jump-Forward Decoding을 통해 토큰당 생성 지연 시간(TPOT)을 50% 이상 단축하는 데 성공했다.

이 글에서는 프롬프트 엔지니어링 기반 구조화의 근본적 한계를 짚어보고, Context-Free Grammar(CFG)와 푸시다운 오토마톤(PDA)이 결합된 로짓 마스킹(Logit Masking)의 내부 동작 메커니즘을 상세히 해부한다. 이어 결정론적 구조 토큰을 한 번에 주입하는 Jump-Forward 가속 원리를 살펴보고, 로컬 Python 스크립트로 직접 구현·검증한 5가지 시나리오와 프로덕션 서빙 도입 시 필수 점검 체크리스트를 정리한다.

1. 프롬프트 기반 구조화의 한계와 가이드 디코딩의 필연성

전통적으로 LLM으로부터 JSON이나 정형 데이터를 얻기 위해 개발자들은 다음과 같은 방식에 의존해 왔다.

  • 프롬프트 강제(Prompt Coercion): 프롬프트에 "반드시 다음 JSON 형식으로만 답하고 부가 설명은 절대 하지 마라"는 지침을 추가하고 스키마 예시를 주입하는 방식이다. 그러나 온도(Temperature)나 컨텍스트 길이에 따라 모델은 여전히 선행 인사말("네, 요청하신 JSON입니다:")이나 후행 마크다운 백틱을 출력하여 파싱 실패를 유발한다.
  • 사후 재시도(Retry Loop): 애플리케이션 단에서 try/catch 블록으로 json.loads()를 실행하고, 실패 시 에러 메시지를 다시 모델에 주입하여 재생성을 요구하는 방식이다. 이는 토큰 소비량을 2~3배 급증시키고 지연 시간(Latency)의 심각한 P99 스파이크를 초래한다.
  • 모듈러 파서 및 교정기(Json Repair): 불완전한 JSON 문자열을 정규식이나 휴리스틱으로 보정하는 라이브러리(예: dirtyjson, json-repair)를 도입하는 방식이다. 하지만 잘린 문자열이나 잘못 닫힌 괄호를 자의적으로 복구하는 과정에서 데이터 필드의 의미론적 왜곡이 발생한다.

이러한 접근법들은 모두 '모델이 이미 생성한 잘못된 텍스트를 사후에 수습하려는 시도'라는 태생적 한계를 갖는다. 반면 가이드 디코딩(Constrained Decoding)은 생성 단계(Generation Step) 자체에 직접 개입하여, 모델이 문법을 위반하는 토큰을 선택할 가능성을 확률적으로 0%로 박제하는 근본적인 사전 예방 메커니즘이다.

Constrained Decoding의 토큰별 로짓 마스킹(Logit Masking) 아키텍처 다이어그램이다. Transformer Decoder가 출력한 전체 어휘의 Raw Logits를 XGrammar PDA 오토마톤이 검사하여 비허용 토큰의 로짓을 -inf로 강제 마스킹하고 Softmax를 통해 100% 문법 정합성을 달성하는 파이프라인을 도식화했다.

2. 문법 기반 가이드 디코딩의 핵심 원리: PDA와 로짓 마스킹

가이드 디코딩의 핵심 파이프라인은 문법 모델링(Grammar Modeling), 상태 전이 추적(State Tracking), 그리고 로짓 마스킹(Logit Masking)의 3단계로 구성된다.

1) Context-Free Grammar(CFG)와 푸시다운 오토마톤(PDA)

과거 Outlines 등의 초기 가이드 디코딩 도구는 정규 표현식(Regex)을 결정적 유한 오토마톤(DFA/FSM)으로 변환하는 방식을 주로 사용했다. 정규 표현식 기반 FSM은 상태 전이가 단순하지만, 임의 깊이의 중첩 객체({"a": {"b": {"c": ...}}})나 재귀적 배열 구조를 표현하지 못해 복잡한 JSON Schema를 처리할 때 상태 폭발(State Explosion) 문제를 겪었다.

최신 XGrammar는 범용 문맥 자유 문법(Context-Free Grammar, CFG)을 채택하고 이를 푸시다운 오토마톤(Pushdown Automaton, PDA)으로 컴파일한다. PDA는 내부 실행 스택(Stack)을 유지하여 다음과 같은 중첩 상태를 효율적으로 관리한다.

  • 객체 시작({)을 만나면 스택에 OBJECT_SCOPE를 푸시하고 다음 허용 토큰을 KEY_STRING 또는 빈 객체 닫기(})로 한정한다.
  • 키가 결정되면 반드시 콜론(:)만이 유효 토큰으로 인정된다.
  • 값 영역에서 배열 시작([)을 만나면 스택에 ARRAY_SCOPE를 추가로 푸시하여 배열 원소 규칙을 중첩 적용한다.
  • 닫는 괄호(], })를 만날 때마다 스택에서 해당 스코프를 팝(Pop)하여 상위 문법 레벨로 복귀한다.

2) Vocabulary Trie와 적응형 비트마스크(Adaptive Bitmask)

LLM의 디코딩 루프는 통상 128,000개 이상의 거대한 어휘 사전(Vocabulary)을 다룬다. 매 토큰 생성 스텝마다 128k개의 토큰 문자열을 일일이 파서에 통과시키는 것은 엄청난 연산 지연(수십 밀리초)을 유발하여 토큰 생성 속도를 수십 배 떨어뜨린다.

XGrammar는 이를 극복하기 위해 두 가지 핵심 최적화를 도입했다.

  • Vocabulary Prefix Trie: 전체 어휘 사전을 BPE/SentencePiece 토큰 조각 단위의 프리픽스 트리(Trie) 구조로 사전 인덱싱한다. 파서는 트리를 탐색하며 현재 문법 상태에서 유효한 서브트리만을 단 한 번의 순회로 빠르게 찾아낸다.
  • Context-Independent Bitmask Cache: 문법 상태와 무관하게 정적으로 유효성이 결정되는 토큰 집합을 비트셋(Bitset) 형태로 사전 계산하여 캐싱한다. 런타임에는 캐시된 비트마스크와 동적 스택 상태를 비트 연산(AND/OR)으로 결합하여 수 마이크로초(µs) 내에 최종 허용 토큰 비트맵을 완성한다.

3) 로짓 마스킹(Logit Masking)과 소프트맥스 확률 강제

Transformer 디코더의 마지막 레이어가 전체 어휘에 대한 로짓 벡터 Z=[z1,z2,…,zV]Z = [z_1, z_2, \dots, z_V]를 출력하면, 가이드 디코딩 엔진은 비트마스크를 적용하여 허용되지 않은 모든 토큰의 로짓을 −∞-\infty로 치환한다.

$z_i^{\text{masked}} = \begin{cases} z_i & \text{if token } i \in \text{ValidTokens} \\ -\infty & \text{otherwise} \end{cases}$

이후 소프트맥스(Softmax) 함수를 통과하면 불법 토큰의 분자 값은 e−∞=0e^{-\infty} = 0이 되므로, 비허용 토큰의 선택 확률은 정확히 0.0%0.0\%가 된다. 모델은 남은 유효 토큰들 중에서만 사전 학습된 확률 분포에 따라 가장 자연스러운 토큰을 샘플링하게 된다.

3. 추론 속도의 혁신: Jump-Forward Decoding 메커니즘

가이드 디코딩의 가장 큰 혁신 중 하나는 문법 제약을 활용해 오히려 LLM의 추론 속도를 비약적으로 끌어올리는 Jump-Forward Decoding (결정론적 고속 전진) 기법이다.

일반 자가회귀(Autoregressive) 순차 생성과 Jump-Forward 최적화 디코딩의 실행 흐름 비교 다이어그램이다. 고정 스키마 키 및 구분자 등 결정론적 토큰 구간을 CPU 오토마톤이 한 번에 스킵하여 GPU Forward 연산 횟수를 50% 이상 절감하는 메커니즘을 도식화했다.

1) 결정론적 토큰 구간의 발견

정형 데이터나 JSON Schema를 출력할 때, 상당수의 토큰은 모델의 '지능적 추론'이 전혀 필요 없는 고정된 문법적 뼈대(Boilerplate)다.
예를 들어 {"status": "success", "code": 200}이라는 JSON을 출력할 때:

  • 시작 중괄호 다음의 "status" 키
  • 키 뒤에 오는 콜론(:)과 여는 따옴표(")
  • 값 뒤에 오는 닫는 따옴표("), 쉼표(,), 그리고 다음 키인 "code"와 콜론(:)

이러한 토큰들은 스키마 정의상 유일하게 허용되는 선택지(Valid Candidate Set Size = 1)이므로, 확률을 계산하기 위해 비싼 GPU Forward Pass를 수행할 이유가 전혀 없다.

2) Fast-Forwarding 파이프라인

Jump-Forward 디코딩 엔진은 PDA 상태를 검사하여 다음 허용 토큰 집합의 원소가 1개뿐인 경우, 모델 추론 루프를 즉시 건너뛰고 해당 토큰(또는 연속된 토큰 문자열 조각)을 생성 버퍼와 KV 캐시에 직접 주입(Append)한다.

  • GPU 연산 횟수 대폭 감소: 50개의 토큰으로 구성된 JSON 응답에서 구조적 토큰이 25개라면, GPU 순방향 패스 횟수가 50회에서 25회로 절반 감축된다.
  • Time-to-First-Token(TTFT) 및 TPOT 단축: 고정 키와 서식 문자열이 실시간 연산 없이 즉시 채워지므로 체감 생성 속도가 2~3배 빨라진다.
  • 비용 및 전력 절감: 대규모 서빙 클러스터에서 동일한 GPU 하드웨어로 처리할 수 있는 초당 동시 요청 수(Throughput)가 비약적으로 증가한다.

4. 기존 구조화 기법 vs XGrammar 가이드 디코딩 비교

JSON Schema 및 구조화 출력을 생성하는 주요 기법들을 기능성과 성능 관점에서 비교하면 다음과 같다.

비교 항목
1. 순수 프롬프트 지시 + Regex/사후 파싱
2. 정규식 기반 FSM 가이드 (초기 Outlines)
3. CFG/PDA + Jump-Forward (XGrammar)
스키마 준수 보장
확률적 (실패율 5~20% 존재)
100% 구조 준수
100% 수학적 준수 보장
중첩 구조 지원
모델 역량에 전적으로 의존
제한적 (상태 폭발 발생 위험)
푸시다운 오토마톤(PDA) 기반 무제한 중첩 지원
토큰당 마스킹 오버헤드
0ms (추론 후 파싱)
수십 ms (어휘 크기 비례 병목)
수 µs (Trie + 적응형 비트마스크 캐시)
결정론적 토큰 가속
미지원 (모든 토큰 GPU 추론)
미지원 (토큰 바이 토큰 순차 실행)
Jump-Forward Decoding으로 GPU Forward 50%+ 절감
서빙 엔진 통합
클라이언트 애플리케이션 단
독립 파이썬 래퍼
vLLM, SGLang, MLC-LLM C++ 코어 네이티브 내장
재시도 비용
파싱 에러 시 전체 재생성 필요
재시도 불필요
재시도 0건 (에러 제로)

5. 로컬 Python으로 검증하는 Constrained Decoding 및 Jump-Forward 시뮬레이션

XGrammar의 내부 아키텍처인 Pushdown Automaton 상태 전이, 토큰 어휘 집합 필터링, 로짓 마스킹(−∞-\infty), 그리고 Jump-Forward 결정론적 전진 메커니즘을 완벽하게 재현한 로컬 Python 검증 스크립트(test_constrained_decoding_xgrammar.py)를 작성하여 실행했다.

직접 실행해 확인한 5가지 검증 시나리오

로컬 터미널 환경에서 위 검증 스크립트를 직접 실행하여 5가지 시나리오가 모두 정상 통과(All 5 Constrained Decoding verification checks passed successfully!)함을 확인했다.

  1. 초기 PDA 상태 전이 및 로짓 마스킹 검증 (Case 1): 생성 시작 시 문법 엔진의 유효 토큰이 여는 중괄호({) 하나로 정확히 제한되고, 로짓 마스킹 및 소프트맥스 연산 후 P('{') = 1.0 (100.0%)으로 산출됨을 확인했다.
  1. 불법 토큰 완벽 차단 및 확률 0.0% 강제 검증 (Case 2): 객체 시작 이후 스키마에 존재하지 않는 임의의 키(invalid_key)나 환각 토큰의 로짓이 -infinity로 설정되어 소프트맥스 확률이 정확히 0.0%로 소멸함을 입증했다.
  1. Jump-Forward 결정론적 전진 최적화 검증 (Case 3): 허용 토큰의 크기가 1인 상태("status" 키 및 콜론 :)에서 GPU Forward Pass 없이 CPU 문법 엔진이 즉시 토큰을 가속 전진시킴을 확인했다.
  1. 전체 디코딩 루프 시뮬레이션 및 100% JSON Schema 적합성 검증 (Case 4): 가이드 디코딩 루프를 거쳐 생성된 최종 문자열({"status":"success","code":200,"items":["apple","banana"]})이 표준 json.loads() 파서에서 단 하나의 구문 에러 없이 파싱되고, status, code, items의 데이터 타입과 enum 제약조건을 완벽히 만족함을 검증했다.
  1. 비정형 디코딩 대비 에러율 0% 및 추론 연산 절감률 검증 (Case 5): 전체 17개의 토큰 생성 과정 중 무려 10개의 토큰이 Jump-Forward로 처리되어, 총 58.8%의 GPU 순방향 추론 연산 패스가 절감됨을 정량적으로 증명했다.

6. 프로덕션 LLM 서빙 도입 및 성능 최적화 체크리스트

  1. JSON Schema의 복잡도 다이어트 및 재귀 제약:
      • 지나치게 깊은 계층 구조나 무제한 anyOf/oneOf 조합은 문법 오토마톤 컴파일 시간과 메모리를 급증시킨다. 가능한 한 정적 프로퍼티 명세와 명시적 enum 목록을 사용하여 오토마톤 크기를 슬림하게 유지해야 한다.
  1. Grammar Pre-compilation과 LRU 캐시 구축:
      • API 요청이 들어올 때마다 실시간으로 JSON Schema를 CFG/PDA로 컴파일하면 수십 밀리초의 지연이 추가된다. 자주 사용되는 도구 호출 스키마나 API 응답 포맷은 서버 기동 시 미리 컴파일하고 LRU 메모리 캐시에 등록해 재사용해야 한다.
  1. 토크나이저 특수 토큰과 서브워드 분할 방어:
      • 동일한 문자열이라도 선행 공백 유무에 따라 BPE 토큰 ID가 달라진다(예: "status" vs " status"). 토크나이저의 어휘 집합 인코딩 특성을 정확히 반영한 Trie를 구축하지 않으면 공백 처리에서 예기치 않은 문법 위반이 발생할 수 있다.
  1. CPU-GPU 비동기 오버랩(Async Pipeline Overlap):
      • GPU가 현재 토큰의 Transformer 연산을 수행하는 동안, CPU는 백그라운드에서 다음 스텝의 유효 비트마스크를 미리 계산하여 전달해야 한다. vLLM과 SGLang의 통합 구현체처럼 비동기 큐를 구성하면 마스킹 연산 오버헤드를 제로(Zero-latency)로 은폐할 수 있다.
  1. 문법 정합성(Syntax)과 사실적 정확성(Semantics)의 분리 모니터링:
      • Constrained Decoding은 출력이 스키마 규칙을 100% 만족하도록 보장할 뿐, 필드 안에 들어가는 데이터의 '사실 여부(Truthfulness)'까지 보장하지는 않는다. 따라서 문법적 검증과 별개로 의미론적 환각이나 잘못된 계산 결과를 점검하는 가드레일(Guardrails) 체계를 병행 운영해야 한다.

참고 자료

확인 기준일: November 10, 2026

추천 글