BAML vs Instructor: Salidas de LLM estructuradas

Salida de LLM tipo segura con BAML e Instructor

Índice

Cuando se trabaja con Modelos de Lenguaje Grande en producción, obtener salidas estructuradas y seguras en cuanto al tipo es crítico.
Dos marcos populares — BAML y Instructor — toman enfoques diferentes para resolver este problema.

Esta comparación te ayuda a elegir la herramienta adecuada para tus aplicaciones de LLM en Python.

circular-flow

Entendiendo los desafíos de salidas estructuradas

Los LLM generan naturalmente texto no estructurado, pero las aplicaciones modernas necesitan datos predecibles y analizables. Ya sea que estés construyendo chatbots, pipelines de extracción de datos o agentes de IA, necesitas objetos JSON, tipos de datos validados y manejo de errores — no respuestas libres de formato.

Tanto BAML como Instructor abordan este desafío, pero con filosofías fundamentalmente diferentes: BAML utiliza un enfoque basado en contrato con generación de código, mientras que Instructor aprovecha el sistema de tipos de Python con validación en tiempo de ejecución. Si te interesa un contexto más amplio sobre enfoques de salida estructurada en diferentes proveedores de LLM, entender estos marcos se vuelve aún más valioso.

BAML: Lenguaje de dominio específico para LLM

BAML (BoundaryML’s language) introduce un DSL dedicado para definir interacciones con LLM. Escribe archivos .baml que declaran tus prompts, tipos y funciones, luego BAML genera código cliente seguro en cuanto al tipo para varios lenguajes, incluido Python.

Características clave de BAML

Seguridad de tipo a través de lenguajes: BAML genera clientes para Python, TypeScript y Ruby desde las mismas definiciones .baml, asegurando coherencia en toda tu pila.

Control de versiones para prompts: Tus prompts viven en archivos .baml, lo que los hace fáciles de rastrear, revisar y probar independientemente del código de la aplicación.

Marco de prueba integrado: BAML incluye herramientas de prueba para validar el comportamiento de los prompts antes del despliegue, detectando problemas temprano en el desarrollo.

Interfaz de juego (playground): La interfaz de juego de BAML te permite iterar sobre los prompts visualmente con retroalimentación inmediata, acelerando los ciclos de desarrollo.

Implementación de ejemplo de BAML

# Primero, define tu esquema en un archivo .baml:
# persona.baml

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

function ExtractPerson(text: string) -> Person {
  client GPT4
  prompt #"
    Extrae información de persona desde: {{ text }}
    Devuelve datos estructurados.
  "#
}

El cliente generado en Python proporciona acceso seguro en cuanto al tipo:

from baml_client import b
from baml_client.types import Person

# Usa el cliente generado
text = "John Smith, 34, software engineer skilled in Python and Go"
result: Person = b.ExtractPerson(text)

print(f"{result.name} tiene {result.age} años")
print(f"Habilidades: {', '.join(result.skills)}")

El enfoque de BAML brilla cuando tienes múltiples servicios consumiendo los mismos contratos de LLM o cuando necesitas garantías fuertes sobre formas de datos a través de límites de lenguaje.

Instructor: Marco nativo de Python

Instructor toma un enfoque centrado en Python, extendiendo modelos Pydantic con capacidades de LLM. Siente natural para desarrolladores de Python que ya usan Pydantic para validación y sugerencias de tipo.

Características clave de Instructor

Cero boilerplate: Instructor funciona directamente con tus modelos Pydantic existentes usando decoradores simples. No se requiere generación de código ni pasos de construcción.

Validación rica: Aprovecha toda la ecosistema de validación de Pydantic — validadores personalizados, restricciones de campo, campos calculados y estructuras anidadas complejas.

Soporte para múltiples proveedores: Funciona de forma fluida con OpenAI, Anthropic, Google y Ollama a través de una interfaz unificada.

Soporte para streaming: Soporte de primera clase para respuestas en streaming con actualizaciones incrementales de modelos Pydantic.

Lógica de reintento: Mecanismos de reintento integrados con retroceso exponencial y recuperación de errores basada en validadores.

Implementación de ejemplo de Instructor

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

# Define tu modelo Pydantic
class Person(BaseModel):
    name: str = Field(description="Nombre completo de la persona")
    age: int = Field(ge=0, le=120, description="Edad en años")
    occupation: str
    skills: list[str] = Field(description="Lista de habilidades profesionales")

# Parchea el cliente de OpenAI
client = from_openai(OpenAI())

