AI Cost Optimization: как сократить расходы на LLM на 60% без потери качества

Что такое пять техник оптимизации стоимости LLM API без деградации качества?

Оптимизация расходов на LLM охватывает пять техник, каждая из которых бьёт по отдельной категории затрат: prompt caching (устраняет повторную обработку системного промпта — скидка 50–90% в зависимости от провайдера), smart routing (направляет каждый запрос к наиболее дешёвой модели, способной его обработать — экономия 10–30x на простых задачах), Batch API (откладывает неинтерактивные запросы со скидкой 50% от стандартной цены), semantic caching (возвращает кешированные ответы на семантически схожие запросы без вызова LLM) и model downsizing (заменяет дорогие flagship-модели на более дешёвые, прошедшие валидацию на задаче-специфичном eval-датасете). Применённые последовательно, эти пять техник типично снижают общие расходы на LLM на 59–67% без измеримой потери качества пользовательских функций.

TL;DR

  • -40–60% расходов на LLM API в типичном production-приложении уходят в три категории потерь: повторяющийся системный промпт, тарифицируемый на каждый запрос; дорогие flagship-модели для тривиальных задач; дублирующиеся запросы от разных пользователей.
  • -Prompt caching сам по себе экономит ~$1 620/месяц для приложения с 10 000 запросов/день на системном промпте в 2 000 токенов — один день внедрения, нулевой риск для качества.
  • -Smart routing экономит ~$2 145/месяц при том же трафике, направляя 60–70% запросов (классификация, извлечение, форматирование) к моделям, в 20–30 раз дешевле flagship — самый значимый единичный рычаг экономии.
  • -Model downsizing — самая рискованная техника, всегда требующая предварительного eval-фреймворка: на задачах сложного reasoning замена Claude Sonnet 4.6 на GPT-5.4-mini даёт падение accuracy на 18.5% — невидимое без измерений.
  • -Порядок внедрения для максимального ROI при минимальном риске: prompt caching → smart routing → Batch API → semantic caching → model downsizing.

Средний LLM-стартап тратит ~$8 000-15 000 в месяц на API-вызовы при 50 000 запросов в день. При этом 40-60% этих расходов уходят на повторяющиеся запросы, избыточные токены и использование дорогих моделей для тривиальных задач.

Снижение расходов на 60% достижимо за 2-3 недели работы. Без деградации качества, без отказа от flagship-моделей на сложных задачах. Пять техник, каждая из которых даёт измеримый результат.

Анатомия LLM-расходов: где теряются деньги

Прежде чем оптимизировать, полезно понять структуру расходов. Типичное распределение для production-приложения с AI-чатом и несколькими пайплайнами:

Категория расходовДоля бюджетаПричина
Повторяющиеся системные промпты25-35%Один и тот же system prompt на каждый запрос, тысячи раз в день
Избыточная мощность модели20-30%GPT-5.4 на задачах, где справляется GPT-5.4-mini
Дублирование запросов15-20%Одинаковые вопросы от разных пользователей
Синхронные вызовы без приоритезации10-15%Batch-задачи по цене реалтайма
Полезная нагрузка15-25%Запросы, которые действительно требуют полной мощности

Каждая техника ниже бьёт по конкретной категории. Порядок подобран по ROI: первая техника даёт максимальный эффект при минимальных усилиях.

Prompt Caching: экономия на повторяющихся префиксах

Prompt caching позволяет провайдеру кешировать начало промпта (system prompt, контекст, инструкции) и не пересчитывать токены при следующих запросах с тем же префиксом.

Anthropic предоставляет prompt caching с августа 2024. OpenAI запустил автоматическое кеширование в октябре 2024. Google Gemini поддерживает context caching с середины 2024.

Как это работает. Провайдер хеширует первые N токенов промпта. Если следующий запрос начинается с того же блока, кешированные токены не тарифицируются по полной цене. У Anthropic скидка составляет 90% на кешированные токены. У OpenAI - 50%.

Anthropic: явное кеширование

У Anthropic кеширование управляется явно через параметр cache_control в сообщениях:

{
  "model": "claude-sonnet-4-6",
  "max_tokens": 1024,
  "system": [
    {
      "type": "text",
      "text": "Ты AI-ассистент для планирования путешествий. Отвечай структурированно, используй markdown. Всегда указывай бюджет, время в пути, рейтинг безопасности локации...",
      "cache_control": {"type": "ephemeral"}
    }
  ],
  "messages": [
    {"role": "user", "content": "Спланируй маршрут по Бали на 7 дней"}
  ]
}

