Реализовать роутер запросов между Groq и GPT-4 с делегированием по сложности

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать роутер запросов между Groq и GPT-4 с делегированием по сложности

1. Цель задачи

Научиться строить систему интеллектуального делегирования между двумя LLM с разной стоимостью и качеством. Требуется реализовать классификатор сложности запроса]], который направляет простые запросы на быструю и дешёвую модель (Groq), а сложные — на более мощную (GPT-4). Итоговая система должна быть развёрнута как микросервис с эндпоинтом /chat и метриками для мониторинга.

Ключевой результат Снижение операционных затрат на 50% при падении accuracy не более 5% по сравнению с использованием только GPT-4 (замер accuracy: pass@1 на калибровочном датасете).


2. Исходные данные

Что нужноОткуда взять
Датасет запросов, размеченный по сложности (простые / сложные)Сгенерировать самостоятельно (см. ниже) или взять открытые (например, SimpleQuestions, HotPotQA)
API-ключи Groq и OpenAIЗарегистрироваться на console.groq.com и platform.openai.com
Baseline-метрики (accuracy, cost) для единственного GPT-4Прогнать датасет через GPT-4, зафиксировать accuracy и total cost
Инфраструктура для запуска роутераЛокальный Python 3.10+, или Docker контейнер

Если нет реального инструмента — симулируем:

  1. Groq — если нет доступа, симулируем через любой локальный LLM (например, gpt-3.5-turbo с ограничением 0.5x стоимости или llama-3.1-8b через Ollama). Задержки можно эмулировать фиксированным time.sleep(0.3).
  2. GPT-4 — вместо платного GPT-4 использовать gpt-4o-mini (доступен через OpenAI API) или локальный Qwen2.5-72B через vLLM (если есть ресурсы). Важно, чтобы разница в качестве между моделями была значимой.
  3. Классификатор сложности — если нет размеченного датасета, размечать можно автоматически: простые запросы — длина < 100 символов и нет вопросительных слов «почему», «как»; сложные — многословные, требуют рассуждений. Либо использовать LLM-as-judge (GPT-4) для разметки 200-300 примеров.

3. Технологический стек

КомпонентИнструментыНазначение
Язык программированияPython 3.10+Разработка роутера и классификатора
API фреймворкFastAPIHTTP-сервер для эндпоинта /chat
Клиенты LLMopenai (v1+), groqВызов моделей
Классификаторscikit-learn (LogisticRegression) или rule-based + LLM-as-judgeОпределение сложности запроса
Логированиеloguru + structlogСтруктурированные логи запросов/ответов
МетрикиPrometheus + клиент prometheus_client (счётчики accuracy, cost, latency)Мониторинг в реальном времени
ЭкспериментыMLflow (опционально)Логирование accuracy и cost по версиям
Контейнеризация (опционально)Docker + docker-composeУпаковка сервиса

4. Этапы выполнения

Этап 1: Подготовка данных и метрик (2 часа)

Действия

  1. Собрать калибровочный датасет (минимум 200 запросов). Используйте комбинацию:

    • 100 простых запросов: «Сколько 2+2?», «Кто написал 'Войну и мир'?», «Переведи 'hello' на русский».
    • 100 сложных запросов: задачи на логику, многопоточные рассуждения, генерацию кода с объяснениями.
    • Формат: CSV с колонками query, complexity_label (1 — сложный, 0 — простой).
  2. Добавить 200 запросов для валидации (смешанные, без разметки) — для финального A/B теста.

  3. Запустить baseline на GPT-4 (или его симуляторе):

    import openai
    client = openai.OpenAI(api_key="...")
    accuracy, total_cost = 0, 0
    for row in dataset:
        resp = client.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": row["query"]}])
        # оцениваем ответ по эталону (идеально — если датасет размечен)
    

    Зафиксировать accuracy_baseline и cost_baseline.

  4. Создать скрипт metrics.py с функциями:

    • compute_accuracy(responses: list, references: list): сравнивает ответы с эталоном (exact match или LLM-judge).
    • compute_cost(responses: list, model: str): суммирует стоимость токенов по прайсингу OpenAI/Groq.

Ожидаемый результат этапа Скрипт baseline.py, который выводит Accuracy: 0.92, Cost: 15.4$. Датасет dataset.csv с 400 запросами.


Этап 2: Разработка классификатора сложности (3 часа)

Действия

  1. Выбрать архитектуру классификатора:

    • Вариант A (rule-based): Простое правило: длина > 150 символов ИЛИ содержит слова «объясни», «почему», «как работает» → сложный.
    • Вариант B (ML): Использовать TF-IDF + LogisticRegression. Обучить на 200 размеченных запросах.
    • Вариант C (LLM-as-judge): Отправлять запрос GPT-4 с промптом «Определи сложность запроса (прост/сложен), верни только одно слово».
  2. Реализовать функцию classify(query: str) -> bool (True — сложный, False — простой). Для варианта B:

    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.linear_model import LogisticRegression
    import joblib
    
    def train_classifier(dataset):
        vec = TfidfVectorizer(max_features=5000)
        X = vec.fit_transform(dataset["query"])
        model = LogisticRegression()
        model.fit(X, dataset["complexity_label"])
        joblib.dump((vec, model), "classifier.pkl")
    
  3. Протестировать точность классификатора на 100 размеченных запросах (hold-out). Цель: F1-score > 0.85.

Ожидаемый результат этапа Файл classifier.py с обученным классификатором (или правилами), файл classifier.pkl для варианта B.


Этап 3: Реализация роутера (3 часа)

Действия

  1. Создать FastAPI приложение (router.py):

    • Endpoint POST /chat с телом {"query": str, "user_id": str}.
    • Логика:
      from fastapi import FastAPI
      app = FastAPI()
      @app.post("/chat")
      async def chat(request: dict):
          is_complex = classify(request["query"])
          if is_complex:
              model, client = "gpt-4", openai_client
          else:
              model, client = "mixtral-8x7b-32768", groq_client  # Groq
          response = call_llm(client, model, request["query"])
          log(request, response, model, compute_cost(response, model))
          return {"response": response, "model_used": model}
      
  2. Добавить логирование — каждый запрос пишется в JSON-логи: timestamp, query, model, latency, cost, user_id.

  3. Добавить метрики Prometheus:

    • router_requests_total{model="gpt-4"}
    • router_request_latency_seconds{model}
    • router_cost_total{model}
    • router_accuracy_ratio — обновляется раз в минуту из скрипта оценки.
  4. Запустить сервис локально (uvicorn router:app --reload). Проверить curl-запросом.

Ожидаемый результат этапа Рабочий HTTP-сервер на localhost:8000, который отвечает разными моделями в зависимости от классификации.


Этап 4: Оценка качества и стоимости (2 часа)

Действия

  1. Написать скрипт evaluate.py, который прогоняет все 400 запросов через роутер и собирает метрики:

    • total_cost = sum(cost each)
    • accuracy = количество правильных ответов (по эталону) / общее количество
    • cost_reduction = (cost_baseline - total_cost) / cost_baseline * 100
  2. Провести A/B тест:

    • Baseline (B): 100% запросов → GPT-4
    • Router (R): классификатор + делегирование
    • Для каждого запроса зафиксировать ответ и стоимость.
  3. Сравнить результаты:

    print(f"Baseline accuracy: {acc_b}, cost: {cost_b}")
    print(f"Router accuracy: {acc_r}, cost: {cost_r}")
    print(f"Cost reduction: {(cost_b - cost_r)/cost_b*100:.1f}%")
    print(f"Accuracy delta: {(acc_r - acc_b)*100:.1f}pp")
    

    Убедиться, что cost reduction >= 50%, accuracy drop <= 5pp.

Ожидаемый результат этапа Файл results.md с таблицей метрик. Если условие не выполнено — перейти к этапу 5 (оптимизация).


Этап 5: Оптимизация порогов и деплой (2 часа)

Действия

  1. Если accuracy упала больше 5% — изменить порог классификатора:

    • Для ML-модели: уменьшить threshold с 0.5 до 0.4 (больше запросов направить на GPT-4).
    • Для rule-based: добавить дополнительный признак (например, наличие кода).
  2. Если cost reduction меньше 50% — увеличить порог (направлять больше запросов на Groq), но следить за accuracy.

  3. Провести повторную оценку (этап 4), пока не будет выполнено целевое условие.

  4. Упаковать сервис в Docker:

    FROM python:3.10
    COPY . /app
    WORKDIR /app
    RUN pip install -r requirements.txt
    CMD ["uvicorn", "router:app", "--host", "0.0.0.0", "--port", "8000"]
    

    Запустить в docker-compose вместе с Prometheus и Grafana (опционально).

