Содержание
Понять, как экономить токены в OpenRouter, важно ещё до того, как счёт за модели начнёт кусаться. Одна и та же задача может стоить в разы дешевле, если применить пару приёмов. В этой статье разберём четыре рычага: кэш промптов, липкую маршрутизацию, каскад моделей и субагентов. В конце добавим контроль расхода, чтобы траты не превращались в сюрприз. Весь код прогнан на реальном стенде, а цифры экономии настоящие.
Из чего складывается счёт
Плата в OpenRouter идёт за токены: отдельно за вход и отдельно за выход. Вход это ваш промпт со всей историей и контекстом, выход это ответ модели. У разных моделей цена за миллион токенов отличается в десятки раз. Значит, экономия сводится к двум вещам: гонять меньше дорогих токенов и отдавать задачу той модели, которая справится дешевле. Все четыре приёма ниже работают именно на это.
Прикинем на пальцах. Допустим, общий контекст занимает 10 тысяч токенов, и вы шлёте его сто раз за час. Без кэша вы каждый раз платите за все входные токены. С кэшем повторные запросы читают тот же контекст в разы дешевле. На таком объёме разница набегает быстро.
Если вы ещё не выпустили ключ, начните со статьи про старт с OpenRouter. Как управлять выбором провайдера, разобрано в статье про маршрутизацию. Здесь мы идём дальше и считаем деньги.
Дальше пройдём по этой схеме слева направо, каждый шаг на рабочем коде.
Кэш промптов
Часто в запросах повторяется один и тот же большой кусок: системная инструкция, справочник, длинный документ. Платить за него полную цену на каждом вызове обидно. Кэш промптов решает это: провайдер запоминает общий префикс, и повторный запрос читает его из кэша заметно дешевле. Чтение кэша стоит от 0.1 до 0.5 цены обычного входа, в зависимости от провайдера.
Как включить и проверить попадание
У Anthropic кэш включается явно: большой блок помечается полем cache_control. У OpenAI и Gemini кэш работает автоматически для длинных префиксов. Факт попадания видно в ответе: поле cached_tokens внутри usage показывает, сколько токенов прочитано из кэша. Проверим на модели Anthropic с явной пометкой.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
import os, httpx
key = os.environ["OPENROUTER_API_KEY"]
context = "Справочный контекст для кэша. " * 900
def ask():
payload = {
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "system", "content": [
{"type": "text", "text": context, "cache_control": {"type": "ephemeral"}}
]},
{"role": "user", "content": "Ответь одним словом: ок"},
],
"max_tokens": 5,
"session_id": "cache-demo-1",
"usage": {"include": True},
}
r = httpx.post("https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {key}"}, json=payload, timeout=60)
u = r.json()["usage"]
return u["prompt_tokens"], u["prompt_tokens_details"]["cached_tokens"], u["cost"]
for i in (1, 2):
p, cached, cost = ask()
print(f"вызов {i}: prompt={p} cached={cached} cost=${cost:.6f}")
вызов 1: prompt=10818 cached=0 cost=$0.013543 вызов 2: prompt=10818 cached=10801 cost=$0.001122
Первый вызов записал кэш, второй его прочитал. Из 10818 токенов входа 10801 пришли из кэша, и цена упала почти в 12 раз. У кэша есть минимальный размер префикса и короткое время жизни, у Anthropic по умолчанию пять минут. Полный сценарий в файле prompt_cache.py.
Пара нюансов важна на практике. У каждого провайдера свой минимальный размер префикса для кэша, обычно от 1024 токенов. Запись кэша у части провайдеров стоит чуть дороже обычного входа. Зато последующие чтения окупают это с запасом. Поэтому кэш выгоден там, где общий контекст реально переиспользуется, а не мелькает один раз.
Липкая маршрутизация через session_id
Кэш помогает только если следующий запрос попал к тому же провайдеру, где лежит тёплый кэш. У модели с несколькими провайдерами запрос легко улетает к другому, и кэш промахивается. Чтобы этого не было, есть липкая маршрутизация. Поле session_id закрепляет все запросы одной сессии за одним провайдером.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
pinned = [call({"session_id": "orders-bot-42"}) for _ in range(6)]
default = [call({}) for _ in range(6)]
print("session_id:", pinned, "->", len(set(pinned)), "провайдер")
print("default: ", default, "->", len(set(default)), "провайдера")
session_id: ['Crusoe','Crusoe','Crusoe','Crusoe','Crusoe','Crusoe'] -> 1 провайдер default: ['Crusoe','DeepInfra','AkashML','AkashML','AkashML','Cloudflare'] -> 4 провайдера
С фиксированным session_id все шесть запросов ушли к Crusoe. Без него роутер разбросал их по четырём провайдерам. В связке с кэшем это и даёт экономию: тёплый кэш остаётся достижимым. Полный сценарий в файле sticky_session.py.
По умолчанию OpenRouter и так старается держать диалог у одного провайдера. Он хеширует первое системное и первое обычное сообщение и по ним привязывает сессию. Явный session_id нужен, когда таких общих сообщений нет. Он же удобен, когда вы сами хотите управлять привязкой. Это частый случай для ботов и многопользовательских сервисов.
Каскад: дорогая модель только на сложное
Не каждой задаче нужна флагманская модель. Перевод слова или простое форматирование дешёвая модель делает не хуже. Каскад это простое правило на стороне приложения: лёгкие запросы идут на дешёвую модель, тяжёлые эскалируются на сильную. Признак сложности можно взять грубый, например длину запроса или пару ключевых слов.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, openai 2.53.0, OpenRouter API
def is_hard(prompt):
markers = ("докажи", "проанализируй", "спроектируй", "выведи формулу")
return len(prompt) > 200 or any(m in prompt.lower() for m in markers)
def route(prompt):
model = STRONG if is_hard(prompt) else CHEAP
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=60,
extra_body={"usage": {"include": True}},
)
return model, resp.usage.cost
openai/gpt-4o-mini cost=$0.000019 <- Переведи слово cat на русский openai/gpt-4o cost=$0.000673 <- Проанализируй риски миграции монолита
Простой запрос обошёлся в доли тысячной цента на дешёвой модели. Сложный ушёл на сильную и стоил в десятки раз дороже, но только он этого и требовал. Смысл каскада в том, чтобы дорогие токены тратились лишь там, где без них никак. Полный сценарий в файле model_cascade.py.
Эвристика по длине и словам груба и иногда промахивается. Для точности сложность оценивают отдельной дешёвой моделью или классификатором. Но даже простое правило снимает большую часть лишних трат. Главное не гонять поток лёгких запросов через флагман по привычке.
Субагенты: рутину на дешёвую модель
Каскад выбирает модель до запроса. Субагент идёт дальше и позволяет флагману сбрасывать рутину прямо по ходу генерации. Инструмент openrouter:subagent даёт модели воркера: когда попадается самодостаточный кусок вроде суммаризации или форматирования, оркестратор делегирует его дешёвому воркеру. Токены воркера считаются по его ставке, отдельно от оркестратора.
# протестировано на EU-ноде (AWS Stockholm) 2026-08-04: Python 3.12.3, httpx 0.28.1, OpenRouter API
payload = {
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": task}],
"tools": [{
"type": "openrouter:subagent",
"parameters": {"model": "openai/gpt-4o-mini", "max_completion_tokens": 200},
}],
"max_tokens": 400,
"usage": {"include": True},
}
# ... отправляем запрос и читаем usage.server_tool_use_details
делегировано воркеру: 1 раз общая стоимость запроса: $0.004565 вход $/М: оркестратор 2.5, воркер 0.15 (воркер дешевле в ~17 раз)
Оркестратор один раз делегировал рутину воркеру и собрал финальный ответ сам. Ту часть, что ушла воркеру, вы оплатили по цене дешёвой модели, а она по входу почти в 17 раз ниже. На объёме такой перенос рутины экономит заметно. Полный сценарий в файле subagent_demo.py.
Воркеру можно дать и свои инструменты, например веб-поиск, чтобы он опирался на свежие данные. От зацикливания есть защита: субагент не может вызвать сам себя. Делегировать стоит только самодостаточные куски. Совсем мелкую задачу быстрее сделать напрямую, чем описывать её воркеру.
Лимиты и алерты по расходу
Экономия бессмысленна без контроля. У ключа можно задать лимит расхода, а текущие траты видны через API. Эндпоинт /auth/key отдаёт лимит и расход за день, неделю и месяц, а /credits показывает баланс. На эти числа удобно повесить свой алерт.
лимит ключа: None остаток лимита: None расход за день: 0.057796 расход за месяц: 0.057831 баланс аккаунта: 10.0000, потрачено 0.057831
Здесь лимит на ключе не задан, поэтому потолок упирается только в баланс. Если выпустить ключ с лимитом, в поле остатка появятся оставшиеся средства, и запросы сверх лимита будут отклоняться. Это удобный предохранитель для командной работы и для ботов, которые могут разогнаться по расходу. Полный сценарий в файле spend_limit.py.
Для команды ключи с лимитом удобно выпускать программно. Тогда у каждого сервиса свой потолок, а общий баланс защищён. Тонкая настройка доступа и разделение окружений это уже тема отдельной статьи серии про безопасность и работу командой.
Рычаги не исключают друг друга, а складываются. Кэш и липкая маршрутизация работают в паре и держат общий контекст дешёвым. Каскад и субагент дополняют их на уровне выбора модели. На реальном потоке запросов выгоднее включить всё сразу и замерить эффект по счёту.
Ниже сводка по всем четырём рычагам, чтобы держать их под рукой.
| Рычаг | Что экономит | Когда применять |
|---|---|---|
| Кэш промптов | Повторный ввод общего контекста | Длинная общая инструкция или документ |
| Липкая маршрутизация | Промахи кэша между провайдерами | Диалоги и сессии с одной моделью |
| Каскад | Дорогие токены на простых задачах | Смешанный поток лёгких и тяжёлых запросов |
| Субагенты | Рутину внутри сложной генерации | Суммаризация и форматирование по ходу |
Что дальше в серии
Итог такой. Кэш снимает плату за повторный контекст, липкая маршрутизация держит кэш тёплым, каскад бережёт дорогие токены, а субагент сбрасывает рутину на воркера. Лимиты и алерты не дают счёту убежать. Вместе это ощутимо снижает стоимость при том же результате. В следующей статье серии соберём OpenRouter в n8n и make.com без единой строки кода, чтобы те же возможности были доступны в low-code сценариях.



