METABYTE
К списку статей

Почему ваш AI‑чатбот стоит $30k/мес и как снизить расходы

Разбираем реальные драйверы стоимости LLM‑чатботов: токены, контекст, RAG, tool‑calling, кэш. Даём архитектуру и код, как урезать счёт в 2–6 раз без потери качества.

17 мая 202612 мин чтенияAI-research draft
Почему ваш AI‑чатбот стоит $30k/мес и как снизить расходы

Если ваш счёт за чатбот на LLM приближается к $30k в месяц — значит, вы платите не только за интеллект модели, но и за архитектурные потери: раздутый контекст, лишние tool‑calls, отсутствующий кэш и слабую маршрутизацию по моделям. Эти ошибки системные и дорогие.

Коротко: расходы взлетают от трёх вещей — неправильная модель «по умолчанию», слишком длинный контекст без обрезки и агрессивный tool‑calling от RAG/плагинов. Рецепт экономии: маршрутизатор моделей (cheap/fast vs smart), строгая диета токенов (промпт + память + сжатие контекста), семантический кэш, дисциплина функций и телеметрия «стоимости». Ниже — как это выглядит в продакшене.

Где сгорают деньги в LLM‑чатботах

Главная валюта — токены. Итоговая цена = входные токены + выходные токены + эмбеддинги + RAG‑инфраструктура + накладные на логи и ретраи. Типичный разбор одной «дорогой» сессии:

  • Системный промпт 1–3k токенов (роль, стиль, политика безопасности, длинный список инструментов).
  • Память пользователя 3–10k токенов (история, профили, последние N сообщений).
  • Сообщение пользователя 200–800 токенов.
  • RAG: 5–20 документов по 300–800 токенов каждый (реже — «сжатые» версии, чаще — сырые блоки).
  • Функции: 1–3 tool‑call до БД, поиска, платежей, плюс egress/ingress токены.
  • Выход модели 300–1.5k токенов.

Даже при «умеренных» настройках одна реплика может тянуть 5–15k входных токенов. Если вы используете дорогую модель для всех запросов, получите счёт уровня десятков тысяч. И это без учёта ретраев, где токены сгорают повторно.

Быстрый sanity‑чек: откуда $30k/мес

Возьмём порядок цен 2024 для крупных моделей (условно): вход $3–5 за 1M токенов, выход $10–15 за 1M токенов у «сильной» модели; у «мини» — $0.1–0.3 и $0.5–0.7 соответственно. Если у вас 200k обращений/мес, средний запрос — 6k входных + 600 выходных токенов на «тяжёлой» модели, получаем: вход 1.2e9 токенов ($6k), выход 1.2e8 токенов ($1.8k). Звучит терпимо — но добавьте RAG‑эмбеддинги, ретраи, tool‑calls, всплески контекста до 20k, ночные бэч‑прогоны, и в реальности легко выезжаем к $20–40k. Ошибка — считать «средний» кейс, игнорируя «хвост» (длинные диалоги и edge‑кейсы). Именно хвост сжигает бюджет.

Базовая архитектура недорогого чатбота

Цель — разделить «ум» и «дисциплину». Ум — это модель и RAG. Дисциплина — это маршрутизация, бюджетирование токенов, кэш, троттлинг и телеметрия.

Архитектурный скелет:

  • API‑шлюз: аутентификация, лимиты по тарифам/организациям, rate‑limit на пользователя.
  • Cost‑Guard Middleware: считает ожидаемые токены, обрезает контекст, запрещает дорогие пути при исчерпании бюджета, устанавливает «сейф‑слова» для выхода из глубоких цепочек рассуждений.
  • Router (LLM Policy): дешёвая модель по умолчанию; переход на «умную» по детектору сложности (критерии: длина, наличие кода/SQL, неуверенность, история неудачных ответов).
  • RAG Service: эмбеддинги, поиск (гибрид: BM25 + векторы), переранжирование и сжатие контента (LLM‑компрессор перед подстановкой в промпт).
  • Semantic Cache: кэш по смысловой близости вопроса и контекста; возврат готовых ответов/планов действий при высокой близости.
  • Tool Gateway: единая прослойка для функций (БД, платежи, внешние API) с бюджетом на число вызовов и временем.
  • Providers: OpenAI/Anthropic/DeepSeek API + локальный vLLM/LM Studio кластер под узкие задачи.
  • Telemetry: лог токенов, стоимости, «прыжков» маршрутизатора и tool‑calls. Дашборды и алерты по SLA/стоимости.

