6.6 오픈소스 LLM 및 로컬 모델에서의 구조화 출력 구현
OpenAI나 Anthropic과 같은 상용 API 제공자(Provider)들은 네이티브 레벨에서 response_format을 지원하여 강력한 구조화 출력(Structured Outputs)을 보장한다. 그러나 보안, 컴플라이언스(Compliance), 혹은 비용 문제로 인해 Llama, Mistral, Qwen 등 오픈소스 대형 언어 모델(OSLLM)을 온프레미스(On-premise) 로컬 환경에 직접 배포하여 사용하는 경우, 동일한 수준의 데이터 정합성을 확보하는 것은 소프트웨어 엔지니어링 관점에서 까다로운 도전 과제다.
로컬 모델 자체가 본질적으로 JSON 스키마를 완벽히 이해하고 토큰을 생성하는 통제력을 갖춘 것은 아니기 때문에, 추론(Inference) 단계 전후로 강력한 제어 메커니즘을 개입시켜야 한다.
1. 프롬프트 엔지니어링의 정교화 (기본 접근)
상용 API가 제공하는 ‘강제화(Enforcement)’ 기능을 사용할 수 없다면, 우선적으로 모델이 스스로 스키마를 준수하도록 프롬프트의 밀도를 극대화해야 한다. 그러나 이는 통계적 확률을 높일 뿐 100% 결정론적 출력을 보장하지는 못한다.
- System Prompt 내 스키마 주입: 단순히 “JSON으로 줘“라고 요구하지 마라. 요구하는 Pydantic 모델이나 JSON 스키마 명세(Schema Definition) 전체를 시스템 프롬프트에 문자열로 주입하라.
- 퓨샷(Few-shot) 예제의 필수화: 입력과 이에 대응하는 완벽히 포맷팅된 JSON 출력 쌍(Pair)을 최소 2~3개 이상 제시하여 모델 스스로 출력 형태의 컨텍스트를 학습하도록 유도하라.
- 포맷팅 패널티 부여: “만약 JSON 형식을 어기거나 사족(Chatter)을 덧붙일 경우 시스템이 붕괴한다“는 식의 강력한 제약(Negative Constraint) 지시를 포함하라.
2. 외부 라이브러리를 통한 파싱 및 재시도 (클라이언트 레벨)
가장 보편적인 접근 방식은 모델이 다소 불완전한 텍스트를 생성하더라도, 애플리케이션 계층(Client-side)에서 이를 정규화하고 파싱하는 로직을 두텁게 구축하는 것이다.
- Instructor 라이브러리 활용: Python 생태계에서 Pydantic과 LLM을 결합하는 사실상의 표준(De facto)이다. 로컬 모델(예: Ollama 서버를 통한 호출)에 Instructor를 래핑(Wrapping)하면, 모델의 응답을 Pydantic 객체로 변환 시도한다.
- 자동 재시도(Auto-Retry) 루프: 파싱에 실패하여
ValidationError가 발생하면, 앞서 발생한 에러 메시지(예: “age필드는 integer여야 하는데 string이 들어왔음”)를 모델에게 피드백으로 다시 주면서 “이 스키마 에러를 고쳐서 다시 생성하라“고 재요청(Retry)하는 자기 수정(Self-correction) 파이프라인을 구축하라.
3. 제한적 디코딩 (Constrained Decoding) - 근본적 해결책
로컬 환경의 가장 큰 장점은 모델의 가중치나 생성 과정(Decoding Pipeline)에 직접 개입할 수 있다는 점이다. 텍스트 생성 매 단계에서 JSON 스키마에 어긋나는 토큰의 생성 확률을 강제로 0으로 만들어버리는 기술을 **제한적 디코딩(Constrained Decoding)**이라 부르며, 이를 통해 상용 API 수준의 100% 구조화 출력을 달성할 수 있다.
이를 구현하기 위해서는 로컬 모델 서빙 인프라에 다음과 같은 프레임워크를 연동해야 한다.
3.1 GBNF (Grammar-Based Normal Form) 기반 제어 (llama.cpp)
오픈소스 모델 경량화 구동의 핵심인 llama.cpp 생태계는 문법(Grammar) 기반의 출력을 지원한다.
개발자가 요구하는 JSON 스키마를 GBNF(Context-Free Grammar의 일종) 형식으로 변환하여 모델에 제공하면, 모델은 다음 토큰을 선택할 때 GBNF 문법 규칙에 허용된 토큰(예: 따옴표 다음에는 문자, 콜론 다음에는 값) 목록 집합 내에서만 샘플링을 수행한다.
3.2 Outlines, LMQL 등의 제약 엔진 프레임워크 활용
vLLM 같은 고성능 추론 서버를 사용할 경우, 제약 조건을 언어 모델의 디코딩 루프에 주입하는 특화된 라이브러리(예: Outlines)를 도입하라.
이러한 엔진들은 정규 표현식(Regex)이나 Pydantic 모델을 파싱하여, 모델의 어휘 사전(Vocabulary)에 대한 유한 상태 기계(Finite State Machine, FSM)를 구축한다. 모델이 비정상적인 토큰 확률을 높게 산출하더라도, FSM에서 거부된 토큰은 그 마스킹(Masking)되어 출력되지 못하므로 본질적인 스키마 정합성이 강제로 담보된다.