Цель
Отвечать по локальным документам, которые можно обновлять без переобучения модели, и указывать проверяемые источники.
Компоненты
- демонстрационные документы:
data/demo/client-docs/; - embedding-модель:
qwen3-embedding:0.6b; - генеративная модель:
qwen3:8b; - локальный индекс:
data/private/rag.sqlite3; - поиск: cosine similarity;
- формат ответа: Pydantic-модель
RagAnswer.
Построение индекса
uv run python -m agent_lab.rag indexПрограмма разбивает Markdown по заголовкам ##, получает embedding каждого фрагмента и сохраняет вектор вместе с текстом и источником в SQLite.
Вопрос
uv run python -m agent_lab.rag ask \
"Сколько стоит доставка заказа на 4 000 рублей?"Ожидается ответ 490 рублей со ссылкой на delivery.md#Стоимость.
Как работает RAG
- Документы разбиваются на небольшие фрагменты.
- Embedding-модель превращает каждый фрагмент в числовой вектор.
- Вопрос пользователя тоже превращается в вектор.
- По cosine similarity выбираются три ближайших фрагмента.
- Только эти фрагменты передаются генеративной модели.
- Модель формирует ответ и возвращает список источников.
- Python отклоняет ссылку на источник, которого не было среди найденных фрагментов.
Обновление документов
После изменения Markdown-файлов индекс нужно построить повторно:
uv run python -m agent_lab.rag indexЭто и есть важное отличие RAG от fine-tuning: новые факты подключаются переиндексацией документов, а веса модели не меняются.
Ограничения
- учебный поиск перебирает все векторы в SQLite и не рассчитан на миллионы фрагментов;
- качество зависит от разбиения, embedding-модели и формулировки вопроса;
- наличие источника не доказывает, что модель правильно истолковала текст;
- демонстрационные документы вымышлены и не являются правилами реального магазина;
- приватный индекс исключён из Git, но исходные demo-документы публикуются вместе с проектом.
Тесты
uv run pytestТесты проверяют разбиение документов, сравнение векторов, ранжирование и блокировку выдуманного источника без обращения к модели.