Минимальный размер кешируемого блока - 1024 токена для Claude Sonnet/Opus, 2048 для Haiku. Кеш живёт 5 минут и продлевается при каждом обращении.

OpenAI: автоматическое кеширование

У OpenAI prompt caching работает автоматически для промптов длиннее 1024 токенов. Ничего менять в коде не нужно. Кешируется самый длинный общий префикс между запросами.

Расчёт экономии

Системный промпт AI-чата - 2000 токенов. 10 000 запросов в день. Модель Claude Sonnet 4.6.

Без кеширования:

  • Входные токены system prompt: 2 000 * 10 000 = 20M токенов/день
  • Стоимость: 20M * ~$3/1M = ~$60/день

С кешированием (Anthropic, 90% скидка на кешированные):

  • Первый запрос: ~$3/1M (полная цена)
  • Кешированные: ~$0.30/1M
  • Запись в кеш: ~$3.75/1M (на 25% дороже обычного, только при первой записи)
  • Стоимость: 20M * ~$0.30/1M = ~$6/день

Экономия: ~$54/день, ~$1 620/месяц. Это только system prompt. Если кешировать ещё few-shot примеры и контекст, экономия растёт пропорционально.

Паттерн: максимизация кеш-хитов

Структура промпта критична для эффективности кеширования. Кешируемый блок должен быть в начале, переменные части - в конце.

┌─────────────────────────────┐
│  System prompt (кешируется) │  ← 2000+ токенов, одинаковые для всех
├─────────────────────────────┤
│  Few-shot примеры           │  ← 500-1000 токенов, одинаковые для всех
│  (кешируется)               │
├─────────────────────────────┤
│  Контекст пользователя      │  ← Уникальный для каждого запроса
├─────────────────────────────┤
│  Сообщение пользователя     │  ← Уникальный для каждого запроса
└─────────────────────────────┘

Плохой паттерн: имя пользователя в начале system prompt. Каждый запрос от нового пользователя ломает кеш. Правильно: поставить имя после кешируемого блока или вынести в user сообщение.

Smart Routing: правильная модель для каждой задачи

Не каждый запрос требует flagship-модели. Классификация тикета, генерация заголовка, извлечение email из текста - задачи, где GPT-5.4-mini или Gemini 3.1 Flash выдают результат не хуже GPT-5.4 при разнице в цене 10-30x.

Таблица стоимости моделей (апрель 2026)

Цены указаны приблизительно на момент публикации и могут измениться. Актуальные цены — на сайтах провайдеров.

МодельВход ($/1M)Выход ($/1M)Относительно Claude Sonnet 4.6
GPT-5.4-mini~$0.15~$0.60~20x дешевле
Gemini 3.1 Flash~$0.075~$0.30~40x дешевле
DeepSeek-V3~$0.14~$0.28~21x дешевле
Claude Haiku 3.5~$0.80~$4.00~4x дешевле
GPT-5.4~$2~$8~1x (сопоставимо)
Claude Sonnet 4.6~$3~$15baseline
Claude Opus 4.6~$15~$75~5x дороже

Разница между Gemini Flash и Claude Opus - 200x на входе, 250x на выходе. Это не проценты, это порядки.

Классификатор задач

Smart routing начинается с классификации. Два подхода: rule-based (по типу задачи) и LLM-based (LLM-классификатор определяет сложность).

Rule-based routing - проще, предсказуемее, достаточно для большинства случаев:

ROUTING_TABLE = {
    # task_type: model
    "chat_title_generation": "gemini/gemini-2.0-flash",
    "ticket_classification": "gpt-5.4-mini",
    "email_extraction": "gpt-5.4-mini",
    "sentiment_analysis": "gemini/gemini-2.0-flash",
    "translation": "deepseek/deepseek-chat",
    "code_review": "anthropic/claude-sonnet-4-6",
    "complex_reasoning": "anthropic/claude-sonnet-4-6",
    "trip_planning": "anthropic/claude-sonnet-4-6",
}

def route_request(task_type: str, fallback: str = "gpt-5.4-mini") -> str:
    return ROUTING_TABLE.get(task_type, fallback)

LLM-based router - для случаев, когда сложность задачи непредсказуема. Дешёвая модель оценивает входящий запрос и направляет на подходящую:

