269 lines
15 KiB
Markdown
269 lines
15 KiB
Markdown
# Базовый RAG для локального запуска
|
||
|
||
Этот проект представляет собой систему RAG, которая позволяет преобразовывать документацию из Confluence или других источников в формат, пригодный для работы с локальной Ollama, и задавать вопросы по содержимому документов.
|
||
|
||
## Быстрый старт
|
||
|
||
```bash
|
||
cd ..; ./up; cd -
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install beautifulsoup4 markdownify sentence-transformers qdrant-client langchain transformers
|
||
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
|
||
./download.sh 123456789 # <<== pageId страницы в Confluence
|
||
python3 convert.py
|
||
python3 vectorize.py
|
||
python3 rag.py --interactive
|
||
```
|
||
|
||
## Что такое RAG?
|
||
|
||
RAG (Retrieval-Augmented Generation) — это архитектура, которая расширяет возможности генеративных языковых моделей (LLM) за счет интеграции внешних источников знаний.
|
||
Вместо того, чтобы полагаться исключительно на информацию, полученную во время обучения, RAG сначала извлекает релевантные фрагменты из внешней базы знаний, а затем использует их для генерации более точных и информативных ответов.
|
||
|
||
Основные шаги подготовки RAG:
|
||
- **Индексация**: Документы преобразуются в векторные представления (эмбеддинги) и сохраняются в векторной базе данных
|
||
- **Поиск**: При поступлении запроса система ищет наиболее релевантные фрагменты из индексированной базы
|
||
- **Генерация**: Найденные фрагменты используются как контекст для генерации ответа с помощью языковой модели
|
||
|
||
Преимущества RAG:
|
||
- Повышает точность ответов засчёт использования актуальной и специфической информации
|
||
- Позволяет отвечать на вопросы, требующих знаний, не входящих в обучающие данные модели
|
||
- Дает возможность проверить источник информации в сгенерированном ответе
|
||
- Может работать с проприетарными или конфиденциальными данными без дообучения модели
|
||
|
||
Прочесть подробнее можно здесь: https://habr.com/ru/articles/904032/
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
rag/
|
||
├── input_html/ # Входные файлы HTML, загруженные из Confluence
|
||
├── data/ # Входные (конвертированные) Markdown и прочие текстовые файлы
|
||
├── chats/ # Директория для сохранения чатов
|
||
├── download.sh # Скрипт для загрузки страниц из Confluence
|
||
├── convert.py # Скрипт конвертации HTML в Markdown
|
||
├── vectorize.py # Скрипт векторизации Markdown
|
||
├── rag.py # Основной скрипт RAG системы
|
||
├── clear.sh # Скрипт очистки html/md файлов
|
||
└── README.md # Этот файл
|
||
```
|
||
|
||
## Стек
|
||
|
||
* bash
|
||
* python, venv, pip
|
||
* [ollama](https://ollama.com)
|
||
* [qdrant](https://qdrant.tech)
|
||
* [open-webui](https://docs.openwebui.com)
|
||
|
||
## Предварительные требования
|
||
|
||
1. Запустить среду из корня репозитория: [../README.md](../README.md)
|
||
2. Установить ПО: `curl`, `jq`, `python3`, `pip`
|
||
3. Установить зависимости python:
|
||
|
||
```bash
|
||
python3 -m venv .venv
|
||
source ./venv/bin/activate
|
||
pip install beautifulsoup4 markdownify sentence-transformers qdrant-client langchain transformers
|
||
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
|
||
```
|
||
|
||
4. Установить в ollama генеративную модель -- читай ниже.
|
||
|
||
## Подготовка данных
|
||
|
||
### 1. Выгрузка из Confluence
|
||
|
||
Открыть одну или несколько страниц (разделов) Confluence, которые необходимо получить, и обратить внимание на наличие `pageId`, например:
|
||
|
||
```
|
||
https://conf.company.ltd/pages/viewpage.action?pageId=123456789
|
||
https://conf.company.ltd/pages/viewpage.action?pageId=987654321
|
||
```
|
||
|
||
Если URL страницы не содержит `pageId`, можно открыть историю изменений этой страницы, например:
|
||
|
||
```
|
||
https://conf.company.ltd/pages/viewpreviousversions.action?pageId=123456789
|
||
https://conf.company.ltd/pages/viewpreviousversions.action?pageId=987654321
|
||
```
|
||
|
||
Скопировать значения `pageId` и подставить в команду `./download.sh <pageId1> [<pageId2>...]`, например:
|
||
|
||
```bash
|
||
./download.sh 123456789 987654321
|
||
```
|
||
|
||
В результате указанные страницы и все их дочерние будут сохранены в директорию `./input_html/`.
|
||
Файлы будут названы по заголовкам страниц.
|
||
|
||
> [!IMPORTANT]
|
||
> В начале каждого файла будет сохранена исходная ссылка в формате `@@https://conf.company.ltd/pages/viewpage.action?pageId=123456789@@`.
|
||
> Это сделано для того, чтобы в диалоге с моделью источники информации отображались в виде ссылок, а не названий файлов.
|
||
|
||
> [!IMPORTANT]
|
||
> Confluence не позволяет получить готовые макросы в HTML-виде.
|
||
> Хотя в макросах часто есть очень полезная информация, но её приходится выбрасывать просто потому, что загрузка макросов происходит при загрузке страницы браузером.
|
||
> В файлах будет находиться HTML-текст с готовыми к загрузке макросами, но не загруженными.
|
||
> Поэтому, например, содержания, списки дочерних страниц, встраиваемые диаграммы и пр. плюшечки будут вырезаны.
|
||
>
|
||
> Да, можно запросить страницы простым curl/wget, но (1) будет сложнее получить мета-инфу о странице и (2) даже после очистки HTML-тегов в тексте останется очень много мусора (меню, футер, навигация...)
|
||
>
|
||
> Да, можно комбинировать разные подходы и всё сильно усложнить, но я не хочу.
|
||
|
||
### 2. Конвертация страниц в Markdown
|
||
|
||
Этот формат наиболее хорошо подходит для цитирования, потому что не содержит лишних символов, которые будут мешать хорошей токенизации и векторизации.
|
||
|
||
Для конвертации следует выполнить команду:
|
||
|
||
```bash
|
||
python3 convert.py
|
||
```
|
||
|
||
В результате все html-файлы будут сохранены в директорию `./data/`.
|
||
Файлы будут названы по заголовкам страниц, внутри также сохранится ссылка на исходную страницу `@@...@@`.
|
||
|
||
Для получения справки по скрипту выполни команду:
|
||
|
||
```
|
||
python3 convert.py --help
|
||
```
|
||
|
||
### 3. Векторизация (индексирование)
|
||
|
||
Файлы `./data/*` должны быть проиндексированы.
|
||
|
||
Для того, чтобы проиндексировать документы, выполнить команду:
|
||
|
||
```bash
|
||
python3 vectorize.py
|
||
```
|
||
|
||
Весь текст делится на отрезки (чанки) с некоторым перекрытием.
|
||
Это делается для оптимизации количества информации, которое будет делиться на токены.
|
||
Перекрытие обеспечивает смысловую связь между чанками.
|
||
|
||
Каждый чанк разбивается на токены, которые в виде векторов сохраняются в векторную СУБД Qdrant.
|
||
При работе RAG, близость векторов токенов обеспечивает наибольшее смысловое совпадение разных слов => чанков => предложений => абзацев => документов.
|
||
Как следствие:
|
||
- наибольшее смысловое совпадение с вопросом пользователя;
|
||
- молниеносный поиск по индексу чанков (частям документов);
|
||
- корректное насыщение контекста для генеративной модели.
|
||
|
||
Для получения справки по скрипту выполни команду:
|
||
|
||
```
|
||
python3 vectorize.py --help
|
||
```
|
||
|
||
### 4. Запуск
|
||
|
||
Для работы с RAG в интерактивном режиме (симуляция диалога) следует выполнить команду:
|
||
|
||
```
|
||
python3 rag.py --interactive
|
||
```
|
||
|
||
> [!IMPORTANT]
|
||
> Модель не запоминает ничего, поскольку сам диалог не попадает в контекст!
|
||
> В целом, это похоже на гуглёж.
|
||
|
||
Для разового запуска RAG следует выполнить команду:
|
||
|
||
```
|
||
python3 rag.py --query "твой запрос здесь"
|
||
```
|
||
|
||
Для получения справки по скрипту выполни команду:
|
||
|
||
```
|
||
python3 rag.py --help
|
||
```
|
||
|
||
> [!NOTE]
|
||
> У скрипта очень довольно аргументов для гибкой настройки.
|
||
|
||
#### Кастомный системный промпт
|
||
|
||
Если хочется уточнить роль генеративной модели, можно создать текстовый файл и прописать туда всё необходимое, учитывая следующие правила:
|
||
|
||
1. Шаблон `{{sources}}` будет заменён на цитаты документов, найденных в qdrant
|
||
2. Шаблон `{{query}}` будет заменён на запрос пользователя
|
||
3. Если этих двух шаблонов не будет в промпте, результаты будут непредсказуемыми
|
||
4. Каждая цитата в списке цитат формируется в формате:
|
||
```xml
|
||
<source id="Z">
|
||
Lorem ipsum dolor sit amet
|
||
</source>
|
||
```
|
||
5. При вызове `rag.py` указать путь к файлу промпта, используя аргумент `--sys-prompt $путь_к_файлу`
|
||
6. Если указанного файла не существует, то будет применён промпт по умолчанию.
|
||
|
||
Посмотреть полный промпт можно указав аргумент `--show-prompt` при вызове `rag.py`.
|
||
|
||
## Неплохие модели для экспериментов
|
||
|
||
Обозначения:
|
||
* ☑️ — по умолчанию
|
||
* 🧠 — размышляющая
|
||
* 🏋️ — требуются ресурсы
|
||
|
||
### Эмбеддинг
|
||
|
||
- [`sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2`](https://hf.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2) ☑️
|
||
- [`nomad-embed-text`](https://ollama.com/library/nomad-embed-text)
|
||
- ...
|
||
|
||
### Ранжирование
|
||
|
||
- [`cross-encoder/ms-marco-MMarco-mMiniLMv2-L12-V1`](https://hf.co/cross-encoder/ms-marco-MMarco-mMiniLMv2-L12-V1)
|
||
- [`cross-encoder/ms-marco-MiniLM-L-6-v2`](https://hf.co/cross-encoder/ms-marco-MiniLM-L-6-v2) ☑️
|
||
- [`cross-encoder/ms-marco-TinyBERT-L-2-v2`](https://hf.co/cross-encoder/ms-marco-TinyBERT-L-2-v2)
|
||
- ...
|
||
|
||
> [!NOTE]
|
||
> Другие можно найти здесь: https://github.com/AlexeyMalafeev/ruformers
|
||
|
||
### Генеративные
|
||
|
||
Список по убыванию качества ответов и размера модели, по возрастанию скорости ответов на обычном домашнем ПК.
|
||
|
||
- [`deepseek-r1:8b`](https://ollama.com/library/deepseek-r1) 🏋️🧠
|
||
- [`qwen3:8b`](https://ollama.com/library/qwen3) 🏋️🧠
|
||
- [`dolphin3:8b`](https://ollama.com/library/dolphin3)🏋️
|
||
- [`cogito:8b`](https://ollama.com/library/cogito)🏋️
|
||
- [`openchat:7b`](https://ollama.com/library/openchat) 🏋️☑️
|
||
- [`phi4-mini:3.8b`](https://ollama.com/library/phi4-mini)
|
||
- [`gemma3:4b`](https://ollama.com/library/gemma3)
|
||
- [`gemma3n:e4b`](https://ollama.com/library/gemma3n)
|
||
- [`gemma3n:e2b`](https://ollama.com/library/gemma3n)
|
||
|
||
Также можно посмотреть на [эти модели](../README.md#models) или свои собственные.
|
||
|
||
## Дисклеймер
|
||
|
||
Проект родился на энтузиазме из личного любопытства.
|
||
|
||
**Цель:** изучить современные технологии.
|
||
|
||
**Задачи:**
|
||
|
||
1. облегчить поиск информации о проекте среди почти 2000 тысяч документов в корпоративной Confluence, относящихся к нему;
|
||
2. обеспечить минимум телодвижений для развёртывания RAG с нуля внутри команды;
|
||
3. построить воспроизводимую среду для запуска проекта.
|
||
|
||
Здесь не было задачи сделать всё сложно и по красоте.
|
||
|
||
Этот проект -- пазл, который позволяет пошагово, по косточкам понять и настроить работу RAG.
|
||
|
||
Частично (в качестве агентов) в проекте участвовали модели семейств qwen, clause и chatgpt.
|
||
|
||
## Дополнительные материалы
|
||
|
||
* https://github.com/ollama/ollama/blob/main/docs/api.md
|
||
* https://habr.com/ru/articles/881268/
|
||
* https://habr.com/ru/companies/oleg-bunin/articles/835910/
|