Перейти к содержимому
PD
EN RU
AI-автоматизация 8 мин чтения

Переход на открытую модель - это миграция, а не правка конфига

Перевод автоматизации с платного API на свою open-weight модель кажется заменой одной строки. Рассказываю, по какому плану я переезжаю, чтобы сэкономить и не сломать продакшен.

PD

Pavel Duglas

AI Automation & MVP Architect

Раз в пару недель мне пишет клиент примерно одно и то же: «Вышла новая открытая модель, влезает в одну видеокарту, по бенчмаркам почти как то, за что мы платим. Может, просто переключимся?» Честный ответ: да, часто можно. Но обычно это делают так: меняют base URL, подставляют новое имя модели, гоняют три тестовых prompt, видят приличный ответ и деплоят. Через две недели пайплайн извлечения начинает терять поля, классификатор плывёт, и никто не связывает это с переездом. Ответы же выглядели нормально.

Я отношусь к переносу задачи с закрытого API на open-weight модель так же, как к миграции базы данных. Есть состояние «до», состояние «после», способ доказать, что они эквивалентны, и путь назад. Ниже план, по которому я работаю на реальных проектах.

Почему все сейчас переезжают

Причины настоящие, и дело не только в деньгах:

  • Стоимость на объёме. Узкая задача, которая крутится миллионы раз в месяц, - именно там счёт за API начинает по-настоящему болеть.
  • Контроль над данными. Многие клиенты не могут отправлять договоры, медицинские записи или внутренние тикеты третьей стороне. У российских компаний это ещё и упирается в 152-ФЗ и требования не выносить персональные данные за периметр.
  • Стабильность. Модель на своём сервере никто не обновит молча, не снимет с поддержки и не порежет по rate limit в ваш самый загруженный час.
  • Задержка. Маленькая модель рядом с воркерами на коротких запросах легко обгоняет большую удалённую.

А вот недостаточные причины: скриншот лидерборда, пост про токены в секунду или смутное ощущение, что open source как-то правильнее. Это повод поставить эксперимент, а не мигрировать.

Шаг 1. Выберите одну задачу, а не всю систему

Не надо мигрировать «весь AI». Мигрируйте одну конкретную задачу. Хорошие кандидаты для первого раза совпадают по трём признакам:

  1. Большой объём. Если задача запускается 2 000 раз в месяц, экономия никогда не окупит потраченное время.
  2. Узкая постановка. Классификация, извлечение полей, маршрутизация, дедупликация, короткие переписывания. Всё, где есть понятный правильный ответ.
  3. Измеримый результат. Корректность можно проверить автоматически или дешёвой ручной проверкой.

Худшие кандидаты: агентные циклы с кучей инструментов, рассуждения над длинными грязными документами и всё клиентское, где важен тон, а что такое «хорошо», никто так и не сформулировал. Это пока оставьте на frontier API. Роутер моделей может отправлять туда сложные 10% запросов, а простые 90% пускать на ваш сервер.

Шаг 2. Eval-набор собирайте из продакшена, а не из головы

Главная ошибка - тестировать на prompt, которые вы написали руками. Ваши примеры чистые и аккуратные. Продакшен таким не бывает.

Что делаю я:

  • Выгружаю из логов от 300 до 1 000 реальных запросов, с разбивкой по типам. Обязательно беру странные: пустые поля, смесь русского и английского, огромные входы, откровенный мусор.
  • Рядом с каждым входом сохраняю ответ текущей модели. Это базовая линия, а не эталон.
  • Прошу человека разметить 100-200 примеров как верные или неверные для обеих моделей. Именно здесь обычно выясняется, что текущая модель тоже ошибалась в 6% случаев.
  • Для каждой задачи фиксирую метрику: точное совпадение извлечённых полей, точность меток для классификатора, долю валидного JSON для структурированного вывода.

На выходе получается нормальная сравнительная таблица вместо ощущений. «Новая модель: 94,1% точности по полям, старая: 95,3%, валидный JSON: 99,8% против 100%» - по этому можно принять решение. «Вроде примерно так же» - нельзя.

Шаг 3. Prompt придётся переносить

