Система семантического поиска и анализа резюме на базе RAG (Retrieval-Augmented Generation). Использует гибридный поиск (Dense + Sparse/BM25) в Qdrant и локальную LLM (Ollama) для генерации осмысленных ответов.
Система спроектирована по принципу разделения ответственности:
- Parser — разовый скрипт для парсинга .txt резюме, извлечения метаданных (навыки, стаж) и векторизации через intfloat/multilingual-e5-large. Стратегия: 1 Резюме = 1 Чанк (сохраняет контекст).
- Qdrant — векторная БД, хранит эмбеддинги и полнотекстовый индекс (BM25).
- API (FastAPI) — принимает запросы, выполняет Hybrid Search (Dense + Sparse с фьюжном через RRF), применяет жесткие фильтры по навыкам и отправляет топ-результаты в LLM.
- Ollama — локальная LLM, которая читает найденные резюме и формирует финальный ответ для рекрутера.
- Frontend — встроенный в FastAPI минималистичный Chat-UI на HTML/JS (с рендерингом Markdown).
- Hybrid Search: Объединение семантического поиска (Embeddings) и лексического (BM25) через Reciprocal Rank Fusion (RRF) в Qdrant. Находит синонимы, но при этом не пропускает точные названия технологий (например, "Spring Boot").
- Умное фильтрование: Извлеченные из текста навыки сохраняются в виде list в payload Qdrant, что позволяет делать точечные фильтры до обращения к LLM.
- Оптимизация Docker-образов: Эмбеддинг-модель (~2.2 ГБ) скачивается на этапе docker build, а не при старте контейнера. Используются slim образы и непривилегированные пользователи.
- E5 Prefixes: Корректная работа с префиксами passage: и query:, обязательными для модели семейства E5.
- Docker & Docker Compose
- Ollama (установленная и запущенная локально)
- Скачанная модель в Ollama (например, ollama pull llama3 или qwen2.5)
1. Подготовка данных
Помести текстовые резюме в папку resumes в корне проекта.
Важно: Формат .txt файлов должен строго соответствовать шаблону (секция "Навыки:" с перечислением через переносы строк).
2. Настройка переменных
В файле docker-compose.yml в блоке api -> environment укажи модель, которую скачал в Ollama:
OLLAMA_MODEL: llama3 # Поменяйте на свою, например qwen2.5:4b3. Запуск системы
Подними инфраструктуру и API (Qdrant + Web UI):
docker compose up -d --build4. Индексация резюме Когда Qdrant и API запущены, выполни парсинг и создание векторов (контейнер отработает и завершится):
docker compose --profile manual up parser --buildВ логах вы увидите прогресс: "Загрузка модели...", "Найдено файлов: X", "Успешно проиндексировано...".
5. Использование
Открой в браузере: http://localhost:8000
Введи запрос на естественном языке (например: "Нужен Java разработчик со стажем, знавший Spring"). При необходимости используй нижнюю строку для жесткого фильтра по стеку (например: Git, Java).
Система также предоставляет чистое API по адресу http://localhost:8000/search:
POST /search
{
"query": "Нужен Android разработчик, знающий Firebase",
"top_k": 5,
"required_skills": ["Firebase", "Git"]
}Ответ:
{
"answer": "По вашему запросу найдено 2 подходящих кандидата:\n\n1. **Иван Иванов** (Java разработчик)...",
"found_resumes_count": 2
}- Почему 1 Резюме = 1 Чанк? Резюме в среднем занимают 500-1500 токенов. Модели вроде
bge-m3илиmultilingual-e5-largeлегко покрывают этот контекст. Нарезка резюме на куски по 512 токенов привела бы к потере связи между опытом работы и навыками кандидата. - Безопасность: В обоих Dockerfile создаются непривилегированные пользователи (
appuser), контейнеры не работают отroot.