6.3.5. Constrained Decoding(제약된 디코딩)과 Context-Free Grammar(CFG) 기반 토큰 샘플링 원리
거대 언어 모델(LLM)이 개발자가 요구하는 엄격한 구조화 데이터(Structured Data)를 100% 무결하게 출력하도록 강제하는 기술의 궁극적인 아키텍처 종착지는, 프롬프트 엔지니어링(Prompt Engineering)이라는 나약한 텍스트 ’부탁’도 아니고, 파인튜닝(Fine-Tuning)이라는 가중치(Weight) 편향의 ’세뇌’도 아니다.
그것은 모델의 심층 신경망을 거쳐 최종적으로 단어 토큰(Token)이 현실 세계의 바이트(Byte) 텍스트로 튕겨 나가는 찰나의 순간, 그 생성 과정의 물리적 확률 공간 자체를 수학적으로 영구 차단하고 통제해 버리는 폭력적이고 강력한 기술, 바로 **‘제약된 디코딩(Constrained Decoding)’**이다.
이 기술의 동작 메커니즘을 명확히 이해하는 것은, 스스로의 창의성에 취해 헛소리를 늘어놓는 99%의 확률론적 에이전트를, 한 치의 오차도 허용하지 않는 100%의 무결점 결정론적 오라클(Deterministic Oracle)로 단숨에 승격시키는 시스템 엔지니어링의 핵심 원리를 깨닫는 것과 같다.
1. 토큰 확률 분포(Logit) 스코어와 개입 마스킹(Masking)의 이해
LLM이 입력받은 문맥을 바탕으로 다음 텍스트(Next Token)를 만들어내는 디코딩(Decoding) 과정을 극도로 단순화하여 들여다보면 다음과 같다.
모델의 제일 마지막 레이어(Logits Layer)는 자기 자신의 내부 사전에 존재하는 모든 단어 조각(예: 토크나이저에 정의된 약 10만 개의 토큰 전체)에 대하여, “현재 문맥상 다음에 올 확률이 얼마나 높은가“에 대한 날것의 실수 벡터 점수(Logit)를 무자비하게 계산해 낸다. 그리고 이 점수들에 소프트맥스(Softmax) 정규화 함수를 씌워 0~1 사이의 확률값으로 변환한 뒤, 샘플링(Sampling) 알고리즘을 거쳐 최종적으로 화면에 띄울 단 한 개의 단어 토큰을 뽑아낸다.
만약 모델이 JSON을 파싱하다가 우연히 {"customer_age": 부분까지 텍스트를 생성(Generation)했다면, 똑똑하고 평범한 LLM 모델은 앞선 대화 문맥에 따라 숫자 29 토큰이나, 혹은 알파벳 문자열 "twenty-nine" 토큰에 매우 높은 확률 점수 랭킹을 최상단에 배정할 것이다.
여기서 **제약된 디코딩(Constrained Decoding)**의 마법은, 자연스러운 소프트맥스 확률 샘플링이 일어나기 바로 1밀리초(ms) 직전인 찰나의 틈새 공간에 전격적으로 개입한다.
만약 오라클 설계자가 Pydantic 스키마를 통해 해당 속성의 값을 반드시 integer(정수) 형식만 갖도록 요구했다면, 런타임에 개입한 제어기(Controller Middleware)는 숫자 0~9를 제외한 알파벳이나 특수 기호 문자열에 해당하는 나머지 9만 9천여 개 모든 토큰의 확률(Logit) 값을 강제로 마이너스 무한대(-∞)로 잔인하게 덮어씌워 버린다(Logit Masking).
이렇게 로짓(Logit)이 물리적으로 마스킹되는 순간, 아무리 모델의 뇌신경망이 문맥상 "twenty-nine"이라는 영단어를 압도적인 1위로 출력하고 싶어 안달이 났더라도, 소프트맥스 확률이 0.000000%로 소멸해 고정되었기 때문에 물리적으로 그 단어를 뱉어낼 컴퓨팅 경로가 우주에서 사라지게 된다.
2. Context-Free Grammar (CFG)와 유한 상태 기계 트래커(FSM Tracker)
그렇다면 이러한 억압적인 토큰 마스킹 계산 작업을 실시간 추론(Inference) 과정에서, 그것도 1초에 수십~수백 번씩 쏟아지는 수만 개의 토큰 확률 트리에 대해 밀리초(ms) 단위의 랙(Lag) 없이 어떻게 정확하고 빠르게 수행할 수 있을까?
여기서 비로소 수십 년 전 컴퓨터 공학의 가장 고전적이고 우아한 학문인 컴파일러 언어 이론, **‘형식 언어(Formal Language)’**의 구조가 딥러닝 트랜스포머 아키텍처와 경이롭게 결합한다.
- [정적 스키마 컴파일 (Pre-Compute Compile)]: LLM의 추론 작동이 시작되기 전, 로컬 백엔드 오픈소스 엔진(예: vLLM,
Outlines,Guidance,llama.cpp의 GBNF 구문 엔진 등)은 개발자가 정적으로 던진 Pydantic JSON Schema 객체를 먼저 파싱한다. 그리고 이를 대수학적인 컨텍스트 프리 그래머(CFG, Context-Free Grammar) 문법 규칙 텍스트나, 고속 탐색이 가능한 노드 트리인 유한 상태 기계(FSM, Finite State Machine) 데이터 구조로 메모리에 완벽하게 컴파일(Compile)하여 인덱스(Index)로 올려둔다. - [상태 전이 트래킹 (State Transition Tracking)]: FSM이 가동되면, 모델이 텍스트 토큰을 하나씩 생성할 매 순간(Step)마다 이 FSM 포인터의 상태(State) 노드 커서는 한 단계씩 전이(Transition)된다. 예컨대 FSM의 강제 초깃값 상태 구역에서는 무조건 JSON 시작인 중괄호 열기
{토큰 단 하나만을 생성할 수 있도록 다른 모든 우주의 토큰 로짓을 마스킹해버린다.{가 생성되고 나면 다음 상태 전이를 통해 오직 쌍따옴표"토큰과 공백문자 스페이스바(Space) 토큰만을 허용하는 식이다. - [생성 경로의 절대적 봉쇄 (Path Pruning)]: 만약 모델이 키 이름 토큰인
"customer_name"의 출력을 끝내고 마침내 매핑 기호인:토큰을 찍었다면, FSM 상태 머신은 현재의 그래프 노드 상태를 엄격히 평가하여 *‘다음 유효한 토큰은 반드시 Value 문자열 데이터의 시작인"쌍따옴표 여야만 한다’*는 것을 대수학적으로 선언한다. 그리고 숫자를 의미하는0-9토큰이나 배열을 나타내는[Токен으로 향하는 다른 모든 오답의 갈림길 경로(Path) 확률을 잔인하게 차단(Pruning)해 버린다.
3. 소결: 확률론적 유창성(Fluency)을 포섭한 완벽한 결정론(Determinism)
제약된 디코딩(Constrained Decoding) 아키텍처는, 거대 언어 모델(LLM)이 수천억 개의 파라미터로 구축한 방대한 문해력 기반의 ’비정형적 사실 추론 기능(Reasoning)’과, 엔터프라이즈 데이터베이스가 요구하는 ’구조적이고 수학적인 엄격함(Structural Rigidity)’을 충돌 없이 완벽하게 분리해 낸 예술적인 소프트웨어 공학의 정수다.
모델의 거대한 딥러닝 블랙박스 신경망은 주어진 스키마 빈칸에 *“어떤 팩트 기반의 데이터 문자열을 채워 넣어야 가장 정답에 가까울지”*를 지금 이 순간에도 여전히 확률론적(Probabilistic)으로 치열하고 자유롭게 고민한다. 그러나 그 아름답고 복잡한 뇌수혈관 고민의 결과값이 현실 세계의 텍스트 터미널로 현현(Manifestation)되어 쏟아지는 바로 그 마지막 출구 목구멍(디코딩 레이어)에는, 프로그래머가 차갑게 설계해 둔 수학적인 톱니바퀴 가이드레일(CFG 상태 기계)이 숨 막힐 듯 촘촘하게 박혀 있다.
이 놀라운 토큰 마스킹 억제 기술 덕분에, 현대의 소프트웨어 오라클 엔지니어는 더 이상 LLM 모델이 뱉어낸 JSON 응답 텍스트 제일 끝에 쉼표 ,가 멍청하게 하나 빠지지는 않았는지, Integer 타입 필드 공간에 "unknown"이라는 String 텍스트를 박아넣어 GSON 파서(Parser)를 터뜨리지는 않을지 두려워하며, 수백 줄의 볼품없는 정규표현식(Regex) try-except 파싱 패치 코드를 백엔드 단에 덕지덕지 발라댈 필요가 완전히 없어졌다.
데이터 구조 포맷 형식주의에 관한 한, 오라클은 완벽한 침묵과 구문론적인 100% 결정론으로 시스템 아키텍처의 영구적인 평화를 보장하게 된 것이다.