Prompt не переносятся между семействами моделей один в один. Вот что меняется при переезде.

Chat template и системный prompt

Открытые модели обучены на конкретном chat template. Если сервер применяет не тот шаблон, качество падает так, что кажется, будто модель просто глупая. Используйте шаблон, который идёт вместе с моделью, и один раз проверьте глазами итоговую последовательность токенов. Кроме того, небольшие модели слабее следуют системному prompt, поэтому важные инструкции, спрятанные в system, иногда стоит перенести ближе к самой задаче.

Структурированный вывод

Именно здесь ломается большинство моих миграций. Frontier API с JSON-режимом почти никогда не возвращает невалидный JSON. Маленькая открытая модель будет это делать в 1-3% случаев, а на пайплайне в 50 000 вызовов в день это очень много битых записей.

Лечится это не строгим prompt, а constrained decoding. Движки вроде vLLM и llama.cpp умеют принудительно ограничивать вывод JSON-схемой или грамматикой, и невалидный ответ становится невозможным на уровне токенов. Для всех задач извлечения я включаю это по умолчанию. Валидатор схемы при этом оставляю: валидный JSON вполне может содержать выдуманное значение.

Токенизаторы

У разных токенизаторов один и тот же текст может занимать на 10-30% больше токенов. Для русского текста разница бывает особенно заметной. Это ломает бюджеты контекста, которые вы аккуратно подбирали, а если чанкинг считает токены старым токенизатором, вход будет молча обрезаться. Пересчитайте всё новым токенизатором и поправьте размеры чанков.

Few-shot примеры

Маленькие модели опираются на примеры гораздо сильнее, чем frontier. Два-три хороших примера на сложные случаи часто закрывают половину разрыва в точности. Но они съедают контекст, так что измеряйте оба эффекта.

Шаг 4. Меряйте под реальной нагрузкой

Цифра «100 токенов в секунду на домашней видеокарте» почти всегда про генерацию одного запроса. Ваш пайплайн не обрабатывает один запрос. Он обрабатывает сорок параллельных воркеров в 9 утра, когда очередь забивается.

Что нужно измерить до решения:

  • Пропускную способность на пиковой конкурентности в запросах в минуту, а не в токенах в секунду.
  • p95 задержки, а не среднее. Очереди убивает хвост.
  • Поведение на тех длинах контекста, которые у вас реально бывают. Длинные prompt съедают память и уменьшают число запросов, которые помещаются на карту одновременно.
  • Влияние квантизации на точность. 4-битная модель влезает в железо подешевле, но прогоняйте eval-набор именно на той сборке, которую будете деплоить. Я видел, как 4-битная версия теряла два-три пункта на извлечении и при этом никак не проседала на классификации.

Используйте нормальный serving-движок с батчингом. Самописный скрипт-обёртка вокруг модели выдаст лишь малую долю того, что та же карта даёт за батчинг-сервером.

Шаг 5. Посчитайте экономику честно

Условный пример, похожий на то, что я часто вижу. Задача извлечения: 2 миллиона запросов в месяц, около 1 500 входных и 200 выходных токенов на запрос. По ценам API среднего уровня это может выйти примерно в $2 000 в месяц. Арендованная GPU, которая вытянет такую нагрузку, стоит где-то $1 000-1 200 в месяц при работе 24/7.

Выглядит как очевидная победа. Теперь добавим то, о чём обычно забывают:

  • Простой. Если нагрузка неравномерная, вы платите за карту и в 3 часа ночи. Если только не построите автоскейлинг, а это отдельный проект.
  • Резервирование. Одна GPU - это одна точка отказа. Две GPU съедают половину экономии.
  • Время на эксплуатацию. Обновления драйверов, падения по памяти, мониторинг, чьи-то ночные алерты. Даже четыре часа в месяц сеньорского времени стоят денег.
  • Счёт за резервный API. Он у вас останется для пиков и аварий.

В итоге реальная экономия в этом примере может составить 20-30%, а не 50%. На таком объёме это всё равно имеет смысл, а аргумент про контроль данных может оказаться важнее денег. На объёме в десять раз меньше это почти никогда не окупается. Моё грубое правило: если задача стоит на API меньше $500 в месяц, не поднимайте свой сервер только ради экономии.

