BAML과 Instructor: 구조화된 LLM 출력
BAML과 Instructor를 활용한 타입 안전한 LLM 출력
프로덕션 환경에서 대규모 언어 모델(LLM)을 사용할 때 구조화되고 타입 안전(structured, type-safe)한 출력을 얻는 것은 매우 중요합니다. 두 가지 인기 있는 프레임워크인 BAML과 Instructor는 이 문제를 해결하는 서로 다른 접근 방식을 취합니다.
이 비교 분석은 Python LLM 애플리케이션에 적합한 도구를 선택하는 데 도움이 됩니다.

구조화된 출력의 과제 이해하기
LLM은 본질적으로 비구조화된 텍스트를 생성하지만, 현대 애플리케이션은 예측 가능하고 파싱 가능한 데이터가 필요합니다. 채팅봇, 데이터 추출 파이프라인 또는 AI 에이전트를 구축하는 경우, 자유 형식의 응답이 아닌 JSON 객체, 검증된 데이터 타입 및 오류 처리 기능이 필요합니다.
BAML과 Instructor는 모두 이 과제를 해결하지만 근본적으로 다른 철학을 가지고 있습니다. BAML은 코드 생성을 통한 계약 우선(contract-first) 접근 방식을 사용하는 반면, Instructor는 Python의 타입 시스템을 활용한 런타임 검증(runtim validation)을 활용합니다. 다양한 LLM 제공업체 간의 구조화된 출력 접근 방식에 대한 더 넓은 맥락에 관심이 있다면, 이러한 프레임워크를 이해하는 것은 더욱 가치가 있습니다. 프레임워크 레이어 아래의 검증, 거부 처리(refusals) 및 pytest 픽스처에 대해서는 Python에서 견고하게 작동하는 LLM 구조화된 출력 검증을 참조하십시오.
BAML: LLM을 위한 도메인 특화 언어
BAML(BoundaryML의 언어)은 LLM 상호작용을 정의하기 위한 전용 도메인 특화 언어(DSL)를 도입합니다. 프롬프트, 타입 및 함수를 선언하는 .baml 파일을 작성하면 BAML이 Python을 포함한 여러 언어에 대한 타입 안전한 클라이언트 코드를 생성합니다.
BAML의 주요 기능
언어 간 타입 안전성: BAML은 동일한 .baml 정의에서 Python, TypeScript, Ruby 클라이언트를 생성하여 스택 전반의 일관성을 보장합니다.
프롬프트 버전 관리: 프롬프트는 .baml 파일에 저장되어 애플리케이션 코드와 독립적으로 추적, 검토 및 테스트하기 쉽게 합니다.
내장 테스트 프레임워크: BAML에는 배포 전 프롬프트 동작을 검증하여 개발 초기 단계에서 문제를 발견할 수 있는 테스트 도구가 포함되어 있습니다.
플레이그라운드 인터페이스: BAML 플레이그라운드를 사용하면 시각적으로 프롬프트를 반복하여 즉각적인 피드백을 받을 수 있으며, 이를 통해 개발 주기를 단축할 수 있습니다.
BAML 예제 구현
# 먼저 .baml 파일에서 스키마를 정의합니다:
# persona.baml
class Person {
name string
age int
occupation string
skills string[]
}
function ExtractPerson(text: string) -> Person {
client GPT4
prompt #"
Extract person information from: {{ text }}
Return structured data.
"#
}
생성된 Python 클라이언트는 타입 안전한 접근을 제공합니다:
from baml_client import b
from baml_client.types import Person
# 생성된 클라이언트 사용
text = "John Smith, 34, software engineer skilled in Python and Go"
result: Person = b.ExtractPerson(text)
print(f"{result.name} is {result.age} years old")
print(f"Skills: {', '.join(result.skills)}")
BAML의 접근 방식은 동일한 LLM 계약을 사용하는 여러 서비스가 있거나 언어 경계 전반에 걸쳐 데이터 형식에 대한 강력한 보장이 필요할 때 빛을 발합니다.
Instructor: Pydantic 네이티브 Python 프레임워크
Instructor는 Python 우선 접근 방식을 취하며, Pydantic 모델에 LLM 기능을 확장합니다. 이미 검증과 타입 힌트에 Pydantic을 사용하고 있는 Python 개발자에게는 자연스러운 느낌을 줍니다.
Instructor의 주요 기능
불필요한 코드(Boilerplate) 제로: Instructor는 간단한 데코레이터를 사용하여 기존 Pydantic 모델과 직접 작동합니다. 코드 생성 또는 빌드 단계가 필요하지 않습니다.
풍부한 검증: Pydantic의 전체 검증 생태계를 활용하십시오. 사용자 정의 검증기, 필드 제약, 계산 필드 및 복잡한 중첩 구조를 사용할 수 있습니다.
다중 제공업체 지원: 통합 인터페이스를 통해 OpenAI, Anthropic, Google, Ollama와 원활하게 작동합니다.
스트리밍 지원: 점진적인 Pydantic 모델 업데이트와 함께 스트리밍 응답에 대한 일류 클래스(first-class) 지원을 제공합니다.
재시도 로직: 지수 백오프(exponential backoff)와 검증기 기반 오류 복구를 갖춘 내장 재시도 메커니즘을 제공합니다.
Instructor 예제 구현
from pydantic import BaseModel, Field
from instructor import from_openai
from openai import OpenAI
# Pydantic 모델 정의
class Person(BaseModel):
name: str = Field(description="Full name of the person")
age: int = Field(ge=0, le=120, description="Age in years")
occupation: str
skills: list[str] = Field(description="List of professional skills")
# OpenAI 클라이언트 패치
client = from_openai(OpenAI())
# 구조화된 데이터 추출
text = "John Smith, 34, software engineer skilled in Python and Go"
result = client.chat.completions.create(
model="gpt-4",
response_model=Person,
messages=[
{"role": "user", "content": f"Extract person info: {text}"}
]
)
print(f"{result.name} is {result.age} years old")
print(f"Skills: {', '.join(result.skills)}")
Instructor의 강점은 단순함과 Python 생태계와의 통합에 있습니다. 이미 Pydantic을 사용하고 있다면 학습 곡선은 최소화됩니다. Python을 처음 접하거나 Python 특화 패턴에 대한 빠른 참조가 필요한 개발자를 위해, 당사의 Python 치트시트는 이러한 프레임워크와 함께 유용한 문법 안내를 제공합니다.
상세 비교: BAML vs Instructor
개발 경험
BAML은 추가적인 빌드 단계와 도구 설정이 필요합니다. .baml 파일을 작성하고 생성기를 실행한 다음 생성된 코드를 가져옵니다. 이는 프롬프트 엔지니어링과 애플리케이션 로직 사이에 명확한 분리를 생성하며, 이는 대규모 팀에 이점이 될 수 있습니다.
Instructor는 설정 마찰이 전혀 없습니다. pip install만으로 준비가 완료됩니다. 프롬프트는 코드와 함께 존재하므로 소규모 프로젝트나 프로토타입의 경우 빠른 반복이 더 쉽습니다.
타입 안전성 및 검증
BAML은 생성된 코드에서 컴파일 시간 타입 검사를 제공합니다. IDE는 아무것도 실행하기 전에 사용 가능한 필드가 무엇인지 정확히 알고 있습니다. 동일한 .baml 파일이 모든 지원 언어의 클라이언트를 생성하기 때문에 언어 간 일관성이 보장됩니다.
Instructor는 Pydantic을 통해 런타임 검증을 제공합니다. Python 타입 힌트가 IDE 지원을 제공하지만, 오류는 실행 중에 나타납니다. 이는 Python의 표준 방식이지만 BAML의 생성 코드만큼 정적 보장이 적다는 것을 의미합니다.
로컬 LLM과의 작업
두 프레임워크 모두 프라이버시, 비용 관리 및 오프라인 개발에 중요한 로컬 모델을 지원합니다. Ollama 또는 기타 로컬 LLM 제공업체를 사용할 때, 외부 API 의존성 없이 동일한 구조화된 출력의 혜택을 유지할 수 있습니다. Ollama, Qwen3 및 Python 또는 Go를 사용한 구조화된 출력으로 LLM 제약에 대한 심층 분석을 원한다면, 이러한 프레임워크는 하위 레벨 API 위에 프로덕션 준비된 추상화를 제공합니다.
BAML은 .baml 파일에서 클라이언트를 구성하여 Ollama에 연결합니다:
# .baml 파일에서:
client OllamaLocal {
provider ollama
options {
model "llama2"
base_url "http://localhost:11434"
}
}
Instructor는 OpenAI 호환 API를 통해 Ollama와 함께 작동합니다:
from openai import OpenAI
from instructor import from_openai
client = from_openai(OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # dummy key
))
로컬 모델로 작업할 때는 모든 모델이 구조화된 출력을 동일한 신뢰도로 처리하지 않기 때문에 Ollama 및 GPT-OSS 모델의 잠재적인 구조화된 출력 문제에 유의해야 합니다.
오류 처리 및 재시도
BAML은 구성 가능한 전략으로 프레임워크 수준에서 재시도를 처리합니다. 스키마 검증 오류는 오류 컨텍스트와 함께 자동 재프롬팅을 트리거합니다.
Instructor는 사용자 정의 동작을 위한 훅(hooks)을 갖춘 장식적(decorative) 재시도 로직을 제공합니다. 수정된 프롬프트로 재시도를 트리거하는 검증기를 정의할 수 있습니다:
from instructor import patch
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
def extract_with_retry(text: str) -> Person:
return client.chat.completions.create(
model="gpt-4",
response_model=Person,
messages=[{"role": "user", "content": text}]
)
테스트 및 관측성
BAML은 .baml 파일에서 직접 테스트 케이스를 작성하여 다양한 입력에 대한 프롬프트 동작을 검증할 수 있는 테스트 프레임워크를 포함합니다. 플레이그라운드는 시각적 디버깅을 제공합니다.
Instructor는 표준 Python 테스트 프레임워크와 통합됩니다. pytest 픽스처, 모킹 라이브러리 및 어설션 헬퍼를 Python 코드처럼 사용할 수 있습니다.
성능 고려 사항
런타임 성능은 비슷합니다. 두 프레임워크는 궁극적으로 동일한 LLM API 호출을 수행합니다. 검증 및 파싱의 오버헤드는 네트워크 지연 시간 및 모델 추론 시간에 비해 미미합니다.
개발 속도는 크게 다릅니다:
- BAML의 코드 생성은 더 나은 자동 완성 및 조기 오류 감지를 의미하지만 빌드 단계가 필요합니다.
- Instructor의 데코레이터 접근 방식은 더 빠른 이터레이션을 의미하지만 런타임 오류 발견이 필요합니다.
수백만 개의 요청을 처리하는 프로덕션 시스템의 경우, 두 프레임워크 모두 부하를 동일하게 처리합니다. 선택은 성능 특성보다 개발 워크플로우 선호도에 더 달려 있습니다.
BAML을 선택해야 할 때
다음이 필요할 때 BAML을 선택하십시오:
- 다중 언어 지원: Python, TypeScript, Ruby 서비스에서 동일한 LLM 계약에 접근
- 계약 우선 개발: 구현 전에 LLM 인터페이스가 설계되는 API 스타일 개발
- 팀 협업: 프롬프트 엔지니어링 워크플로우를 애플리케이션 개발과 분리
- 강력한 타이핑 보장: 전체 스택 전반의 컴파일 시간 검사
- 시각적 프롬프트 개발: 프롬프트에 대한 플레이그라운드 기반 반복
Instructor를 선택해야 할 때
다음과 같은 경우 Instructor를 선택하십시오:
- Python 전용 프로젝트: 언어 간 일관성이 필요하지 않음
- 빠른 프로토타이핑: 구조화된 출력을 작동시키기 위한 최소한의 설정
- Pydantic 통합: 기존 Pydantic 모델 및 검증기 활용
- 간단한 배포: 관리할 빌드 단계나 생성된 코드가 없음
- 풍부한 Python 생태계: Python 특화 라이브러리 및 패턴 사용
접근 방식 결합
일부 프로젝트는 두 프레임워크를 모두 사용함으로써 혜택을 얻습니다. 예를 들어, 언어 간 클라이언트가 필요한 고객 facing API에는 BAML을 사용하고, 빠른 이터레이션이 필요한 내부 Python 서비스에는 Instructor를 사용할 수 있습니다.
프로젝트가 성숙함에 따라 프레임워크 간 전환도 가능합니다. 빠른 검증을 위해 Instructor로 시작한 후, 더 넓은 언어 지원이나 엄격한 계약이 필요할 때 BAML로 전환할 수 있습니다.
실제 사례
데이터 추출 파이프라인 (BAML)
문서 처리 시스템은 BAML을 사용하여 송장, 계약서 및 영수증에서 구조화된 데이터를 추출합니다. .baml 정의는 ML 팀과 백엔드 서비스 간의 계약 역할을 하며, 웹 대시보드에는 TypeScript 클라이언트, 배치 처리에는 Python 클라이언트가 사용됩니다.
고객 지원 봇 (Instructor)
지원 봇은 Instructor를 사용하여 티켓을 분류하고, 사용자 의도를 추출하며, 응답을 생성합니다. 팀은 Pydantic 모델을 사용하여 프롬프트를 빠르게 반복하며, 검증기가 추출된 전화번호, 이메일 및 티켓 ID가 형식 요구 사항을 충족하는지 보장합니다.
멀티모달 AI 에이전트 (양쪽 모두)
AI 에이전트 시스템은 분산 시스템 전반의 타입 안전성을 보장하기 위해 핵심 에이전트 간 통신 계약에 BAML을 사용하며, 개별 에이전트는 사용자 입력의 유연하고 Python 네이티브한 처리를 위해 내부적으로 Instructor를 사용합니다. 유사한 패턴은 Python으로 MCP 서버 구축 시에도 적용되며, 여기서 구조화된 출력은 AI 어시스턴트와의 신뢰할 수 있는 도구 통합을 가능하게 합니다.
마이그레이션 및 통합 경로
이미 LLM과 기본 JSON 파싱을 사용하고 있다면, 두 프레임워크 모두 간단한 마이그레이션 경로를 제공합니다:
JSON에서 BAML로: JSON 스키마를 BAML 타입 정의로 변환하고, 프롬프트를 .baml 파일로 이동하고, 클라이언트를 생성한 다음 수동 파싱을 생성된 타입으로 대체합니다.
JSON에서 Instructor로: JSON 구조와 일치하는 Pydantic 모델을 추가하고, instructor를 설치하고, OpenAI 클라이언트를 패치한 다음 JSON 파싱을 response_model 매개변수로 대체합니다.
두 마이그레이션 모두 점진적으로 진행될 수 있으며, 코드베이스를 한 번에 완전히 변환할 필요는 없습니다.
미래 전망 및 커뮤니티
두 프레임워크 모두 활발히 개발 중이며 강력한 커뮤니티를 보유하고 있습니다:
BAML(BoundaryML)은 언어 지원 확대, 플레이그라운드 개선 및 테스트 기능 강화에 중점을 두고 있습니다. 상업적 뒷받침은 장기적인 안정성을 시사합니다.
Instructor는 잦은 업데이트, 광범위한 문서화 및 증가하는 채택률을 바탕으로 강력한 오픈소스 존재감을 유지하고 있습니다. 이 프로젝트는 Jason Liu와 기여자들에 의해 잘 관리되고 있습니다.
결론
BAML과 Instructor는 구조화된 LLM 출력을 위한 두 가지 우수하지만 독특한 접근 방식을 나타냅니다. BAML의 계약 우선, 다중 언어 철학은 엄격한 타입 요구사항을 가진 분산 시스템을 구축하는 팀에 적합합니다. Instructor의 Python 네이티브, Pydantic 기반 접근 방식은 빠른 개발 및 Python 중심 스택에 적합합니다.
어느 것이 절대적으로 우월한 것은 아니며, 선택은 팀의 규모, 언어 선호도, 개발 워크플로우 및 타입 안전성 요구 사항에 따라 달라집니다. 많은 팀은 프로토타이핑을 위해 Instructor로 시작한 후 프로덕션 다중 서비스 아키텍처를 위해 BAML로 채택하는 것이 양쪽의 장점을 모두 제공하는 최선의 방법임을 발견할 것입니다.
유용한 링크
이 사이트의 관련 글
- Ollama, Qwen3 및 Python 또는 Go를 사용한 구조화된 출력으로 LLM 제약
- 인기 있는 LLM 제공업체 간의 구조화된 출력 비교 - OpenAI, Gemini, Anthropic, Mistral 및 AWS Bedrock
- Ollama GPT-OSS 구조화된 출력 문제
- Python으로 MCP 서버 구축: 웹 검색 및 스크래핑
- Python 치트시트
- Ollama 치트시트