В пятницу основной провайдер начал отвечать 429. Команда переключила переменную MODEL, но резервная модель не поддерживала прежнюю схему tools, иначе считала токены и возвращала другой тип ошибки. Переключатель существовал только на слайде.
Провайдера выбирают под измеримый workload: качество на своём eval-наборе, задержку, лимиты, доступность региона, политику данных и полную стоимость запроса. Название модели без версии и параметров недостаточно для воспроизводимости.
Адаптер на границе
Внутренний контракт должен описывать потребность приложения, сохраняя значимые возможности провайдера.
from typing import Protocol
from pydantic import BaseModel
class GenerateRequest(BaseModel):
messages: list[dict]
max_output_tokens: int
response_schema: dict | None = None
idempotency_key: str
class GenerateResult(BaseModel):
text: str
model: str
input_tokens: int
output_tokens: int
provider_request_id: str | None = None
class ModelProvider(Protocol):
async def generate(self, request: GenerateRequest) -> GenerateResult: ...Streaming, prompt caching, computer use и особенности tool calling не надо прятать под фиктивным общим знаменателем. Для них лучше capability flags и специализированные методы. Иначе абстракция обещает переносимость, которой нет.
Gateway централизует ключи, маршрутизацию, бюджеты и telemetry. За это он добавляет собственный отказ и может обрезать провайдерские поля. Нужны timeout до gateway и до upstream, корреляция request ID и путь прямого вызова для диагностики.
Локальная модель оправдана, когда требования к данным, стабильная нагрузка или специализированное качество покрывают стоимость GPU и эксплуатации. «Токены бесплатны» обычно не включает idle capacity, batching, обновления драйверов и дежурство.
Failure modes
Резервная модель не проходила evals; fallback удвоил запрос после неопределённого timeout; регион не соответствует договору; alias модели незаметно обновился; gateway логирует prompt; лимиты считаются на организацию, а приложение думает, что на ключ. Переключение следует репетировать, включая structured output и tools.
Правило главы
Переносимость является проверенным сценарием, а не интерфейсом с методом generate. Фиксируйте версии и возможности, измеряйте workload, прогоняйте резервный маршрут до аварии.
Практикум
Возьмите 50 производственных примеров. Сравните два облачных провайдера и одну локальную модель по качеству, p95, цене успешного результата и поддержке нужных возможностей. Затем отключите основной маршрут, проверьте fallback на 429, timeout и несовместимой схеме. Запишите, при каких условиях переключение запрещено.