RAG с DSPy оптимизацией

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: RAG с DSPy оптимизацией

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

Научиться применять фреймворк DSPy для программной оптимизации RAG-пайплайна. Разработать небольшой датасет из 200 примеров «вопрос–контекст–эталонный ответ», реализовать RAG как DSPy-программу и оптимизировать её с помощью алгоритма MIPRO, добившись повышения метрики Faithfulness не менее чем на 10% относительно базовой версии (без DSPy). Ключевой результат работающий DSPy-оптимизированный RAG с зафиксированным улучшением faithfulness и воспроизводимым пайплайном оценки.

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

Что нужноОткуда взять
Датасет 200 пар (question, context, reference_answer)Сгенерировать вручную или адаптировать существующий (HotpotQA, FiQA, ASQA) – подробнее в симуляции
LLM для генерации ответов (baseline и DSPy)OpenAI API / Anthropic / локальная модель через Ollama (например, llama3-8b)
Embedding модельsentence-transformers (all-MiniLM-L6-v2) или OpenAI embeddings
Векторная БДFAISS (in‑memory) или Qdrant (Docker)
Инструмент оценки faithfulnessRAGAS (faithness) или NLI‑модель (roberta-large-mnli)
Фреймворк DSPyУстановить pip install dspy-ai
Код-репозиторийGitHub / GitLab – для версионирования

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

  1. Датасет Если готового размеченного датасета нет, создайте 200 синтетических примеров:
    • Выберите 30–40 статей из Wikipedia или документации (по одной тематике, например, про Python).
    • Для каждой статьи напишите 5–7 вопросов разного уровня сложности (фактологические, объяснительные).
    • В качестве контекста возьмите релевантный параграф из статьи, а в качестве reference_answer – ответ, сформулированный человеком (или LLM с проверкой).
    • Сохраните в JSON: {"question": "...", "context": "...", "reference_answer": "..."}.
  2. LLM Если нет доступа к платным API, используйте локальную модель через Ollama (ollama run llama3).
  3. Векторная БД FAISS подходит для пет-проекта – установите pip install faiss-cpu.
  4. Оценка faithfulness Если RAGAS не подходит из-за LLM‑зависимости, используйте простую эвристику: проверяйте, содержится ли каждый n-gram ответа в контексте (fallback). Но лучше всё же RAGAS.

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

КомпонентИнструментыНазначение
Язык программированияPython 3.10+Реализация пайплайна
Фреймворк RAG‑оптимизацииDSPy (dspy-ai)Построение LLM-программы и оптимизация
OptimizerMIPRO (в составе DSPy)Оптимизация промптов и параметров
Embeddingsentence-transformers (all-MiniLM-L6-v2)Векторизация документов и вопросов
Векторная БДFAISSИндексация и поиск по сходству
LLM (базовая генерация)OpenAI GPT‑3.5‑turbo / Anthropic Claude / Ollama llama3Генерация ответов
Оценка faithfulnessRAGAS (faithness)Метрика верности ответов контексту
Управление зависимостямиconda / poetry / requirements.txtВоспроизводимость среды

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

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

Действия

  1. Создайте виртуальное окружение и установите зависимости:
    # requirements.txt
    dspy-ai>=2.4.0
    sentence-transformers>=2.2.0
    faiss-cpu>=1.7.0
    datasets>=2.10.0
    ragas>=0.1.0
    openai>=1.0.0
    tqdm
    
  2. Соберите датасет из 200 примеров (см. раздел «Если нет реального инструмента»). Формат — JSON-список объектов с ключами question, context, reference_answer.
  3. Разделите датасет на обучающую (150 примеров) и тестовую (50 примеров) выборки. Убедитесь, что контексты не пересекаются.
  4. Загрузите embedding модель и создайте индекс FAISS для всех контекстов:
    from sentence_transformers import SentenceTransformer
    import faiss
    import numpy as np
    
    model = SentenceTransformer('all-MiniLM-L6-v2')
    contexts = [item['context'] for item in train_data + test_data]
    embeddings = model.encode(contexts)
    index = faiss.IndexFlatL2(embeddings.shape[1])
    index.add(np.array(embeddings))
    
  5. Настройте доступ к LLM (если используете OpenAI, установите переменную окружения OPENAI_API_KEY).