# Extrae datos estructurados
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"Extrae información de persona: {text}"}
    ]
)

print(f"{result.name} tiene {result.age} años")
print(f"Habilidades: {', '.join(result.skills)}")

La fortaleza de Instructor radica en su simplicidad e integración con el ecosistema de Python. Si ya estás usando Pydantic, la curva de aprendizaje es mínima. Para desarrolladores nuevos en Python o que necesiten referencias rápidas para patrones específicos de Python, nuestro cheatsheet de Python proporciona recordatorios útiles de sintaxis junto a estos marcos.

Comparación detallada: BAML vs Instructor

Experiencia de desarrollo

BAML requiere un paso adicional de construcción y configuración de herramientas. Escribe archivos .baml, ejecuta el generador y luego importa el código generado. Esto crea una separación clara entre ingeniería de prompts y lógica de aplicación, lo cual puede ser beneficioso para equipos más grandes.

Instructor tiene cero fricción de configuración — instala con pip y ya estás listo. Tus prompts viven junto a tu código, lo que facilita iteraciones rápidas para proyectos pequeños o prototipos.

Seguridad de tipo y validación

BAML proporciona verificación de tipo en tiempo de compilación en el código generado. Tu IDE conoce exactamente qué campos están disponibles antes de ejecutar algo. La consistencia entre lenguajes está garantizada ya que el mismo archivo .baml genera clientes para todos los lenguajes compatibles.

Instructor ofrece validación en tiempo de ejecución a través de Pydantic. Aunque las sugerencias de tipo de Python proporcionan soporte del IDE, los errores aparecen durante la ejecución. Esto es estándar para Python, pero significa menos garantía estática que el código generado de BAML.

Trabajo con modelos locales de LLM

Ambos marcos admiten modelos locales, lo cual es crucial para privacidad, control de costos y desarrollo offline. Cuando usas Ollama u otros proveedores de modelos locales de LLM, mantienes los mismos beneficios de salida estructurada sin depender de APIs externas. Para un análisis más profundo sobre restringir LLMs con salida estructurada usando Ollama, Qwen3 y Python o Go, estos marcos proporcionan abstracciones listas para producción sobre las APIs de nivel inferior.

BAML se conecta a Ollama configurando el cliente en tu archivo .baml:

# En tu archivo .baml:
client OllamaLocal {
  provider ollama
  options {
    model "llama2"
    base_url "http://localhost:11434"
  }
}

Instructor trabaja con Ollama a través de la API compatible con OpenAI:

from openai import OpenAI
from instructor import from_openai

client = from_openai(OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # clave dummy
))

Tenga en cuenta que al trabajar con modelos locales, debe estar consciente de posibles problemas de salida estructurada con Ollama y modelos GPT-OSS, ya que no todos los modelos manejan salidas estructuradas con la misma fiabilidad.

Manejo de errores y reintentos

BAML maneja reintentos a nivel del marco con estrategias configurables. Los errores en la validación de esquema disparan un reprompt automático con contexto de error.

Instructor proporciona lógica de reintento decorativa con ganchos para comportamiento personalizado. Puedes definir validadores que desencadenen reintentos con prompts modificados:

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

Pruebas y observabilidad

BAML incluye un marco de pruebas donde puedes escribir casos de prueba directamente en archivos .baml, validando el comportamiento de los prompts en diferentes entradas. El playground proporciona depuración visual.

Instructor se integra con marcos estándar de pruebas de Python. Puedes usar fixtures de pytest, bibliotecas de simulación y ayudantes de aserción como cualquier código de Python.

Consideraciones de rendimiento

El rendimiento en tiempo de ejecución es comparable — ambos marcos hacen básicamente las mismas llamadas de API de LLM. El sobrecarga para validación y análisis es insignificante en comparación con la latencia de red y el tiempo de inferencia del modelo.

Velocidad de desarrollo difiere significativamente:

  • La generación de código de BAML significa mejor autocompletado y detección de errores más temprana, pero requiere un paso de construcción
  • El enfoque decorativo de Instructor significa iteraciones más rápidas, pero descubrimiento de errores en tiempo de ejecución

Para sistemas de producción que procesan millones de solicitudes, ambos marcos manejan la carga igual de bien. Tu elección depende más de preferencias de flujo de trabajo de desarrollo que de características de rendimiento.

Cuándo elegir BAML

