Реализовать synthetic benchmark генератор

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать synthetic benchmark генератор

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

Разработать автоматическую систему, которая еженедельно создаёт новый набор тестовых примеров (вопросов/задач) с помощью LLM. Генерация должна быть контролируемой по тематике, сложности и формату, а ключевым требованием является отсутствие контаминации: сгенерированные задачи не должны пересекаться с известными публичными бенчмарками (MMLU, HumanEval, MATH и др.) и с ранее сгенерированными пулами. Система автономна, логирует метаданные и может быть запущена по расписанию.

Ключевой результат каждую неделю автоматически формируется валидный набор тестов (≥200 задач), прошедших проверку на пересечение с эталонными бенчмарками и с предыдущими выпусками.

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

Что нужноОткуда взять
API LLM (генерация)OpenAI / Anthropic / локальная модель (через LocalAI)
Эталонные бенчмарки (MMLU, HumanEval, GSM8K, MATH, BigBench)Открытые датасеты (Hugging Face datasets)
Описание предметной области и требований к задачамОт заказчика / из ТЗ на продукт
Схема хранения предыдущих выпусковРазработать самостоятельно: SQLite или CSV
Шаблоны промптов для генерацииСоздать на основе анализа существующих бенчмарков

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

  1. Использовать open‑source модель (например, microsoft/phi-3-mini-4k-instruct) через библиотеку transformers с кэшированием ответов.
  2. Эталонные бенчмарки загрузить с Hugging Face один раз и сохранить локально в FAISS‑индекс для быстрого поиска дубликатов.
  3. Расписание реализовать через cron (или Task Scheduler) — запуск main.py с флагом --week N.

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

КомпонентИнструментыНазначение
Генерация текстаOpenAI API / local‑модель (transformers, vLLM)Создание вопросов, эталонных ответов
Оркестрация пайплайнаPython 3.11, Typer (CLI), scheduleУправление шагами, запуск по расписанию
Детекция контаминацииFAISS (cosine similarity), sentence‑transformers (all‑MiniLM‑L6‑v2)Векторный поиск дубликатов среди эталонов
Хранение метаданныхSQLite (через SQLAlchemy)Логирование каждого вопроса, хэша, темы, недели
Версионирование и CIGit, GitHub Actions (ежедневный запуск при пушах в main)Управление кодом, автоматический прогон
Тестированиеpytest, pytest‑covЮнит‑тесты компонентов
МониторингPrometheus + Grafana (опционально) / просто текстовые логиСбор метрик: количество сгенерированных, процент контаминации

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

Этап 1: Проектирование архитектуры (4 часа)

Действия

  1. Определить формат одной задачи: JSON с полями id, question, expected_answer, source_benchmark (null), topic, difficulty, keywords, creation_week, hash_question.
  2. Спроектировать схему БД: таблица generated_questions + таблица contamination_log. Создать SQLAlchemy модели.
  3. Выбрать метрику детекции контаминации: cos_sim > 0.85 между эмбеддингами сгенерированного вопроса и эталонными вопросами.
  4. Набросать высокоуровневую диаграмму компонентов в Mermaid (опционально, включить в README).

Ожидаемый результат этапа документ ARCHITECTURE.md с описанием схемы данных, потока работы и критериев дубликата.

Этап 2: Реализация генератора вопросов (12 часов)

Действия

  1. Написать функцию generate_batch(topic: str, n: int) -> list[dict]:
    • Формирует промпт с инструкцией (пример: «Сгенерируй 10 математических задач уровня сложности B, каждая с ответом. Не используй задачи из MMLU или GSM8K.»).
    • Парсит ответ LLM (ожидаем JSON‑массив).
    • Проставляет creation_week, вычисляет SHA256‑хэш вопроса (канонизированный текст).
  2. Реализовать управление seed‑темами (берём из topics.yaml или аргумента командной строки).
  3. Добавить retry‑логику с exponential backoff при ошибках API.
  4. Написать юнит‑тест: моковый ответ LLM, проверка количества и структуры.
# example/generator.py
import hashlib, json
from openai import OpenAI

