Проект демонстрирует, как создать систему RAG (Retrieval-Augmented Generation) с использованием графа. Реализован с помощью следующих технологий:
- LangGraph — фреймворк для построения агентов с помощью графов
- LangChain — фреймворк для создания приложений на базе LLM
- llama.cpp — движок для инфернса open-source LLM на ЦПУ/ГПУ
- Qdrant — высокопроизводительная векторная база данных
- MLflow — платформа для создания моделей и AI/LLM приложений
- SentenceTransformers — фреймворк для создания высокого качества эмбеддингов для текста
- Ragas — набор инструментов для оценки и оптимизации приложений, использующих LLM
Граф реализованной системы имеет следующий вид:

где основные узлы это:
Retrieve Documents— извлечение релевантных документов на основании вопроса пользователяEvaluate Documents— оценка релевантности извлеченных документовSearch Online— поиск информации онлайн, если извлеченные документы не позволяют ответить на вопрос или в базе данных не содержится ответа на вопросGenerate Answer— генерация ответа на основании полученных документов
условные ребра графа
Hallucinations detected— оценка сгенерированного ответа на наличие галлюцинаций (выдуманные факты, не содержащиеся в извлеченных документах, ответ не отвечающий на вопрос)Question not addressed— оценка сгенерированного ответа насколько точно он отвечает на вопрос пользователя.
Установим необходимые пакеты на Ubuntu:
sudo apt -q update
sudo apt -q -y install build-essential cmake wget zip unzip
gcc --version
cmake -versionФормирование __pycache__ файлов в директории ~/.cache а не в дириктории проекта:
echo 'export PYTHONPYCACHEPREFIX="${HOME}/.cache/pycache"' >> "${HOME}/.bashrc"
Установим docker по инстркуциями из официальной документации:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; donesudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get updatesudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginПроверим, что docker установлен с помощью команды:
sudo docker -vДобавим команду запуска docker не из-под суперпользователя
sudo usermod -aG docker $USERСоздаем в домашней директории новую директорию .llama.cpp:
cd && mkdir .llama.cpp && cd .llama.cppСкачиваем последний релиз llama.cpp для Linux Ubuntu:
wget https://github.com/ggml-org/llama.cpp/releases/download/b6960/llama-b6960-bin-ubuntu-x64.zipРаспаковываем zip архив и удаляем после распаковки:
unzip llama-b6960-bin-ubuntu-x64.zip && rm llama-b6960-bin-ubuntu-x64.zipДля запуска из любой директории llama.cpp сервера созданим мягкую ссылку на исполняемую программу:
sudo ln -s ~/.llama.cpp/build/bin/llama-server /usr/local/bin/llama-serverПроверяем работоспособность программы:
llama-server -hВ результате будет получена справка по запуску сервера:
----- common params -----
-h, --help, --usage print usage and exit
--version show version and build info
--completion-bash print source-able bash completion script
--verbose-prompt print a verbose prompt before generation (default: false)
Создадим директорию для храниния скачиваемых моделей
mkdir modelsДля реализации проекта скачаем модель unsloth/Qwen3-0.6B-GGUF с квантизацией Q8_K_XL:
wget -O ~/.llama.cpp/models/Qwen3-0.6B-UD-Q8_K_XL.gguf https://huggingface.co/unsloth/Qwen3-0.6B-GGUF/resolve/main/Qwen3-0.6B-UD-Q8_K_XL.gguf?download=trueНе обязательно! Можно также скачать для локального пользования модель unsloth/gpt-oss-20b-GGUF с квантизацией Q8_K_XL:
wget -O ~/.llama.cpp/models/gpt-oss-20b-UD-Q8_K_XL.gguf https://huggingface.co/unsloth/gpt-oss-20b-GGUF/resolve/main/gpt-oss-20b-UD-Q8_K_XL.gguf?download=trueЗапустить сервер для теста можно с помощью команды в терминале:
llama-server -m ~/.llama.cpp/models/gpt-oss-20b-UD-Q8_K_XL.gguf --jinjaСервер будет доступен в веб браузере по адресу: http://localhost:8080/
Ссылка на датасет: galileo-ai/ragbench Описание датасета: RAGBench: Explainable Benchmark for Retrieval-Augmented Generation Systems
Датасет для проекта будет формироваться из данных для теста, домены: медицина, юрисприденция, финансы, техническая поддержка клиентов, общий домен. Из каждого домена берется по 25 документов, размер которых превышает 2500 символов. Из этих 25 документов по каждому домену берется 5 документов (вопрос и правильный ответ) для формирования валидационных данных. Валидационные данные нужны для финальной проверки работоспособности реализованной системы.
Устанавливаем пакетный менеджер uv:
curl -LsSf https://astral.sh/uv/install.sh | shЧтобы изменения внесённые в файл bashrc в текущем сеансе терминала вступили в силу, файл необходимо перезагрузить:
source ~/.bashrcКопируем репозиторий:
git clone https://github.com/dmt-zh/llmops.git && cd llmopsСоздаем виртуальное окружение и устанавливаем зависимости:
uv sync --lockedДля установки дополнительных зависимостей проверки кода (линтеров):
uv sync --group lintСоздаем .env файл на основании файла .env.example:
cp .env.example .envРедактируем переменные в файле .env
Добавляем права запуска скрипту main.py
chmod +x main.pyВ Makefile содержаться команды управления инфраструктурой и проверки кода с помощью линтера ruff. Получить справку можно с помощью следующей команды:
make helpВ результате будет получен следующий вывод:
› help: Display help message with available commands
› llama-server-up: Start inference of a local LLM with llama.cpp server
› llama-server-down: Terminate llama.cpp server
› services-up: Start local services using Docker Compose
› services-down: Stop and remove local services
› lint-check: Check code for linting issues without making changes
› lint-fix: Fix linting issues using ruff
Запускаем сервер llama.cpp в фоновом режиме с моделью Qwen3-0.6B-UD-Q8_K_XL.gguf:
make llama-server-upЗапускаем инфраструктуру для работы RAG системы: Qdrant, MLflow сервер, Postgres, MinIO S3 (Postgres и MinIO S3 нужны для работы MLflow):
make services-upНеобходимо подождать пока сервер MLflow начнет работу. Запуск сервера можно посмотреть с помощью команды:
docker logs mlflow-server --tail 50 -fПри успешном запуске MLflow сервера, будет соответствующая запись логах
[MLflow] Security middleware enabled with default settings (localhost-only). To allow connections from other hosts, use --host 0.0.0.0 and configure --allowed-hosts and --cors-allowed-origins.
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
...
INFO: Application startup complete.
INFO: 127.0.0.1:45410 - "GET /health HTTP/1.1" 200 OK
INFO: 127.0.0.1:50666 - "GET /health HTTP/1.1" 200 OK
Основной точкой входа для работы RAG системы является скрипт main.py. С помощью следующей команды можно посмотреть справку по доступным аргументам запуска:
./main.pyВ результате будет получен следующий вывод:
Usage: main.py [OPTIONS] COMMAND [ARGS]...
The entrypoint of the project.
Options:
--help Show this message and exit.
Commands:
create-collection Create a new Qdrant collection.
create-datasets Download and create datasets for RAG systems.
delete-collection Delete all data from the Qdrant collection.
draw-gpraph Draw the graph and save its image to `static` folder.
process-question Run the Q&A processing workflow.
rag-evaluation RAG evaluation on a specific domain.
Скачиваем данные и формируем основной и валидационный датасеты:
./main.py create-datasetsПосле завершеня работы команды будет сформирована следующая файловая структура:
├── data
│ ├── download_data.py
│ ├── eval_dataset.json
│ └── main_dataset.json
Создаем коллекцию в векторной базе данных и наполняем ее эмбеддингами на основании частей текста, полученных из документов основного датасета main_dataset.json:
./main.py create-collection
На выполнение этой команды может потребоваться от 30 мин. до 2 часов, в зависимости от мощности CPU. В результате векторная база данных будет содержать около 2 тыс. эмбеддингов. Созданная коллекция будет доступна в веб браузере по ссылке:
http://localhost:6333/dashboard#/collections

