LLM에게 “반드시 JSON으로 답해”라고 프롬프트를 줘도 따옴표가 빠지거나 설명 문장이 섞일 수 있습니다. 파싱 실패를 재시도 로직으로만 막으면 지연시간과 비용이 늘고, 에이전트·업무 자동화에서는 잘못된 필드 하나가 다음 시스템까지 영향을 줍니다.
vLLM의 Structured Outputs는 생성 가능한 다음 토큰을 JSON Schema·정규식·문법 규칙에 맞게 제한합니다. 프롬프트로 형식을 부탁하는 방식보다 강하지만, 구조가 맞는 것과 내용이 사실인 것은 별개입니다. 스키마 검증과 업무 규칙 검증은 반드시 분리해야 합니다.
핵심 요약
- JSON Schema는 객체·배열·필수 필드·자료형을 강제할 때 사용합니다.
- choice는 허용 값이 몇 개뿐일 때, regex는 짧은 문자열 패턴에 적합합니다.
- grammar는 SQL 일부 문법이나 DSL처럼 계층적 규칙이 필요할 때 사용합니다.
- 최근 vLLM에서는 structured_outputs를 사용하며 과거 guided_* 필드는 제거·변경될 수 있습니다.
- 제약 디코딩은 구문을 보장하지만 의미·사실성·권한까지 보장하지 않습니다.
1. 프롬프트 JSON과 무엇이 다른가
방법장점한계
| 프롬프트 지시 | 쉽고 어떤 서버에서도 사용 | 설명 문장, 누락, 잘못된 따옴표 가능 |
| 출력 후 파싱·수정 | 기존 시스템에 추가하기 쉬움 | 모호한 자동 수정과 재시도 비용 |
| Structured Outputs | 생성 단계에서 허용 토큰 제한 | 백엔드 지원·스키마 복잡도·초기 비용 |
제약 디코딩은 각 단계에서 현재 상태에 허용되는 토큰만 마스킹해 샘플링합니다. 그래서 닫는 괄호나 필수 키 같은 구문 오류를 예방할 수 있습니다.
2. JSON Schema와 Pydantic 예제
OpenAI 호환 API를 사용할 때는 Pydantic 모델에서 JSON Schema를 만들고 response_format에 전달하는 패턴이 관리하기 쉽습니다.
from enum import Enum
from pydantic import BaseModel, Field
from openai import OpenAI
class Sentiment(str, Enum):
positive = "positive"
neutral = "neutral"
negative = "negative"
class ReviewResult(BaseModel):
sentiment: Sentiment
score: float = Field(ge=0, le=1)
keywords: list[str]
client = OpenAI(base_url="http://localhost:8000/v1", api_key="local")
response = client.chat.completions.create(
model="YOUR_MODEL",
messages=[{"role": "user", "content": "이 리뷰를 분석해줘: ..."}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "review_result",
"schema": ReviewResult.model_json_schema(),
},
},
)
result = ReviewResult.model_validate_json(response.choices[0].message.content)
서버가 형식을 제한했더라도 클라이언트에서 다시 Pydantic 검증을 수행합니다. API 버전, 모델, 백엔드별 지원 범위가 다를 수 있으므로 운영 환경의 vLLM 문서와 스키마 호환성을 확인하세요.
3. JSON Schema 설계 원칙
- 필드를 최소화합니다. 한 번의 호출에 모든 업무 데이터를 넣으면 스키마와 프롬프트가 함께 복잡해집니다.
- 열거형을 적극 사용합니다. 자유 텍스트 대신 상태 코드를 제한하면 후속 분기가 단순해집니다.
- 필수와 선택을 명확히 합니다. 값이 없을 때 null인지 필드 생략인지 정합니다.
- 범위를 지정합니다. 점수는 0~1, 개수는 양수처럼 가능한 제약을 모델에 표현합니다.
- 중첩을 얕게 유지합니다. 너무 깊은 객체와 거대한 enum은 컴파일·생성 비용을 늘릴 수 있습니다.
4. choice·regex·grammar는 언제 쓰나
choice
approve, review, reject처럼 출력 전체가 제한된 후보 중 하나라면 가장 단순합니다. JSON 객체가 필요 없는 라우팅 작업에 적합합니다.
regex
날짜, 사내 티켓 번호, 제한된 식별자처럼 짧고 평평한 패턴에 유용합니다. 복잡한 JSON을 정규식 하나로 표현하려고 하면 유지보수가 어려워집니다.
티켓 예시: INC-[0-9]{6}
날짜 예시: 20[0-9]{2}-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])
grammar
CFG나 문법 표현을 지원하는 백엔드를 사용하면 SQL의 제한된 하위 집합, 수식, 도메인 전용 언어처럼 계층 구조를 강제할 수 있습니다. 다만 문법이 허용한 SQL이라도 실행 권한과 안전성이 보장되는 것은 아닙니다.
5. Structured Outputs가 막지 못하는 오류
- 존재하지 않는 고객 ID를 형식에 맞게 생성하는 의미 오류
- 숫자 범위 안이지만 업무상 잘못된 점수
- 문법적으로 유효하지만 권한을 벗어난 SQL·도구 호출
- 프롬프트 인젝션으로 잘못된 의도를 수행하는 문제
- 스키마 버전이 소비자 서비스와 맞지 않는 계약 오류
따라서 파이프라인은 구문 제약 → Pydantic 검증 → 업무 규칙 검증 → 권한 확인 → 실행으로 구성합니다. 생성된 SQL이나 셸 명령을 검증 없이 직접 실행해서는 안 됩니다.
6. 성능과 운영 체크리스트
- 첫 요청의 문법·스키마 컴파일 비용과 이후 캐시된 요청을 나눠 측정합니다.
- 복잡한 스키마와 단순한 스키마의 TTFT·TPOT를 비교합니다.
- 구문 실패율뿐 아니라 의미 검증 실패율과 재시도율을 기록합니다.
- 스키마에 버전을 부여하고 생산자·소비자 호환성 테스트를 둡니다.
- 모델별로 enum 준수, 필드 채움, 긴 배열 생성을 회귀 테스트합니다.
- vLLM 업그레이드 전 guided_* 사용 여부와 백엔드 변경을 확인합니다.
공식 자료
정리: Structured Outputs의 목표는 “보기 좋은 JSON”이 아니라 시스템 간 계약을 생성 단계부터 지키는 것입니다. 가장 작은 스키마로 시작하고, 의미와 권한은 애플리케이션 계층에서 다시 검증하면 안정적인 LLM 자동화 파이프라인을 만들 수 있습니다.
'AI·LLM 엔지니어링' 카테고리의 다른 글
| RAG Hybrid Search 실전 가이드 — Dense·BM25·RRF로 검색 정확도 높이기 (0) | 2026.07.26 |
|---|---|
| vLLM Speculative Decoding 실전 가이드 — Draft Model·N-gram·EAGLE 선택법 (0) | 2026.07.26 |
| 임베딩 모델 선택과 평가 — Cosine·Recall@k·MRR·nDCG 실전 기준 (0) | 2026.07.26 |
| vLLM KV Cache 완전정리 — 메모리 계산·Prefix Caching·FP8 튜닝법 (2) | 2026.07.26 |
| LLM 양자화 완전정리 — FP16·INT8·INT4, GGUF·AWQ·GPTQ 선택법 (0) | 2026.07.26 |