Elige BAML cuando necesites:

  • Soporte multilenguaje: Acceder a los mismos contratos de LLM desde servicios de Python, TypeScript y Ruby
  • Desarrollo basado en contrato: Desarrollo estilo API donde las interfaces de LLM se diseñan antes de la implementación
  • Colaboración en equipo: Flujos de trabajo separados de ingeniería de prompts y desarrollo de aplicaciones
  • Garantías de tipado fuerte: Verificaciones en tiempo de compilación en toda tu pila
  • Desarrollo visual de prompts: Iteración en el playground sobre prompts

Cuándo elegir Instructor

Elige Instructor cuando quieras:

  • Proyectos exclusivamente en Python: No se requiere consistencia entre lenguajes
  • Prototipado rápido: Configuración mínima para hacer funcionar salidas estructuradas
  • Integración con Pydantic: Aprovechar modelos y validadores existentes de Pydantic
  • Despliegue simple: Sin pasos de construcción ni código generado para gestionar
  • Ecosistema rico de Python: Usar bibliotecas y patrones específicos de Python

Combinar enfoques

Algunos proyectos se benefician de usar ambos marcos. Por ejemplo, podrías usar BAML para APIs orientadas al cliente que necesiten clientes multilenguaje, mientras usas Instructor para servicios internos de Python que necesiten iteración rápida.

También puedes cambiar entre marcos a medida que madura tu proyecto — empezar con Instructor para validación rápida, luego moverte a BAML cuando necesites soporte multilenguaje o contratos más estrictos.

Casos de uso reales

Pipeline de extracción de datos (BAML)

Un sistema de procesamiento de documentos usa BAML para extraer datos estructurados de facturas, contratos y recibos. Las definiciones .baml sirven como contratos entre el equipo de ML y los servicios de backend, con clientes de TypeScript para el panel web y clientes de Python para el procesamiento por lotes.

Bot de soporte al cliente (Instructor)

Un bot de soporte usa Instructor para clasificar tickets, extraer intenciones del usuario y generar respuestas. El equipo itera rápidamente sobre los prompts usando modelos Pydantic, con validadores que aseguran que números de teléfono, correos electrónicos e identificadores de tickets cumplan con requisitos de formato.

Agente AI multimodal (Ambos)

Un sistema de agentes AI usa BAML para contratos de comunicación entre agentes, asegurando seguridad de tipo en el sistema distribuido, mientras que agentes individuales usan Instructor internamente para procesamiento flexible de entradas de usuario nativo de Python. Patrones similares se aplican cuando construyes servidores MCP en Python, donde salidas estructuradas permiten integración confiable de herramientas con asistentes de IA.

Rutas de migración e integración

Si ya estás usando análisis de JSON básico con LLMs, ambos marcos ofrecen rutas de migración sencillas:

De JSON a BAML: Convierte tus esquemas JSON a definiciones de tipo de BAML, mueve los prompts a archivos .baml, genera clientes y reemplaza el análisis manual con tipos generados.

De JSON a Instructor: Añade modelos Pydantic que coincidan con tu estructura JSON, instala instructor, parchea tu cliente de OpenAI y reemplaza el análisis de JSON con parámetros response_model.

Ambas migraciones pueden ser incrementales — no necesitas convertir todo tu códigobase de inmediato.

Perspectiva futura y comunidad

Ambos marcos se desarrollan activamente con comunidades fuertes:

BAML (BoundaryML) se enfoca en expandir el soporte de lenguaje, mejorar el playground y mejorar las capacidades de prueba. El respaldo comercial sugiere estabilidad a largo plazo.

Instructor mantiene una fuerte presencia en open source con actualizaciones frecuentes, documentación extensa y adopción en crecimiento. El proyecto está bien mantenido por Jason Liu y sus colaboradores.

Conclusión

BAML e Instructor representan dos excelentes pero distintas aproximaciones a salidas estructuradas de LLM. El enfoque basado en contrato y multilenguaje de BAML es adecuado para equipos que construyen sistemas distribuidos con requisitos estrictos de tipo. El enfoque nativo de Python y basado en Pydantic de Instructor se adapta a desarrollo rápido y pilas centradas en Python.

Ninguno es universalmente mejor — tu elección depende del tamaño de tu equipo, preferencias de lenguaje, flujo de trabajo de desarrollo y requisitos de seguridad de tipo. Muchos equipos encontrarán que comenzar con Instructor para prototipado, luego adoptar BAML para arquitecturas multi-servicio en producción, ofrece lo mejor de ambos mundos.

Enlaces útiles

Artículos relacionados en este sitio

Referencias externas