ROUTER_PROMPT = """Оцени сложность задачи пользователя по шкале 1-3:
1 - простая (классификация, извлечение, форматирование)
2 - средняя (суммаризация, перевод, ответ на вопрос)
3 - сложная (рассуждение, анализ, генерация плана)

Ответь ТОЛЬКО числом: 1, 2 или 3."""

COMPLEXITY_TO_MODEL = {
    1: "gpt-5.4-mini",         # ~$0.15/$0.60
    2: "deepseek/deepseek-chat",  # ~$0.14/$0.28
    3: "anthropic/claude-sonnet-4-6",  # ~$3/$15
}

Стоимость самого роутера: ~50 входных токенов + ~1 выходной токен через GPT-5.4-mini. Это ~$0.000008 за классификацию. При 10 000 запросов в день — ~$0.08/день. Экономия от маршрутизации — десятки долларов в день.

Реальное распределение

Практика показывает, что в типичном AI-приложении 60-70% запросов попадают в категорию “простые”, 20-25% - “средние”, и только 10-15% требуют flagship-модели.

СценарийМодельЗапросов/деньСтоимость/день
Без routingClaude Sonnet 4.6 на всё10 000~$90
С routingMix (70/20/10)10 000~$18.50

Экономия: ~$71.50/день, ~$2 145/месяц.

Подробнее о настройке multi-provider routing через LiteLLM - в статье о multi-provider архитектуре.

Batch API: отложенные задачи за полцены

OpenAI, Anthropic и Google предлагают Batch API - отправка пакета запросов с получением результатов в течение 24 часов. Скидка: 50% от обычной цены.

Не каждая задача требует ответа за 200 мс. Генерация еженедельных отчётов, массовая классификация контента, обогащение данных, обработка загруженных документов - всё это может подождать.

Какие задачи подходят для Batch API

ПодходитНе подходит
Ежедневная/еженедельная аналитикаЧат в реальном времени
Массовая классификация (100+ элементов)Автокомплит
Генерация эмбеддингов для поискаStreaming-ответы
Обработка загруженных файловЛюбой интерактив
Периодическое обогащение данныхТриггерные уведомления

OpenAI Batch API

import json
from openai import OpenAI

client = OpenAI()

# 1. Подготовка JSONL-файла с запросами
requests = []
for i, item in enumerate(items_to_classify):
    requests.append({
        "custom_id": f"item-{i}",
        "method": "POST",
        "url": "/v1/chat/completions",
        "body": {
            "model": "gpt-5.4-mini",
            "messages": [
                {"role": "system", "content": "Классифицируй товар по категориям: electronics, clothing, food, other. Ответь одним словом."},
                {"role": "user", "content": item["description"]}
            ],
            "max_tokens": 10
        }
    })

# Записать в файл
with open("batch_input.jsonl", "w") as f:
    for req in requests:
        f.write(json.dumps(req) + "\n")

# 2. Загрузить файл
batch_file = client.files.create(
    file=open("batch_input.jsonl", "rb"),
    purpose="batch"
)

# 3. Создать batch
batch = client.batches.create(
    input_file_id=batch_file.id,
    endpoint="/v1/chat/completions",
    completion_window="24h"
)

# 4. Проверить статус (позже)
status = client.batches.retrieve(batch.id)
# status.status: "validating" → "in_progress" → "completed"

Anthropic Message Batches

У Anthropic аналогичный механизм - Message Batches API. Скидка 50%, результаты в течение 24 часов.

import anthropic

client = anthropic.Anthropic()

batch = client.messages.batches.create(
    requests=[
        {
            "custom_id": f"item-{i}",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 100,
                "messages": [
                    {"role": "user", "content": f"Классифицируй: {item['description']}"}
                ]
            }
        }
        for i, item in enumerate(items)
    ]
)

Расчёт экономии

1 000 документов в день на классификацию. Средний запрос: 500 входных + 20 выходных токенов. Модель GPT-5.4-mini.

Синхронно:

  • Входные: 500K * ~$0.15/1M = ~$0.075
  • Выходные: 20K * ~$0.60/1M = ~$0.012
  • Итого: ~$0.087/день

Batch API (50% скидка):

  • Итого: ~$0.044/день

На GPT-5.4-mini экономия скромная в абсолютных числах. Но для Claude Sonnet 4.6 на 10 000 запросов:

Синхронно: ~$45/день → Batch: ~$22.50/день. Экономия: ~$675/месяц.

