AI VIDEO BRIEFING
LLM 구조화 출력 원리 — 정규식과 유한 상태 기계로 JSON 형식을 보장하는 방법
LLM에 JSON을 요청하면 형식이 조금씩 어긋나 후처리가 필요하다. 정규식과 유한 상태 기계로 형식을 강제하는 구조화 출력의 작동 원리, 문맥 자유 문법과 토큰 불일치 문제, 그리고 형식을 조일수록 정확도가 떨어지는 역설을 정리했다.

핵심 메시지
쉽게 이해하기
자연어로 일정을 말하면 앱이 알아듣게 만드는 상황을 떠올려 보자. "목요일 정오부터 오후 6시" 같은 입력을 JSON으로 바꾸는 일은 요즘 모델에게 어렵지 않다. 문제는 모델이 출력에 설명을 덧붙이거나, 요일을 대문자로 쓸지 소문자로 쓸지 같은 표기가 매번 달라진다는 점이다. 프롬프트를 다듬고 후처리 규칙을 붙여 어느 정도 막을 수 있지만, 구조화 출력은 형식 자체를 어길 수 없게 만드는 접근이다.
OpenAI API에서는 파이썬의 Pydantic이나 타입스크립트의 Zod로 스키마를 정의해 요청에 함께 넘긴다. 그러면 응답이 지정한 형식에 반드시 들어맞는다. 다만 영상은 두 가지를 분명히 한다. 형식이 맞는다고 내용까지 맞는 것은 아니며, 당시 이 API는 리스트·객체 같은 기본 기능만 지원하고 정규식이나 검증 함수 같은 고급 기능은 지원하지 않았다는 점이다.
오픈소스 쪽에서는 Outlines가 같은 일을 한다. 재미있는 것은 내부 동작이다. JSON 스키마든 무엇이든 일단 정규식으로 변환한 다음, 그 정규식을 인식하는 유한 상태 기계를 만든다. 생성 도중 지금 어느 상태에 있는지 추적하면서, 각 후보 토큰이 그 상태에서 전이 가능한지 확인해 유효/무효 마스크를 만들고, 유효한 것들만 남겨 다시 정규화한 뒤 샘플링한다. 이 과정을 반복하면 결과는 반드시 정규식에 들어맞는다.
비용이 문제다. 라마 계열은 어휘가 12만 8천 개 수준이라 매 스텝 전부 검사하면 부담이 크다. 여기서 유한 상태 기계의 성질이 도움이 된다. 이 기계는 기억이 없어서 어떤 경로로 왔든 현재 상태만 같으면 허용 토큰이 같다. 따라서 상태별 허용 토큰 목록을 미리 계산해 두면 실행 중 조회 한 번으로 끝난다.
정규식으로 표현할 수 있는 것은 정규 언어까지다. 괄호 짝이 맞는지 같은 다단계 중첩은 정규식으로 판정할 수 없다. 영상은 OpenAI의 구조화 출력 API가 중첩을 다섯 단계까지만 지원하며, 중첩 깊이가 제한된 JSON은 사실 정규식으로도 처리 가능하다고 짚는다. 하지만 프로그래밍 언어의 구문 트리처럼 깊은 중첩이 필요하면 문맥 자유 문법이 필요하다. 가장 단순한 구현은 증분 파싱이다. 파서가 "입력이 더 필요하다"는 예외를 내는지, "이건 어떤 문자열을 붙여도 유효해질 수 없다"는 예외를 내는지로 토큰 허용 여부를 판정한다. 다만 미리 계산할 것이 없어 느리다. 상태 기계에 스택을 붙인 푸시다운 오토마타를 쓰면 상태와 스택 최상단 조합으로 허용 토큰을 사전 계산할 수 있다.
근본적인 걸림돌은 토큰과 터미널의 불일치다. 문법의 터미널은 여는 괄호나 숫자처럼 사람이 정한 단위인데, LLM 토큰은 통계적으로 학습된 단위라 경계가 다르다. GPT-4 토크나이저에서는 여는 괄호 두 개가 한 토큰이 되고, 닫는 괄호와 마이너스가 붙어 한 토큰이 되며, 10만 같은 숫자는 두 토큰으로 쪼개진다. 파이썬 코드 한 줄을 파이썬 파서 토큰과 GPT-4 토큰으로 각각 쪼개면 개수는 비슷해도 경계가 전혀 다르다. 가장 쉬운 대응은 한 토큰이 터미널 경계를 넘지 못하게 막는 것인데, 과도한 제약이라 정확도가 조금 떨어진다. 최근 연구로는 어휘에 정렬된 하위 문법을 쓰는 방법, 그리고 토크나이저를 별도의 상태 기계로 모델링해 문법 오토마타와 합성함으로써 아예 LLM 토큰을 입력으로 받는 기계를 만드는 딥마인드 쪽 접근이 소개된다.
마지막은 실무에서 놓치기 쉬운 이야기다. 어떤 연구는 모델에게 특정 형식을 강제하면 여러 벤치마크에서 정확도가 일관되게 떨어진다는 결과를 보고했다. 형식은 맞는데 답이 틀리고, 형식 제약 없이 자연어로 답하게 하면 맞히는 식이다. 제약이 강할수록 성능 저하가 커지므로, 처음 예시처럼 요일 대소문자를 굳이 하나로 고정하기보다 둘 다 받아들이는 편이 낫다는 결론이다.
주요 인사이트
- 구조화 출력이 보장하는 것은 형식이지 사실이 아니다. 스키마를 강제해도 값이 틀릴 수 있으므로 검증 단계를 없애면 안 된다.
- JSON 스키마를 정규식으로 환원한다는 발상은 언뜻 어색하지만, 정규식이 곧 유한 상태 기계이고 상태 기계는 디코딩 루프에 얹기 가장 싼 형태라는 점에서 자연스럽다. 형식 제약 문제를 형식 언어 이론의 문제로 옮겨 놓은 것이다.
- 토큰-터미널 불일치는 라이브러리 버그가 아니라 토크나이저가 통계적으로 학습된다는 사실에서 나오는 구조적 문제다. 그래서 해법도 문법 쪽이 아니라 토크나이저를 상태 기계로 모델링해 합성하는 식으로 간다.
- 형식을 조일수록 정확도가 떨어진다는 결과는, 제약이 모델이 답을 정리하며 쓰는 여유 공간을 빼앗는다는 점을 시사한다. 스키마 설계는 엄격함이 아니라 필요한 만큼만 좁히는 균형의 문제다.
- 속도와 표현력 사이에도 균형이 있다. 유한 상태 기계는 사전 계산으로 빠르지만 중첩을 못 다루고, 증분 파싱은 문법을 그대로 쓰지만 매 스텝 전체 어휘를 검사해야 해서 느리다.
자주 묻는 질문
구조화 출력을 쓰면 LLM이 항상 정답을 주나요?
아닙니다. 보장되는 것은 출력이 지정한 형식에 들어맞는다는 점뿐입니다. 값 자체는 틀릴 수 있고, 오히려 형식을 강하게 제약할수록 정확도가 떨어진다는 연구 결과가 소개됩니다.
형식은 어떻게 강제되나요?
생성 도중 각 스텝에서 현재 상태를 추적하고, 후보 토큰 하나하나가 그 상태에서 전이 가능한지 확인해 무효 토큰을 걸러낸 뒤 남은 것들만 정규화해 샘플링합니다. 형식을 벗어나는 토큰은 애초에 뽑힐 수 없습니다.
JSON에도 문맥 자유 문법이 필요한가요?
대체로 필요 없습니다. 영상은 OpenAI의 구조화 출력 API가 중첩을 다섯 단계까지만 지원하며, 중첩 깊이가 제한된 JSON은 정규식으로도 다룰 수 있다고 설명합니다. 프로그래밍 언어의 구문 트리처럼 깊은 중첩이 필요할 때 문맥 자유 문법이 등장합니다.
토큰과 문법 터미널이 어긋나면 왜 문제가 되나요?
상태 기계의 전이는 터미널 단위로 정의돼 있는데 모델은 터미널 경계를 무시한 토큰을 뱉기 때문입니다. 닫는 괄호와 다음 문자가 한 토큰으로 붙어 나오면 어느 상태로 옮겨야 할지 판정하기가 까다로워집니다.
원문과 출처
이 글은 원본 영상의 자막을 바탕으로 한국어 독자를 위해 요약했습니다. 전체 맥락과 최신 정보는 원문에서 확인하세요.
YouTube 원본 영상 보기 ↗