제약 생성과 문법 강제
제약 생성과 문법 강제
Guidance의 진짜 힘은 출력이 항상 형식 규칙을 지키도록 강제한다는 데 있어요. gen()에 정규식이나 문법을 엮으면, 모델이 그 규칙을 어기는 토큰을 아예 고르지 못해요. "거의 유효한 JSON"이 아니라 구조 면에서 보장된 출력이 필요할 때 Guidance를 떠올리면 돼요.
gen과 정규식
gen(regex=...)으로 필드를 정규식에 강제할 수 있어요. 예를 들어 주문 날짜를 YYYY-MM-DD 형식으로만 받고 싶다면:
from guidance import system, user, assistant, gen
from guidance.models import Transformers
lm = Transformers("microsoft/Phi-4-mini-instruct")
with system():
lm += "You are a structured data assistant."
with user():
lm += "Extract the order date from: 'Order shipped on 2025-03-14'."
with assistant():
lm += gen("date", regex=r"\d{4}-\d{2}-\d{2}")
print(lm["date"]) # 2025-03-14
정규식 기반 제약은 로컬 백엔드에서 가장 강력하게 동작해요. 원격 API 백엔드는 토큰 경계·로짓에 접근할 수 없어 기능 일부가 제한될 수 있어요.
출처: https://github.com/guidance-ai/guidance/blob/main/docs/api_examples.rst
문맥 자유 문법(CFG)
정규식보다 복잡한 구조 — 예를 들어 JSON 객체 안에 중첩 필드가 있는 형태 — 는 문맥 자유 문법으로 표현할 수 있어요. gen(regex=...)로 문법 직접 검증도 가능하고, 여러 문법 조각을 합성해 정교한 템플릿을 만들 수 있어요.
from guidance import gen
from guidance.models import Mock
grammar = "expr=" + gen(regex=r"\d+([+*]\d+)*", name="expr")
assert grammar.match("expr=12+7*3") is not None
assert grammar.match("expr=12+*3") is None
문법을 만들고 나면 로컬 mock 모델로 lm += grammar처럼 실행해 원하는 값이 나오는지 확인할 수 있어요.
상태 없는 문법(Stateless grammar)
@guidance(stateless=True)로 만들면 문법 조각이 순수하게 합성 가능한 상태가 되어 더 빠르게 처리돼요. gen(), select(), 문자열 리터럴, one_or_more() 같은 조각을 하나의 문맥 자유 문법으로 묶는 구조예요. 성능이 중요한 제약 생성 작업에서 이 방식이 선호돼요.
토큰 힐링(Token healing)
Guidance의 또 다른 효율 요소로 토큰 힐링이 있어요. 프롬프트가 토큰 중간에서 끝나면(예: URL의 http:), 순진한 생성은 깨진 출력을 내놓기 쉬워요. Guidance는 마지막 프롬프트 토큰으로 되돌아가 제약 아래에서 다시 생성해 경계 문제를 피해요. 이 최적화는 토큰 수준 접근이 가능한 로컬 백엔드(Transformers, llama.cpp)에서 완전히 동작해요.
더 알아보기
- 시작하기: Getting Started
- 튜토리얼: Tutorials
- 프롬프트 설계: Art of Prompt Design