BAML vs Instructor : Sorties structurées des LLM

Sorties de LLM typées avec BAML et Instructor

Sommaire

Lors du travail avec des modèles de langage de grande taille (LLM) en production, obtenir des outputs structurés et sûrs de type est critique. Deux frameworks populaires - BAML et Instructor - adoptent des approches différentes pour résoudre ce problème.

Cette comparaison vous aide à choisir l’outil adapté à vos applications LLM en Python.

circular-flow

Comprendre les défis des outputs structurés

Les LLM génèrent naturellement du texte non structuré, mais les applications modernes ont besoin de données prévisibles et analysables. Que vous construisiez des chatbots, des pipelines d’extraction de données ou des agents IA, vous avez besoin d’objets JSON, de types de données validés et de gestion des erreurs — et non de réponses libres.

BAML et Instructor abordent tous deux ce défi, mais avec des philosophies fondamentalement différentes : BAML utilise une approche « contract-first » avec génération de code, tandis qu’Instructor exploite le système de types de Python avec validation à l’exécution. Si vous souhaitez un contexte plus large sur les approches d’output structuré chez différents fournisseurs LLM, la compréhension de ces frameworks devient encore plus précieuse. Pour la validation, les refus et les fixtures pytest sous toute couche de framework, consultez La validation des outputs structurés LLM en Python qui tient la route.

BAML : Langage dédié aux LLM

BAML (le langage de BoundaryML) introduit un DSL (langage spécifique au domaine) dédié pour définir les interactions avec les LLM. Vous écrivez des fichiers .baml qui déclarent vos prompts, types et fonctions, puis BAML génère un code client sûr de type pour plusieurs langages, dont Python.

Fonctionnalités clés de BAML

Sûreté de type translangage : BAML génère des clients pour Python, TypeScript et Ruby à partir des mêmes définitions .baml, garantissant la cohérence dans toute votre stack.

Contrôle de version des prompts : Vos prompts résident dans des fichiers .baml, ce qui facilite leur traçabilité, leur revue et leur test indépendamment du code applicatif.

Framework de test intégré : BAML inclut des outils de test pour valider le comportement des prompts avant le déploiement, permettant de détecter les problèmes tôt dans le cycle de développement.

Interface Playground : Le playground BAML vous permet d’itérer visuellement sur les prompts avec un retour immédiat, accélérant ainsi les cycles de développement.

Exemple d’implémentation BAML

# Définissez d'abord votre schéma dans un fichier .baml :
# persona.baml

class Person {
  name string
  age int
  occupation string
  skills string[]
}

function ExtractPerson(text: string) -> Person {
  client GPT4
  prompt #"
    Extraire les informations de la personne à partir de : {{ text }}
    Retourner des données structurées.
  "#
}

Le client Python généré offre un accès sûr de type :

from baml_client import b
from baml_client.types import Person

# Utiliser le client généré
text = "John Smith, 34, ingénieur logiciel expert en Python et Go"
result: Person = b.ExtractPerson(text)

print(f"{result.name} a {result.age} ans")
print(f"Compétences : {', '.join(result.skills)}")

L’approche de BAML brille lorsque vous avez plusieurs services consommant les mêmes contrats LLM ou lorsque vous avez besoin de garanties fortes sur les formes des données aux frontières des langages.

Instructor : Framework Python natif Pydantic

Instructor adopte une approche centrée sur Python, en étendant les modèles Pydantic avec des capacités LLM. Il semble naturel aux développeurs Python utilisant déjà Pydantic pour la validation et les indices de type.

Fonctionnalités clés d’Instructor

Zéro boilerplate : Instructor fonctionne directement avec vos modèles Pydantic existants grâce à des décorateurs simples. Aucune génération de code ni étape de build requise.

Validation riche : Exploitez tout l’écosystème de validation de Pydantic — validateurs personnalisés, contraintes de champs, champs calculés et structures imbriquées complexes.

Support multi-fournisseurs : Fonctionne harmonieusement avec OpenAI, Anthropic, Google et Ollama via une interface unifiée.

Support du streaming : Support de classe pour les réponses en streaming avec mises à jour incrémentales des modèles Pydantic.

Logique de réessaie : Mécanismes de réessaie intégrés avec backoff exponentiel et récupération d’erreurs basée sur les validateurs.

Exemple d’implémentation Instructor

from pydantic import BaseModel, Field
from instructor import from_openai
from openai import OpenAI

# Définir votre modèle Pydantic
class Person(BaseModel):
    name: str = Field(description="Nom complet de la personne")
    age: int = Field(ge=0, le=120, description="Âge en années")
    occupation: str
    skills: list[str] = Field(description="Liste des compétences professionnelles")

