Локальная RAG-система для семантического поиска по текстам книг и ответов на вопросы с опорой на найденные фрагменты. Работает полностью офлайн — интернет нужен только для первой загрузки моделей эмбеддингов.
| Слой | Технологии |
|---|---|
| Фронтенд | React 19, TypeScript, Vite, framer-motion |
| Бэкенд | Python, FastAPI, LangChain |
| Эмбеддинги | intfloat/multilingual-e5-small |
| Векторная БД | ChromaDB (персистентная) |
| LLM | Ollama (локально) |
| Поиск | Гибридный BM25 + Векторный + расширение контекста (radius=2) |
Запрос пользователя
│
├── Векторный поиск (ChromaDB, cosine similarity)
│
└── BM25 лексический поиск (с русским стеммером)
│
Гибридное слияние (RRF + lexical overlap)
│
Расширение контекста (соседние чанки, radius=2)
│
Топ-N результатов → LLM → Ответ
- Python 3.12
- Node.js 18+ — нужен для запуска фронтенда (
npm install,npm run dev) - Ollama — установлен и запущен
git clone https://github.com/f4rceful/BookRAG.git
cd BookRAG# Рекомендуется (лучший баланс качества и скорости):
ollama pull qwen2.5:7b
# Или более мощный вариант:
ollama pull gemma3:12bcd backend
python -m venv venv
# Windows:
venv\Scripts\activate
# Linux/macOS (bash/zsh):
source venv/bin/activate
# Установка зависимостей (автоматически определяет GPU):
python setup.py
uvicorn main:app --host 0.0.0.0 --port 8000Бэкенд будет доступен на http://localhost:8000.
Swagger-документация: http://localhost:8000/docs
setup.pyавтоматически определяет видеокарту (NVIDIA CUDA, AMD ROCm, Apple Silicon MPS) и устанавливает подходящую версию PyTorch.
В новом терминале:
cd BookRAG/frontend
npm install
npm run devФронтенд откроется на http://localhost:5173.
Первый запуск: при старте бэкенд автоматически проиндексирует все книги из
backend/books/в фоне. Прогресс отображается в интерфейсе на странице «Управление книгами». Поиск будет доступен по мере готовности каждой книги. Полная индексация 4 книг занимает 10–20 минут (зависит от железа).
docker-compose up --buildМодель можно сменить через страницу «Настройки» в интерфейсе — без перезапуска сервера.
Настройки бэкенда находятся в backend/.env:
# Модель Ollama (должна быть скачана через ollama pull)
MODEL_NAME=qwen2.5:7b
# URL Ollama (по умолчанию локальный)
OLLAMA_BASE_URL=http://localhost:11434
# Модель эмбеддингов (скачается автоматически с HuggingFace)
EMBEDDING_MODEL=intfloat/multilingual-e5-small
# Параметры нарезки текста (влияют на качество поиска)
CHUNK_SIZE=800
CHUNK_OVERLAP=300
INDEX_BATCH_SIZE=100Важно: после изменения
CHUNK_SIZEилиCHUNK_OVERLAPудалите книгу из базы и загрузите её заново — старые чанки в ChromaDB не обновляются автоматически.
Поддерживается формат .txt (до 50 МБ). Кодировка определяется автоматически (UTF-8, UTF-8 BOM, Windows-1251 и другие).
Положите .txt файлы в backend/books/ и запустите (или перезапустите) бэкенд — книги проиндексируются автоматически в фоне. Прогресс индексации отображается в интерфейсе в реальном времени.
- Откройте фронтенд → страница «Управление книгами»
- Перетащите файл или нажмите на область загрузки
- Нажмите «Загрузить и проиндексировать»
curl -X POST http://localhost:8000/api/upload \
-F "file=@/path/to/book.txt"Для «Войны и мира» (~3 МБ, ~3500 чанков) индексация занимает 2–4 минуты.
| Метод | Эндпоинт | Описание |
|---|---|---|
GET |
/ |
Health check |
GET |
/api/books |
Список загруженных книг |
GET |
/api/stats |
Количество книг и чанков в базе |
GET |
/api/indexing-progress |
Прогресс авто-индексации |
DELETE |
/api/book/{filename} |
Удалить книгу из базы |
POST |
/api/upload |
Загрузить книгу (.txt) |
POST |
/api/search |
Гибридный поиск фрагментов |
POST |
/api/ask |
Вопрос-ответ |
POST |
/api/ask/stream |
Стриминг ответа (SSE) |
GET |
/api/models |
Текущая модель |
GET |
/api/models/available |
Список моделей в Ollama |
POST |
/api/model |
Сменить модель (с валидацией) |
Поиск фрагментов:
curl -X POST http://localhost:8000/api/search \
-H "Content-Type: application/json" \
-d '{"query": "письмо Татьяны к Онегину", "top_k": 5}'Вопрос-ответ:
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question": "Почему Раскольников убил старуху-процентщицу?", "top_k": 5}'Смена модели:
curl -X POST http://localhost:8000/api/model \
-H "Content-Type: application/json" \
-d '{"model_name": "qwen2.5:7b"}'Если модель не установлена в Ollama, вернётся 400 со списком доступных моделей.
Модель можно переключить на странице «Настройки» без перезапуска сервера.
| Модель | RAM | Качество на русском | Скорость |
|---|---|---|---|
qwen2.5:7b ⭐ |
~5 ГБ | ★★★★★ | Средняя |
qwen2.5:3b |
~2 ГБ | ★★★★☆ | Быстрая |
gemma3:12b |
~8 ГБ | ★★★★★ | Медленная |
Важно: не используйте модели с суффиксом
:thinking— они значительно замедляют ответ из-за режима размышлений.
В репозитории уже есть четыре тестовые книги в папке backend/books/:
- Булгаков — «Мастер и Маргарита»
- Достоевский — «Преступление и наказание»
- Пушкин — «Евгений Онегин»
- Чехов — «Вишнёвый сад»
После запуска бэкенда они индексируются автоматически. Ниже — примеры запросов и ожидаемые ответы.
Запрос:
curl -X POST http://localhost:8000/api/search \
-H "Content-Type: application/json" \
-d '{"query": "письмо Татьяны к Онегину", "top_k": 2}'Ответ:
{
"results": [
{
"content": "Письмо Татьяны к Онегину\n\nЯ к вам пишу - чего же боле?\nЧто я могу еще сказать?\nТеперь, я знаю, в вашей воле\nМеня презреньем наказать.",
"source": "Пушкин — «Евгений Онегин».txt",
"score": 0.955,
"location": "Стр. ~55"
},
{
"content": "Письмо Онегина к Татьяне\n\nПредвижу все: вас оскорбит\nПечальной тайны объясненье.\nКакое горькое презренье...",
"source": "Пушкин — «Евгений Онегин».txt",
"score": 0.955,
"location": "Стр. ~154"
}
]
}Запрос:
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question": "Почему Раскольников убил старуху-процентщицу?", "top_k": 5}'Ответ:
{
"answer": "Согласно предоставленным фрагментам, Раскольников убил старуху-процентщицу и её сестру Лизавету. Мотивы его поступка сложны и не сводятся к простому желанию ограбления.\n\nУбийство Раскольникова было «идейным» преступлением — в отличие от схожего реального дела Данилова, мотивы которого были более элементарными. Раскольников руководствовался теорией, по которой «необыкновенный» человек вправе переступить моральный закон ради высшей цели. Однако его «честная, добрая природа постоянно проявлялась сквозь болезненную рефлексию» — он мучился и в итоге сам пришёл с повинной.",
"sources": ["Достоевский — «Преступление и наказание».txt"]
}Запрос:
curl -X POST http://localhost:8000/api/search \
-H "Content-Type: application/json" \
-d '{"query": "Воланд на Патриарших прудах", "top_k": 2}'Ответ:
{
"results": [
{
"content": "Попросим мистера Воланда! Замаскированного великана, клетчатого помощника и кота встретили аплодисментами. Коровьев раскланялся с публикой...",
"source": "Булгаков — «Мастер и Маргарита».txt",
"score": 0.953,
"location": "Стр. ~120"
},
{
"content": "Попытки Иванушки сочинить заявление относительно таинственного консультанта не привели ни к чему. Он написал: «В ОГПУ. Вчера около семи часов...»",
"source": "Булгаков — «Мастер и Маргарита».txt",
"score": 0.952,
"location": "Стр. ~85"
}
]
}Запрос:
curl -X POST http://localhost:8000/api/ask \
-H "Content-Type: application/json" \
-d '{"question": "Кто купил вишнёвый сад и почему?", "top_k": 5}'Ответ:
{
"answer": "Согласно предоставленным фрагментам, вишнёвый сад купил Лопахин. Он купил его, потому что Любовь Андреевна и другие владельцы имения не смогли расплатиться с долгами — имение было выставлено на торги. Лопахин заранее предлагал разбить землю на дачные участки, чтобы спасти имение, но владельцы так и не решились принять его проект до начала торгов.",
"sources": ["Чехов — «Вишнёвый сад».txt"]
}