Обзор BentoML: упаковка модели в API показывает, как Python-класс с декораторами превращается в HTTP-сервис, как собирается Bento с зависимостями и как проверить контракт входных и выходных данных до развёртывания. Ниже — граница между Service, Bento и деплоем, учебный контракт sentiment и разбор частых ошибок.
Задача BentoML
Документация проекта на docs.bentoml.com описывает BentoML как открытую Python-библиотеку для построения онлайн-систем вывода, оптимизированных под AI-приложения и инференс моделей. Библиотека ставится из PyPI командой pip install bentoml; в разделе про начало работы указана рекомендация Python 3.9+, а в учебном hello-world — Python 3.11. Эти значения относятся к разным страницам и разным сценариям, поэтому при выборе версии интерпретатора ориентируйтесь на требования вашей модели.
Задача библиотеки — снять с разработчика рутину вокруг серверной обвязки. Документация перечисляет четыре направления: построение API для любой AI/ML-модели, упаковка и контейнеризация, повышение утилизации CPU и GPU, а также кастомизация бизнес-логики. Под утилизацией понимаются встроенные механизмы вроде адаптивного батчинга, параллелизма моделей, многостадийных пайплайнов и композиции нескольких моделей. Так документация описывает возможности фреймворка; показатели конкретного сервиса измеряют отдельно.
Практическая ценность проявляется на стыке двух миров. Скрипт инференса обычно живёт в ноутбуке или CLI-утилите: он читает данные, вызывает модель, печатает результат. Продукту нужен сетевой endpoint с предсказуемой схемой запроса и ответа, с версионированием модели и с воспроизводимой средой. BentoML закрывает именно этот переход: вы описываете класс, помечаете методы декораторами, а фреймворк создаёт REST-сервер, маршруты и клиентские обёртки.
Границы применимости тоже стоит держать в голове. BentoML упаковывает сервис и управляет его жизненным циклом. Качество самой модели остаётся вашей ответственностью: правильность sentiment-классификатора проверяют на отдельной размеченной выборке. Успешный ответ сервера подтверждает доступность сервиса. Фреймворк отвечает за транспорт, схему и упаковку; за метрики качества отвечает процесс оценки модели.
Service и API
Согласно странице про онлайн-сервисы, BentoML Service — это класс Python, помеченный декоратором @bentoml.service. Класс описывает логику вывода модели: загрузку весов, предобработку, вызов инференса. Методы, доступные по HTTP, помечаются декоратором @bentoml.api. По соглашению сервис определяют в файле service.py, хотя модуль и имя атрибута можно задать в формате <module_name>:<attribute_name>.
Документация приводит пример сервиса суммаризации: класс Summarization загружает пайплайн Hugging Face, а метод summarize принимает строку и возвращает строку. Контекстный менеджер bentoml.importing() используется для импортов, нужных во время обслуживания. Такой же каркас подходит для собственной модели: в __init__ загружаются веса, в методе с @bentoml.api выполняется предсказание.
Маршрут по умолчанию выводится из имени функции. Метод summarize становится доступен по пути /summarize на порту 3000 при локальном запуске. Параметр route в декораторе @bentoml.api задаёт собственный путь для одного endpoint, а параметр path_prefix в @bentoml.service добавляет общий префикс ко всем маршрутам сервиса, включая health-check /livez и /readyz. Разница существенная: route настраивает один метод, path_prefix — весь сервис.
Внутри метода доступен объект bentoml.Context. Через него читают заголовки входящего запроса, меняют статус-код, заголовки и cookie ответа, а также работают со словарём ctx.state. Документация называет этот словарь per-worker: значения видны в пределах одного воркера и разделяются между API-методами этого воркера. Хранить в нём глобальное состояние всего сервиса при нескольких воркерах не стоит.
Жизненный цикл закрывают хуки. Декоратор @bentoml.on_deployment выполняет глобальную подготовку один раз до запуска воркеров, а @bentoml.on_shutdown — очистку при остановке: закрытие соединений и освобождение ресурсов. Для длительных операций документация предлагает задачи через @bentoml.task с очередью; для параллельных вызовов — свойство .to_async и asyncio.gather.
Упаковка Bento
Bento — это артефакт сборки: сервис, модель, зависимости и метаданные, из которых получается воспроизводимый образ. Документация относит упаковку к отдельному шагу «Packaging for deployment» и указывает, что BentoML генерирует воспроизводимые Docker-образы для развёртывания в любом окружении. Модель подключается через bentoml.models.HuggingFaceModel или bentoml.models.BentoModel; в примере с iris-классификатором модель берётся как BentoModel("iris_sklearn:latest"), а файл загружается через path_of("model.pkl").
Порядок работы выглядит так. Сначала описывается сервис и проверяется локально. Затем фиксируются зависимости и версия модели. После этого собирается Bento и контейнеризуется образ. Документация упоминает, что сервис можно запустить в Docker-контейнерах или Kubernetes, а также развернуть на собственном железе. Для GPU-сценариев есть отдельный раздел про работу с видеокартами.
Отдельного внимания заслуживает история с Runner. Документация прямо говорит: Runner — устаревшая концепция BentoML 1.1, где сервис собирался из компонентов Service и Runner. Начиная с BentoML 1.2 фреймворк перешёл на класс Python как единицу описания сервиса. Для миграции со старых версий есть функция bentoml.runner_service(), превращающая Runner во внутренний сервис. Руководство по интеграции на huggingface.co содержит код с Runner и старыми версиями CUDA — этот пример относится к прежней архитектуре. Для нового проекта берите текущие примеры с @bentoml.service и @bentoml.api со страниц docs.bentoml.com.
Зависимости и ресурсы
Ресурсы объявляются в декораторе @bentoml.service. Документация показывает словарь resources с ключами cpu, memory и gpu: например, {"cpu": "2"}, {"gpu": 1}, {"cpu": "200m", "memory": "512Mi"}. Таймаут задаётся в словаре traffic, например {"timeout": 10}. Все поля конфигурации опциональны и имеют значения по умолчанию, поэтому сервис запустится и без явных ресурсов.
| Параметр | Где задаётся | Пример из документации |
|---|---|---|
cpu |
resources в @bentoml.service |
"2", "200m" |
memory |
resources |
"512Mi", "4Gi" |
gpu |
resources |
1, "1" |
timeout |
traffic |
10, 600 |
workers |
@bentoml.service |
1, 4 |
envs |
@bentoml.service |
[{"name": "HF_TOKEN"}] |
Переменные окружения перечисляются в списке envs; в примере с vLLM там указан HF_TOKEN. Секреты передавайте через окружение процесса или секреты платформы. В коде сервиса оставляйте обращение к ним. Значения ресурсов применяются и при локальном запуске, и при работе образа в Kubernetes, что помогает держать одинаковые лимиты на всех этапах.
Синхронные и асинхронные API ведут себя по-разному. Для синхронной логики BentoML создаёт пул воркеров подходящего размера. Асинхронные методы освобождают event loop, пока ждут результат, и документация предупреждает: блокирующие вызовы внутри асинхронного API останавливают event loop и мешают работе health-check /readyz. Если синхронный метод нужно вызвать в асинхронном контексте, документация предлагает свойство .to_async, превращающее синхронный метод сервиса в асинхронный.
Учебный контракт
Разберём учебный контракт sentiment, заданный автором примера. Вход — объект {"text": str} с непустой строкой до 200 символов. Выход — {"label": positive|negative|neutral, "score": float от 0 до 1}. Эти ограничения требуют явных validator и model rules: в любом BentoML Service они не возникают автоматически. Ниже — учебный пример; результат работы модели не измерялся.
from __future__ import annotations
import os
import bentoml
from pydantic import BaseModel, Field, field_validator
class SentimentInput(BaseModel):
text: str = Field(min_length=1, max_length=200)
@field_validator("text")
@classmethod
def not_blank(cls, v: str) -> str:
if not v.strip():
raise ValueError("text must not be blank")
return v
class SentimentOutput(BaseModel):
label: str
score: float = Field(ge=0.0, le=1.0)
@field_validator("label")
@classmethod
def known_label(cls, v: str) -> str:
allowed = {"positive", "negative", "neutral"}
if v not in allowed:
raise ValueError(f"label must be one of {sorted(allowed)}")
return v
@bentoml.service(
name="sentiment_service",
resources={"cpu": "1", "memory": "1Gi"},
traffic={"timeout": 10},
)
class SentimentService:
def __init__(self) -> None:
self.model_revision = os.environ.get("MODEL_REVISION", "local-dev")
self.model = self._load_model()
def _load_model(self):
# Загрузка весов вашей модели. Версия фиксируется MODEL_REVISION.
raise NotImplementedError("подключите свою модель")
@bentoml.api
def classify(self, payload: SentimentInput) -> SentimentOutput:
raw = self.model.predict(payload.text)
return SentimentOutput(label=raw["label"], score=raw["score"])
Полностью заполненный положительный fixture: вход {"text": "отлично"}, вымышленный ответ {"label": "positive", "score": 0.8} — форма проходит контракт. Это вымышленное учебное значение; модель его не выдавала, уверенность не измерялась. Отрицательные fixtures: {"text": ""} не проходит входной validator; {"label": "positive", "score": 1.2} не проходит выходной validator. Проверки упаковки: revision модели и зависимости зафиксированы, CPU или GPU объявлены, health-check отделён от оценки качества.
Local и managed deployment
Локальный запуск выполняется командой bentoml serve. Документация показывает, что сервер поднимается на http://localhost:3000, а метод summarize доступен по пути /summarize. Вызвать endpoint можно через curl, через Python-клиент bentoml.SyncHTTPClient("http://localhost:3000") или через Swagger UI: на странице сервера в разделе Service APIs есть кнопка «Try it out» и поле Request body.
Локальная проверка даёт быстрый цикл обратной связи. Вы видите, как схема превращается в маршрут, как отвечает валидатор на некорректный ввод и как выглядит тело ответа. Документация советует создавать виртуальное окружение для изоляции зависимостей и приводит команды для macOS, Linux и Windows. После успешной локальной проверки переходите к сборке Bento и контейнера.
| Этап | Команда или действие | Что проверяем |
|---|---|---|
| Локальный запуск | bentoml serve |
Маршруты, схемы, валидаторы |
| Вызов API | curl, Python-клиент, Swagger UI | Формат запроса и ответа |
| Сборка Bento | Упаковка сервиса и модели | Фиксация revision и зависимостей |
| Контейнер | Генерация Docker-образа | Воспроизводимость среды |
| Развёртывание | Docker, Kubernetes, собственное железо | Ресурсы и таймауты |
Управляемый вариант размещения — BentoCloud, которую документация упоминает как один из путей развёртывания. Документация также описывает агентские навыки: кодирующий агент может развернуть BentoML Service в Kubernetes или EC2. Тарифы, регионы и условия доступа из России проверяйте на сайте провайдера перед выбором площадки.
Ограничения
BentoML упаковывает сервис и не делает sentiment-модель правильной. Успешный ответ сервера говорит о том, что транспорт и схема работают; качество предсказаний проверяют на отдельной размеченной выборке. Смешивать эти две проверки в одном шаге не стоит: health-check отвечает за доступность процесса, а оценка качества — за метрики модели.
Латентность, пропускную способность и стоимость измеряют для выбранных модели, железа и нагрузки. Документация описывает механизмы адаптивного батчинга и параллелизма. Если вам нужны конкретные показатели, их получают на вашем стенде с вашей моделью.
Старый код с Runner относится к BentoML 1.1. Примеры из сторонних руководств могут содержать устаревшие версии CUDA и конструкции, которых нет в текущем API. Сверяйтесь со страницами docs.bentoml.com и переносите оттуда актуальные @bentoml.service и @bentoml.api. Отдельно проверяйте совместимость версии Python: разные страницы документации рекомендуют 3.9+ и 3.11.
Частые вопросы
Что такое BentoML простыми словами?
Это открытая Python-библиотека для построения онлайн-систем вывода моделей. Вы описываете класс с декоратором @bentoml.service, помечаете методы декоратором @bentoml.api, а фреймворк создаёт REST-сервер и маршруты. Документация называет её Unified Model Serving Framework для API инференса и мультимодельных систем.
Как запустить BentoML локально?
Установите библиотеку через pip install bentoml, опишите сервис в service.py и выполните bentoml serve. Сервер поднимется на http://localhost:3000, а метод станет доступен по маршруту из его имени. Проверить вызов можно через curl, Python-клиент или Swagger UI.
Чем BentoML отличается от FastAPI?
FastAPI даёт веб-фреймворк общего назначения, а BentoML добавляет к нему слой работы с моделями: загрузку весов, версионирование, упаковку в Bento и контейнеризацию. Внутри BentoML можно монтировать ASGI-приложения FastAPI через @bentoml.asgi_app. Выбор зависит от того, нужна ли вам готовая обвязка вокруг инференса.
Как задать зависимости и ресурсы сервиса?
Ресурсы перечисляются в словаре resources декоратора @bentoml.service: cpu, memory, gpu. Таймаут задаётся в traffic, переменные окружения — в списке envs. Все поля опциональны и имеют значения по умолчанию, а конфигурация применяется и локально, и в контейнере.
Можно ли развернуть BentoML в Docker и Kubernetes?
Документация указывает, что BentoML генерирует воспроизводимые Docker-образы для развёртывания в любом окружении. Сервис можно запускать в Docker-контейнерах или Kubernetes, а также на собственном железе. Управляемый вариант размещения — BentoCloud; условия доступа проверяйте на сайте провайдера.