Посмотреть на кластеры эмбеддингов можно странице коллекции по ссылке в веб браузере:
http://localhost:6333/dashboard#/collections/domain_knowledge/visualize

Что бы получить изображение графа выполнения необходимо выполнить команду:
./main.py draw-gpraphИзображение графа будет сохранено в директори ./static/graph.png
Обработка вопросов осуществляется с помощью команды:
./main.py process-question --q 'вопрос_который_необходимо обработать'Параметр --q необязательный. Если его не передавать, то вопросы будут считываться из файла questions.txt:
./main.py process-questionДля детального понимания что происходит на каждом шаге работы RAG системы используется MLflow сервер для автоматического трекинга вызовов, происходящих в Langchain и OpenAI API.
После выполнения команд ./main.py process-question --q 'вопрос_который_необходимо_обработать' и ./main.py process-question можно проанализировать ход выполненя обработки вопросов на странице трекинга: http://localhost:5000/#/experiments/1/traces

Можно также детально ознакомится с графов выполнения по каждому отдельному запросу нажав на идентификатор нужного вопроса.

Так же можно посмотреть всю информация по отедьльному шагу, как промты, так и метаинформацию.

Для оценки качества работы RAG системы используется валидационный набор данных, сформированных на этапе формирования данных. Валидационный датасет содержит 5 доменов (medical, legal, finance, tech, general), в каждом по 5 вопросов.
В качестве метрик для оценки используются метрики из билиотеки Ragas — набор инструментов для оценки и оптимизации приложений, использующих LLM.
В проекте испльзуются следующие метрики:
- ResponseRelevancy (answer_relevancy) — oценивает релевантность ответа по отношению к заданному вопросу. Ответы, содержащие неполную, избыточную или ненужную информацию, штрафуются. Оценка может варьироваться от 0 до 1, где 1 — наивысший балл.
- ContextRelevance (context_relevance) — оценка релевантности найденных документов, которая должна основываться на введенных пользователем данных. Оценка может варьироваться от 0 до 1, где 1 — наивысший балл.
- Faithfulness (faithfulness) — показатель оценивает, насколько фактическое содержание ответа соответствует извлеченному контексту. Он варьируется от 0 до 1, при этом более высокие баллы указывают на лучшую согласованность. Ответ считается достоверным, если все содержащиеся в нем утверждения могут быть подтверждены извлеченным контекстом.
- SemanticSimilarity (semantic_similarity) — oценивается семантическое сходство эталонного ответа с сгенерированным ответом. Для количественной оценки семантического сходства используется модель distiluse-base-multilingual-cased-v1, конвертирующая сгенерированный и эталонный ответы в эмбеддинги, котрые сравниваются между собой по косинусному расстоянию.
Запуск оценки качества осуществляется с помощью команды:
./main.py rag-evaluation --domain <domain>Например, для оценки системы по медицинскому домену, необходимо выполнить следующую команду:
./main.py rag-evaluation --domain medicalВ резульате выполнения программы, на странице трекинга экспериментов (http://localhost:5000/#/experiments), появятся два эксперимента:
Medical domain Traces— запуск валидационного набора данных на медицинском доменеMedical domain Evaluation— оценка каждого запроса по метрикам
На странице Medical domain Evaluation будут отображены посчитанные метрики:





