BAML превращает хаос промпт-инжиниринга в стройный код
Знакомая ситуация? Вы пишете очередной сервис на базе LLM, и ваш код начинает тонуть в бесконечных f-строках, конкатенациях и сложных JSON-шаблонах. Каждый новый промпт — это боль: его сложно читать, поддерживать и, что самое главное, тестировать. А когда модель возвращает кривой JSON или просто отказывается следовать инструкциям, начинается отладка методом пристального взгляда.
Сегодня я хочу рассказать о проекте, который предлагает элегантное решение этой проблемы. Встречайте BAML (Basically a Made-up Language) — специализированный язык, который превращает промпт-инжиниринг из искусства в настоящую инженерную дисциплину.
Что такое BAML и зачем он нужен?
Если коротко, BAML — это язык для описания взаимодействия с большими языковыми моделями. Вместо того чтобы хранить промпты в строковых переменных внутри Python или TypeScript кода, вы выносите их в отдельные .baml файлы.
Основная идея BAML проста и гениальна: промпт — это функция. У этой функции есть типизированные входные параметры и, что самое важное, типизированный возвращаемый результат.
Вы описываете логику взаимодействия с LLM на BAML, а специальный компилятор (написанный на Rust, так что все очень быстро) генерирует для вас нативный, полностью типизированный клиент для вашего основного языка: Python, TypeScript, Ruby, Go и других.
В итоге ваш основной код становится чистым. Вы просто вызываете сгенерированную функцию, как любую другую, а вся магия работы с LLM остается под капотом.
Ключевые возможности, которые цепляют
Давайте разберемся, что делает BAML таким привлекательным для разработчика.
1. Промпты как функции: прощайте, f-строки!
Забудьте о "супе" из строк. В BAML вы описываете функцию, ее аргументы и возвращаемый тип данных. Это сразу делает код на порядок читаемее и проще в поддержке.
Вот как выглядит определение функции-агента в BAML:
// Определяем классы для входных и выходных данных
class Message {
role string
content string
}
class ReplyTool {
response string
}
class StopTool {
action "stop" @description(#"
когда стоит закончить диалог
"#)
}
// А вот и сама функция
function ChatAgent(message: Message[], tone: "happy" | "sad") -> StopTool | ReplyTool {
// Указываем, какую модель использовать
client "openai/gpt-4o-mini"
// А здесь сам промпт с использованием шаблонизатора Jinja
prompt #"
Be a {{ tone }} bot.
{{ ctx.output_format }}
{% for m in message %}
{{ _.role(m.role) }}
{{ m.content }}
{% endfor %}
"#
}
После этого в Python вы можете вызвать эту функцию так, будто она всегда там была:
from baml_client import b
from baml_client.types import Message, StopTool
# ... (код для инициализации диалога)
tool = b.ChatAgent(messages, "happy")
if isinstance(tool, StopTool):
print("Goodbye!")
else:
# Все поля типизированы и доступны через автодополнение
print(tool.response)
2. Итерации со скоростью света прямо в VSCode
Одна из главных болей при работе с LLM — медленный цикл обратной связи. Поменял промпт, перезапустил приложение, ввел тестовые данные, посмотрел результат... Прошло несколько минут.
BAML решает эту проблему с помощью нативного расширения для VSCode (скоро и для JetBrains/Neovim). Оно предоставляет "песочницу" (Playground) прямо в редакторе.
Вы можете менять промпт, задавать разные входные параметры и мгновенно видеть результат, включая сырой запрос к API модели.
Авторы утверждают, что это ускоряет итерации в 10, а то и в 240 раз. Вместо того чтобы тестировать 10 идей за 20 минут, вы можете протестировать 240. Звучит как магия, но это просто хороший инструментарий.
3. Надежный tool-calling для любой модели
Что если вы хотите использовать модель, которая не поддерживает нативное "вызывание инструментов" (tool-calling)? Или поддерживает, но криво? BAML и тут приходит на помощь с алгоритмом SAP (schema-aligned parsing).
Эта технология позволяет BAML "добывать" структурированные данные даже из неидеальных ответов модели, например, когда JSON обернут в Markdown или ему предшествует какой-то текст. Это означает, что вы можете получать надежные типизированные результаты даже с моделями, которые вышли "вчера" и еще не обзавелись всеми нужными фичами.
4. Гибкое управление моделями
BAML позволяет легко переключаться между сотнями моделей от разных провайдеров (OpenAI, Anthropic, Gemini, Azure и любые OpenAI-совместимые). Хотите заменить gpt-4o-mini на o3-mini? Просто поменяйте одну строчку в .baml файле.
function Extract() -> Resume {
client openai/gpt-4o-mini
client openai/o3-mini
prompt #"
....
"#
}
Более того, BAML поддерживает продвинутые стратегии:
- Политики повторных запросов (Retries): Автоматически повторить запрос, если модель упала или вернула некорректный ответ.
- Резервные модели (Fallbacks): Если GPT-4 не справился, автоматически попробовать Anthropic Claude 3.
- Ротация моделей (Round-robin): Распределять нагрузку между несколькими моделями.

5. Удобная работа со стримингом
Создание "печатающих" UI, как в ChatGPT, может быть непростой задачей. BAML значительно ее упрощает, генерируя утилиты и хуки (например, для React/Next.js), которые позволяют легко работать с потоковыми ответами. Причем стриминг тоже полностью типизирован!
Зачем вообще создавать новый язык?
Авторы проекта приводят отличную аналогию. Когда-то давно мы писали веб-страницы так:
def home():
return "<button onclick=\"() => alert(\\\"hello!\\\")\">Click</button>"
Это было неудобно, нечитаемо и вело к ошибкам. Потом появились шаблонизаторы, а затем и фреймворки вроде React с JSX, которые позволили описывать интерфейс декларативно и структурированно:
function Home() {
return <button onClick={() => setCount(prev => prev + 1)}>
{count} clicks!
</button>
}
BAML делает для промпт-инжиниринга то же самое, что JSX сделал для веба. Он дает структуру, типизацию и инструменты там, где раньше был хаос из склеенных строк.
Выводы: кому стоит попробовать BAML?
BAML — это не просто очередной шаблонизатор. Это полноценный фреймворк, который привносит лучшие инженерные практики в мир разработки AI-приложений.
Кому особенно зайдет BAML:
- Командам, работающим над сложными AI-агентами и workflow. Когда у вас десятки промптов, поддерживать их в виде строк становится невозможно.
- Разработчикам, ценящим типизацию и надежность. Гарантия того, что модель вернет данные в нужном формате, дорогого стоит.
- Всем, кто хочет ускорить цикл разработки. Интеграция с IDE — это настоящая killer-фича, которая экономит уйму времени.
- Тем, кто строит мультимодальные системы. BAML позволяет легко переключаться между разными провайдерами и моделями, не переписывая код.
Проект полностью опенсорсный (Apache 2), работает локально без отправки ваших данных куда-либо (кроме прямых запросов к LLM, которые вы сами настроите) и активно развивается. Если вы чувствуете, что "промпты в строках" — это путь в никуда, обязательно загляните на GitHub BoundaryML/baml и попробуйте его в своем следующем проекте.