SYSTEM_PROMPT = """Ты генератор тестовых вопросов. 
Отвечай ТОЛЬКО JSON-массивом объектов с ключами: question, expected_answer, topic, difficulty.
Не используй задачи из публичных бенчмарков (MMLU, HumanEval, GSM8K, MATH, BigBench)!"""

def generate_questions(client, topic: str, n: int) -> list[dict]:
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": f"Тема: {topic}, сгенерируй {n} задач."}
        ],
        temperature=0.8,
        response_format={"type": "json_object"}
    )
    raw = resp.choices[0].message.content
    questions = json.loads(raw)  # ожидаем массив или объект с массивом
    for q in questions:
        q["hash"] = hashlib.sha256(q["question"].encode()).hexdigest()
    return questions

Ожидаемый результат этапа стабильный модуль generator.py, проходящий 5+ юнит‑тестов и генерирующий 100 задач за < 2 минут (с эмуляцией API).

Этап 3: Реализация детектора контаминации (8 часов)

Действия

  1. Загрузить эталонные бенчмарки из Hugging Face: mmlu, gsm8k, math, hendrycks_test. Взять по 1000 вопросов из каждого (случайная подвыборка).
  2. Вычислить эмбеддинги для эталонных и сгенерированных вопросов через sentence-transformers/all-MiniLM-L6-v2.
  3. Построить FAISS‑индекс по эталонным векторам.
  4. Для каждого сгенерированного вопроса:
    • Получить top‑1 ближайший эталон.
    • Если cos_sim ≥ 0.85 → mark as contaminated = True.
  5. Сохранить результат в таблицу contamination_log.
  6. Написать тест на симулированных дубликатах (взять один эталонный вопрос и слегка перефразировать – должно быть обнаружено).
# example/detector.py
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np

class ContaminationDetector:
    def __init__(self, reference_texts: list[str]):
        self.model = SentenceTransformer("all-MiniLM-L6-v2")
        ref_emb = self.model.encode(reference_texts, show_progress_bar=False)
        self.index = faiss.IndexFlatIP(ref_emb.shape[1])
        self.index.add(np.array(ref_emb, dtype=np.float32))
        self.ref_texts = reference_texts
        self.threshold = 0.85

    def check(self, question_text: str) -> tuple[bool, float]:
        emb = self.model.encode([question_text], show_progress_bar=False)
        scores, idxs = self.index.search(np.array(emb, dtype=np.float32), 1)
        score = float(scores[0][0])
        return (score >= self.threshold), score

Ожидаемый результат этапа модуль detector.py с FAISS‑индексом, точность обнаружения явных дубликатов > 95%.

Этап 4: Интеграция и еженедельный запуск (6 часов)

Действия

  1. Написать главный скрипт pipeline.py:
    • Получает аргумент --week (дефолт – вычисляется из даты).
    • Выбирает 5 тем из topics.yaml (random или по очереди).
    • Для каждой темы вызывает generator (n=40 → 200 задач всего).
    • Прогоняет все через detector.
    • Отбрасывает контаминированные.
    • Дополняет выборку до 200 повторной генерацией (если менее 200 чистых).
    • Сохраняет чистые вопросы в SQLiteCSV‑дамп для использования).
    • Пишет лог: сколько сгенерировано, сколько отброшено, метрики по темам.
  2. Настроить cron (каждую пятницу в 02:00) или GitHub Actions workflow с триггером schedule.
  3. Добавить оповещение в Telegram/email при успехе или ошибке (через python-telegram-bot или просто write‑to‑file).

Ожидаемый результат этапа работающий пайплайн, который можно запустить командой python pipeline.py --week 12 и получать чистый дамп benchmark_week12.json.

Этап 5: Валидация и мониторинг (6 часов)

Действия

  1. Разработать тесты на полную интеграцию (mock‑LLM и mock‑FAISS): проверить, что pipeline не падает и генерирует ≥200 чистых вопросов.
  2. Выполнить ручную валидацию на 100 вопросах: выбрать 10 случайных, проверить качество (осмысленность, корректность ответа), отсутствие явных копий из MMLU (human check).
  3. Добавить скрипт report.py, который выдаёт статистику по всем неделям: общее количество, динамика контаминации, распределение тем.
  4. Задокументировать инструкцию по запуску и добавлению новых тем.