Шаг 6. Выкатывайте как миграцию

Сначала shadow-режим

Отправляйте продакшен-трафик в обе модели, но используйте только ответ старой. Логируйте оба ответа и автоматически сравнивайте их по метрике из шага 2. Держите так неделю, обязательно захватив пиковые дни. Shadow-трафик находит типы входов, которые ваш eval-набор пропустил.

Поэтапное переключение

Переводите на новую модель 5%, потом 25%, потом 100% трафика. Следите за теми же метриками и за сигналами ниже по цепочке: как часто люди правят результат, сколько записей не проходит валидацию, сколько ретраев вы генерируете.

Оставьте путь назад

Путь через API остаётся в коде под флагом минимум на месяц. Настройте автоматический fallback: если ваш сервер не ответил по таймауту или выдал невалидный результат после повторной попытки с ограничением, запрос уходит в API. Тогда ваш бюджет ошибок превращается в строчку расходов, а не в аварию.

Зафиксируйте всё

Закрепите конкретный файл весов по хэшу, квантизацию, версию serving-движка и chat template. Считайте всё это вместе с prompt одним артефактом сборки. Весь смысл своего хостинга в том, что ничего не меняется, пока вы сами этого не захотите. Не выбрасывайте это преимущество, подтягивая «latest» при каждом деплое.

Чек-лист, который я прохожу перед тем, как сказать «да»

  • Выбрана одна задача: большой объём, узкая постановка, измеримый результат
  • Eval-набор собран из реальных логов и размечен человеком
  • Prompt перенесён: правильный chat template, добавлены примеры, токены пересчитаны
  • Для структурированного вывода включён constrained decoding, валидатор на месте
  • Пропускная способность и p95 замерены на реальной конкурентности и на той самой квантизованной сборке
  • Экономика посчитана с учётом простоя, резервирования, эксплуатации и резервного API
  • Неделя в shadow-режиме, потом поэтапное переключение
  • Fallback на API подключён и проверен по-настоящему: GPU-сервер выключали руками
  • Веса, движок и шаблон зафиксированы и версионированы

Открытые модели сейчас действительно хороши, и для большой части задач автоматизации это правильный выбор. Но между «она ответила на мой тестовый prompt» и «она переваривает 50 000 грязных продакшен-запросов в день» лежит ровно та работа, из которой и состоит миграция. Сделайте её один раз как следует, и следующий переезд станет рутинной задачей, а не лотереей.

Вопросы и ответы

Можно ли просто поменять base URL, если сервер модели совместим с OpenAI API?

Технически да, код заработает. Но совместимость протокола не означает совместимость поведения. Другой chat template, другой токенизатор, другая склонность ломать JSON. Совместимый API экономит время на интеграции, но eval-набор, shadow-режим и поэтапный rollout всё равно нужны.

С какого объёма имеет смысл поднимать свою модель ради экономии?

Мой ориентир: если задача обходится на API дешевле $500 в месяц, переезжать только ради денег не стоит. Аренда GPU, резервирование, простой и время на эксплуатацию съедят разницу. Исключение - когда данные в принципе нельзя отправлять третьей стороне. Тогда вопрос не в экономии, а в том, можно ли вообще использовать модель.

Что делать, если открытая модель проигрывает текущей пару процентов точности?

Сначала попробуйте закрыть разрыв: добавить few-shot примеры на сложные случаи, проверить chat template, взять менее агрессивную квантизацию. Если разрыв остаётся, решайте на уровне бизнеса: сколько стоит каждая ошибка и сколько вы экономите. Часто разумный вариант - роутер, который отправляет сложные или неуверенные запросы в API, а основную массу обрабатывает своей моделью.

Похожие статьи

  • #open-weight models
  • #LLM migration
  • #self-hosting
  • #AI cost
  • #evals
  • #automation pipelines

Есть идея? Давайте превратим её в работающий продукт.

Получите понятную архитектуру, рабочий MVP и систему, которую можно тестировать, продавать и масштабировать.

Или оставьте заявку здесь

Заявка приходит мне в Telegram. Обычно отвечаю в течение нескольких часов.