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) |
| Инструмент оценки faithfulness | RAGAS (faithness) или NLI‑модель (roberta-large-mnli) |
| Фреймворк DSPy | Установить pip install dspy-ai |
| Код-репозиторий | GitHub / GitLab – для версионирования |
Если нет реального инструмента — симулируем:
- Датасет Если готового размеченного датасета нет, создайте 200 синтетических примеров:
- Выберите 30–40 статей из Wikipedia или документации (по одной тематике, например, про Python).
- Для каждой статьи напишите 5–7 вопросов разного уровня сложности (фактологические, объяснительные).
- В качестве контекста возьмите релевантный параграф из статьи, а в качестве reference_answer – ответ, сформулированный человеком (или LLM с проверкой).
- Сохраните в JSON: {"question": "...", "context": "...", "reference_answer": "..."}.
- LLM Если нет доступа к платным API, используйте локальную модель через Ollama (ollama run llama3).
- Векторная БД FAISS подходит для пет-проекта – установите pip install faiss-cpu.
- Оценка faithfulness Если RAGAS не подходит из-за LLM‑зависимости, используйте простую эвристику: проверяйте, содержится ли каждый n-gram ответа в контексте (fallback). Но лучше всё же RAGAS.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Реализация пайплайна |
| Фреймворк RAG‑оптимизации | DSPy (dspy-ai) | Построение LLM-программы и оптимизация |
| Optimizer | MIPRO (в составе DSPy) | Оптимизация промптов и параметров |
| Embedding | sentence-transformers (all-MiniLM-L6-v2) | Векторизация документов и вопросов |
| Векторная БД | FAISS | Индексация и поиск по сходству |
| LLM (базовая генерация) | OpenAI GPT‑3.5‑turbo / Anthropic Claude / Ollama llama3 | Генерация ответов |
| Оценка faithfulness | RAGAS (faithness) | Метрика верности ответов контексту |
| Управление зависимостями | conda / poetry / requirements.txt | Воспроизводимость среды |
4. Этапы выполнения
Этап 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 - Соберите датасет из 200 примеров (см. раздел «Если нет реального инструмента»). Формат — JSON-список объектов с ключами
question, context,reference_answer. - Разделите датасет на обучающую (150 примеров) и тестовую (50 примеров) выборки. Убедитесь, что контексты не пересекаются.
- Загрузите 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)) - Настройте доступ к LLM (если используете OpenAI, установите переменную окружения
OPENAI_API_KEY).
Ожидаемый результат этапа
- Готовый датасет (train.json, test.json).
- Работающий FAISS-индекс с контекстами.
- Виртуальное окружение с установленными пакетами.
Этап 2: Baseline RAG без DSPy (1 час)
Действия
- Реализуйте простой RAG-пайплайн на Python:
- Прогоните baseline на тестовой выборке (50 примеров).
- Оцените 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 - Зафиксируйте baseline score – он станет отправной точкой для оптимизации.
Ожидаемый результат этапа
- Значение faithfulness для baseline (например, 0.72).
- Сохранённые baseline-ответы в файле baseline_answers.json.
Этап 3: Реализация RAG как DSPy-программы (1.5 часа)
Действия
- Импортируйте DSPy и задайте конфигурацию LLM:
import dspy lm = dspy.OpenAI(model='gpt-3.5-turbo', max_tokens=200) dspy.settings.configure(lm=lm) - Определите модули DSPy:
- Протестируйте DSPy-программу на нескольких примерах, убедитесь, что формат ответа совпадает с baseline.
- Загрузите обучающую выборку и постройте 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)]
Ожидаемый результат этапа
Этап 4: Оптимизация с MIPRO (1.5 часа)
Действия
- Инициализируйте оптимизатор 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) - Настройте параметры MIPRO:
num_candidate_programs=8(количество пробных программ на итерацию).max_bootstrapped_demos=3(число демонстраций для few-shot).minibatch_size=25(размер мини-батча).
- Запустите оптимизацию на
trainset:teleprompter = MIPRO(prompter=lm, metric=faithfulness_metric, num_candidate_programs=8, max_bootstrapped_demos=3) optimized_rag = teleprompter.compile(student_rag, trainset=trainset) - Сохраните лучший оптимизированный модуль в файл (через
pickleилиdspy.serialize). - Прогоните
optimized_ragна тестовой выборке и вычислите faithfulness.
Ожидаемый результат этапа
- Файл
optimized_rag.pklили.jsonс сериализованным модулем. - Новая метрика faithfulness на тесте (например, 0.79).
- Убедиться, что прирост составляет не менее 10% относительно baseline.
Этап 5: Анализ и документирование (30 минут)
Действия
- Сравните ответы baseline и оптимизированной версии на 5–10 примерах качественно.
- Зафиксите все изменения, которые MIPRO внёс в промпты (можно вывести
optimized_rag.demosиlm.history). - Напишите короткий отчёт (README или Jupyter notebook), включив:
- Цель и датасет.
- Архитектура DSPy-программы.
- Baseline и оптимизированная faithfulness.
- Анализ, какие компоненты сильнее всего повлияли.
- Сделайте коммит в репозиторий.
Ожидаемый результат этапа
- Воспроизводимый ноутбук / скрипт.
- README с выводами.
5. Критерии приемки (Definition of Done)
- Датасет из 200 примеров сохранён в
data/train.jsonиdata/test.json. - Baseline RAG работает, faithfulness зафиксирован (значение в
metrics/baseline.json). - DSPy-программа RAG успешно компилируется с MIPRO и даёт faithfulness ≥ baseline × 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 RAG | 1 ч |
| 3. DSPy-программа | 1.5 ч |
| 4. Оптимизация MIPRO | 1.5 ч |
| 5. Анализ и документирование | 30 мин |
| Итого | 5.5 ч |
Примечание Для первого раза может потребоваться на 1–2 часа больше из-за отладки DSPy и датасета.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 118 | Основы RAG: извлечение и генерация |
| 124 | Метрики оценки RAG (faithfulness, answer_relevancy) |
| 137 | DSPy vs LangChain vs LlamaIndex |
| 203 | Оптимизация промптов с DSPy |
| 215 | MIPRO: как работает оптимизатор |
| 312 | Сбор синтетических датасетов для RAG |
| 411 | Работа с FAISS: индексация и поиск |
| 508 | Оценка faithfulness через NLI |
| 620 | Fine-tuning эмбеддингов для retrieval |
| 735 | A/B тестирование RAG-пайплайнов |
10. Чек-лист самопроверки
- Я создал датасет из 200 примеров и разделил его на train/test.
- Baseline RAG запускается и выдаёт faithfulness – я записал его значение.
- DSPy-программа корректно собирает Retrieve и Generate, ошибок при тестовом запуске нет.
- MIPRO успешно завершил оптимизацию (не упал с таймаутом или исключением).
- Итоговый faithfulness на тесте вырос не менее чем на 10% относительно baseline.
- Все файлы (датасет, код, результаты) сохранены в репозитории, README написан.