Учебник

Как снизить счёт за AI в 3-5 раз без программиста за вечер

Ваш бизнес использует AI-чат или генерацию ответов, а счёт за API растёт на тысячи долларов в месяц. Разбираем три простых способа сократить расходы на 60-80%: кеширование промптов у провайдера, кеширование ответов через Redis и маршрутизацию запросов по сложности. Всё настраивается за 2-8 часов, без найма разработчиков.

Макс Космов··6 мин чтения

У вас болит: менеджеры каждый день задают AI-помощнику одни и те же вопросы про прайс и условия, а вы платите за каждый ответ как за новый. При 100 000 запросов в месяц разница между оптимизированным и неоптимизированным стеком - $2000-6000. При миллионе - десятки тысяч. Большинство оптимизаций занимают 1-3 дня и не требуют снижения качества.

Что входит в счёт за AI

Счёт за LLM API (языковые модели от OpenAI, Anthropic) состоит из трёх частей:

  • Input токены - то, что вы отправляете модели (системный промпт + контекст + вопрос). Цена: для Claude Sonnet 4.5 - $3 за 1 млн токенов, для GPT-4o - $2.50, для Claude Haiku 3 - $0.25.
  • Output токены - ответ модели. Они дороже в 3-5 раз: Claude Sonnet 4.5 - $15/1M, GPT-4o - $10/1M.
  • Cached токены - то, что модель запомнила после предыдущих запросов. Самый дешёвый тип: у Claude скидка 90% ($0.30/1M), у OpenAI - 50% (~$1.25/1M).

Разберём на примере. Допустим, у вас AI-помощник для консультаций по стройматериалам. Каждый запрос содержит системный промпт (500 токенов), контекст из каталога (3000 токенов), вопрос клиента (50 токенов) и ответ (300 токенов).

Без кеша: (3550 × $3 + 300 × $15) / 1M = $0.0107 за запрос. С кешированием промпта (системный + контекст): 3500 cached × $0.30/1M + 50 input × $3/1M + 300 output × $15/1M = $0.0057 за запрос. Экономия 47%.

tiktoken: подсчёт токенов до отправки запроса

tiktoken - библиотека от OpenAI для точного подсчёта токенов без вызова API. Нужна, чтобы прикинуть стоимость до того, как отправить запрос.

import tiktoken

def estimate_cost(messages: list[dict], model: str = "gpt-4o") -> dict:
 """Точная оценка стоимости до отправки."""
 enc = tiktoken.encoding_for_model(model)
 
 tokens_per_message = 3
 tokens_per_name = 1
 
 total_tokens = 0
 for message in messages:
 total_tokens += tokens_per_message
 for key, value in message.items():
 total_tokens += len(enc.encode(str(value)))
 if key == "name":
 total_tokens += tokens_per_name
 total_tokens += 3
 
 prices = {"gpt-4o": {"input": 2.50, "output": 10.00}}
 cost_per_1m = prices.get(model, {"input": 3.00, "output": 15.00})
 
 return {
 "input_tokens": total_tokens,
 "estimated_cost_input": total_tokens * cost_per_1m["input"] / 1_000_000,
 }

result = estimate_cost(messages, "gpt-4o")
if result["estimated_cost_input"] > 0.05:
 logger.warning(f"Expensive request: {result}")

Для batch-оценки датасета tiktoken критически важен: предварительный подсчёт 10 000 примеров занимает секунды и позволяет оценить месячный счёт до запуска.

import pandas as pd
import tiktoken

enc = tiktoken.get_encoding("cl100k_base")

df = pd.read_csv("training_data.csv")
df["prompt_tokens"] = df["prompt"].apply(lambda x: len(enc.encode(x)))
df["completion_tokens"] = df["completion"].apply(lambda x: len(enc.encode(x)))

print(f"Total input tokens: {df['prompt_tokens'].sum():,}")
print(f"Estimated monthly cost (GPT-4o): ${df['prompt_tokens'].sum() * 2.5 / 1e6:.2f}")

Prompt Caching: кеширование у провайдера

Prompt caching - это когда модель запоминает начало вашего промпта между запросами. Если промпт повторяется, вы платите меньше.

OpenAI делает это автоматически, если промпт длиннее 1024 токенов и его начало повторяется. Скидка - 50% на кешированные input-токены. Для максимальной эффективности статичную часть ставьте первой:

messages = [
 {"role": "system", "content": LONG_STATIC_SYSTEM_PROMPT},
 {"role": "user", "content": f"Контекст базы знаний:\n{LARGE_KNOWLEDGE_BASE}"},
 {"role": "assistant", "content": "Понял, готов отвечать на вопросы."},
 {"role": "user", "content": user_question}
]

