BAML vs Instructor: strukturyzowane wyjścia LLM
Bezpieczne typowo wyjścia LLM z BAML i Instructor
Pracując z dużymi modelami językowymi (LLM) w środowisku produkcyjnym, kluczowe jest uzyskiwanie ustrukturyzowanych wyjść bezpiecznych typowo.
Dwa popularne frameworki – BAML i Instructor – stosują różne podejścia do rozwiązania tego problemu.
To porównanie pomoże Ci wybrać odpowiednie narzędzie do Twoich aplikacji Pythonowych opartych na LLM.

Zrozumienie wyzwań związanych z ustrukturyzowanymi wyjściami
Modele LLM naturalnie generują niestrukturyzowany tekst, ale nowoczesne aplikacje wymagają przewidywalnych, możliwych do rozparsowania danych. Niezależnie od tego, czy budujesz czatboty, potoki ekstrakcji danych, czy agentów AI, potrzebujesz obiektów JSON, zwalidowanych typów danych i obsługi błędów – a nie swobodnych odpowiedzi.
Oba frameworki – BAML i Instructor – rozwiązują to wyzwanie, ale z fundamentalnie różnymi filozofiami: BAML wykorzystuje podejście „contract-first” (umowa najpierw) z generowaniem kodu, podczas gdy Instructor wykorzystuje system typów Pythona ze zwalidowaniem w czasie wykonania. Jeśli interesuje Cię szerszy kontekst dotyczący podejść do ustrukturyzowanych wyjść w różnych dostawcach LLM, zrozumienie tych frameworków staje się tym bardziej cenne. W kwestii walidacji, odrzuceń oraz fixture’ów pytest poniżej warstw frameworkowych, zobacz artykuł Walidacja ustrukturyzowanych wyjść LLM w Pythonie, która się sprawdza.
BAML: Język specyficzny dla domeny (DSL) dla LLM
BAML (język BoundaryML) wprowadza dedykowany DSL do definiowania interakcji z LLM. Tworzysz pliki .baml, które deklarują Twoje prompty, typy i funkcje, a następnie BAML generuje bezpieczne typowo kod klienta dla wielu języków, w tym Pythona.
Kluczowe cechy BAML
Bezpieczeństwo typów w wielu językach: BAML generuje klienty dla Pythona, TypeScript i Ruby z tych samych definicji .baml, zapewniając spójność w całym stosie technologicznym.
Kontrola wersji promptów: Twoje prompty znajdują się w plikach .baml, co ułatwia ich śledzenie, przeglądanie i testowanie niezależnie od kodu aplikacji.
Wbudowany framework testowy: BAML zawiera narzędzia testowe do walidacji zachowań promptów przed wdrożeniem, pozwalając wychwycić problemy wczesnie w procesie deweloperskim.
Interfejs Playground: Playground BAML pozwala na wizualną iterację promptów z natychmiastową informacją zwrotną, przyspieszając cykle deweloperskie.
Przykładowa implementacja BAML
# Najpierw zdefiniuj schemat w pliku .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.
"#
}
Wygenerowany klient Pythona zapewnia bezpieczny typowo dostęp:
from baml_client import b
from baml_client.types import Person
# Użyj wygenerowanego klienta
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)}")
Podejście BAML błyszczy, gdy masz wiele usług korzystających z tych samych kontraktów LLM lub gdy potrzebujesz silnych gwarancji dotyczących kształtów danych na granicach języków.
Instructor: Framework Pythona natywnie oparty na Pydantic
Instructor stosuje podejście „Python-first”, rozszerzając modele Pydantic o możliwości LLM. Wygląda to naturalnie dla programistów Pythona już używających Pydantic do walidacji i podpowiedzi typów.
Kluczowe cechy Instructor
Brak kodu boilerplate: Instructor współpracuje bezpośrednio z istniejącymi modelami Pydantic przy użyciu prostych dekoratorów. Nie wymagane jest generowanie kodu ani kroki budowania.
Bogata walidacja: Wykorzystaj cały ekosystem walidacji Pydantic – niestandardowe walidatory, ograniczenia pól, pola obliczane i złożone, zagnieżdżone struktury.
Wsparcie dla wielu dostawców: Bezproblemowa współpraca z OpenAI, Anthropic, Google i Ollama poprzez zjednoczony interfejs.
Wsparcie dla strumieniowania: Pierwszoklasowe wsparcie dla odpowiedzi strumieniowych z przyrostowymi aktualizacjami modeli Pydantic.
Logika ponawiania prób: Wbudowane mechanizmy ponawiania prób z wykładniczym cofaniem (exponential backoff) i odzyskiwaniem błędów opartym na walidatorach.
Przykładowa implementacja Instructor
from pydantic import BaseModel, Field
from instructor import from_openai
from openai import OpenAI
# Zdefiniuj swój model 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")
# Patchuj klienta OpenAI
client = from_openai(OpenAI())
# Ekstrahuje ustrukturyzowane dane
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)}")
Siłą Instruktora jest jego prostota i integracja z ekosystemem Pythona. Jeśli już używasz Pydantic, krzywa uczenia się jest minimalna. Dla programistów nowych w Pythonie lub potrzebujących szybkiego odniesienia do specyficznych dla Pythona wzorców, nasz Python cheatsheet dostarcza pomocnych przypomnień składniowych obok tych frameworków.
Szczegółowe porównanie: BAML vs Instructor
Doświadczenie deweloperskie
BAML wymaga dodatkowego kroku budowania i konfiguracji narzędzi. Piszesz pliki .baml, uruchamiasz generator, a następnie importujesz wygenerowany kod. Tworzy to wyraźne oddzielenie inżynierii promptów od logiki aplikacji, co może być korzystne dla większych zespołów.
Instructor ma zerową barьерę wejścia – wystarczy pip install i jesteś gotowy. Twoje prompty znajdują się obok kodu, co ułatwia szybką iterację w mniejszych projektach lub prototypach.
Bezpieczeństwo typów i walidacja
BAML zapewnia sprawdzanie typów w czasie kompilacji w wygenerowanym kodzie. Twój IDE zna dokładnie dostępne pola przed uruchomieniem czegokolwiek. Spójność międzyjęzykowa jest gwarantowana, ponieważ ten sam plik .baml generuje klienty dla wszystkich obsługiwanych języków.
Instructor oferuje walidację w czasie wykonania poprzez Pydantic. Podczas gdy podpowiedzi typów Pythona zapewniają wsparcie IDE, błęły pojawiają się podczas wykonania. Jest to standard dla Pythona, ale oznacza mniejsze statyczne gwarancje niż w wygenerowanym kodzie BAML.
Praca z lokalnymi LLM
Oba frameworki obsługują lokalne modele, co jest kluczowe dla prywatności, kontroli kosztów i dewelopmentu offline. Korzystając z Ollama lub innych lokalnych dostawców LLM, zachowujesz te same korzyści z ustrukturyzowanych wyjść bez zależności od zewnętrznych API. Aby głębiej zanurzyć się w ograniczaniu LLM ustrukturyzowanym wyjściem z użyciem Ollama, Qwen3 i Pythona lub Go, te frameworki dostarczają abstrakcji gotowych do produkcji nad niższymi warstwami API.
BAML łączy się z Ollama poprzez konfigurację klienta w pliku .baml:
# W Twoim pliku .baml:
client OllamaLocal {
provider ollama
options {
model "llama2"
base_url "http://localhost:11434"
}
}
Instructor współpracuje z Ollama poprzez kompatybilne z OpenAI API:
from openai import OpenAI
from instructor import from_openai
client = from_openai(OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # dummy key
))
Należy pamiętać, że przy pracy z lokalnymi modelami, warto być świadomym potencjalnych problemów z ustrukturyzowanym wyjściem w Ollama i modelach GPT-OSS, ponieważ nie wszystkie modele obsługują ustrukturyzowane wyjścia z równą niezawodnością.
Obsługa błędów i ponawianie prób
BAML obsługuje ponawianie prób na poziomie frameworku z konfigurowalnymi strategiami. Błędy w walidacji schematu wywołują automatyczne ponowne promptowanie z kontekstem błędu.
Instructor dostarcza dekoratorową logikę ponawiania prób z haczykami do niestandardowego zachowania. Możesz zdefiniować walidatory, które wywołują ponowne próby z zmodyfikowanymi promptami:
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}]
)
Testowanie i obserwowalność
BAML zawiera framework testowy, gdzie możesz pisać przypadki testowe bezpośrednio w plikach .baml, walidując zachowanie promptów dla różnych wejść. Playground dostarcza wizualnego debugowania.
Instructor integruje się ze standardowymi frameworkami testowymi Pythona. Możesz używać fixture’ów pytest, bibliotek mockujących i pomocników asercji tak jak w dowolnym kodzie Pythona.
Rozważania dotyczące wydajności
Wydajność w czasie wykonania jest porównywalna – oba frameworki ostatecznie wykonują te same wywołania API LLM. Nadmiarowość związana z walidacją i parsowaniem jest znikoma w porównaniu do opóźnień sieciowych i czasu wnioskowania modelu.
Szybkość deweloperska różni się znacząco:
- Generowanie kodu w BAML oznacza lepsze autocomplete i wcześniejsze wykrywanie błędów, ale wymaga kroku budowania
- Podejście dekoratorskie Instruktora oznacza szybszą iterację, ale wykrywanie błędów w czasie wykonania
W systemach produkcyjnych przetwarzających miliony żądań, oba frameworki radzą sobie z obciążeniem równie dobrze. Twój wybór zależy bardziej od preferencji dotyczących przepływu pracy deweloperskiej niż od charakterystyk wydajnościowych.
Kiedy wybrać BAML
Wybierz BAML, gdy potrzebujesz:
- Wsparcia wielojęzykowego: Dostępu do tych samych kontraktów LLM z usług Python, TypeScript i Ruby
- Dewelopmentu contract-first: Dewelopmentu w stylu API, gdzie interfejsy LLM są projektowane przed implementacją
- Współpracy zespołowej: Oddzielenia przepływów pracy inżynierii promptów od dewelopmentu aplikacji
- Silnych gwarancji typowania: Sprawdzania w czasie kompilacji w całym stosie
- Wizualnego dewelopmentu promptów: Iteracji na promptach napędzanej przez Playground
Kiedy wybrać Instructor
Wybierz Instructor, gdy chcesz:
- Projektów tylko w Pythonie: Brak potrzeby spójności międzyjęzykowej
- Szybkiego prototypowania: Minimalnej konfiguracji, aby uzyskać działające ustrukturyzowane wyjścia
- Integracji z Pydantic: Wykorzystania istniejących modeli i walidatorów Pydantic
- Prostego wdrożenia: Braku kroków budowania lub wygenerowanego kodu do zarządzania
- Bogatego ekosystemu Pythona: Używania specyficznych dla Pythona bibliotek i wzorców
Łączenie podejść
Niektóre projekty mogą korzystać z obu frameworków. Na przykład, możesz używać BAML do API skierowanych do klienta, które potrzebują klientów wielojęzykowych, podczas gdy używasz Instruktora do wewnętrznych usług Pythona, które potrzebują szybkiej iteracji.
Możesz również przechodzić między frameworkami w miarę dojrzewania projektu – zaczynając od Instruktora do szybkiej walidacji, a następnie przechodząc do BAML, gdy potrzebujesz szerszego wsparcia językowego lub ścislejszych kontraktów.
Przykłady zastosowań w świecie rzeczywistym
Potok ekstrakcji danych (BAML)
System przetwarzania dokumentów używa BAML do ekstrakcji ustrukturyzowanych danych z faktur, umów i paragonów. Definicje .baml pełnią rolę kontraktów między zespołem ML a usługami backendowymi, z klientami TypeScript dla dashboardu webowego i klientami Python dla przetwarzania wsadowego.
Bot obsługi klienta (Instructor)
Bot wsparcia używa Instruktora do klasyfikacji zgłoszeń, ekstrakcji intencji użytkowników i generowania odpowiedzi. Zespół szybko iteruje na promptach używając modeli Pydantic, z walidatorami zapewniającymi, że wyekstrahowane numery telefonów, adresy e-mail i ID zgłoszeń spełniają wymagania formatowe.
Multimodalny agent AI (Oba)
System agentów AI używa BAML do podstawowych kontraktów komunikacji agent-to-agent, zapewniając bezpieczeństwo typów w rozproszonej systemie, podczas gdy indywidualne agenty używają Instruktora wewnętrznie do elastycznego, natywnego dla Pythona przetwarzania wejść użytkownika. Podobne wzorce stosuje się przy budowaniu serwerów MCP w Pythonie, gdzie ustrukturyzowane wyjścia umożliwiają niezawodną integrację narzędzi z asystentami AI.
Ścieżki migracji i integracji
Jeśli już używasz podstawowego parsowania JSON z LLM, oba frameworki oferują proste ścieżki migracji:
Z JSON do BAML: Konwertuj swoje schematy JSON do definicji typów BAML, przenieś prompty do plików .baml, wygeneruj klienty i zastąp ręczne parsowanie wygenerowanymi typami.
Z JSON do Instructor: Dodaj modele Pydantic pasujące do struktury JSON, zainstaluj instructor, patchuj klienta OpenAI i zastąp parsowanie JSON parametrami response_model.
Obie migracje mogą być inkrementalne – nie musisz konwertować całego kodu na raz.
Perspektywy przyszłe i społeczność
Oba frameworki są aktywnie rozwijane z silnymi społecznościami:
BAML (BoundaryML) koncentruje się na rozszerzaniu wsparcia językowego, poprawie playgroundu i ulepszaniu możliwości testowania. Komercyjne wsparcie sugeruje długoterminową stabilność.
Instructor utrzymuje silną obecność open-source z częstymi aktualizacjami, obszerną dokumentacją i rosnącą adopcją. Projekt jest dobrze maintained przez Jasona Liu i współtwórców.
Podsumowanie
BAML i Instructor reprezentują dwa doskonałe, ale odmienne podejścia do ustrukturyzowanych wyjść LLM. Filozofia BAML „contract-first” i wielojęzykowa pasuje do zespołów budujących rozproszone systemy ze ścisłymi wymaganiami typowymi. Natywnie dla Pythona, oparte na Pydantic podejście Instruktora pasuje do szybkiego dewelopmentu i stosów skupionych na Pythonie.
Żaden z nich nie jest uniwersalnie lepszy – Twój wybór zależy od wielkości zespołu, preferencji językowych, przepływu pracy deweloperskiej i wymagań dotyczących bezpieczeństwa typów. Wiele zespołów znajdzie, że zaczynając z Instruktorem do prototypowania, a następnie adoptując BAML do produkcyjnych architektur wieloserviceowych, oferuje to, co najlepsze z obu światów.
Przydatne linki
Powiązane artykuły na tej stronie
- Ograniczanie LLM ustrukturyzowanym wyjściem: Ollama, Qwen3 & Python lub Go
- Porównanie ustrukturyzowanych wyjść w popularnych dostawcach LLM - OpenAI, Gemini, Anthropic, Mistral i AWS Bedrock
- Problemy z ustrukturyzowanym wyjściem Ollama GPT-OSS
- Budowanie serwerów MCP w Pythonie: WebSearch & Scrape
- Python Cheatsheet
- Ollama Cheatsheet