AI VIDEO BRIEFING

LLM JSON 출력 강제하기: 문법 제약 디코딩이 프롬프트보다 확실한 이유와 한계

LLM에 “올바른 JSON을 반환하라”고 지시해도 깨진 출력은 나온다. 토큰 점수와 샘플러 사이에 문법 상태를 끼워 넣어 불가능한 토큰을 아예 차단하는 문법 제약 디코딩의 작동 원리와 그 보장의 경계를 정리했다.

프롬프트로는 못 막는다: LLM이 올바른 JSON만 내놓게 만드는 문법 제약 디코딩 영상 대표 이미지

핵심 메시지

  • 프롬프트는 올바른 구조가 나올 확률을 높일 뿐, 잘못된 다음 토큰을 불가능하게 만들지는 못한다.
  • 문법 제약 디코딩은 모델이 매긴 토큰 점수와 샘플러 사이에 파서 상태를 끼워 넣고, 규칙을 깨는 토큰의 점수를 음의 무한대로 떨어뜨려 확률을 0으로 만든다.
  • 모델 토큰은 문자가 아니라서 하나의 토큰이 따옴표·쉼표·필드 경계를 한꺼번에 넘을 수 있고, 첫 글자만 보는 검사로는 합법 여부를 판정할 수 없다.
  • 중첩된 객체와 배열을 다루려면 유한 상태만으로는 부족하고 스택처럼 자라는 파서 상태가 필요하다.
  • 구조가 맞다는 보장은 값이 사실이라거나 그 호출이 허가됐다는 보장이 전혀 아니다.

쉽게 이해하기

챗봇 화면에 답을 띄울 때는 평범한 문장이면 충분하지만, 그 답을 도구에 그대로 넘겨야 한다면 이야기가 달라진다. 영상은 파리의 기온을 섭씨로 묻는 날씨 도구 호출을 예로 든다. city 필드에 Paris, unit 필드에 C가 들어가야 하는데, 모델은 여전히 토큰을 하나씩 뱉을 뿐이라 따옴표 하나가 빠지면 객체 전체가 깨지고, 다시 시도하면 이번엔 unit 대신 units라고 써서 파서는 통과하지만 도구가 거부한다.

흔히 쓰는 방어선은 생성이 끝난 뒤에 검사하는 파이프라인이다. 모델이 응답을 만들면 JSON 파서가 문법을 보고, 스키마 검증기가 필드를 보고, 그다음에야 도구가 호출을 받는다. 문제는 모든 판정이 너무 늦게 내려진다는 점이다. 게다가 스키마를 완벽히 지키면서 사용자가 요청하지 않은 도시를 가리키는 호출은 두 검사를 모두 통과한다. 재시도와 사후 수리는 실패한 다음에만 작동하고, 실수가 문장부호 수준을 넘어서면 무엇을 고쳐야 할지도 모호해진다.

그래서 강제할 수 있는 규칙을 왼쪽으로, 즉 모델이 점수를 매긴 직후와 샘플러 사이로 옮긴다. 여기서 진짜 어려운 문제가 생긴다. 토큰은 문자가 아니기 때문이다. unit 값 자리에서 선택지가 C와 F뿐일 때, C만 담은 조각도, C와 닫는 따옴표를 함께 담은 조각도, 심지어 C·따옴표·닫는 중괄호를 한꺼번에 담은 조각도 합법일 수 있다. 반면 Celsius는 첫 글자가 그럴듯해 보여도 한 글자짜리 선택지를 만족시킬 길이 없어 죽은 후보다. 신뢰할 만한 제약은 토큰 ID, 그것이 디코딩되는 바이트, 그 내용을 소비하는 문법 규칙이라는 세 층을 모두 이어 붙여 “이 후보를 받아들여도 완성으로 가는 길이 남아 있는가”를 물어야 한다.

실제 루프는 단순하다. 스키마를 이 모델의 토크나이저와 함께 컴파일해 상태를 가진 인식기를 만들고, 현재 파서 상태에서 어떤 어휘 토큰이 살아남는지 검사해 합법 토큰 마스크를 만든다. 마스크는 불법 후보의 점수만 음의 무한대로 내리고 합법 후보들의 상대적 점수는 건드리지 않으며 모델 가중치도 바꾸지 않는다. 하나가 샘플링되면 바이트를 디코딩해 인식기에 통과시키고 파서 상태를 전진시킨 뒤 반복한다. 정지 토큰조차 마스킹 대상이어서, 파서가 수용 상태에 도달하기 전에는 생성을 끝낼 수 없다.