Anthropic требует явного указания cache_control. Минимум 1024 токена для Haiku, 2048 для Sonnet/Opus. Скидка - 90% на кешированные токены.

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
 model="claude-sonnet-4-5",
 max_tokens=1024,
 system=[
 {
 "type": "text",
 "text": LONG_SYSTEM_PROMPT,
 "cache_control": {"type": "ephemeral"}
 }
 ],
 messages=[
 {
 "role": "user",
 "content": [
 {
 "type": "text",
 "text": LARGE_CONTEXT_DOCUMENT,
 "cache_control": {"type": "ephemeral"}
 },
 {"type": "text", "text": user_question}
 ]
 }
 ]
)

usage = response.usage
print(f"Cache hit: {usage.cache_read_input_tokens} tokens")
print(f"Cache write: {usage.cache_creation_input_tokens} tokens")

Для RAG-систем (когда AI ищет ответ в вашей базе знаний) с большим системным промптом или статичным контекстом экономия 40-60% реальна с первого дня.

Semantic Caching: кеширование ответов через Redis

Semantic caching отличается от prompt caching: вы кешируете финальные ответы на своей стороне, используя векторное сходство запросов. Если новый запрос похож на уже отвеченный (например, «как заказать доставку» и «как оформить доставку»), API не вызывается - ответ берётся из кеша.

from langchain.cache import RedisSemanticCache
from langchain.globals import set_llm_cache
from langchain_openai import OpenAIEmbeddings
import redis

redis_url = "redis://localhost:6379"
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

set_llm_cache(
 RedisSemanticCache(
 redis_url=redis_url,
 embedding=embeddings,
 score_threshold=0.95
 )
)

from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-4-5")

response1 = llm.invoke("Что такое LoRA в контексте fine-tuning?")

response2 = llm.invoke("Объясни LoRA для дообучения моделей")

Исследования RedisLabs показывают экономию 68.8% API-вызовов на типичных FAQ/поддержке. Ключевые параметры:

  • TTL: 1-24 часа для динамичных данных, 7 дней для стабильных
  • Порог сходства: 0.92-0.95 для баланса между попаданиями и качеством
  • Отдельный кеш по пользователю: для персонализированных систем

Model routing: маршрутизация по сложности

Не все запросы одинаково сложны. Простой вопрос «сколько стоит бетон?» не требует дорогой модели - достаточно дешёвой. Маршрутизация по сложности даёт 40-60% экономии без ухудшения качества.

# litellm_config.yaml
model_list:
 - model_name: smart
 litellm_params:
 model: anthropic/claude-sonnet-4-5
 api_key: os.environ/ANTHROPIC_API_KEY
 - model_name: fast
 litellm_params:
 model: anthropic/claude-haiku-3
 api_key: os.environ/ANTHROPIC_API_KEY
 - model_name: balanced
 litellm_params:
 model: openai/gpt-4o-mini
 api_key: os.environ/OPENAI_API_KEY

router_settings:
 routing_strategy: least-busy
 fallbacks: [{"smart": ["balanced", "fast"]}]
 
budget_manager:
 max_budget: 100
 strategy: cost

Функция выбора модели по сложности:

from litellm import Router

router = Router(config_file_path="litellm_config.yaml")

def route_by_complexity(prompt: str, context_length: int) -> str:
 COMPLEX_KEYWORDS = ["проанализируй", "сравни", "объясни почему", "дай подробный"]
 
 is_complex = (
 any(kw in prompt.lower() for kw in COMPLEX_KEYWORDS)
 or context_length > 10000
 or len(prompt) > 500
 )
 return "smart" if is_complex else "fast"

model = route_by_complexity(user_prompt, len(context))
response = router.completion(model=model, messages=messages)

Сравнение стоимости за 1 млн input токенов: Claude Haiku 3 - $0.25, GPT-4o-mini - $0.15, Claude Sonnet 4.5 - $3.00, GPT-4o - $2.50. Если 80% запросов направлять на дешёвые модели, а 20% - на дорогие, средняя стоимость составит ~$0.65/1M - экономия 75% против использования только Sonnet.

Output compression и пакетная обработка

Output compression - сокращение длины ответов. Вместо развёрнутых текстов просите модель отвечать кратко или в формате JSON.

system_prompt = """Отвечай строго в JSON:
{"answer": "краткий ответ до 2 предложений", "key_points": ["пункт1", "пункт2"]}"""

