Снизить расходы на LLM API можно тремя рычагами: платить меньше за каждый токен (кэш префикса, batch, дешёвая модель), отправлять меньше токенов (короткий контекст, ограничение вывода) и не оплачивать неудачные попытки (маршрутизация с эскалацией и проверка результата). Начинать нужно не с поиска самой дешёвой модели, а с метрики: цены успешной задачи. Разбираем, как её считать, что сегодня написано в документации OpenAI, Anthropic, Google и DeepSeek про кэширование, и как собрать роутер, который не ломает качество.
Считайте цену успешной задачи, а не запроса
Цена за миллион токенов в прайсе не говорит, сколько стоит результат. Дешёвая модель, которая ошибается в каждой второй задаче, заставляет делать повторы, а повтор оплачивается так же, как первая попытка. Поэтому база расчёта такая:
цена_успешной_задачи = стоимость_запроса × попытки_на_задачу / доля_успеха
Здесь стоимость запроса считается по формуле input × цена_input + cached × цена_cached + output × цена_output, попытки на задачу включают повторы и шаги агента, а доля успеха измеряется на вашем наборе проверок. Пример на условных числах (это расчёт по формуле, а не замер):
| Вариант | Стоимость одной попытки | Доля успеха | Цена успешной задачи |
|---|---|---|---|
| Дешёвая модель | $0.002 | 60% | $0.002 / 0.6 = $0.0033 |
| Сильная модель | $0.010 | 95% | $0.010 / 0.95 = $0.0105 |
| Дешёвая, при неудаче сильная (идеальная проверка) | $0.002 + 0.4 × $0.010 = $0.006 | 0.6 + 0.4 × 0.95 = 98% | $0.006 / 0.98 = $0.0061 |
Если ручная доработка неудачных ответов тоже стоит денег, разница между вариантами ещё больше. Доли успеха берите только из собственных проверок, подробнее о том, как их собрать, в статье как сравнить LLM на своих задачах.
Из чего складывается счёт: input, cached, output
У большинства API три ценовые ветки. Input-токены: всё, что вы отправили. Cached-токены: часть input, совпавшая с ранее обработанным префиксом, она стоит дешевле. Output-токены: то, что сгенерировала модель, они дороже input, а у рассуждающих моделей включают токены размышлений. Отсюда практические выводы:
- Длинный статичный префикс (системный промпт, инструкции, примеры, описания инструментов) нужно ставить в начало и не менять между запросами. Любое отличие внутри префикса сбрасывает кэш для всего, что идёт после него.
- Переменную часть (вопрос пользователя, найденные фрагменты, время, идентификаторы) ставьте в конец.
- Лимит
max_tokensи просьба отвечать структурно и кратко режут самую дорогую ветку.
Prompt caching у четырёх провайдеров
Механики различаются, а скидки задаёт прайс конкретной модели. Ниже только то, что указано в документации на 25 октября 2026 года.
| Провайдер | Как включается | Минимум для кэша | Что в документации про цену и срок |
|---|---|---|---|
| OpenAI | По умолчанию для поддерживаемых моделей | 1024 токена для GPT-5.6 и новее, у старых моделей зависит от настроек запроса | Скидка на кэшированный input до 95%. Для GPT-5.6 и новее запись в кэш на 30 минут стоит 1.25× обычного input, чтение 0.1× (на GPT-6.1 Sol 0.05×). Через Responses API можно выбрать минимальное время жизни 12 часов, запись тогда 2.5× |
| Anthropic | Поле cache_control на верхнем уровне запроса (автоматически) или на блоках (до 4 брейкпоинтов) | Зависит от модели: от 512 до 4096 токенов | Запись на 5 минут стоит +25% к input, на 1 час 2× input. Чтение 0.1× input, а для Opus 5.5 и Sonnet 5.5 0.05×, для Fable 5.1 и Mythos 5.1 0.025×. TTL продлевается при каждом попадании |
| Google Gemini | Неявное кэширование включено по умолчанию для Gemini 2.5 и новее | 2048 токенов для Gemini 2.5, 4096 для Gemini 3.x и 4 | Экономия передаётся автоматически, в ответе есть total_cached_tokens. Явные кэши поддерживаются только на части моделей через generateContent; в Interactions API только неявные |
| DeepSeek | По умолчанию, кэш на диске | Не указан порог, кэш строится по границам запросов и фиксированным интервалам токенов | В ответе поля prompt_cache_hit_tokens и prompt_cache_miss_tokens. Кэш работает по принципу best-effort, гарантий попадания нет |
Цены DeepSeek показывают масштаб эффекта: по прайсу для deepseek-flash (V4.1-Flash) входные токены с попаданием в кэш стоят $0.003 за миллион в непиковое время, а без попадания $0.15; вывод $0.6. В пиковое время все ставки удваиваются. Страница модели в каталоге: DeepSeek V4.1 Flash.
Расчёт для запроса с 6000 входных токенов, из которых 5000 статичный префикс, и 300 выходных, по тем же ценам, непиковое время:
- Без кэша: 6000 × $0.15 / 1M + 300 × $0.6 / 1M = $0.0009 + $0.00018 = $0.00108.
- С кэшем: 5000 × $0.003 / 1M + 1000 × $0.15 / 1M + $0.00018 = $0.000345.
Разница примерно в три раза на запрос, и чем больше префикс относительно ответа, тем она выше. Если же префикс короткий, а вывод длинный, кэш почти ничего не даст: считайте на своих пропорциях, а не по чужим процентам.
Код: считаем стоимость по usage
Сначала научитесь видеть расходы. Скрипт ниже читает поле usage из ответа OpenAI-совместимого API и учитывает кэшированные токены, если провайдер их возвращает (у OpenAI это prompt_tokens_details.cached_tokens, у DeepSeek prompt_cache_hit_tokens):
from dataclasses import dataclass
@dataclass
class Price:
input: float # $ за 1M токенов
cached: float # $ за 1M кэшированных input-токенов
output: float # $ за 1M токенов вывода
def cached_tokens(usage: dict) -> int:
details = usage.get("prompt_tokens_details") or {}
return details.get("cached_tokens") or usage.get("prompt_cache_hit_tokens") or 0
def request_cost(usage: dict, p: Price) -> float:
prompt = usage.get("prompt_tokens", 0)
cached = min(cached_tokens(usage), prompt)
out = usage.get("completion_tokens", 0)
return ((prompt - cached) * p.input + cached * p.cached + out * p.output) / 1_000_000
def cost_per_success(cost_per_attempt: float, attempts: float, success_rate: float) -> float:
if success_rate <= 0:
return float("inf")
return cost_per_attempt * attempts / success_rate
if __name__ == "__main__":
flash = Price(input=0.15, cached=0.003, output=0.6) # непиковые цены deepseek-flash
usage = {"prompt_tokens": 6000, "prompt_cache_hit_tokens": 5000, "completion_tokens": 300}
print(round(request_cost(usage, flash), 6)) # 0.000345
print(round(cost_per_success(0.002, 1, 0.6), 4)) # 0.0033
Складывайте результат в лог вместе с именем модели, версией промпта и меткой задачи: без этого нельзя ответить, какая функция продукта съедает бюджет. Как устроить такой лог, показано в статье про LLMOps: версии промптов и мониторинг.
Сократите контекст до того, как платить за него
- Не отправляйте историю целиком: оставьте последние N реплик, а старое сожмите в краткую сводку (одна дешёвая модель делает сводку раз в несколько шагов).
- В RAG ограничивайте число фрагментов и их длину; плохо нарезанные документы раздувают контекст без пользы.
- Описания инструментов агента занимают токены в каждом запросе. Держите только нужные для текущего шага и ставьте их в кэшируемый префикс.
- У рассуждающих моделей ограничивайте бюджет размышлений там, где это поддерживается. Токены размышлений тарифицируются как вывод.
Batch: когда результат не нужен сразу
Для массовых задач (разметка, классификация, суммаризация архива, оценка датасета) есть пакетные API. По документации OpenAI Batch API даёт 50% скидки относительно синхронных запросов, отдельные лимиты и окно выполнения до 24 часов; один пакет вмещает до 50 000 запросов. У Anthropic Message Batches тарифицируются по 50% от стандартных цен, большинство пакетов завершается в течение часа, но срок истекает через 24 часа. Если задача не интерактивная, это самая простая экономия, не требующая менять промпты.
Маршрутизация: дешёвая модель, затем эскалация
Идея роутера: отправить задачу на дешёвую модель, автоматически проверить результат и только при провале перейти к более сильной. Ключевое слово здесь «автоматически проверить»: без дешёвой проверки (парсинг JSON, схема, регулярное выражение, юнит-тест, сверка с источником) роутер превращается в лотерею. Пример на OpenAI-совместимом клиенте, где в TIERS можно подставить любые модели и base_url:
import json, os
from openai import OpenAI
TIERS = [
{"name": "cheap", "base_url": "https://api.deepseek.com", "key_env": "DEEPSEEK_API_KEY", "model": "deepseek-flash"},
{"name": "strong", "base_url": "https://openrouter.ai/api/v1", "key_env": "OPENROUTER_API_KEY", "model": "z-ai/glm-5.2"},
]
def valid(text: str) -> bool:
"""Дешёвая проверка: это JSON с нужными полями."""
try:
data = json.loads(text)
return isinstance(data.get("category"), str) and isinstance(data.get("confidence"), (int, float))
except (ValueError, AttributeError):
return False
def route(messages: list[dict], max_tokens: int = 300) -> dict:
last_error = None
for tier in TIERS:
client = OpenAI(base_url=tier["base_url"], api_key=os.environ[tier["key_env"]])
try:
r = client.chat.completions.create(
model=tier["model"], messages=messages, max_tokens=max_tokens, temperature=0,
)
except Exception as exc: # 429, 5xx, таймаут: идём на следующий уровень
last_error = exc
continue
text = r.choices[0].message.content or ""
if valid(text):
return {"tier": tier["name"], "text": text, "usage": r.usage.model_dump()}
raise RuntimeError(f"Все уровни не справились: {last_error}")
Что важно в реальной системе: ведите долю эскалаций (если дешёвый уровень проходит проверку реже 50-60% запросов, он не окупается), логируйте уровень, на котором получен ответ, и ограничивайте число уровней двумя-тремя. Для агентов, где дешёвые модели берут рутинные шаги, см. маленькие LLM в агенте. Цены и модели разных провайдеров сравнивайте на странице провайдеров.
Бесплатные API: что можно вынести на них
Бесплатные уровни полезны для прототипов, разметки небольших датасетов и дешёвого уровня роутера, но у них есть потолки:
- OpenRouter: для моделей с суффиксом
:freeдействуют дневные и минутные лимиты, они зависят от того, пополняли ли вы счёт; разбор в статье лимиты OpenRouter. - Groq: лимиты заданы на модель по запросам и токенам в минуту и в день. В таблице лимитов для
openai/gpt-oss-120bуказано 30 запросов в минуту, 1000 в день, 8K токенов в минуту и 200K в день; актуальные значения для вашего аккаунта показывает страница Limits. - Gemini: лимиты считаются на проект, а не на ключ, суточная квота сбрасывается в полночь по тихоокеанскому времени, и бесплатный доступ есть не у всех моделей, это видно в прайсе (у одной из моделей линейки указано, что free tier недоступен).
Практическое правило: бесплатный уровень ставьте в начало роутера на ненагруженных задачах и всегда держите платный резерв на ответ 429. Общий обзор вариантов есть в гайде по бесплатным LLM API. Конфиденциальные данные через бесплатные эндпоинты без проверки политики данных провайдера не отправляйте.
Как проверить, что оптимизация не сломала качество
- До изменений соберите набор из 50-200 реальных задач с критерием успеха и зафиксируйте базовую долю успеха и цену успешной задачи.
- Меняйте одну вещь за раз: кэш (качество не меняется, проверяйте только долю попаданий), сокращение контекста, замену модели, роутер.
- После каждого изменения прогоняйте тот же набор несколько раз: выход LLM недетерминирован, и одна пробежка не отличает улучшение от шума.
- Смотрите не только среднее, но и худшие случаи.
- В продакшене сравнивайте долю эскалаций, долю ручных исправлений и жалоб до и после, а изменения выкатывайте на часть трафика.
Итого
Порядок действий: сначала измерить цену успешной задачи и разложить расходы на input, cached и output; затем включить кэш, выстроив промпт как стабильный префикс и переменный хвост; вынести фоновые задачи в batch; сократить контекст; и лишь потом внедрять маршрутизацию с проверкой результата. После каждого шага сверяйтесь с набором проверок, а не с прайс-листом.