# Patch du client OpenAI
client = from_openai(OpenAI())

# Extraire des données structurées
text = "John Smith, 34, ingénieur logiciel expert en Python et Go"
result = client.chat.completions.create(
    model="gpt-4",
    response_model=Person,
    messages=[
        {"role": "user", "content": f"Extraire les infos de la personne : {text}"}
    ]
)

print(f"{result.name} a {result.age} ans")
print(f"Compétences : {', '.join(result.skills)}")

La force d’Instructor réside dans sa simplicité et son intégration avec l’écosystème Python. Si vous utilisez déjà Pydantic, la courbe d’apprentissage est minimale. Pour les développeurs nouveaux à Python ou ayant besoin d’une référence rapide pour les patterns spécifiques à Python, notre cheat sheet Python fournit des rappels de syntaxe utiles en complément de ces frameworks.

Comparaison détaillée : BAML vs Instructor

Expérience de développement

BAML nécessite une étape de build supplémentaire et une configuration d’outils. Vous écrivez des fichiers .baml, lancez le générateur, puis importez le code généré. Cela crée une séparation claire entre l’ingénierie des prompts et la logique applicative, ce qui peut être bénéfique pour les équipes plus larges.

Instructor n’offre aucune friction de configuration — un pip install et vous êtes prêt. Vos prompts résident à côté de votre code, facilitant l’itération rapide pour les petits projets ou les prototypes.

Sûreté de type et validation

BAML fournit la vérification de type à la compilation dans le code généré. Votre IDE sait exactement quels champs sont disponibles avant que vous ne lanciez quoi que ce soit. La cohérence translangage est garantie car le même fichier .baml génère des clients pour tous les langages supportés.

Instructor offre une validation à l’exécution via Pydantic. Bien que les indices de type Python offrent un support IDE, les erreurs apparaissent lors de l’exécution. C’est standard en Python, mais cela signifie moins de garanties statiques que le code généré par BAML.

Travail avec des LLM locaux

Les deux frameworks supportent les modèles locaux, ce qui est crucial pour la confidentialité, le contrôle des coûts et le développement hors ligne. Lors de l’utilisation d’Ollama ou d’autres fournisseurs LLM locaux, vous conservez les mêmes avantages d’output structuré sans dépendances API externes. Pour une exploration plus approfondie de la contrainte des LLM avec des outputs structurés en utilisant Ollama, Qwen3 et Python ou Go, ces frameworks fournissent des abstractions prêtes pour la production au-dessus des APIs de bas niveau.

BAML se connecte à Ollama en configurant le client dans votre fichier .baml :

# Dans votre fichier .baml :
client OllamaLocal {
  provider ollama
  options {
    model "llama2"
    base_url "http://localhost:11434"
  }
}

Instructor fonctionne avec Ollama via l’API compatible OpenAI :

from openai import OpenAI
from instructor import from_openai

client = from_openai(OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # clé factice
))

Notez qu’en travaillant avec des modèles locaux, vous devez être conscient des problèmes potentiels d’output structuré avec Ollama et les modèles GPT-OSS, car tous les modèles ne gèrent pas les outputs structurés avec la même fiabilité.

Gestion des erreurs et réessais

BAML gère les réessais au niveau du framework avec des stratégies configurables. Les erreurs de validation de schéma déclenchent un re-prompting automatique avec un contexte d’erreur.

Instructor fournit une logique de réessaie décorative avec des hooks pour des comportements personnalisés. Vous pouvez définir des validateurs qui déclenchent des réessais avec des prompts modifiés :

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}]
    )

Tests et observabilité

BAML inclut un framework de test où vous pouvez écrire des cas de test directement dans des fichiers .baml, validant le comportement des prompts sur différentes entrées. Le playground offre un débogage visuel.

Instructor s’intègre avec les frameworks de test Python standards. Vous pouvez utiliser des fixtures pytest, des bibliothèques de mocking et des aides d’assertion comme pour n’importe quel code Python.

Considérations de performance

La performance à l’exécution est comparable — les deux frameworks finissent par faire les mêmes appels API LLM. La surcharge pour la validation et l’analyse est négligeable par rapport à la latence réseau et au temps d’inférence du modèle.

La vitesse de développement diffère sensiblement :

  • La génération de code de BAML signifie un meilleur autocomplétion et une détection d’erreurs plus précoce, mais nécessite une étape de build.
  • L’approche par décorateurs d’Instructor signifie une itération plus rapide mais une découverte d’erreurs à l’exécution.

Pour les systèmes de production traitant des millions de requêtes, les deux frameworks gèrent la charge également bien. Votre choix dépend plus des préférences de workflow de développement que des caractéristiques de performance.

