LM Format Enforcer
LM Format Enforcer
LM Format Enforcer는 언어 모델의 출력 형식(JSON Schema, Regex 등)을 강제하는 라이브러리예요. 로컬 LLM의 출력 로짓을 처리해 구조화된 출력을 안정적으로 얻는 방법을 알아봅시다.
출처: 문서
본문
LM Format Enforcer는 언어 모델의 출력 형식(JSON Schema, Regex 등)을 강제하는 라이브러리입니다. 원하는 출력 구조를 LLM에게 "제안"만 하는 대신, LM Format Enforcer는 실제로 LLM 출력이 원하는 스키마를 따르도록 "강제"할 수 있습니다.

LM Format Enforcer는 로컬 LLM(현재 LlamaCPP와 HuggingfaceLLM 백엔드를 지원)과 함께 동작하며, LLM의 출력 로짓(logits)을 처리하는 방식으로만 동작합니다. 덕분에 생성 루프 자체를 수정하는 다른 솔루션과 달리 빔 서치(beam search)와 배칭(batching) 같은 고급 생성 방법을 지원할 수 있습니다. 자세한 비교표는 LM Format Enforcer 페이지에서 확인하세요.
JSON Schema 출력
LlamaIndex에서는 LM Format Enforcer와의 초기 통합을 제공해 구조화된 출력(더 구체적으로는 pydantic 객체)을 아주 쉽게 생성할 수 있게 합니다.
예를 들어 다음 스키마로 노래 앨범을 생성한다고 해 봅시다.
class Song(BaseModel):
title: str
length_seconds: int
class Album(BaseModel):
name: str
artist: str
songs: List[Song]
LMFormatEnforcerPydanticProgram을 만들고 원하는 pydantic 클래스 Album을 지정하고 적절한 프롬프트 템플릿을 제공하기만 하면 됩니다.
참고:
LMFormatEnforcerPydanticProgram은 프롬프트 템플릿의 선택적{json_schema}파라미터에 pydantic 클래스의 json 스키마를 자동으로 채워 넣습니다. 이는 LLM이 올바른 JSON을 자연스럽게 생성하도록 돕고 형식 강제기의 개입을 줄여 출력 품질을 높일 수 있습니다.
program = LMFormatEnforcerPydanticProgram(
output_cls=Album,
prompt_template_str="Generate an example album, with an artist and a list of songs. Using the movie {movie_name} as inspiration. You must answer according to the following schema: \n{json_schema}\n",
llm=LlamaCPP(),
verbose=True,
)
이제 추가 사용자 입력을 넣어 프로그램을 실행할 수 있습니다. 여기서는 으스스한 느낌으로 '샤이닝(The Shining)'에서 영감을 받은 앨범을 만들어 보겠습니다.
output = program(movie_name="The Shining")
pydantic 객체를 얻었습니다.
Album(
name="The Shining: A Musical Journey Through the Haunted Halls of the Overlook Hotel",
artist="The Shining Choir",
songs=[
Song(title="Redrum", length_seconds=300),
Song(
title="All Work and No Play Makes Jack a Dull Boy",
length_seconds=240,
),
Song(title="Heeeeere's Johnny!", length_seconds=180),
],
)
자세한 내용은 이 노트북으로 실습해 보세요.
정규표현식 출력
LM Format Enforcer는 regex 출력도 지원합니다. LlamaIndex에는 정규표현식에 대한 기존 추상화가 없으므로, LM Format Generator를 주입한 뒤 LLM을 직접 사용하겠습니다.
regex = r'"Hello, my name is (?P<name>[a-zA-Z]*)\. I was born in (?P<hometown>[a-zA-Z]*). Nice to meet you!"'
prompt = "Here is a way to present myself, if my name was John and I born in Boston: "
llm = LlamaCPP()
regex_parser = lmformatenforcer.RegexParser(regex)
lm_format_enforcer_fn = build_lm_format_enforcer_function(llm, regex_parser)
with activate_lm_format_enforcer(llm, lm_format_enforcer_fn):
output = llm.complete(prompt)
이렇게 하면 LLM이 우리가 지정한 정규표현식 형식으로 출력을 생성하게 됩니다. 출력을 파싱해 이름 붙은 그룹(named groups)을 얻을 수도 있습니다.
print(output)
# "Hello, my name is John. I was born in Boston, Nice to meet you!"
print(re.match(regex, output.text).groupdict())
# {'name': 'John', 'hometown': 'Boston'}
자세한 내용은 이 노트북을 참고하세요.