1. Архитектура ML-сервиса: слои, контракты API, паттерны и нефункциональные требования
Архитектура ML-сервиса: слои, контракты API, паттерны и нефункциональные требования
Production ML‑сервис (FastAPI + LLM/CV/классическая модель) — это не «обёртка вокруг predict», а система, где важны изоляция ответственности, стабильные контракты, управляемые зависимости и заранее продуманные нефункциональные требования (NFR).
1) Слои: как разделять ответственность
Цель слоёв — сделать сервис изменяемым (модель/хранилище/транспорт можно менять), тестируемым и предсказуемым.
Рекомендуемая структура (по смыслу, не по папкам):
Визуализация потоков зависимостей:
2) Контракты API: что фиксируем заранее
Контракт — это то, за что вы «держите удар» при изменениях.
Формат запросов/ответов
/v1. Важно при изменениях схем и семантики.Единый формат ошибок
Клиентам важно получать предсказуемые ошибки.
code, message, details, trace_id.Идемпотентность
Если запрос может быть повторён (ретраи клиента, сетевые сбои), нужен ключ идемпотентности.
Idempotency-Key + хранение результата/статуса.Синхронный vs асинхронный инференс
POST /jobs → job_id, затем GET /jobs/{id} или вебхук.3) Паттерны, которые чаще всего окупаются
ModelPort, StoragePort, QueuePort.
- Инфраструктура предоставляет адаптеры. Это снижает связанность и упрощает тесты.4) Нефункциональные требования (NFR): что определить до реализации
| Область | Что зафиксировать | Почему важно |
|---|---|---|
| Производительность | SLO по p95/p99 latency, throughput, лимиты payload | ML‑инференс нестабилен по времени |
| Надёжность | таймауты, ретраи, деградация функционала | зависимость от GPU/внешних систем |
| Масштабирование | горизонтальное (реплики), конкурентность, очереди | иначе «упрётесь» в CPU/GPU/IO |
| Наблюдаемость | метрики, логи, трассировка, trace_id | поиск причин деградаций |
| Безопасность | authn/authz, защита PII, лимиты, аудит | модели часто обрабатывают чувствительные данные |
| Воспроизводимость | версии модели, данных, конфигов, откат | без этого невозможно расследовать инциденты |
| Стоимость | бюджет на токены/инференс, лимиты, квоты | LLM может быть дорогим |
Практическая рекомендация: NFR оформляйте как «контракт команды с самой собой» — конкретные числа (таймауты, лимиты, SLO), а не общие слова.
---
Задания для закрепления
1) Опишите 4–5 слоёв вашего будущего сервиса и перечислите, что точно нельзя помещать в Application слой.
2) Спроектируйте контракт endpoint’а инференса: какие поля входа/выхода, какие коды ошибок, как будет выглядеть trace_id.
3) Решите, где вам нужен async‑режим. Опишите жизненный цикл job: статусы, получение результата, обработка ошибок.
4) Выберите 3 паттерна из статьи и для каждого напишите: какую проблему он решает именно в ML‑сервисе.
5) Сформулируйте 6 NFR для сервиса: 2 про производительность, 2 про надёжность, 2 про безопасность.
<details> <summary> Ответы (примерные ориентиры) </summary>
1) Слои: API → Application → Domain (опционально) → Infrastructure + cross‑cutting. В Application нельзя класть: SQL/ORM запросы, конкретные SDK клиентов, работу с файловой системой «в лоб», детали загрузки модели, HTTP‑клиенты внешних сервисов.
2) Контракт инференса: вход — input (текст/изображение/путь/байты), params (опционально), request_id/idempotency_key (если нужно), выход — result, model_version, latency_ms, trace_id. Ошибки: 422 (валидация), 401/403 (доступ), 429 (лимиты), 503 (модель/зависимость недоступна), 500 (неожиданное). trace_id — строковый идентификатор корреляции, возвращается в ответе и логах.
3) Async нужен, если инференс может превышать HTTP‑таймауты/непредсказуем по времени, или если требуется очередь на GPU. Жизненный цикл: queued → running → succeeded/failed (+ canceled). Результат доступен по job_id, ошибки содержат code/message/trace_id.
4) Примеры:
5) Примеры NFR:
</details>