Поток запроса:

  1. Пользователь → API → нормализация текста, детект языка, проверка тарифа.
  2. Cost‑Guard прикидывает токены и обрезает историю/контекст по «бюджету на шаг».
  3. Semantic Cache: если попадание > τ, сразу отдаём кэш или короткий ответ + ссылки.
  4. Router выбирает модель: mini/sonnet/4o и т.п.
  5. RAG (по необходимости): топ‑k, фильтры, переранжирование, компрессия цитат.
  6. Модель отвечает, потоково стримим пользователю (SSE/WebSocket), параллельно считаем стоимость и пишем метрики.

Мы часто видим, как пропущенный пункт 2 или 3 удваивает счёт. Один слой дисциплины окупает день внедрения.

Три главных рычага экономии

1) Маршрутизатор моделей

Дешёвая модель (mini) справляется с 60–80% бытовых вопросов, переформулировок и «коротких» задач. Сильная нужна для редких сложных кейсов (много контекста, программирование, правовые формулировки). Простой эвристический роутер уже экономит 30–50%. Подбор моделей по продакшен‑кейсам мы разбирали в статье OpenAI GPT‑5 vs Claude vs DeepSeek для продакшн‑SaaS.

2) Диета токенов: контекст и память

  • Системный промпт: держите <600–800 токенов. Все политики вынесите в «модули» и подмешивайте по необходимости.
  • История: храните краткий саммари последних N сообщений, полную историю — в cold storage; при всплеске — динамически ресуммаризируйте.
  • RAG‑вставки: до 2–4 документов по 150–300 токенов каждый, после компрессии; добавляйте цитаты/ссылки вместо полного текста.

3) Семантический кэш

Кэшируйте:

  • Частые вопросы (FAQ‑уровень),
  • «Планы» для tool‑calling (разобранная последовательность действий),
  • Результаты тяжёлых запросов к базе/поиску.

Семантический кэш снижает стоимость до 2–3 раз на повторяющихся доменных вопросах. Для сегментов b2b с повторами — must‑have.

// TypeScript: middleware + простой роутер и кэш
import crypto from 'crypto'
import { createClient } from 'redis'

const redis = createClient({ url: process.env.REDIS_URL })
await redis.connect()

type Model = 'mini' | 'smart'

function estimateTokens(s: string) {
  // Грубая оценка: 1 токен ≈ 4 символа латиницы / 2–3 кириллицы
  return Math.ceil(Buffer.byteLength(s, 'utf8') / 3.5)
}