Ожидаемый результат этапа отчёт о валидации, документация README.md, репозиторий готов к деплою.

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

  • 1. Система еженедельно генерирует ≥200 уникальных вопросов (без дублей внутри одного выпуска).
  • 2. Контаминация с эталонными бенчмарками не превышает 1% от всего выпуска (т.е. допускается ≤2 совпадения).
  • 3. Контаминация с предыдущими выпусками (все недели) ≤ 1% (проверка по хэшу).
  • 4. Вопросы имеют структурированный JSON-формат с полями question, expected_answer, topic, difficulty, creation_week.
  • 5. Система автоматически запускается по расписанию (cron/GitHub Actions) без ручного вмешательства.
  • 6. Все ошибки генерации/детекции логируются, пайплайн не завершается аварийно при временных сбоях (retry).
  • 7. Код покрыт юнит-тестами (минимум 10 тестов, покрытие ≥70%).
  • 8. Приложена документация по запуску, настройке и добавлению новых тем/бенчмарков.

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

Основной артефакт репозиторий с кодом, содержащий:

  • pipeline.py – точка входа.
  • generator.py, detector.py, models.py, config.py.
  • benchmarks/ – папка с еженедельными дампами (начиная с недели 1).
  • tests/ – 10+ тестов.
  • README.md с описанием и инструкцией.

Содержимое финального выпуска (benchmark_week12.json):

[
  {
    "id": "W12_001",
    "question": "Вычислите двойной интеграл ∫∫_D x^2 y dxdy, где D – треугольник с вершинами (0,0), (1,0), (0,1).",
    "expected_answer": "1/12",
    "topic": "математический анализ",
    "difficulty": "средний",
    "creation_week": 12,
    "hash": "a1b2c3d4...",
    "contamination_check_passed": true
  }
]

Опционально дашборд с метриками (Grafana) или простой HTML-отчёт.

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

СложностьРешение
LLM повторно генерирует задачи из своих тренировочных данных (даже если запретить в промпте)Систематически проверять через детектор контаминации; если процент >5% – менять промпт (добавлять few-shot с «непохожими» примерами) или использовать более новую модель.
Дрейф домена со временем (темы надоедают)Ротация тем на основе рейтинга: меньше всего использованные по истории – приоритет. Добавить возможность автоматического расширения тем через LLM.
Ложные срабатывания детектора (cos_sim >0.85, но задачи разные)Оптимизировать порог (подобрать на валидации) или добавить второй проход: проверка n-грамм.
Сбой API на этапе генерацииRetry до 3 раз с увеличением задержки; если всё равно ошибка – пропустить тему и записать в лог.
Увеличение размера FAISS-индекса (сотни тысяч эталонов)Использовать IVF-индекс (квантование) или подгружать только релевантный поднабор по теме.

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

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

Примечание для первого раза (из‑за настройки окружения, загрузки моделей) время можно увеличить на 20–30%.

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

ВопросТема
12Методы оценки качества LLM
47Понятие контаминации тестовых данных
103Синтетические датасеты: генерация и валидация
215Использование sentence-transformers для поиска дубликатов
324FAISS: построение индексов и поиск по сходству
401Распределённая генерация задач через LLM (промпт-инжиниринг)
512CI/CD для ML-пайплайнов (GitHub Actions)
678Параметризованные юнит-тесты с mock-объектами
789Хранение метаданных экспериментов (SQLite vs MLflow)
890Оценка дрейфа данных при генерации

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

  • Я проверил, что пайплайн запускается без ошибок и генерирует 200 задач за один прогон.
  • Я убедился, что ни одна из сгенерированных задач не совпала с эталонными бенчмарками (cos_sim < 0.85).
  • Я проверил, что логи контаминации записываются в БД и содержат понятный уровень детализации.
  • Я добавил тест на случай контаминации (внёс заведомо похожий вопрос) и убедился, что детектор его отбрасывает.
  • Я задокументировал в README инструкцию по добавлению новой темы и запуску «вручную».