中文翻译暂不可用,显示俄语原文。
Реализовать cost-aware routing на основе классификатора сложности (BERT)
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать cost-aware routing на основе классификатора сложности (BERT)
1. Цель задачи
Разработать систему routing|cost-aware routing для LLM-приложения, которая перенаправляет «простые» запросы на дешёвую модель (например, GPT-3.5-turbo), а «сложные» — на дорогую (например, GPT-4). Для классификации сложности запроса обучить BERT-классификатор на исторических данных. Добиться снижения затрат на LLM-вызовы на 60% при падении качества (accuracy) не более 5% по сравнению с использованием только дорогой модели.
Ключевой результат Рабочий пайплайн: классификатор → роутер → две модели, метрики cost и accuracy на тестовом наборе, автоматический отчёт.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Исторические логи запросов к LLM (минимум 5000 примеров) | Реальные данные production / синтезированные через LLM (см. ниже) |
| Ответы (качество) от дешёвой и дорогой модели для каждого запроса | Прогнать запросы через обе модели или взять из existing logs |
| Разметка «сложности» запроса (бинарно: simple / complex) | Разметить руками / полуавтоматически (через LLM-as-judge) |
| Две LLM: дешёвая и дорогая | OpenAI API / локальные модели (например, Qwen2.5-0.5B vs Qwen2.5-7B) |
| Python-среда | Локальная или Colab (GPU для BERT) |
| Бюджет на API (опционально) | ~$5–10 для тестового набора |
Если нет реальных данных — симулируем:
- Сгенерировать 6000 синтетических запросов через GPT-4-turbo, промпт:
"Сгенерируй 1000 вопросов на русском языке для тестирования LLM, половина — простые фактологические (например, 'Сколько планет в Солнечной системе?'), половина — сложные (требующие многошагового рассуждения, например, 'Если 2 поезда выехали навстречу друг другу...')". - Прогнать все запросы через дешёвую и дорогую модель, сравнить качество ответов (например, через GPT-4-as-judge или BERTScore).
- Разметить «сложность» как
simple, если ответы обеих моделей совпали по качеству (или разница <5% метрики), иначеcomplex.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Классификатор | BERT (bert-base-multilingual-cased) | Обучение классификатора сложности |
| LLM (дешёвая) | GPT-3.5-turbo / Qwen2.5-1.5B | Генерация ответов |
| LLM (дорогая) | GPT-4 / Qwen2.5-7B | Генерация ответов (baseline) |
| Бекенд роутера | Python FastAPI (опционально) | Промышленный вариант |
| Оценка качества | BERTScore, GPT-4-as-judge | Метрики accuracy ответов |
| Мониторинг | Prometheus + Grafana (опционально) | Сбор метрик cost и accuracy в real-time |
| Эксперименты | MLflow / Weights & Biases | Логирование обучения и результатов |
| Среда | Python 3.10+, PyTorch, transformers, scikit-learn | Обучение и тестирование |
4. Этапы выполнения
Этап 1: Подготовка данных и разметка сложности (оценка 1.5 часа)
Действия
-
Сбор / генерация запросов
- Если нет исторических данных — сгенерировать 6000 запросов по схеме из раздела 2.
- Разделить на train (70%), validation (15%), test (15%).
-
Получение ответов от обеих моделей
import openai def get_response(model, prompt): response = openai.ChatCompletion.create(model=model, messages=[{"role": "user", "content": prompt}]) return response.choices[0].message.content -
Оценка качества и разметка
- Использовать GPT-4-as-judge для оценки каждого ответа по шкале 1-5 (или сравнить ответы между собой).
- Разметить label=0 (simple), если разница в оценке <= 1 балл, и label=1 (complex), если разница >= 2 балла.
- Добавить колонку label в датасет.
-
Препроцессинг для BERT
Ожидаемый результат этапа
- Файл dataset.parquet с полями: query, label, response_cheap, response_expensive.
- Токенизированные input_ids, attention_mask в
train_ds.pt.
Этап 2: Обучение BERT-классификатора (оценка 1.5 часа)
Действия
-
Определение модели
from transformers import BertForSequenceClassification, Trainer, TrainingArguments model = BertForSequenceClassification.from_pretrained('bert-base-multilingual-cased', num_labels=2) -
Конфигурация обучения
- Batch size: 16
- Learning rate: 2e-5
- Epochs: 3
- Weight decay: 0.01
- TrainingArguments(output_dir='./classifier', evaluation_strategy='epoch').
-
Запуск Trainer
trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset) trainer.train() -
Сохранение лучшей модели
-
Оценка на validation
Ожидаемый результат этапа
- Модель классификатора в папке
./cost_router_bert. - Метрики валидации в логах.
Этап 3: Интеграция роутера и цикл оценки (оценка 1.5 часа)
Действия
-
Реализация роутера
def route_query(query, threshold=0.5): inputs = tokenizer(query, return_tensors='pt', truncation=True, max_length=128) outputs = model(**inputs) prob = torch.softmax(outputs.logits, dim=1)[0][1].item() # вероятность complex if prob > threshold: return 'expensive' else: return 'cheap' -
Тестирование на тестовом наборе
- Для каждого запроса из test split:
- Выбрать модель по
route_query. - Взять соответствующий ответ из датасета (если он был получен ранее).
- Сравнить качество ответа с эталоном (ответ дорогой модели).
- Выбрать модель по
- Вычислить cost (сумма cost всех вызовов) и accuracy (доля ответов, не уступающих эталону больше чем на 1 балл).
- Для каждого запроса из test split:
-
Симуляция cost
- Цена за токен: cheap = $0.0015/1K токенов ввода, $0.002/1K вывода; expensive = $0.03 / $0.06 (примерные цены OpenAI).
- Рассчитать среднюю стоимость и фактическую стоимость роутера.
-
Сравнение с baseline
Ожидаемый результат этапа
- Таблица с метриками: baseline дорогой, baseline дешёвый, роутер.
- Предварительное значение cost saving и accuracy drop.
Этап 4: Оптимизация порога и финальное тестирование (оценка 1 час)
Действия
-
Подбор порога
-
Финальная проверка на test
- Взять лучший порог (например, 0.6).
- Прогнать тестовый набор: финальные метрики.
-
A/B тест (опционально)
Ожидаемый результат этапа
- Оптимальный порог и финальные метрики.
- График (или таблица) в отчёте.
Этап 5: Написание отчёта и подготовка презентации (оценка 30 минут)
Действия
-
Создание отчёта
- Markdown-документ с разделами: постановка, данные, обучение, результаты, выводы.
- Включить таблицу метрик и графика.
-
Фиксация артефактов
- Модель
cost_router_bert. - Код пайплайна (Jupyter notebook или Python-скрипты).
- Файл с результатами
final_metrics.json.
- Модель
Ожидаемый результат этапа
- Отчёт в корне проекта.
- Все артефакты в репозитории.
5. Критерии приемки (Definition of Done)
- Обученный BERT-классификатор сохранён в
./cost_router_bert/и загружается без ошибок. - Реализован модуль роутера (
router.py), который принимает строку запроса и возвращает имя модели ('cheap'или'expensive'). - На тестовом наборе (минимум 500 запросов) средняя стоимость вызовов снижена на 60% и более по сравнению с baseline (дорогая модель).
- Падение accuracy (по метрике GPT-4-as-judge) не превышает 5% от baseline дорогой модели.
- Воспроизводимый пайплайн:
python train.py && python evaluate.py— выдаёт те же метрики. - В корне проекта лежит
report.mdс описанием эксперимента, таблицей метрик и графиком порога. - Код покрыт комментариями и docstring, понятен для ревью.
- (Доп.) Модель можно запустить в реальном API (FastAPI эндпоинт
/v1/chat/completionsс routing).
6. Ожидаемый результат
Артефакты
| Файл | Содержание |
|---|---|
cost_router_bert/ | Папка с весами BERT-классификатора и токенизатором |
router.py | Функция route_query(query, threshold=0.6) |
final_metrics.json | JSON: {"baseline_expensive_cost": 12.34, "baseline_expensive_accuracy": 1.0, "router_cost": 4.94, "router_accuracy": 0.96, "cost_saving_pct": 60.0, "accuracy_drop_pct": 4.0} |
report.md | Документ с описанием шагов, метриками и графиком |
notebook.ipynb (опц.) | Jupyter notebook с полным экспериментом |
Дополнительные результаты (опционально):
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Неравномерное распределение классов (слишком много simple) | Взвешенная loss (class_weight), oversampling, или порог по квантили |
| Качество разметки (шум) | Использовать majority vote между двумя judge-моделями; исключить неопределённые примеры (разница оценки 1 балл) |
| Дорогие API-вызовы для тестового набора | Использовать локальные модели (например, Qwen2.5-1.5B vs 7B) — cost равен только электроэнергии |
| BERT не хватает контекста (запросы > 512 токенов) | Использовать Longformer или обрезать запрос до первых 128 токенов; при необходимости дообучить с поддержкой длинных последовательностей |
| Cost saving не достигает 60% | Уменьшить порог (направить больше запросов на cheap); проверить, не упала ли accuracy сильно; возможно, нужно подобрать другую дешёвую модель (например, Claude Haiku) |
| Падение accuracy > 5% | Увеличить порог; собрать больше сложных примеров; дообудить BERT на большем датасете |
8. Бюджет времени (оценка)
| Этап | Время (часы) |
|---|---|
| 1. Подготовка данных и разметка | 1.5 |
| 2. Обучение BERT-классификатора | 1.5 |
| 3. Интеграция роутера и оценка | 1.5 |
| 4. Оптимизация порога и финал | 1.0 |
| 5. Отчёт и артефакты | 0.5 |
| Итого | 6.0 |
Примечание для первого раза: Время может увеличиться до 8–10 часов из-за настройки окружения, API-лимитов и отладки. Рекомендуется выделить два рабочих дня.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 15 | Выбор модели LLM под задачу |
| 42 | Оценка стоимости вызовов LLM |
| 78 | Создание классификатора для routing |
| 121 | Fine-tuning BERT под кастомную задачу |
| 189 | Метрики качества ответов (BERTScore, GPT-judge) |
| 210 | A/B тестирование в production |
| 305 | Оптимизация latency и cost |
| 420 | Методы разметки данных (human + LLM) |
| 555 | Подбор порога классификации |
| 713 | Мониторинг cost и качества в real-time |
10. Чек-лист самопроверки
- Я проверил, что классификатор загружается и выдаёт предсказания для любого нового запроса.
- Я запустил
evaluate.pyна тестовом наборе и получил cost saving >= 60% и accuracy drop <= 5%. - Я проверил, что роутер корректно переключается между cheap и expensive моделями (не использует одну постоянно).
- Я построил график «cost vs accuracy» для разных порогов и выбрал оптимальный.
- Я написал отчёт
report.md, который объясняет каждое решение и содержит таблицу с метриками. - (Доп.) Я протестировал сервис через FastAPI и убедился, что он отвечает без ошибок.