При переходе на structured JSON output токены сокращаются на 30-50% без потери информации.

OpenAI Batch API даёт скидку 50% при асинхронной обработке с выполнением в течение 24 часов. Подходит для: генерация описаний товаров, ночная аналитика, суммаризация документов.

from openai import OpenAI
import json

client = OpenAI()

batch_requests = [
 {"custom_id": f"req-{i}", "method": "POST", "url": "/v1/chat/completions",
 "body": {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": doc}]}}
 for i, doc in enumerate(documents)
]

with open("/tmp/batch.jsonl", "w") as f:
 for req in batch_requests:
 f.write(json.dumps(req, ensure_ascii=False) + "\n")

batch_file = client.files.create(file=open("/tmp/batch.jsonl", "rb"), purpose="batch")
batch = client.batches.create(input_file_id=batch_file.id, конечная точка API="/v1/chat/completions", completion_window="24h")
print(f"Batch ID: {batch.id}")

Anthropic Message Batches работает аналогично с той же скидкой 50%.

Реальный пример: снижение с $8000 до $1600 в месяц

Разберём на примере RAG-продукта для техподдержки стройфирмы. 200 000 запросов в месяц, Claude Sonnet 4.5, средний запрос 4000 input + 400 output токенов.

Исходный счёт: 200 000 × (4000 × $3 + 400 × $15) / 1M = $3600/мес. Реальный счёт был выше из-за избыточных промптов - $8000/мес.

Шаг 1 - Anthropic Prompt Caching на системный промпт (800 токенов) и базовый контекст (2000 токенов): кеш hit rate 85%. Экономия: ~$1500/мес.

Шаг 2 - Semantic Caching (Redis LangCache): 35% запросов - FAQ-вопросы с высоким сходством. Cache hit rate 35%, TTL 6 часов. Экономия: ~$1200/мес.

Шаг 3 - Model routing: 60% простых вопросов переведены на Claude Haiku ($0.25/1M vs $3/1M). Экономия: ~$2100/мес.

Шаг 4 - Output compression: structured output вместо prose. Средний output сократился с 400 до 220 токенов. Экономия: ~$600/мес.

Итог: $8000 -> $1600/мес. Снижение 80% за 3 недели разработки.

Частые вопросы

Prompt caching и semantic caching - одно и то же?

Нет. Prompt caching - кеширование начала промпта на серверах провайдера; экономит деньги, но не снижает количество API-вызовов. Semantic caching - кеширование финальных ответов в Redis; полностью исключает API-вызов при попадании. Они совместимы и дополняют друг друга.

Как убедиться, что кеш не возвращает устаревшие ответы?

Для semantic caching - установите TTL и обнуляйте кеш при обновлении знаний. Для запросов, чувствительных ко времени (цены, даты), добавляйте timestamp в запрос - это гарантированно меняет хеш и не попадает в кеш. Отслеживайте cache hit rate по категориям.

LiteLLM router vs собственная логика маршрутизации?

LiteLLM проще запустить и поддерживать: встроенный fallback, retry, бюджетный менеджер, учёт затрат. Собственная логика оправдана только если нужны специфические правила (например, маршрутизация по уровню пользователя). Начинайте с LiteLLM.

Насколько деградирует качество при переходе на GPT-4o-mini?

Зависит от задачи. На извлечение данных, FAQ, классификацию - разница минимальна (1-3%). На сложные рассуждения - падение 10-20%. Правильный подход: протестируйте на своих данных перед внедрением.

Как отслеживать ROI каждой оптимизации?

Используйте Langfuse - добавляйте к трейсам метаданные: cache_hit, model_used, routing_tier. Стройте дашборды: стоимость по модели, cache hit rate, экономия от кеширования. Так вы увидите, что работает.

Что дальше

После контроля стоимости - следующая задача: безопасное тестирование изменений в боевой среде. Начните с малого: включите кеширование у провайдера (Anthropic или OpenAI) - это даст 40-60% экономии без единой строчки кода с вашей стороны. Если ваш AI-помощник работает через готовый сервис (например, Chatbase или Botpress), уточните в поддержке, поддерживают ли они prompt caching. Если да - вы уже сэкономили.

Если используете свой код - возьмите шаблон промпта с cache_control из раздела про Anthropic и вставьте его в свой проект. Это займёт не больше часа.

AI Компас (t.me/kosmoslab_ai) - канал для предпринимателей в РФ и СНГ, которые применяют AI в своём бизнесе без программиста. Разбираем инструменты и схемы - без курсов и теории.