Ожидаемый результат этапа

  • Готовый датасет (train.json, test.json).
  • Работающий FAISS-индекс с контекстами.
  • Виртуальное окружение с установленными пакетами.

Этап 2: Baseline RAG без DSPy (1 час)

Действия

  1. Реализуйте простой RAG-пайплайн на Python:
    • По запросу найдите топ‑3 контекста по косинусной близости (FAISS).
    • Сформируйте промпт: "Ответь на вопрос, используя только предоставленный контекст.\nКонтекст: {context}\nВопрос: {question}\nОтвет:"
    • Отправьте в LLM и получите ответ.
  2. Прогоните baseline на тестовой выборке (50 примеров).
  3. Оцените faithfulness с помощью RAGAS:
    from ragas.metrics import faithfulness
    from ragas.llms import llm as ragas_llm  # требует LLM для оценки
    
    # Подготовка data_sample в формате RAGAS
    data = {
        "question": [item['question'] for item in test_data],
        "contexts": [[item['context']] for item in test_data],
        "answer": baseline_answers,
        "reference": [item['reference_answer'] for item in test_data]
    }
    score = faithfulness.score(data)  # float от 0 до 1
    
  4. Зафиксируйте baseline score – он станет отправной точкой для оптимизации.

Ожидаемый результат этапа

Этап 3: Реализация RAG как DSPy-программы (1.5 часа)

Действия

  1. Импортируйте DSPy и задайте конфигурацию LLM:
    import dspy
    lm = dspy.OpenAI(model='gpt-3.5-turbo', max_tokens=200)
    dspy.settings.configure(lm=lm)
    
  2. Определите модули DSPy:
    • Retrieve: обёртка над FAISS-индексом (возвращает top‑k контекстов).
    • GenerateAnswer: dspy.ChainOfThought с сигнатурой context, question -> answer.
    • RAG: собирает Retrieve + GenerateAnswer.
  3. Протестируйте DSPy-программу на нескольких примерах, убедитесь, что формат ответа совпадает с baseline.
  4. Загрузите обучающую выборку и постройте dspy.Example объекты:
    trainset = [dspy.Example(question=q, context=c, answer=ref).with_inputs('question', 'context')
                for q, c, ref in zip(train_questions, train_contexts, train_refs)]
    

Ожидаемый результат этапа

  • Класс RAG на DSPy, готовый к оптимизации.
  • Набор trainset из 150 примеров.

Этап 4: Оптимизация с MIPRO (1.5 часа)

Действия

  1. Инициализируйте оптимизатор MIPRO:
    from dspy.evaluate import Evaluate
    from dspy.teleprompt import MIPRO
    
    # Задайте метрику – faithfulness (обёртка над RAGAS)
    def faithfulness_metric(example, pred, trace=None):
        # пример: вычислить faithfulness между pred.answer и example.context
        # используйте ту же функцию, что и на этапе 2
        return compute_faithfulness(pred.answer, example.context)
    
  2. Настройте параметры MIPRO:
    • num_candidate_programs=8 (количество пробных программ на итерацию).
    • max_bootstrapped_demos=3 (число демонстраций для few-shot).
    • minibatch_size=25 (размер мини-батча).
  3. Запустите оптимизацию на trainset:
    teleprompter = MIPRO(prompter=lm, metric=faithfulness_metric, 
                         num_candidate_programs=8, max_bootstrapped_demos=3)
    optimized_rag = teleprompter.compile(student_rag, trainset=trainset)
    
  4. Сохраните лучший оптимизированный модуль в файл (через pickle или dspy.serialize).
  5. Прогоните optimized_rag на тестовой выборке и вычислите faithfulness.

Ожидаемый результат этапа

  • Файл optimized_rag.pkl или .json с сериализованным модулем.
  • Новая метрика faithfulness на тесте (например, 0.79).
  • Убедиться, что прирост составляет не менее 10% относительно baseline.