효과를 볼 때는 세 가지를 따로 봐야 한다고 영상은 강조한다. 유효한 출력을 얼마나 안정적으로 만드는가, 스키마 언어를 얼마나 넓게 강제할 수 있는가, 성능 대가는 얼마인가다. 유효성은 극적으로 움직여서, XGrammar는 함수 호출 벤치마크에서 Llama 3.1 8B의 문법적 정확성이 62%에서 100%로 올랐다고 보고했고 OpenAI는 별도의 비공개 내부 평가에서 93%에서 100%로 올랐다고 밝혔다. 반면 커버리지는 좁아서 2024년 말 스냅숏 기준 JSON Schema Bench는 어느 guidance 릴리스에서 공식 테스트 45개 범주 중 13개만 완전히 지원한다고 봤고, 지연은 설정에 따라 H100에서 엔진 교체 후 출력 토큰당 시간이 44.2ms에서 6.88ms로 떨어진 실험이 있는가 하면 배치 1 측정에서는 6.2ms에서 6.3ms로 거의 움직이지 않은 사례도 있다.

주요 인사이트

  • 핵심 차이는 “선호하게 만든다”와 “선택 불가능하게 만든다” 사이에 있다. 프롬프트는 모델에게 계약을 지키기를 부탁하고, 문법 제약은 계약을 어기는 선택지를 아예 후보에서 지운다.
  • 규칙의 성격에 따라 필요한 기억이 다르다. C냐 F냐 같은 정규적 선택에는 작은 유한 상태 기계로 충분하지만, 중괄호와 대괄호가 중첩될 때마다 나중에 갚아야 할 닫는 기호가 쌓이므로 재귀적 JSON에는 스택처럼 자라는 상태가 필요하다.
  • “JSON 스키마”라는 말이 오해를 부른다. 스키마 자체가 곧 실행 가능한 기계는 아니고, 엔진이 자신이 지원하는 부분집합만 토크나이저와 함께 컴파일해 실제 제약으로 만든다.
  • 세 가지 점수판은 서로 합쳐지지 않는다. 유효성에서 100%가 나왔다는 사실이 스키마 커버리지가 넓다거나 다른 배포 환경의 비용을 예측해준다는 뜻은 전혀 아니다.
  • 보장의 범위를 층으로 나눠야 한다. 마스크는 디코딩을 지키고 수용 상태는 지원되는 형식을 증명할 뿐, 값이 요청과 일치하는지 확인하는 의미 검사, 최소 권한을 강제하는 허가 게이트, 실제 효과를 확인하는 실행 통제는 모두 그 위층의 별개 업무다.

자주 묻는 질문

프롬프트를 아주 잘 쓰면 올바른 JSON을 받을 수 있지 않나요?

프롬프트는 합법적인 출력이 나올 확률을 높일 수는 있지만, 모델의 어휘에서 불법적인 다음 토큰을 제거하지는 못합니다. 영상은 지시를 따랐는데도 따옴표가 빠지거나 필드 이름을 units로 잘못 쓰는 사례로 이 한계를 보여줍니다.

문법 제약을 걸면 모델의 답변 품질이 떨어지나요?

마스크는 불법 후보의 점수만 음의 무한대로 내려 확률을 0으로 만들고, 살아남은 합법 후보들의 상대적 점수는 그대로 둡니다. 후보들을 똑같은 확률로 만들지도 않고 모델 가중치를 바꾸지도 않습니다.

JSON에만 쓸 수 있는 기법인가요?

JSON은 영상에서 쓴 예시일 뿐입니다. 같은 아이디어로 SQL, 프로그램 문법, XML을 비롯해 엔진이 지원하는 다른 문법에도 생성을 묶어 둘 수 있습니다.

스키마를 지킨 호출이면 실행해도 안전한가요?

아닙니다. 영상은 사용자가 프라하를 물었는데 파리를 향한 호출이 파싱과 스키마 검증을 모두 통과하는 경우, 그리고 정책이 외부 도구를 막아 권한 검사에서 걸려야 하는 경우를 보여줍니다. 스키마 준수는 허가도, 안전성도, 실제 효과에 대한 증명도 아닙니다.

원문과 출처

이 글은 원본 영상의 자막을 바탕으로 한국어 독자를 위해 요약했습니다. 전체 맥락과 최신 정보는 원문에서 확인하세요.

YouTube 원본 영상 보기 ↗

관련 AI 소식