Когда мой небольшой сайд-проект вышел на двести долларов в месяц, я сначала не поверил. Запустил debug-лог — и картина открылась такая: тривиальная задача, а я скармливал GPT-4 целую простыню контекста каждый раз, когда пользователь жал кнопку. Кнопку, которая могла бы работать и с GPT-3.5. Или вообще без модели.
Я разобрался, как это чинить. Делюсь — потому что половина статей на эту тему написана так, будто автор никогда не платил из своего кармана.
Промпт — это не бесплатно
Самая очевидная и самая недооценённая вещь: каждый токен в запросе — это деньги. Причём входные и выходные стоят одинаково (у GPT-4o mini — копейки, у GPT-4o — уже чувствительно).
Когда я впервые посмотрел на свой типичный запрос, у меня глаз дёрнулся. Системный промпт на 500 токенов, контекст диалога ещё на 2000, плюс пользовательский вопрос на 50. А задача — определить, содержит ли текст номер телефона.
Фактически я платил за 2550 токенов, чтобы модель сказала «да» или «нет».
Что сделал: вынес валидацию по регулярке на клиент, оставил модель только для случаев, когда регулярка не справляется. Запрос упал до 150 токенов. Счёт за тот эндпоинт — в семь раз.
Мораль простая: подумай, действительно ли задаче нужен LLM. Если задача решается правилом — напиши правило.
Выбирайте модель под задачу, а не под гордость
GPT-4o — круто. Для сложных рассуждений, многошаговых задач, кода с нюансами — незаменим. Но городить его ради классификации или простого рерайта — всё равно что вызывать грузовик, чтобы привезти хлеб.
Я для себя вывел простую лесенку. Простые задачи — GPT-4o mini или даже GPT-3.5 Turbo. Категоризация, суммаризация, форматирование — они справляются. И стоят копейки. Средние задачи — GPT-4o mini с памятью и контекстом получше. Сложные — GPT-4o, когда нужен реальный reasoning и несколько итераций.
На практике оказалось, что 70–80% моих вызовов — это задачи из первой категории. Раньше я лил всё в GPT-4o и удивлялся чеку.
Ещё полезная штука: не бойтесь переключаться динамически. Я написал простую функцию, которая определяет «сложность» задачи по ключевым словам и длине входа. Простой запрос — на mini, сложный — на полную модель.
Кэширование — это не оптимизация, это базовое уважение к своему кошельку
Самый простой способ слить деньги — запрашивать у API одно и то же несколько раз. Типичный сценарий: вопрос «что такое RAG» задают сто раз в день разные пользователи. Ответ один и тот же. А платите вы сто раз.
Я реализовал два уровня кэширования. Первый — точный хэш. Беру SHA-256 от промпта плюс модели плюс системного промпта, ищу в Redis. Попал — отдаю закэшированный ответ. Работает для повторяющихся запросов. Второй — семантический кэш. Использую эмбеддинги (ada-002, стоит копейки) и ищу похожие запросы по косинусной близости. Если similarity больше 0.95 — тоже отдаю закэшированное. Не идеально, но для многих задач хватает.
На моём проекте семантический кэш сэкономил примерно 35% вызовов. То есть треть денег просто так.
Streaming и early stopping
Мелочь, но раздражает: отправляете запрос на генерацию, а модель производит 500 токенов, хотя достаточно 50. Вы платите за все 500.
Два решения. Первое — max_tokens. Ставьте жёсткий потолок. Если знаете, что ответ не бывает длиннее N символов — ограничьте. Модель всё равно остановится раньше, если упрётся в смысл, но запасной парашют не помешает.
Второе — streaming с early termination. Читаю поток токенов на клиенте. Если вижу сигнал окончания («<|im_end|>», естественное завершение мысли) — закрываю соединение. Модель не тратит ресурсы на доgenerating.
Звучит сложно — на практике в моём случае это дало экономию около 15% на генерации.
Batch API — для больших объёмов
Когда мне понадобилось обработать 10 000 текстов для разметки, я сначала слал по одному. Убил день и 40 долларов. Потом попробовал Batch API — отправляешь все запросы списком, получаешь результат в течение суток (или быстрее). Цена — вдвое дешевле.
Не для интерактивных задач, разумеется. Но если у вас есть фоновая обработка, очередь документов, bulk-анализ — Batch API это очевидный ход, который многие пропускают.
Итого — без пафоса
Я не делал ничего героического. Просто перед каждым запросом задавал себе три вопроса: туда ли я отправляю задачу (может, она решается проще), какая модель реально нужна (может, mini хватит), и был ли этот запрос уже (может, ответ закэширован).
Счёт упал с $200 до $65 в месяц на том же проекте. Без потери функциональности.
Экономия в $135 — не деньги? Ну, зависит от масштаба. Когда я говорю с людьми, у которых тысячи пользователей и сотни вызовов в минуту — эти принципы масштабируются соответственно.
В любом случае — платить за задачу больше, чем она стоит, странно. Даже если вы не экономите на спичках.
А у меня всё. Если есть вопросы по реализации — пишите, разберёмся.