Semantic Caching: одинаковые вопросы не нужно обрабатывать дважды

Prompt caching экономит на уровне провайдера (повторяющийся префикс). Semantic caching экономит на уровне приложения: если пользователь задаёт вопрос, похожий на уже обработанный, ответ берётся из локального кеша без вызова LLM.

“Какая погода в Бали в декабре?” и “Погода на Бали в декабре” - семантически идентичные запросы. Платить за второй не нужно.

Как устроен semantic cache

Запрос пользователя


Генерация embedding (text-embedding-3-small: $0.02/1M токенов)


Поиск в vector store (Qdrant / Redis / pgvector)

    ├── similarity > 0.95 → вернуть кешированный ответ (0 LLM-токенов)

    └── similarity < 0.95 → вызвать LLM → сохранить ответ + embedding в кеш

Реализация на Redis + OpenAI embeddings

import hashlib
import json
import numpy as np
import redis
from openai import OpenAI

client = OpenAI()
cache = redis.Redis(host="localhost", port=6379, db=0)

SIMILARITY_THRESHOLD = 0.95
CACHE_TTL = 3600  # 1 час

def get_embedding(text: str) -> list[float]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

def cosine_similarity(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

def cached_completion(messages: list[dict], model: str = "gpt-5.4") -> str:
    user_message = messages[-1]["content"]
    query_embedding = get_embedding(user_message)

    # Поиск в кеше
    cached_keys = cache.keys("sem_cache:*")
    for key in cached_keys:
        cached_data = json.loads(cache.get(key))
        similarity = cosine_similarity(query_embedding, cached_data["embedding"])
        if similarity >= SIMILARITY_THRESHOLD:
            return cached_data["response"]

    # Кеш-промах: вызвать LLM
    response = client.chat.completions.create(
        model=model,
        messages=messages
    )
    result = response.choices[0].message.content

    # Сохранить в кеш
    cache_key = f"sem_cache:{hashlib.md5(user_message.encode()).hexdigest()}"
    cache.setex(
        cache_key,
        CACHE_TTL,
        json.dumps({"embedding": query_embedding, "response": result})
    )

    return result

Для production лучше использовать vector database (Qdrant, Pinecone, pgvector) вместо линейного поиска по ключам Redis. С 10 000+ записей в кеше линейный поиск становится узким местом.

GPTCache: готовое решение

GPTCache от Zilliz - open-source библиотека для semantic caching. Поддерживает несколько embedding-моделей, vector stores и стратегий эвикции.

from gptcache import cache
from gptcache.adapter import openai
from gptcache.embedding import OpenAI as OpenAIEmbedding
from gptcache.manager import CacheBase, VectorBase, get_data_manager
from gptcache.similarity_evaluation import SearchDistanceEvaluation

# Инициализация
embedding = OpenAIEmbedding()
cache_base = CacheBase("sqlite")
vector_base = VectorBase("faiss", dimension=embedding.dimension)

cache.init(
    embedding_func=embedding.to_embeddings,
    data_manager=get_data_manager(cache_base, vector_base),
    similarity_evaluation=SearchDistanceEvaluation()
)

# Использование — интерфейс идентичен openai
response = openai.ChatCompletion.create(
    model="gpt-5.4",
    messages=[{"role": "user", "content": "Погода в Бали в декабре"}]
)

Расчёт экономии

Предположения: 10 000 запросов в день, 30% кеш-хитов (консервативно для FAQ-like сценариев), модель GPT-5.4.

Без semantic cache:

  • 10 000 * (500 input + 200 output tokens)
  • Стоимость: 5M * ~$2/1M + 2M * ~$8/1M = ~$26/день

С semantic cache (30% хитов):

  • 7 000 запросов к LLM: ~$18.20
  • 10 000 embeddings: 5M * ~$0.02/1M = ~$0.10
  • Итого: ~$18.30/день

Экономия: ~$7.70/день, ~$230/месяц. При 50% кеш-хитов (реалистично для support-ботов) экономия вырастает до ~$390/месяц.

Model Downsizing: системная замена дорогих моделей

Самая интуитивная техника и при этом самая рискованная при неправильном подходе. Заменить Claude Sonnet 4.6 на GPT-5.4-mini для конкретной задачи - это не переключение тумблера. Это процесс с eval-фреймворком.

Процесс downsizing

  1. Собрать eval-датасет. 50-100 реальных примеров из production-логов для каждой задачи. Входные данные + ожидаемые ответы (или критерии качества).

  2. Прогнать baseline. Текущая модель на eval-датасете. Записать метрики: accuracy, format compliance, latency, cost.

  3. Прогнать кандидата. Более дешёвая модель на том же датасете. Те же метрики.

  4. Сравнить. Если дешёвая модель выдаёт >95% от качества дорогой - безопасно переключать.

  5. A/B тест в production. 10% трафика на новую модель, мониторинг через Langfuse или аналог.

Eval-фреймворк

import json
from dataclasses import dataclass

@dataclass
class EvalResult:
    model: str
    task: str
    accuracy: float
    avg_latency_ms: float
    cost_per_1k_requests: float
    format_compliance: float  # % ответов в нужном формате

def run_eval(model: str, task: str, dataset: list[dict]) -> EvalResult:
    results = []
    total_cost = 0
    total_latency = 0
    correct = 0
    format_ok = 0

    for example in dataset:
        start = time.time()
        response = call_llm(model=model, messages=example["messages"])
        latency = (time.time() - start) * 1000

        total_latency += latency
        total_cost += response.usage.total_cost

        if evaluate_correctness(response.content, example["expected"]):
            correct += 1
        if validate_format(response.content, example.get("format_schema")):
            format_ok += 1

        results.append(response)

    n = len(dataset)
    return EvalResult(
        model=model,
        task=task,
        accuracy=correct / n,
        avg_latency_ms=total_latency / n,
        cost_per_1k_requests=(total_cost / n) * 1000,
        format_compliance=format_ok / n,
    )

# Сравнение
baseline = run_eval("anthropic/claude-sonnet-4-6", "ticket_classification", dataset)
candidate = run_eval("gpt-5.4-mini", "ticket_classification", dataset)

print(f"Accuracy: {baseline.accuracy:.1%}{candidate.accuracy:.1%}")
print(f"Cost/1K:  ${baseline.cost_per_1k_requests:.2f} → ${candidate.cost_per_1k_requests:.2f}")
print(f"Latency:  {baseline.avg_latency_ms:.0f}ms → {candidate.avg_latency_ms:.0f}ms")

Типичные результаты downsizing

ЗадачаБылоСталоAccuracy deltaCost delta
Классификация тикетовClaude Sonnet 4.6GPT-5.4-mini-1.2%-95%
Извлечение emailGPT-5.4GPT-5.4-mini-0.3%-94%
Генерация заголовковClaude Sonnet 4.6Gemini 3.1 Flash-0.8%-97%
СуммаризацияGPT-5.4DeepSeek-V3-2.1%-94%
Сложный reasoningClaude Sonnet 4.6GPT-5.4-mini-18.5%-95%

Последняя строка - пример того, почему eval обязателен. На сложных задачах с reasoning дешёвые модели проседают значительно. Замена вслепую приведёт к деградации продукта.

Комбинированная стратегия: сводная таблица

Каждая техника в изоляции даёт 10-50% экономии на своём участке. В комбинации эффекты складываются.

Расчёт для приложения с 10 000 запросов/день, средний запрос 500 input + 200 output токенов, базовая модель Claude Sonnet 4.6:

ТехникаЭкономия/месяцСложность внедренияВремя внедрения
Prompt Caching~$1 620Низкая1 день
Smart Routing~$2 145Средняя3-5 дней
Batch API~$675Низкая1-2 дня
Semantic Caching~$230-390Средняя2-3 дня
Model Downsizing~$500-1 500Высокая1-2 недели (с eval)

Baseline: ~$2 700/месяц (всё на Claude Sonnet 4.6, без оптимизаций).

После оптимизации: ~$900-1 100/месяц.

Итоговая экономия: 59-67%. Порядок внедрения: prompt caching (мгновенный эффект, ноль рисков) → smart routing (большой эффект, требует классификации задач) → batch API (перевод отложенных задач) → semantic caching → model downsizing (последний, так как требует eval).

Мониторинг расходов: без observability оптимизация слепая

Оптимизация без мониторинга - угадывание. Три метрики, которые стоит отслеживать ежедневно:

Cost per request - средняя стоимость одного LLM-вызова. Разбивка по задачам и моделям. Резкий рост означает регрессию: кеш перестал работать, routing отправляет на дорогую модель, промпт раздулся.

Cache hit rate - для prompt caching и semantic caching. Падение hit rate ниже порога (например, <20% для semantic cache) сигнализирует о проблемах: изменился характер запросов, TTL слишком короткий, threshold слишком высокий.

Quality score - accuracy/relevance на eval-датасете. После каждого downsizing или смены модели прогон eval покажет, не просела ли точность.

Langfuse хранит каждый trace: модель, токены, стоимость, latency. Дашборд стоимости по моделям строится за минуты. LiteLLM интегрируется с Langfuse нативно - каждый вызов через прокси автоматически логируется.

# litellm_config.yaml
litellm_settings:
  success_callback: ["langfuse"]

environment_variables:
  LANGFUSE_PUBLIC_KEY: "pk-..."
  LANGFUSE_SECRET_KEY: "sk-..."
  LANGFUSE_HOST: "https://cloud.langfuse.com"

Чего не стоит делать

Оптимизировать до запуска. Преждевременная оптимизация вредит и здесь. Сначала запустить, получить реальные данные по usage, потом оптимизировать по фактам.

Жертвовать качеством ради экономии. Если downsizing роняет accuracy с 95% до 80% - это не экономия, это деградация продукта. Пользователи уйдут, и сэкономленные $500/месяц не компенсируют потерю.

Игнорировать скрытые расходы. Semantic cache требует vector database. Eval-фреймворк требует времени на разметку датасета. Эти расходы включать в ROI.

Забывать про цены провайдеров. Модели дешевеют. Проверять pricing каждые 2-3 месяца — цены меняются быстро и в сторону снижения.

Итог

Пять техник, применённых последовательно, снижают LLM-расходы на 60% и более. Prompt caching и batch API не требуют архитектурных изменений и дают эффект в первый день. Smart routing и model downsizing требуют больше работы, но дают наибольшую абсолютную экономию.

Ключевой принцип: каждая оптимизация должна быть измеримой. Eval перед downsizing. Мониторинг после любого изменения. Дашборд с cost per request в Langfuse или аналоге. Без метрик оптимизация превращается в угадывание, а экономия - в деградацию.


Нужна помощь с оптимизацией AI-расходов? Я помогаю стартапам внедрять AI-решения и строить продукты — belov.works.

Часто задаваемые вопросы

Что представляет собой минимальная настройка prompt caching — и есть ли случаи, когда она увеличивает расходы?
Минимальная настройка для Anthropic — добавить cache_control: {type: "ephemeral"} в блок системного промпта, одна строка конфигурации. Для OpenAI кеширование работает автоматически для промптов длиннее 1024 токенов без изменений в коде. Кеширование увеличивает расходы в одном сценарии: системный промпт чуть меньше минимального порога (1024 токена для Claude Sonnet/Opus), но cache_control добавлен — это запускает запись в кеш по цене 125% от стандартного входного тарифа без последующих кеш-хитов. Проверьте количество токенов системного промпта перед включением кеширования. Если меньше минимального порога — либо расширьте промпт контекстом, улучшающим качество вывода, либо оставьте кеширование отключённым.
Как безопасно тестировать smart routing в production без риска деградации для реальных пользователей?
Используйте shadow routing: в течение 1–2 недель запускайте обе модели — текущую и более дешёвого кандидата — параллельно на каждом запросе, логируйте оба ответа в Langfuse, но показывайте пользователям только ответ текущей модели. По завершении shadow-периода сравните два набора ответов по вашим критериям качества — ручная проверка выборки или автоматический eval при наличии размеченного ground truth. Если ответы дешёвой модели оцениваются в пределах 5% от дорогой по вашим критериям, переключайте трафик. Подход стоит примерно 2x в период shadow, но устраняет риск деградации продукта, собирая данные на реальном распределении запросов, которое синтетические eval-датасеты пропускают.
При каком объёме запросов semantic caching оправдывает инфраструктурные расходы на vector database?
При менее 1000 запросов в день достаточно реализации с линейным поиском по Redis — накладные расходы на lookup незначительны, vector database добавляет операционную сложность без ощутимого прироста скорости. При 1000–10 000 запросов в день Redis остаётся жизнеспособным при кеше до 5000 записей; сверх этого переходите на pgvector (если уже используете Postgres) или управляемый сервис вроде Qdrant Cloud. При более 10 000 запросов в день с высокой долей повторяющихся вопросов (support-боты, FAQ-ассистенты) инфраструктурные расходы всегда окупаются — при 50% кеш-хитов на GPT-5.4 экономия достигает ~$390/месяц при 10 000 запросов в день, легко покрывая стоимость управляемой vector database.