Quand choisir BAML

Choisissez BAML lorsque vous avez besoin :

  • Support multi-langage : Accéder aux mêmes contrats LLM depuis des services Python, TypeScript et Ruby.
  • Développement contract-first : Développement style API où les interfaces LLM sont conçues avant l’implémentation.
  • Collaboration d’équipe : Séparer les workflows d’ingénierie des prompts du développement applicatif.
  • Garanties de typage fort : Vérifications à la compilation dans toute votre stack.
  • Développement visuel de prompts : Itération sur les prompts pilotée par un playground.

Quand choisir Instructor

Choisissez Instructor lorsque vous voulez :

  • Projets uniquement Python : Pas besoin de cohérence translangage.
  • Prototypage rapide : Configuration minimale pour faire fonctionner les outputs structurés.
  • Intégration Pydantic : Exploiter des modèles et validateurs Pydantic existants.
  • Déploiement simple : Pas d’étapes de build ou de code généré à gérer.
  • Écosystème Python riche : Utiliser des bibliothèques et patterns spécifiques à Python.

Combinaison des approches

Certains projets bénéficient de l’utilisation des deux frameworks. Par exemple, vous pourriez utiliser BAML pour les APIs facing client nécessitant des clients translangages, tandis qu’Instructor est utilisé pour des services Python internes nécessitant une itération rapide.

Vous pouvez également transitionner entre les frameworks au fur et à mesure que votre projet mûrit — commencer avec Instructor pour une validation rapide, puis passer à BAML lorsque vous avez besoin d’un support plus large des langages ou de contrats plus stricts.

Cas d’utilisation réels

Pipeline d’extraction de données (BAML)

Un système de traitement de documents utilise BAML pour extraire des données structurées à partir de factures, contrats et reçus. Les définitions .baml servent de contrats entre l’équipe ML et les services backend, avec des clients TypeScript pour le tableau de bord web et des clients Python pour le traitement par lots.

Bot de support client (Instructor)

Un bot de support utilise Instructor pour classer les tickets, extraire les intentions des utilisateurs et générer des réponses. L’équipe itère rapidement sur les prompts en utilisant des modèles Pydantic, avec des validateurs garantissant que les numéros de téléphone, emails et IDs de tickets extraits respectent les exigences de format.

Agent IA multi-modal (Les deux)

Un système d’agent IA utilise BAML pour les contrats de communication agent-à-agent de base, garantissant la sûreté de type dans le système distribué, tandis que les agents individuels utilisent Instructor en interne pour un traitement flexible et natif Python des entrées utilisateur. Des patterns similaires s’appliquent lors de la construction de serveurs MCP en Python, où les outputs structurés permettent une intégration fiable des outils avec les assistants IA.

Chemins de migration et d’intégration

Si vous utilisez déjà une analyse JSON basique avec les LLM, les deux frameworks offrent des chemins de migration simples :

De JSON à BAML : Convertir vos schémas JSON en définitions de types BAML, déplacer les prompts dans des fichiers .baml, générer les clients et remplacer l’analyse manuelle par des types générés.

De JSON à Instructor : Ajouter des modèles Pydantic correspondant à votre structure JSON, installer instructor, patcher votre client OpenAI et remplacer l’analyse JSON par des paramètres response_model.

Les deux migrations peuvent être incrémentales — vous n’avez pas besoin de convertir toute votre base de code en une seule fois.

Perspectives futures et communauté

Les deux frameworks sont activement développés avec des communautés solides :

BAML (BoundaryML) se concentre sur l’expansion du support des langages, l’amélioration du playground et l’augmentation des capacités de test. Le soutien commercial suggère une stabilité à long terme.

Instructor maintient une présence open-source forte avec des mises à jour fréquentes, une documentation extensive et une adoption croissante. Le projet est bien maintenu par Jason Liu et les contributeurs.

Conclusion

BAML et Instructor représentent deux approches excellentes mais distinctes pour les outputs LLM structurés. La philosophie contract-first et multi-langage de BAML convient aux équipes construisant des systèmes distribués avec des exigences de type strictes. L’approche native Python et basée sur Pydantic d’Instructor s’adapte au développement rapide et aux stacks centrés sur Python.

Aucun n’est universellement meilleur — votre choix dépend de la taille de votre équipe, des préférences de langage, du workflow de développement et des exigences de sûreté de type. Beaucoup d’équipes trouveront que commencer avec Instructor pour le prototypage, puis adopter BAML pour les architectures multi-services en production, offre le meilleur des deux mondes.

Liens utiles

Articles connexes sur ce site

Références externes

S'abonner

Recevez de nouveaux articles sur les systèmes, l'infrastructure et l'ingénierie IA.