Ты построил бота, подключил базу данных, задеплоил на сервер. Всё работает. Но потом приходит счёт от Anthropic или OpenAI — и оказывается, что бот за месяц съел $200. А если пользователей станет 1000? $2000. Оптимизация стоимости — это не «потом», это «с первого дня». В этом уроке — конкретные техники, которые снижают расходы на 60-95%.
Каждый запрос к Claude или GPT стоит денег. Стоимость складывается из двух частей:
Стоимость одного запроса = Входные токены × Цена + Выходные токены × Цена
Входные токены (input):
Системный промпт + История чата + Вопрос пользователя
Выходные токены (output):
Ответ модели
Важно: выходные токены стоят в 3-5 раз дороже входных!
| Модель | Вход (за 1M токенов) | Выход (за 1M токенов) | Для чего |
|---|---|---|---|
| Claude Haiku 4.5 | $1.00 | $5.00 | Простые задачи, классификация |
| Claude Sonnet 4.6 | $3.00 | $15.00 | Баланс цена/качество |
| Claude Opus 4.6 | $5.00 | $25.00 | Сложные задачи, агенты |
| GPT-4o-mini | $0.15 | $0.60 | Самый дешёвый от OpenAI |
| GPT-4o | $2.50 | $10.00 | Основная модель OpenAI |
Аналогия. Модели — как рестораны. Haiku — это столовая: быстро, дёшево, для простых блюд. Sonnet — кафе: хорошее качество за разумную цену. Opus — ресторан с мишленовской звездой: лучшее качество, но за каждое блюдо платишь втрое.
Источник: Anthropic — Pricing
https://platform.claude.com/docs/en/about-claude/pricing
Самая простая и мощная оптимизация — не использовать дорогую модель там, где справится дешёвая.
┌─────────────────────────────────────────────────────────┐
│ КАКУЮ МОДЕЛЬ ДЛЯ КАКОЙ ЗАДАЧИ │
│ │
│ Haiku ($1/$5) │
│ Классификация: "позитивный / негативный" │
│ Извлечение данных: "найди email в тексте" │
│ Простые ответы: FAQ, шаблонные ответы │
│ Маршрутизация: "это вопрос про оплату или доставку?" │
│ │
│ Sonnet ($3/$15) │
│ Генерация текста: статьи, письма, описания │
│ Анализ: разбор документа, резюме │
│ Код: написание и объяснение кода │
│ Чат-бот: основной рабочий бот │
│ │
│ Opus ($5/$25) │
│ Сложный анализ: юридические документы, исследования │
│ Агенты: многошаговые задачи с инструментами │
│ Математика и логика: сложные вычисления │
│ Критические задачи: где ошибка стоит дорого │
└─────────────────────────────────────────────────────────┘
Вместо одной дорогой модели на все запросы — маленькая модель решает, какую использовать:
# router.py — маршрутизатор: выбирает модель в зависимости от сложности
from anthropic import Anthropic
client = Anthropic()
def classify_complexity(question):
"""Haiku классифицирует сложность вопроса."""
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=50,
messages=[{
"role": "user",
"content": f"""Оцени сложность вопроса: simple или complex.
simple = FAQ, факт, простой ответ
complex = анализ, рассуждение, многошаговая задача
Вопрос: {question}
Ответь одним словом: simple или complex"""
}]
)
return response.content[0].text.strip().lower()
def smart_answer(question):
"""Отправляет вопрос в подходящую модель."""
complexity = classify_complexity(question)
if complexity == "simple":
model = "claude-haiku-4-5" # $1/$5 — дешёвый
print(f" → Haiku (простой вопрос)")
else:
model = "claude-sonnet-4-6" # $3/$15 — средний
print(f" → Sonnet (сложный вопрос)")
response = client.messages.create(
model=model,
max_tokens=1024,
messages=[{"role": "user", "content": question}]
)
return response.content[0].text
# Примеры
print(smart_answer("Какая столица Франции?")) # → Haiku
print(smart_answer("Объясни теорию относительности")) # → Sonnet
Экономия: если 70% запросов простые — экономишь 60-70% бюджета.
Каждый запрос к Claude содержит:
Системный промпт: 500 токенов (одинаковый каждый раз!)
История разговора: 2000 токенов (растёт с каждым сообщением)
Вопрос пользователя: 50 токенов (новый каждый раз)
Без кеширования: каждый запрос = 2550 входных токенов
За 1000 запросов: 2,550,000 токенов × $3/1M = $7.65
С кешированием: системный промпт + история кешируются
Первый запрос: 2550 токенов (полная цена + запись в кеш)
Последующие: 50 новых токенов + 2500 из кеша (скидка 90%)
За 1000 запросов: ~$1.00
Экономия: ~87%
Запрос 1 (первый раз):
[Системный промпт] [История] [Вопрос]
────────────────── ────────── ───────
Записывается в кеш Записывается Обрабатывается
(×1.25 цена) в кеш (обычная цена)
Запрос 2 (из кеша):
[Системный промпт] [История] [Новый вопрос]
────────────────── ────────── ─────────────
Читается из кеша Читается Обрабатывается
(×0.1 цена — 90% скидка!) (обычная цена)
# Автоматическое кеширование (рекомендуемый способ)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
cache_control={"type": "ephemeral"}, # ← автоматическое кеширование
system="Ты — помощник техподдержки компании X. Вот правила: ...(длинный текст)...",
messages=[{"role": "user", "content": "Как вернуть товар?"}]
)
# Проверяем экономию
usage = response.usage
print(f"Входные токены: {usage.input_tokens}")
print(f"Из кеша: {getattr(usage, 'cache_read_input_tokens', 0)}")
print(f"Записано в кеш: {getattr(usage, 'cache_creation_input_tokens', 0)}")
Идеально:
✓ Большой системный промпт (500+ токенов)
✓ Много запросов с одинаковым контекстом
✓ Чат-бот с длинной историей
✓ RAG с одним и тем же набором документов
Не поможет:
✗ Каждый запрос уникальный (нет повторяющегося контекста)
✗ Промпт меньше 1024 токенов (минимум для кеширования)
✗ Запросы реже чем раз в 5 минут (кеш истекает)
Реальный результат: один разработчик описал снижение с $720 до $72 в месяц — экономия 90% — просто включив prompt caching.
Источники:
Anthropic — Prompt Caching
https://platform.claude.com/docs/en/build-with-claude/prompt-caching
Если запросы не требуют мгновенного ответа — отправляй их пакетом. API обработает их в фоне за 50% стоимости.
Обычный запрос:
Отправил → Получил ответ через 2 секунды
Цена: 100%
Batch (пакет):
Отправил 1000 запросов → Получил все ответы через 1-24 часа
Цена: 50% (скидка 50%)
Идеально для batch:
✓ Обработка 1000 отзывов → классификация
✓ Перевод 500 описаний товаров
✓ Генерация резюме для 200 документов
✓ Еженочная аналитика данных
✓ Подготовка ответов на FAQ
Не подходит для batch:
✗ Чат-бот (пользователь ждёт ответ прямо сейчас)
✗ Интерактивные приложения
✗ Срочные задачи
# batch_classify.py — классификация 100 отзывов за 50% стоимости
from anthropic import Anthropic
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
client = Anthropic()
# Список отзывов для классификации
reviews = [
"Отличный товар, буду заказывать ещё!",
"Ужасное качество, верну обратно.",
"Нормально, ничего особенного.",
# ... ещё 97 отзывов
]
# 1. Создаём пакет запросов
requests = [
Request(
custom_id=f"review-{i}",
params=MessageCreateParamsNonStreaming(
model="claude-haiku-4-5", # дешёвая модель для простой задачи
max_tokens=50,
messages=[{
"role": "user",
"content": f"Классифицируй отзыв одним словом (positive/negative/neutral): {review}"
}]
)
)
for i, review in enumerate(reviews)
]
# 2. Отправляем пакет
batch = client.messages.batches.create(requests=requests)
print(f"Пакет создан: {batch.id}")
print(f"Статус: {batch.processing_status}")
# 3. Ждём результатов (обычно 1-24 часа)
import time
while True:
batch = client.messages.batches.retrieve(batch.id)
if batch.processing_status == "ended":
break
print(f"Обработка... ({batch.request_counts.processing} в очереди)")
time.sleep(60)
# 4. Собираем результаты
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
text = next(
(b.text for b in result.result.message.content if b.type == "text"),
""
)
print(f"{result.custom_id}: {text}")
100 отзывов × Haiku:
Обычный API: ~$0.03
Batch API: ~$0.015 (скидка 50%)
Для 100 отзывов разница копеечная.
100 000 отзывов × Haiku:
Обычный API: ~$30
Batch API: ~$15
$15 экономии — уже заметно.
10 000 документов × Sonnet (генерация резюме):
Обычный API: ~$500
Batch API: ~$250
$250 экономии — существенно.
Batch API: -50%
Prompt caching: -90% на повторяющийся контекст
Вместе: до -95% от обычной цены
Пример: 10 000 запросов с общим системным промптом
Обычная цена: $500
Batch + caching: $25-50
Источники:
Anthropic — Batch Processing
https://platform.claude.com/docs/en/build-with-claude/batch-processing
# Плохо: модель генерирует длинный ответ
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096, # ← разрешаем длинный ответ
messages=[{"role": "user", "content": "Какая столица Франции?"}]
)
# Ответ: "Столица Франции — Париж. Париж является крупнейшим городом
# Франции и одним из важнейших... (500 токенов бла-бла-бла)"
# Стоимость вывода: 500 × $15/1M = $0.0075
# Хорошо: ограничиваем и инструктируем
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=256, # ← ограничиваем длину
messages=[{
"role": "user",
"content": "Какая столица Франции? Ответь одним предложением."
}]
)
# Ответ: "Столица Франции — Париж."
# Стоимость вывода: 10 × $15/1M = $0.00015
# Экономия: 50 раз дешевле!
# Плохо: раздутый промпт (300 токенов)
system = """Ты — дружелюбный помощник по имени Алекс.
Ты работаешь в компании "Электроника Плюс".
Ты должен всегда быть вежливым и профессиональным.
Ты должен отвечать на русском языке.
Если ты не знаешь ответ, скажи что не знаешь.
Не придумывай информацию.
Используй простой язык, понятный обычному человеку.
Если вопрос не по теме магазина, вежливо перенаправь.
Форматируй ответы с помощью списков, когда уместно.
Всегда спрашивай, нужна ли ещё помощь в конце ответа."""
# Хорошо: лаконичный промпт (100 токенов)
system = """Помощник "Электроника Плюс". Отвечай кратко, на русском.
Не знаешь — скажи. Не по теме — перенаправь. В конце: "Чем ещё помочь?"."""
# Проблема: история растёт → каждое сообщение дороже
# Плохо: отправляем ВСЮ историю (может вырасти до 50 000 токенов)
messages = full_conversation_history # 100 сообщений = 50K токенов
# Хорошо: ограничиваем историю последними N сообщениями
MAX_HISTORY = 10 # последние 10 сообщений
messages = full_conversation_history[-MAX_HISTORY:]
# Ещё лучше: суммаризация + последние сообщения
# (из урока 3.3 — память агента)
def optimize_history(messages, max_messages=10):
"""Если история длинная — суммаризируем старые сообщения."""
if len(messages) <= max_messages:
return messages
old_messages = messages[:-max_messages]
recent_messages = messages[-max_messages:]
# Суммаризируем старые сообщения (дёшево, один раз)
summary = summarize(old_messages) # из урока 3.3
return [
{"role": "user", "content": f"Краткое содержание предыдущего разговора: {summary}"},
{"role": "assistant", "content": "Понял, продолжаем."},
*recent_messages
]
Claude позволяет контролировать, сколько думать перед ответом:
# Простой вопрос — минимальные усилия
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
output_config={"effort": "low"}, # ← быстро и дёшево
messages=[{"role": "user", "content": "Переведи 'hello' на русский"}]
)
# Сложная задача — максимальные усилия
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=8192,
output_config={"effort": "max"}, # ← глубокий анализ (только Opus)
messages=[{"role": "user", "content": "Проанализируй этот контракт..."}]
)
Уровни усилий:
low — быстрый ответ, минимум рассуждений. Для простых задач.
medium — стандартный баланс.
high — глубокий анализ (по умолчанию).
max — максимальная глубина (только Opus 4.6).
Используй low для простых задач — модель отвечает быстрее и тратит меньше выходных токенов.
# cost_tracker.py — простой трекер расходов
import json
from datetime import datetime
COST_LOG = "api_costs.jsonl"
# Цены за 1 токен (для расчёта)
PRICES = {
"claude-haiku-4-5": {"input": 1.0 / 1_000_000, "output": 5.0 / 1_000_000},
"claude-sonnet-4-6": {"input": 3.0 / 1_000_000, "output": 15.0 / 1_000_000},
"claude-opus-4-6": {"input": 5.0 / 1_000_000, "output": 25.0 / 1_000_000},
}
def log_cost(model, usage):
"""Записывает стоимость запроса в лог."""
prices = PRICES.get(model, PRICES["claude-sonnet-4-6"])
input_cost = usage.input_tokens * prices["input"]
output_cost = usage.output_tokens * prices["output"]
total_cost = input_cost + output_cost
entry = {
"timestamp": datetime.now().isoformat(),
"model": model,
"input_tokens": usage.input_tokens,
"output_tokens": usage.output_tokens,
"cost_usd": round(total_cost, 6),
}
with open(COST_LOG, "a") as f:
f.write(json.dumps(entry) + "\n")
return total_cost
def get_daily_spend():
"""Считает расходы за сегодня."""
today = datetime.now().date().isoformat()
total = 0.0
try:
with open(COST_LOG) as f:
for line in f:
entry = json.loads(line)
if entry["timestamp"].startswith(today):
total += entry["cost_usd"]
except FileNotFoundError:
pass
return total
# Использование
from anthropic import Anthropic
client = Anthropic()
model = "claude-sonnet-4-6"
response = client.messages.create(
model=model,
max_tokens=1024,
messages=[{"role": "user", "content": "Привет!"}]
)
cost = log_cost(model, response.usage)
print(f"Запрос стоил: ${cost:.4f}")
print(f"Потрачено сегодня: ${get_daily_spend():.4f}")
# Проверяем бюджет перед каждым запросом
DAILY_BUDGET = 5.00 # $5 в день
def safe_request(model, messages, max_tokens=1024):
"""Запрос с проверкой бюджета."""
daily_spend = get_daily_spend()
if daily_spend >= DAILY_BUDGET:
return "Дневной бюджет исчерпан. Попробуйте завтра."
response = client.messages.create(
model=model,
max_tokens=max_tokens,
messages=messages
)
cost = log_cost(model, response.usage)
print(f"Расход: ${cost:.4f} | Сегодня: ${daily_spend + cost:.4f} / ${DAILY_BUDGET}")
return response.content[0].text
Когда нужен конкретный формат — используй structured output. Модель возвращает JSON по схеме, без лишнего текста.
# Без structured output:
# "Давайте проанализируем этот отзыв. Он содержит позитивные элементы,
# в частности... В целом, я бы классифицировал его как позитивный
# с уверенностью около 0.85..."
# → 80 токенов вывода
# Со structured output:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=100,
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
"confidence": {"type": "number"}
},
"required": ["sentiment", "confidence"],
"additionalProperties": False
}
}
},
messages=[{
"role": "user",
"content": "Классифицируй: 'Отличный товар, рекомендую!'"
}]
)
# → {"sentiment": "positive", "confidence": 0.95}
# → 15 токенов вывода (в 5 раз меньше!)
┌─────────────────────────────────────────────────────────┐
│ СТРАТЕГИИ ОПТИМИЗАЦИИ СТОИМОСТИ │
│ │
│ Стратегия Экономия Сложность Когда │
│ ───────── ──────── ───────── ───── │
│ Правильная модель 60-80% Низкая Всегда │
│ Prompt caching до 90% Низкая Повторы │
│ Batch API 50% Средняя Не срочно │
│ Короткие ответы 50-90% Низкая Всегда │
│ Управление историей 30-70% Средняя Чат-боты │
│ Effort parameter 20-50% Низкая Простые │
│ Structured output 50-80% Средняя Извлечение │
│ Маршрутизатор 60-70% Высокая Много задач │
│ │
│ Комбинация всех: до 95% │
└─────────────────────────────────────────────────────────┘
Сценарий: Telegram-бот техподдержки
1000 пользователей
5 сообщений в день на пользователя
= 5000 запросов в день
= 150 000 запросов в месяц
Без оптимизации (Sonnet, длинные ответы):
Средний запрос: 1000 входных + 500 выходных токенов
Входные: 150M × $3/1M = $450
Выходные: 75M × $15/1M = $1125
Итого: ~$1575/мес
С полной оптимизацией:
1. Маршрутизатор: 70% запросов → Haiku
Haiku: 105K запросов × (1000×$1/1M + 200×$5/1M) = $210
Sonnet: 45K запросов × (1000×$3/1M + 300×$15/1M) = $337
2. Prompt caching: -90% на повторяющийся контекст
Эффективная стоимость: ~$80
3. Короткие ответы: "Ответь в 2-3 предложениях"
Ещё -40% на выходных токенах: ~$50
Итого: ~$50-80/мес (вместо $1575!)
Экономия: 95%
# Проверяем стоимость запроса ДО отправки
count = client.messages.count_tokens(
model="claude-sonnet-4-6",
messages=messages,
system=system_prompt
)
estimated_cost = count.input_tokens * 3.0 / 1_000_000
print(f"Входных токенов: {count.input_tokens}")
print(f"Оценка стоимости входа: ${estimated_cost:.4f}")
# Если слишком дорого — обрежь историю или используй модель дешевле
if count.input_tokens > 10_000:
print("Слишком длинный контекст! Суммаризирую историю...")
messages = optimize_history(messages)
Добавь log_cost() из этого урока в бота из урока 4.1. После каждого запроса к Claude записывай стоимость в файл. В конце дня выведи общую сумму.
Реализуй classify_complexity() из этого урока. Протестируй на 10 разных вопросах: простые («Который час?») и сложные («Объясни квантовую запутанность»). Правильно ли маршрутизатор выбирает модель?
Возьми системный промпт из бота (урок 4.1) и сократи его в 2-3 раза, сохранив смысл. Сравни качество ответов до и после. Изменилось ли что-то?
Задача 1: Почему выходные токены дороже входных?
Задача 2: Бот отправляет 10 000 запросов в день с одинаковым системным промптом на 2000 токенов. Какую стратегию применить первой?
Задача 3: Когда НЕ стоит использовать Batch API?
Задача 4: Что эффективнее: сократить системный промпт с 1000 до 500 токенов или ограничить ответ с 500 до 100 токенов?
| Термин | Что значит |
|---|---|
| Входные токены (input) | Токены, которые ты отправляешь модели (промпт + история + вопрос) |
| Выходные токены (output) | Токены, которые модель генерирует в ответ (стоят в 3-5× дороже) |
| Prompt caching | Кеширование повторяющегося контекста на серверах Anthropic — скидка 90% |
| Batch API | Пакетная обработка запросов в фоне — скидка 50% |
| Маршрутизатор моделей | Паттерн: дешёвая модель решает, какую модель использовать для ответа |
| Effort parameter | Управление глубиной рассуждений модели (low/medium/high/max) |
| Structured output | Принудительный формат ответа (JSON по схеме) — меньше лишних токенов |
| Token counting | Подсчёт токенов ДО отправки запроса для оценки стоимости |
| max_tokens | Ограничение максимальной длины ответа модели |
| cache_control | Параметр API для включения кеширования промптов |
| Суммаризация истории | Замена длинной истории чата кратким пересказом для экономии токенов |
| TTL (Time to Live) | Время жизни кеша: 5 минут (по умолчанию) или 1 час |
Проблема:
API стоит денег. Без оптимизации бот с 1000 пользователей
может обходиться в $1500/мес.
7 стратегий (от простого к сложному):
1. Правильная модель — Haiku для простого, Sonnet для среднего
2. Prompt caching — 90% скидка на повторяющийся контекст
3. Batch API — 50% скидка на фоновые задачи
4. Короткие ответы — "ответь в 2 предложениях" = в 5 раз дешевле
5. Управление историей — не отправлять 100 сообщений, достаточно 10
6. Effort parameter — low для простых задач
7. Structured output — JSON вместо текста = меньше токенов
Правила:
• Выходные токены в 3-5× дороже входных → контролируй длину ответа
• Считай расходы с первого дня → log_cost() в каждый запрос
• Комбинируй стратегии → batch + caching = до 95% экономии
• Ставь лимиты → DAILY_BUDGET защищает от сюрпризов
Мы прошли все инструменты: от первого API-запроса до оптимизации на тысячи пользователей. В финальном уроке — архитектура AI-продуктов: как объединить всё изученное в одну систему. Проектирование, паттерны, масштабирование и реальные архитектуры продуктов.