Этап 5: Анализ и документирование (30 минут)

Действия

  1. Сравните ответы baseline и оптимизированной версии на 5–10 примерах качественно.
  2. Зафиксите все изменения, которые MIPRO внёс в промпты (можно вывести optimized_rag.demos и lm.history).
  3. Напишите короткий отчёт (README или Jupyter notebook), включив:
    • Цель и датасет.
    • Архитектура DSPy-программы.
    • Baseline и оптимизированная faithfulness.
    • Анализ, какие компоненты сильнее всего повлияли.
  4. Сделайте коммит в репозиторий.

Ожидаемый результат этапа

  • Воспроизводимый ноутбук / скрипт.
  • README с выводами.

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

  • Датасет из 200 примеров сохранён в data/train.json и data/test.json.
  • Baseline RAG работает, faithfulness зафиксирован (значение в metrics/baseline.json).
  • DSPy-программа RAG успешно компилируется с MIPRO и даёт faithfulnessbaseline × 1.10.
  • Оптимизированный модуль сохранён и загружается без ошибок.
  • В репозитории есть файл requirements.txt для воспроизведения окружения.
  • Код разбит на этапы (ноутбук или папки src/).
  • README содержит инструкцию по запуску и анализ улучшения faithfulness.
  • Проведена ручная проверка 5 ответов — нет галлюцинаций, ответы соответствуют контексту.

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

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

  • Репозиторий с кодом, датасетом и документацией (структура ниже).
  • Оптимизированный DSPy-модуль (optimized_rag.pkl).
  • Файл metrics/results.csv со значениями faithfulness для baseline и оптимизированной версии.

Дополнительные артефакты (опционально):

  • Развёрнутый анализ того, как MIPRO изменил промпты (лог сравнения).
  • График зависимости faithfulness от числа итераций оптимизации.

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

СложностьРешение
LLM API лимиты/ошибки при оптимизацииСнизить num_candidate_programs до 4, использовать кэширование (dspy.Cache). При локальной модели – ограничить батч.
Медленная оценка faithfulness на RAGASВычислять faithfulness не на каждом шаге MIPRO, а на каждом 5-м, или заменить простой эвристикой на этапе tuning, а финальную оценку сделать RAGAS.
MIPRO не улучшает метрикуУвеличить max_bootstrapped_demos до 5, добавить больше обучающих примеров, проверить качество датасета (нет ли противоречий).
Не хватает памяти при FAISSИспользовать IndexIVFFlat вместо IndexFlatL2, уменьшить размерность эмбеддингов (например, all‑MiniLM‑L6‑v2 уже 384).
DSPy несовместим с версией PythonУстановить Python 3.10–3.11, свежую версию DSPy (pip install --upgrade dspy-ai).

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

ЭтапВремя
1. Подготовка датасета и среды1 ч
2. Baseline RAG1 ч
3. DSPy-программа1.5 ч
4. Оптимизация MIPRO1.5 ч
5. Анализ и документирование30 мин
Итого5.5 ч

Примечание Для первого раза может потребоваться на 1–2 часа больше из-за отладки DSPy и датасета.

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

ВопросТема
118Основы RAG: извлечение и генерация
124Метрики оценки RAG (faithfulness, answer_relevancy)
137DSPy vs LangChain vs LlamaIndex
203Оптимизация промптов с DSPy
215MIPRO: как работает оптимизатор
312Сбор синтетических датасетов для RAG
411Работа с FAISS: индексация и поиск
508Оценка faithfulness через NLI
620Fine-tuning эмбеддингов для retrieval
735A/B тестирование RAG-пайплайнов

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

  • Я создал датасет из 200 примеров и разделил его на train/test.
  • Baseline RAG запускается и выдаёт faithfulness – я записал его значение.
  • DSPy-программа корректно собирает Retrieve и Generate, ошибок при тестовом запуске нет.
  • MIPRO успешно завершил оптимизацию (не упал с таймаутом или исключением).
  • Итоговый faithfulness на тесте вырос не менее чем на 10% относительно baseline.
  • Все файлы (датасет, код, результаты) сохранены в репозитории, README написан.