Проблема: почему выбор LLM — это не разовая задача, а постоянная головная боль
Представьте: вы запускаете продукт на Claude 3.5 Sonnet. Все работает идеально. Через месяц выходит Claude 3.7 Haiku — дешевле на 40%, но качество чуть хуже. Еще через две недели Anthropic выпускает Claude 3.7 Sonnet с улучшенным reasoning. А параллельно Amazon добавляет в Bedrock новую модель от Cohere, которая лучше справляется с немецким языком.
Ваш продукт теперь должен:
- Автоматически обнаруживать новые модели
- Тестировать их на ваших реальных задачах
- Сравнивать качество ответов
- Учитывать стоимость токена
- Адаптировать промпты под каждую модель
- Маршрутизировать запросы к лучшему кандидату
И делать это непрерывно. Каждый день. Без вашего участия.
Классическая ошибка: выбрать одну модель и забыть. Через три месяца вы платите в 2 раза больше за тот же результат. Или получаете ответы хуже, чем могли бы.
Решение Beekeeper: лидерборд, который живет своей жизнью
Beekeeper — это не просто прокси к Bedrock. Это система, которая:
- Сканирует Bedrock на наличие новых моделей
- Создает для каждой модели несколько вариантов промптов
- Запускает A/B тесты на реальных запросах пользователей
- Строит лидерборд по метрике cost/quality
- Маршрутизирует трафик к топ-3 моделям
- Пересчитывает рейтинги каждые 24 часа
Архитектурно это выглядит так:
1 Сканер моделей: кто сегодня в баре?
Первая часть системы — это сканер, который каждые 6 часов опрашивает Bedrock API. Не просто list_models, а с детальной информацией:
import boto3
import json
from datetime import datetime
bedrock = boto3.client('bedrock', region_name='us-east-1')
def scan_available_models():
response = bedrock.list_foundation_models()
models = []
for model in response['modelSummaries']:
# Получаем детали по каждой модели
details = bedrock.get_foundation_model(
modelIdentifier=model['modelId']
)
# Важно: проверяем поддержку system prompt
supports_system_prompt = False
for capability in details['modelDetails']['inferenceTypesSupported']:
if 'SYSTEM_PROMPT' in capability:
supports_system_prompt = True
break
models.append({
'id': model['modelId'],
'provider': model['providerName'],
'name': model['modelName'],
'input_price': details['modelDetails']['inputPrice'],
'output_price': details['modelDetails']['outputPrice'],
'context_window': details['modelDetails']['contextWindow'],
'supports_system_prompt': supports_system_prompt,
'discovered_at': datetime.utcnow().isoformat()
})
return models
На 20 января 2026 года в Bedrock доступны десятки моделей от Anthropic, Meta, Cohere, AI21 Labs, Amazon Titan. Каждая со своей спецификой.
| Провайдер | Флагманская модель (2026) | Особенность |
|---|---|---|
| Anthropic | Claude 3.7 Sonnet | Лучший reasoning, дорогой |
| Meta | Llama 4 70B | Баланс цена/качество |
| Cohere | Command R+ 2026 | Мультиязычность, RAG |
| Amazon | Titan Text G2 | Дешево, для простых задач |
2 Фабрика промптов: одна задача, десять способов объяснить
Вот где начинается магия. Для каждой новой модели Beekeeper создает 3-5 вариантов системных промптов. Не просто копирует, а адаптирует под особенности модели.
Пример: у вас задача "извлечь сущности из текста".
Как НЕ делать:
# Плохо: один промпт для всех моделей
system_prompt = "Извлеки сущности из текста"
Как делает Beekeeper:
# Для Claude 3.7 (любит структуру)
claude_prompt = """Ты — эксперт по извлечению информации.
Твоя задача: проанализировать текст и извлечь все именованные сущности.
Формат ответа:
1. PERSON: [имя]
2. ORGANIZATION: [название]
3. LOCATION: [место]
Если сущность не найдена — пиши "N/A"."""
# Для Llama 4 (лучше работает с примерами)
llama_prompt = """Пример:
Текст: "Илон Маск основал SpaceX в Калифорнии."
Ответ:
PERSON: Илон Маск
ORGANIZATION: SpaceX
LOCATION: Калифорния
Теперь извлеки сущности из этого текста:"""
# Для Command R+ (мультиязычный)
cohere_prompt = """Extract named entities. Return in JSON format:
{
"persons": [],
"organizations": [],
"locations": []
}
Text: """
Зачем такие сложности? Потому что LLM понимают цель, но игнорируют её если промпт не адаптирован под их "менталитет".
3 A/B тестирование на живом трафике
Самый спорный момент: Beekeeper тестирует новые модели на реальных запросах пользователей. 5% трафика уходит на эксперименты.
Алгоритм:
- Новая модель появляется в Bedrock
- Beekeeper создает для нее промпты
- Следующие 100 запросов (5% трафика) идут параллельно к новой модели и текущему лидеру
- Ответы оцениваются по трем метрикам
Важно: пользователь получает ответ от текущего лидера. Ответ новой модели сохраняется для оценки, но не показывается. Никто не страдает от экспериментов.
Метрики оценки:
def evaluate_response(expected, actual, cost, latency):
"""Оценка ответа по трем осям"""
# 1. Качество (0-100)
quality_score = calculate_similarity(expected, actual)
# 2. Стоимость (нормализованная)
# Берем самую дешевую модель как baseline = 100
cost_score = (cheapest_model_cost / cost) * 100
# 3. Скорость
latency_score = max(0, 100 - (latency * 10)) # 10 сек = 0 баллов
# Итоговый score с весами
total_score = (quality_score * 0.6) + (cost_score * 0.3) + (latency_score * 0.1)
return total_score
Веса настраиваются под задачу. Для чат-бота качество важнее (0.8), для логирования — цена (0.7).
4 Лидерборд: кто сегодня король?
Каждые 24 часа система пересчитывает рейтинги. Лидерборд — это не статичная таблица, а живой организм.
| Ранг | Модель + Промпт | Score | Цена/1K токенов | Доля трафика |
|---|---|---|---|---|
| 🥇 | Claude 3.7 Sonnet (v3 prompt) | 94.2 | $0.015 | 60% |
| 🥈 | Llama 4 70B (v2 prompt) | 91.8 | $0.008 | 25% |
| 🥉 | Command R+ 2026 (JSON prompt) | 89.5 | $0.006 | 10% |
| 4 | Claude 3.7 Haiku (v1 prompt) | 85.1 | $0.003 | 5% |
Трафик распределяется по весу score. Но с нюансом: разница в 5 баллов — не значит ровное распределение. Beekeeper использует softmax для плавного перехода.
Маршрутизатор: умное распределение запросов
Когда приходит запрос, маршрутизатор смотрит не только на лидерборд. Он анализирует:
- Длину промпта (некоторые модели плохо работают с 10к токенами)
- Язык (для немецкого лучше Cohere)
- Тип задачи (классификация, генерация, summarization)
- Текущую загрузку моделей (реагирует на throttling)
Пример логики:
class LLMRouter:
def route_request(self, prompt, task_type, language='en'):
candidates = self.leaderboard.get_top_models()
# Фильтруем по языку
if language != 'en':
candidates = [m for m in candidates if m.supports_language(language)]
# Фильтруем по длине контекста
if len(prompt) > 4000:
candidates = [m for m in candidates if m.context_window >= 8000]
# Для classification задач можно взять модель подешевле
if task_type == 'classification':
candidates.sort(key=lambda x: x.cost_score, reverse=True)
else:
candidates.sort(key=lambda x: x.total_score, reverse=True)
# Выбираем топ-1 после фильтрации
return candidates[0]
Это напоминает LLMRouter, но с важным отличием: Beekeeper постоянно обновляет правила маршрутизации на основе новых данных.
Нюансы, о которых молчат в документации
1. System prompt — не панацея
Anthropic гордится поддержкой system prompt в Claude 3.7. Но на практике:
- System prompt работает только в начале диалога
- Он "забывается" после 20-30 сообщений
- Некоторые модели игнорируют его, если user prompt слишком длинный
Решение Beekeeper: периодически "впрыскивать" system prompt в середине длинных диалогов. Грязно, но работает.
2. Цены меняются без предупреждения
В ноябре 2025 года Anthropic снизила цены на Claude Haiku на 40%. Внезапно. Beekeeper обнаружил это через 2 часа (сканер цен) и пересчитал лидерборд. Haiku поднялась с 4 на 2 место.
Без автоматического отслеживания вы бы платили старые цены еще месяц.
3. Модели "деградируют" после обновлений
Реальная история: Llama 3.1 8B после обновления в Bedrock стала хуже справляться с кодом. Beekeeper зафиксировал падение score с 87 до 72 за 3 дня. Автоматически понизил модель в лидерборде и отправил алерт.
Человек заметил бы через неделю, когда пользователи начали жаловаться.
Ошибки, которые все совершают (и как их избежать)
Ошибка 1: Тестировать модели на синтетических данных. Результаты не совпадают с реальным трафиком. Всегда тестируйте на реальных запросах (хотя бы 5% трафика).
Ошибка 2: Использовать один промпт для всех моделей. Claude любит структуру, Llama — примеры, Cohere — JSON. Адаптируйте или теряйте 20-30% качества.
Ошибка 3: Игнорировать стоимость токена. Разница между Claude Sonnet и Haiku — 5x. Иногда качество Sonnet того стоит. Иногда — нет. Считайте cost/quality ratio.
Что будет дальше? Прогноз на 2026-2027
Тренды, которые изменят архитектуру динамического выбора LLM:
- Модели-специалисты: Вместо универсальных LLM появятся тысячи узкоспециализированных моделей (только для кода, только для медтекстов, только для юридических документов). Маршрутизатор должен будет определять тип задачи точнее.
- On-the-fly quantization: Bedrock начнет предлагать квантованные версии моделей с разным балансом качество/скорость. Выбор усложнится в 3 раза.
- Мультимодальность как стандарт: Все модели будут работать с текстом, изображениями, аудио. Маршрутизатор должен анализировать тип входных данных.
- Цены упадут до $0.0001/1K токенов: Конкуренция между провайдерами станет жестокой. Разница в 0.0001$ будет влиять на выбор.
Архитектура Beekeeper готова к этому. Потому что она построена на принципе: "все меняется, и мы меняемся вместе со всем".
Последний совет: начните с простого. Не стройте сложную систему сразу. Напишите скрипт, который раз в неделю тестирует 2-3 модели на ваших данных. Сравнивает результаты. Выбирает лучшую. Это уже сэкономит вам 20-30% денег или улучшит качество на 15%.
А когда надоест запускать скрипт вручную — вспомните про лидерборд, который живет своей жизнью. И представьте, как он каждый день находит лучшую комбинацию модели и промпта, пока вы пьете кофе.