За 8 дней частичной занятости я собрал RAG-систему на NestJS + PostgreSQL (pgvector), которая обрабатывает ~11 000 чанков документов.
Первая версия отвечала около 4 минут, после оптимизации - 40–60 секунд.
Главный вывод: RAG - это не «векторный поиск + LLM», а в первую очередь подготовка данных, фильтрация контекста и аккуратная работа с промптами.


Зачем я это делал

Главной целью проекта было создать RAG-систему, которая могла бы отвечать на вопросы на основе моих знаний и опыта, это позволило понять реальную работу с большим количеством документов.

RAG-система была интегрирована с моим сайтом-визиткой site15.ru. Там я показываю и описываю часть проектов за последние 10 лет: скачивания, звёзды, просмотры, npm-библиотеки, счётчики групп, посты и карму на Habr и dev.to.

Технически это реализовано так: фронтенд site15.ru → backend site15.ru → сервер rag-system. Backend передаёт специальный API-ключ, что хотя бы частично защищает сайт от лишних запросов.

Форма ввода API-ключа на фронтенде админки, демонстрирующая защиту доступа
Форма ввода API-ключа на фронтенде админки, демонстрирующая защиту доступа

Таким образом, site15.ru выступает как демо и интерфейс для взаимодействия с RAG.

Фронтенд чата на site15.ru, пример запроса к RAG и ответа
Фронтенд чата на site15.ru, пример запроса к RAG и ответа

Почему RAG оказался сложнее, чем казалось

На старте проект выглядел просто:

RAG = векторный поиск + LLM

На практике оказалось, что большая часть времени уходит на:

  • подготовку и сегментацию данных,

  • фильтрацию контекста,

  • формирование и согласование промптов.

Первая версия системы делала всё последовательно - и отвечала около 4 минут даже на простой вопрос.


Архитектура системы

Источники данных (Telegram переписки, статьи, портфолио, резюме)
            ↓
Backend (NestJS)
 ├─ LLM-модуль
 ├─ PostgreSQL + pgvector
 ├─ RxJS асинхронный pipeline
 ├─ Dialog Manager
 └─ API контроллер для site15.ru
            ↓
RAG-компоненты
 ├─ Question Transformer
 ├─ Фильтрация документов и секций
 ├─ Векторный поиск
 └─ Формирование промпта
            ↓
LLM-провайдеры
(OpenAI / Groq / Ollama)
Swagger документация от backend, демонстрирующая API для RAG
Swagger документация от backend, демонстрирующая API для RAG

Выбор стека

Backend - NestJS
Я постоянно пишу бэкенды на NestJS, поэтому выбор очевиден.

Main файл nestjs
Main файл nestjs

Frontend - React Admin
Нужна была админка, чтобы управлять данными и промптами.

Cписок embedding документов
Cписок embedding документов
Embedding документ
Embedding документ

PostgreSQL + pgvector
Одна система для обычных данных и векторов - проще и надежнее, чем раздельные хранилища.

Таблицы в базе данных
Таблицы в базе данных

Несколько LLM-провайдеров
Поддержка разных провайдеров позволяет использовать бесплатные лимиты и легко переключаться при необходимости.

Список нейронных сетей
Список нейронных сетей

Ключевое архитектурное решение: иерархическая фильтрация

Главная идея - не отправлять в LLM всё подряд.
Pipeline обработки запроса:

Запрос пользователя (frontend site15.ru)
  ↓
Backend site15.ru
  ↓
RAG-сервер: нормализация вопроса
  ↓
Фильтрация по метаданным      (11 000 → ~500)
  ↓
Фильтрация по секциям/заголовкам (500 → ~200)
  ↓
Векторный поиск                (200 → 5–10)
  ↓
Один оптимизированный запрос в LLM
Визуализация pipeline запросов/ответов в админке или диаграмма архитектуры
Визуализация pipeline запросов/ответов в админке или диаграмма архитектуры

Так удалось значительно сократить объём данных, отправляемых в LLM, и ускорить ответы.


Самая большая техническая проблема

Создание метаданных для 11 005 чанков документов.
Так как я хотел, чтобы всё работало, используя только бесплатные лимиты LLM, я не мог создавать метаданные в о��лачных LLM — там я быстро упирался в ограничения, поэтому пришлось запускать локально через LM Studio с моделью qwen2.5-7b-instruct и обработка заняла 2 дня на RTX 2060 SUPER.

LM Studio
LM Studio

Почему первая версия была медленной

Первая версия: 8 промежуточных промптов, последовательные вызовы LLM → ~4 минуты на ответ.

После оптимизации: 4 согласованных промпта, часть этапов параллельна, асинхронная очередь на RxJS → 40–60 секунд.


Промпты и ошибки

Промпты оказались самым сложным моментом. Примеры:

Утечка контекста

Пользователь: "Расскажи про NestJS"
LLM: "NestJS - отличный фреймворк. Кстати, у меня есть телеграм-бот для кофе..."

Конфликт инструкций

Промпт 1: отвечай кратко
Промпт 2: приводи подробные примеры

Вывод: меньше, но согласованных промптов - лучше.


Использование ИИ при разработке

Около 70% кода писалось с помощью ИИ-ассистентов, но без моей архитектурной правки и отладки он не работал бы.

Код сгенерированный AI
Код сгенерированный AI

Безопасность

  • проверка разрешённого IP,

  • проверка API-ключа для запросов с backend site15.ru.

Проект не production-ready, но это позволяет хоть как-то защитить основной сайт от лишних запросов.

AuthGuard - проверка IP / API-ключа в админке
AuthGuard - проверка IP / API-ключа в админке

Развёртывание

  • RAG-сервер на отдельном VPS,

  • docker-compose для PostgreSQL и Ollama,

  • backend через pm2,

  • интеграция с site15.ru.

Docker-compose
Docker-compose

Текущий статус

Экспериментальный проект, не MVP, не production-ready.
Site15.ru выступает как интерфейс для демонстрации работы RAG и статистики проектов.


Планы на будущее

  • Написание тестов пользовательских сценариев (проверка корректности ответов и pipeline).

  • Рефакторинг LLM-модуля в стиле NestJS.

  • Добавление аналитики: время обработки, количество вызовов LLM, расход токенов.

  • Автоматизация деплоя через Docker / Kubernetes.

  • Улучшение точности и скорости через оптимизацию фильтрации контекста.


Выводы

RAG - это сложный инженерный процесс: подготовка данных, фильтрация контекста и аккуратная работа с промптами важнее, чем сама LLM.

За 8 дней частичной занятости удалось собрать рабочую систему, интегрировать её с site15.ru и получить реальный опыт работы с RAG.


Ссылки

https://github.com/site15/rag-system - Репозиторий проекта
https://site15.ru - Мой сайт визитка с чатом поддержки