Куда уходил наш счёт за LLM API и какие архитектурные изменения его урезали
Учёт токенов по шагам, распределение моделей по уровням, сокращение промптов и backoff с jitter снизили расходы кодинг-агента на API; внутри формулы стоимости и типичные ошибки оптимизации.
Счёт за LLM может быть высоким, даже если сами модели не особенно дорогие. В нашем случае виноваты были привычки: один и тот же системный промпт на 1800 токенов уходил с каждым вызовом, сбои повторялись сразу, без всякого backoff, а каждый шаг процесса по умолчанию шёл на флагманскую модель, нужна она там или нет. По нашей оценке, для этой нагрузки цена за токен объясняла примерно 20% проблемы, а архитектура — остальные 80%. Это соотношение относится именно к этой системе и не является общим бенчмарком. Но урок применим везде: прайсовая цена токена говорит о тарифе, а не о том, насколько эффективно ваше приложение его расходует.
Прежде чем менять провайдера или торговаться о цене, мы оснастили расходы метриками, и каждое последующее решение опиралось на данные, а не на чутьё. Нельзя оптимизировать то, что не логируешь.
Привяжите токены к шагам процесса
Первое требование — учёт на уровне шагов. Для каждого вызова API записывайте шаг процесса, модель, входные и выходные токены, а по ночам агрегируйте по шагу и модели. Если цены указаны за миллион токенов, стоимость шага считается так:
step cost =
number of calls × [
(average input tokens ÷ 1,000,000 × input price per 1M tokens)
+
(average output tokens ÷ 1,000,000 × output price per 1M tokens)
]
Неделя данных дала показательную картину для нагрузки кодинг-агента, и главный вывод оказался неожиданным: шаг планирования составлял лишь 8% вызовов, но 41% всех расходов. Дорогим его делало сочетание самых длинных контекстов с самой дорогой моделью. До замеров считалось, что дороже всего генерация кода, и данные показали, что это не так. Само по себе число запросов было неправильной метрикой. Правильной были суммарные расходы на шаг.
Минимальная реализация
Всё оказалось просто, потому что ответы API уже содержат данные о расходе токенов:
import logging
from openai import OpenAI
client = OpenAI(
api_key="KEY",
base_url="https://api.cometapi.com/v1",
)
def tracked_call(step_name, model, messages):
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=0,
)
usage = response.usage
logging.info(
"llm_call step=%s model=%s prompt=%d completion=%d",
step_name,
model,
usage.prompt_tokens,
usage.completion_tokens,
)
return response
Когда такие логи есть, отчёт о стоимости — по сути один GROUP BY. Цель не в навороченной платформе наблюдаемости, а в том, чтобы каждый токен можно было отследить до шага процесса и модели.
Возьмите скидку шлюза, но считайте её минимумом
Наши запросы идут через CometAPI, и при нашем профиле использования итоговая стоимость получалась примерно на 20% ниже, чем при оплате тех же моделей напрямую у провайдеров. Это была экономия почти без усилий: клиентский код и выбор моделей остались прежними, а счёт уменьшился по всем уже используемым моделям. Мы взяли её сразу, потому что она почти не требовала архитектурной работы.
Но главным рычагом шлюз не стал. Последующие архитектурные изменения сэкономили больше, правда, ценой настоящей инженерной работы. Порядок внедрения и финансовый эффект не всегда совпадают, так что дешёвую ценовую экономию берите сразу, а инженерное время тратьте на маршрутизацию моделей, накладные расходы промптов и поведение повторов. Нет смысла откладывать мгновенную экономию, пока строишь что-то изощрённое, но низкий тариф не компенсирует неэффективное устройство запросов. Детали тарификации — в документации CometAPI по ценам.
Направляйте каждый шаг на модель нужного уровня
Распределение по уровням означает, что разные шаги процесса получают разные модели в зависимости от того, какое качество каждому нужно. Планированию флагманская модель действительно была нужна. Вызовов там мало, а влияние велико: плохой план может испортить все последующие действия. У массовых правок и исправлений линтера другие потребности: большой объём, жёсткие рамки, легко повторить или проверить. Логика маршрутизации изменилась соответственно:
def choose_model(step):
if step in ("plan", "hard_refactor"):
return "claude-sonnet-4-6" # High-value reasoning steps
if step in ("bulk_edit", "lint_fix"):
return "gemini-2.5-flash" # Lower-cost, fast, retry failures
return "gpt-5-mini" # Middle-tier default
В результате 46% вызовов ушли на более дешёвую и быструю модель, а дорогая осталась для планирования и сложной генерации. Распределение по уровням дало самое большое сокращение одной статьи счёта, потому что было нацелено на шаги с большим объёмом, а не гонялось за мелкой экономией на редких. Принцип простой: самую сильную модель ставьте там, где ошибки дороже всего обходятся дальше по цепочке, а не везде по умолчанию.
Урежьте повторяющиеся накладные расходы промпта
Системный промпт на 1800 токенов ехал с каждым запросом. Большую его часть составлял защитный шаблонный текст, копившийся месяцами: каждая инструкция при добавлении казалась безобидной, но вместе они превратились в большие постоянные издержки на каждый вызов. Решение — убрать избыточные инструкции, оставить только существенные ограничения и вынести стабильную часть в кэшируемый префикс везде, где провайдер поддерживает кэширование промптов.
Масштаб легко недооценить. На миллионе вызовов один только неизменный системный промпт на 1800 токенов даёт:
1,000,000 calls × 1,800 tokens
= 1.8 billion input tokens
и это ещё без контекста конкретной задачи. Токены промпта — такие же токены, как и все остальные. Раздутый промпт повышал стоимость API и вдобавок съедал лимит токенов в минуту на каждом запросе, так что чистка помогла дважды: снизила регулярные расходы на вход и позволила уместить больше полезной работы в те же лимиты. Сокращение шло раньше кэширования, ведь кэширование лишнего текста лишь законсервировало бы исходную проблему проектирования.
Повторы — с backoff и jitter
Повторы составляли 12% вызовов. Многие следовали за ответом HTTP 429 о превышении лимита: система тут же повторяла запрос почти в тех же условиях, получала очередной 429 и порой, похоже, лишь растягивала период ограничения. Мгновенный повтор заменили экспоненциальным backoff и jitter. Backoff постепенно увеличивает задержку между попытками, а jitter добавляет немного случайности, чтобы параллельные запросы не повторялись в один и тот же момент. Объём повторов упал больше чем вдвое.
Отклонённые запросы тарифицируются по-разному, но слепые повторы всё равно увеличивают объём запросов и могут запускать повторную платную работу, если запрос доходит до выполнения моделью. Здесь контроль повторов повысил надёжность и заодно удержал расходы. Общее правило: повтор должен учитывать причину сбоя, а мгновенная отправка того же запроса — это не восстановление.
Какое изменение важнее всего
Четыре изменения били по разным частям уравнения стоимости:
- Распределение моделей по уровням дало самое большое сокращение одной статьи: 46% вызовов ушли на более дешёвую модель, а дорогая сохранила самую важную работу. На внедрение ушло около дня.
- Маршрутизация через шлюз стала самой простой мгновенной экономией: стоимость того же набора моделей снизилась примерно на 20% почти без инженерной работы.
- Сокращение и кэширование промпта убрали регулярные накладные расходы на вход, ведь даже простейшая задача тащила за собой промпт на 1800 токенов.
- Backoff и jitter сократили лишний объём запросов: доля повторов упала с 12% вызовов до менее чем половины от этого.
Вместе они дали больше, чем любое изменение по отдельности. Не складывайте экономию так, будто это независимые скидки. Цены шлюза меняют тариф за токен, распределение по уровням — средневзвешенный тариф по шагам, сокращение промпта — число входных токенов, а контроль повторов — число запросов. Каждое изменение к тому же меняет базу, с которой работает следующее.
Как посчитать месячную стоимость
Допустим, система обрабатывает миллион запросов в месяц, в среднем по 2000 входных и 500 выходных токенов на запрос. Месячный объём токенов:
Input:
1,000,000 × 2,000
= 2 billion tokens
Output:
1,000,000 × 500
= 500 million tokens
При ценах за миллион токенов базовая месячная стоимость такова:
monthly cost =
2,000 × input price per 1M tokens
+
500 × output price per 1M tokens
У нас шлюз срезал эту базу примерно на 20% ещё до архитектурных изменений. Затем распределение по уровням снизило средневзвешенный тариф, потому что 46% запросов перешли на модель, токен которой стоит в разы дешевле. Сокращение промпта уменьшило число входных токенов в каждом затронутом запросе, а контроль повторов убрал ненужные запросы. Поэтому для корректного расчёта нужны четыре переменные: объём запросов, соотношение входных и выходных токенов, распределение моделей по шагам и актуальные цены каждой модели. Вот почему универсальной цифры «вы сэкономите X%» не бывает. Полезная оценка получается только из вашей собственной нагрузки.
Три способа провалить работу над стоимостью
Оптимизировать редкие шаги, потому что это легко. Соблазнительно выжать копейки из мелкого шага, пока шаг с большим объёмом съедает почти весь бюджет. Сортируйте цели по суммарным расходам, а не по простоте изменения, — именно поэтому учёт должен быть первым.
Снижать стоимость, не проверяя качество. Шаг переводят на модель подешевле, счёт падает, изменение выглядит победой. Через три недели кто-то замечает, что результат стал хуже. Каждую замену модели пропускайте через оценочный набор — репрезентативную подборку задач, которая ловит регрессии. Экономия, не прошедшая планку качества, — не оптимизация.
Строить кэш до замера повторного использования. Кэш помогает только там, где контент действительно повторяется. Навороченный кэш, построенный до проверки повторяемости промптов, может сжечь инженерное время и не задеть главный источник затрат. Сначала выясните, сколько там повторов, и только потом кэшируйте те префиксы или ответы, которые действительно возвращаются. Работа над стоимостью без замеров может улучшить счёт и при этом ухудшить качество, задержку или надёжность.
Не выключайте метрики
Не отключайте инструментирование после первого прохода: расходы ползут вверх. Системный промпт понемногу удлиняется, новый шаг процесса стартует на дорогой модели, возвращается старый баг с повторами, или трафик перетекает на шаг, который раньше был второстепенным. Дашборд, вскрывший исходную проблему, — это и то, что не даёт ей тихо вернуться. Ежемесячный разбор таблицы стоимости по шагам занимает минут десять и уже поймал две регрессии.
Дело в архитектуре, а не в ценах
Цена за токен — всего лишь тариф. Сколько вы реально тратите, зависит от архитектуры: сколько токенов вы покупаете, какие модели их обрабатывают и сколько работы повторяется. Для нагрузки нашего кодинг-агента самой эффективной оказалась такая последовательность:
- Инструментировать каждый вызов по шагу процесса и модели.
- Взять экономию шлюза, не требующую усилий.
- Направить каждый шаг на самую дешёвую модель, которая отвечает его требованиям к качеству.
- Убрать повторяющиеся накладные расходы промпта и кэшировать стабильные префиксы там, где это поддерживается.
- Заменить слепые повторы на backoff и jitter.
- Оставить мониторинг навсегда, чтобы расходы не подкрались обратно.
Самый большой выигрыш — распределение по уровням. Самый быстрый — шлюз, почти 20%. Сокращение промпта убрало постоянные издержки из каждого запроса, а умные повторы сократили трафик повторов больше чем вдвое. Охота за самой низкой рекламной ценой токена значит гораздо меньше, чем понимание того, куда уходят ваши собственные токены. Именно замеры делают архитектуру видимой.