Ожидаемый результат этапа Docker-образ, готовый к деплою, и финальный results.md с подтверждением метрик.


5. Критерии приемки (Definition of Done)

  • Разработан классификатор сложности с точностью F1 >= 0.85 (на hold-out).
  • Роутер отвечает на POST /chat и возвращает ответ и имя использованной модели.
  • Для простых запросов используется модель из пула Groq (или симуляция), для сложных — GPT-4.
  • Общая стоимость обработки калибровочного датасета снижена не менее чем на 50% по сравнению с baseline.
  • Accuracy (pass@1) снижена не более чем на 5 процентных пунктов от baseline.
  • Все запросы и ответы логируются в структурированном виде.
  • Метрики Prometheus доступны и показывают latency, cost, количество запросов по моделям.
  • Сервис упакован в Docker и запускается одной командой docker compose up.
  • Написан README.md с описанием архитектуры, инструкцией по запуску и результатами A/B теста.
  • Исходный код покрыт хотя бы базовыми unit-тестами (pytest) для классификатора и эндпоинта.

6. Ожидаемый результат

Основной артефакт

  • Репозиторий с кодом (классификатор, роутер, скрипты оценки, Dockerfile, docker-compose.yml).
  • Файл results.md, содержащий:

Опциональные результаты

  • Дашборд Grafana для визуализации метрик.
  • Отчёт о влиянии порога классификатора на cost vs accuracy (график).
  • Логирование с trace_id для отслеживания каждого запроса.

7. Возможные сложности и их решение

СложностьРешение
Нет доступа к Groq APIИспользовать локальный Ollama с llama3.1:8b и эмулировать скорость и стоимость (снизить стоимость в коде в 10x).
Нет размеченного датасетаСгенерировать 300 запросов с помощью GPT-4 (попросить придумать простые/сложные вопросы) и разметить автоматически (длина + ключевые слова).
Классификатор даёт низкую точностьПерейти на LLM-as-judge (запрос к GPT-4 за 10-20 токенов) — это повысит accuracy, но незначительно увеличит cost.
Accuracy роутера падает >5%Уменьшить порог классификатора, сбалансировать датасет, добавить больше примеров сложных запросов, которые модель Groq плохо обрабатывает.
Разница в формате ответов между моделямиПостобработка: унифицировать вывод (один и тот же системный промпт) или использовать метрику LLM-judge для оценки качества.

8. Бюджет времени (оценка)

ЭтапВремя (часы)
Этап 1: Подготовка данных и метрик2
Этап 2: Разработка классификатора3
Этап 3: Реализация роутера3
Этап 4: Оценка качества и стоимости2
Этап 5: Оптимизация порогов и деплой2
Итого12 часов

Примечание Для первого раза (без опыта) заложите +50% времени (до 18 часов). Если классификатор ML — время на обучение может увеличиться (загрузка данных, подбор гиперпараметров).


9. Связанные вопросы из базы знаний

ВопросТема
101Как метрики качества (accuracy, F1) применить к LLM-ответам?
202Что такое A/B тестирование в контексте ML-сервисов?
303Как логировать запросы и ответы в production LLM?
404Как оценить стоимость инференса LLM (токены, цена)?
505Чем отличается rule-based классификатор от ML модели?
606Как настроить Prometheus метрики для FastAPI сервиса?
707Какие бывают стратегии делегирования между LLM?
808Как упаковать ML-приложение в Docker?
909Что такое LLM-as-judge и когда его применять?

10. Чек-лист самопроверки

  • Я чётко понимаю условие задачи: снижение cost на 50% при accuracy drop <= 5%.
  • Я собрал датасет минимум 200 запросов с разметкой сложности и эталонными ответами.
  • Я запустил baseline (только GPT-4) и зафиксировал accuracy и cost.
  • Я реализовал классификатор (rule или ML) и получил F1 > 0.85 на hold-out.
  • Я написал роутер на FastAPI, протестировал его curl/postman.
  • Я запустил скрипт evaluate.py и получил метрики, удовлетворяющие условию.
  • Я залогировал каждый шаг эксперимента (что менял, какие пороги) в experiments.log.
  • Я упаковал сервис в Docker и убедился, что он работает из контейнера.
  • Я написал README с инструкцией по запуску и результатами.
  • Я провёл финальную проверку: cost_reduction >= 50% AND accuracy_drop <= 5 pp.