function chooseModel(input: string, contextTokens: number): Model {
  const hasCode = /```|SELECT |INSERT |function\s|class\s|\{|\}|<\/?\w+>/.test(input)
  const long = contextTokens > 4000
  if (hasCode || long) return 'smart'
  if (input.length > 800 && /юридическ|договор|регламент/i.test(input)) return 'smart'
  return 'mini'
}

async function semanticCacheKey(userId: string, q: string, contextHash: string) {
  const h = crypto.createHash('sha256').update(q + '|' + contextHash).digest('hex')
  return `semcache:${userId}:${h}`
}

async function getCached(userId: string, q: string, contextHash: string) {
  const key = await semanticCacheKey(userId, q, contextHash)
  const v = await redis.get(key)
  return v ? JSON.parse(v) : null
}

async function setCached(userId: string, q: string, contextHash: string, value: any, ttlSec = 86400) {
  const key = await semanticCacheKey(userId, q, contextHash)
  await redis.set(key, JSON.stringify(value), { EX: ttlSec })
}

interface CostPolicy { maxIn: number; maxOut: number; maxToolCalls: number }

const POLICIES: Record<string, CostPolicy> = {
  free: { maxIn: 4000, maxOut: 600, maxToolCalls: 1 },
  pro:  { maxIn: 12000, maxOut: 1200, maxToolCalls: 3 },
  ent:  { maxIn: 24000, maxOut: 2000, maxToolCalls: 5 },
}

async function handleChat(req, res) {
  const { userId, plan, message, history, ragSnippets } = req.body
  const policy = POLICIES[plan] || POLICIES.free

  const sysPrompt = 'Ты ассистент. Отвечай кратко. Цитируй источники при RAG.'
  let context = [sysPrompt, history?.summary || '', ...(ragSnippets || [])].join('\n')

  // Бюджет входных токенов
  let budget = policy.maxIn
  const msgTokens = estimateTokens(message)
  let ctxTokens = estimateTokens(context)

  while (ctxTokens + msgTokens > budget && context.length > 200) {
    // Грубая обрезка контекста (в реале — «умное» сжатие LLM)
    context = context.slice(Math.floor(context.length * 0.7))
    ctxTokens = estimateTokens(context)
  }

  const contextHash = crypto.createHash('md5').update(context).digest('hex')
  const cached = await getCached(userId, message, contextHash)
  if (cached) return res.json({ ...cached, cached: true })

  const model: Model = chooseModel(message, ctxTokens + msgTokens)

  const response = await callProvider(model, {
    input: message,
    context,
    maxOutputTokens: policy.maxOut,
    toolCallBudget: policy.maxToolCalls,
  })

  // Телеметрия стоимости (упрощённая)
  const cost = estimateTokens(context + message) * (model === 'smart' ? 0.00002 : 0.000002)
             + estimateTokens(response.text) * (model === 'smart' ? 0.00006 : 0.000006)

  await setCached(userId, message, contextHash, { text: response.text, model, cost })
  return res.json({ text: response.text, model, cost })
}

async function callProvider(model: Model, payload: any) {
  const provider = model === 'smart' ? process.env.PROVIDER_SMART : process.env.PROVIDER_MINI
  const r = await fetch(`${provider}/chat`, {
    method: 'POST', headers: { 'content-type': 'application/json' },
    body: JSON.stringify(payload)
  })
  if (!r.ok) throw new Error('LLM provider error')
  return r.json()
}

Код нарочито упрощён: в продакшене добавляем «умное сжатие» (compress‑chain), трекер реальных токенов от провайдера, контекстуальные политики (категории запросов), а также stratified‑cache (горячий/тёплый/холодный).

RAG без переплат: от индекса до вставки

RAG чаще всего раздувает контекст и приводит к повторным вызовам модели. Основные рычаги:

  • Чанкинг 300–500 токенов с явными заголовками и скользящими окнами 20–30% — повышает recall без лишней избыточности.
  • Гибридный поиск: BM25 (pg_trgm) + векторы (pgvector) и «учёт разделов» (source/section filters) — меньше нерелевантного шума.
  • Переранжирование mini‑моделью (cross‑encoder) до top‑k=3–5 — дешевле, чем тащить 15 длинных фрагментов в «умную» модель.
  • Компрессия перед вставкой: попросить mini‑модель сжать каждый документ до 120–200 токенов с цитатами/якорями.
  • Инъекции: санитария промпта + политики на уровне Retriever, чтобы документы не «просили» расширить контекст.

Выбор хранилища и подхода к векторному поиску

ПодходЭкономияРискиКогда использовать
Postgres + pgvector + pg_trgmНизкая стоимость эксплуатации, меньше сервисовТюнинг индексов критичен; пиковые нагрузки потребуют реплики100К–10М документов, средние нагрузки, DevOps‑дисциплина есть
Managed Vector DB (Pinecone/Weaviate Cloud)Быстрый старт, SLA, auto‑scalingОплата за QPS/хранилище; сетевой egressКогда критичны время и SLA, команда небольшая
Self‑host Weaviate/QdrantГибкость, контроль затратОперационные риски, апгрейдыНужен контроль/изолированность, on‑prem/air‑gapped
Только BM25 (без векторов)0 на эмбеддингиПадение качества на синонимах/парафразахУзкая терминология, короткие документы

Сами эмбеддинги — ещё одна строка в счёте. Оптимизация:

  • Снизить размерность (например, small‑модель для эмбеддингов) и использовать переранжирование поверх.
  • Дедупликация и агрегация «похожих» чанков.
  • Асинхронная индексация, ночные бэчи по тарифам с низкой нагрузкой.

Про «суверенные» модели для RAG/эмбеддингов и как это режет капитальные/операционные издержки — см. наш разбор Sovereign LLM на 80% дешевле? Практика.

Tool‑calling под контролем

Плагины и функции полезны, но без лимитов вы получите каскады: модель запускает поиск, затем БД, затем ещё один поиск… и так 6–8 шагов. Правила:

  • Жёсткий бюджет maxToolCalls на шаг и на сессию.
  • Отложенные инструменты: сначала резюмировать вопрос, сформулировать «план», затем один батч‑запрос.
  • Кэширование результатов инструментов (особенно запросов к товарам/каталогам/отчётам).
  • Guard‑промпты с «ранним выходом» при слабой уверенности: просить пользователя уточнить.
  • Трассировка инструментов с затратами: какие функции сжигают больше всего токенов/времени.

В реальных продуктах дисциплина tool‑calling даёт 10–30% экономии без видимой потери качества. Маленькая мера, большой эффект.

Что ломается в продакшене

  • Prompt‑инъекции, которые «заставляют» подмешивать всё больше контекста, растя стоимость. Лечится фильтрацией и контекст‑компрессией.
  • Ретраи и timeouts: двойные/тройные вызовы на одну реплику. Нужны таймаут‑политики и идемпотентность.
  • Длинные чаты без усечения: «вечные» диалоги быстро переходят 100k токенов истории. Принудительное саммари каждые N сообщений.
  • Отсутствие потоковой отдачи: пользователь ждёт, вы увеличиваете температурy/максимум токенов «на всякий случай». Стрим уменьшает панику и издержки.
  • Платёжки за эмбеддинги: бэчите, кэшируйте, отключайте частую переиндексацию.
  • Непрозрачные графики провайдера: провайдер считает токены иначе, чем вы. Следите за фактом через их usage API и сводите с вашими логами.
  • Нестабильные векторные индексы на больших коллекциях без VACUUM/REINDEX — запросы становятся дорогими и медленными, пока не прилетит ночной алерт.

Одна сухая шутка: самый быстрый способ обнулить бюджет — дать модели задачу «подумать вслух» и забыть выключить chain‑of‑thought в продакшене.

Деньги и ROI: когда $30k — норма, а когда — нет

Формула счёта:

  • Модель: вход + выход токенов × тариф.
  • Эмбеддинги: документы × средняя длина × тариф.
  • Хранилище: объем индексов, QPS.
  • Ретраи/дубли: коэффициент 1.1–1.4, если политика агрессивная.
  • Инструменты: egress и токены на форматирование/парсинг.

Сценарий 1 (дорого):

  • 150k обращений/мес, средний контекст 7k входных + 900 выходных токенов на «умной» модели.
  • 40% запросов идут в RAG (4 вставки по 400 токенов каждая без компрессии), 1.4× ретраи на хвосте.
  • Оценка: вход ~1.47e9, выход ~1.89e8 токенов → с текущими тарифами «сильных» моделей + эмбеддинги/хранилище, счёт легко переваливает $25–35k.

Сценарий 2 (оптимизировано):

  • Тот же трафик, но: 70% запросов на mini, «умная» — только по признаку сложности.
  • Компрессия RAG до 2–3 вставок по 180–220 токенов, кэширование повторов.
  • История в саммари, системный промпт 500–700 токенов.
  • Ретраи ограничены, tool‑calling ≤2/шаг.
  • Итог: вход/выход токенов падают в 2–4 раза, средняя цена за обращение — в 2–6 раз. Итоговый счёт $6–12k вместо $30k.

ROI зависит от конверсий и LTV. Если чат — core‑функция (саппорт, продажи, критичная аналитика), «умная» модель вверху воронки может быть оправдана. Если чат — вспомогательная фича, экономьте агрессивно и давайте пользователю «кнопку умнее ответ» как платную опцию.

Практические чек‑листы по снижению стоимости

  • Включите маршрутизатор: mini по умолчанию; smart — по сложности/неуверенности/кейсу.
  • Пороговый кэш: если семантическая близость > τ и контекст свежий — отдаём из кэша.
  • Компрессор контекста: LLM‑пресс до 150–300 токенов на документ, не более 3–4 доков.
  • История в self‑summary с регулярным пересчётом; хранить полный лог вне промпта.
  • Tool‑budget: жёсткие лимиты на шаг, батчинг запросов, кэширование результатов инструментов.
  • Телеметрия: графики токенов по маршрутам, алерты при росте >20% за час/день.
  • Тесты‑оценки (evals) с «стоимостью на тест»; не гнаться за +0.5 балла, если это ×2 по цене.

FAQ

Почему «длинный контекст» так дорог?

Потому что модель пересчитывает все входные токены. Каждые +2–5k токенов истории — это линейный рост цены на каждый следующий ответ. Храните историю в саммари и подмешивайте только релевантные фрагменты.

Можно ли обойтись без векторной базы?

Иногда да: для узких доменов и коротких документов хватит BM25/pg_trgm. Но для синонимов/парафразов и смешанного языка векторы сильно помогают. Компромисс — Postgres с pgvector, без managed‑стоимости.

Семантический кэш не испортит актуальность?

Решается TTL и «ключом по контексту»: учитывайте версию индекса и хэш источников. Для динамических данных — маленький TTL или кэшируйте только «планы» и пояснения без чисел.

Когда оправдано держать локальную модель (vLLM)?

Когда есть стабильный объём, предсказуемые нагрузки, требования к приватности, и команда SRE. Для рваных нагрузок API‑провайдер часто дешевле по TCO.

Что с chain‑of‑thought — отключать полностью?

В продакшене либо отключайте, либо используйте «скрытую» короткую разметку на mini‑модели только для планирования. Хранить CoT в ответе пользователю редко нужно и всегда стоит денег.

Как выбрать «правильную» mini‑модель?

Соберите свой короткий eval: 50–200 кейсов из реальных диалогов, меряйте качество и стоимость на мини и «умной» модели. Часто разница в качестве <10%, а в цене ×5–×10.

Ключевые выводы

  • Основной драйвер цены — входные токены: усечённый и сжатый контекст экономит больше всего.
  • Маршрутизатор моделей + семантический кэш дают 2–6× экономии без критичной потери качества.
  • RAG без компрессии и переранжирования — прямой путь к переплатам и шуму.
  • Tool‑calling должен иметь бюджет; иначе получаются дорогие «петли» вызовов.
  • Телеметрия стоимости и алерты — обязательны; без них счёт растёт незаметно.

Если вы строите или оптимизируете AI‑чатбот и хотите сократить чек без потери качества, MTBYTE спроектирует архитектуру, внедрит кэш/маршрутизацию и поставит метрики. Напишите нам через /contact — начнём с аудита ваших реальных диалогов и затрат.

СЛЕДУЮЩИЙ ШАГ

Понравилось как мыслим?

Применяем те же принципы в клиентских проектах: AI, автоматизации, продукты, которые не умирают после релиза.