# Support Bot SaaS — RAG-ассистент техподдержки Система поиска решений по истории обращений в техподдержку. Основана на RAG (Retrieval-Augmented Generation) — находит похожие решённые тикеты в векторной БД и формирует ответ на их основе через LLM. Развёрнута на Kubernetes (k0s), используются OpenRouter для эмбеддингов и LLM. --- ## Быстрый старт (локальный Docker) ```bash # 1. Клонировать репозиторий git clone https://git.itoservice.ru/dosnav/support-bot-saas.git cd support-bot-saas # 2. Создать backend/.env из шаблона cp backend/.env.example backend/.env # отредактировать backend/.env — указать OPENROUTER_API_KEY и JWT_SECRET # 3. Запустить docker compose up --build -d ``` После запуска веб-интерфейс доступен по адресу: **http://localhost:8080** ### Первый вход 1. Открой **http://localhost:8080** в браузере 2. Зарегистрируйся (Email + пароль, тариф free) 3. После регистрации автоматически войдёшь в систему 4. Создай коллекцию (например «OFRS») 5. Загрузи JSON-файл с тикетами через кнопку «Выбрать JSON/CSV файл» Формат JSON: ```json [ { "ticket_id": "12345", "category": "Сбой работы", "description": "Описание проблемы", "client": "Имя клиента", "messages": [ {"role": "client", "text": "У меня проблема"}, {"role": "support", "text": "Решение проблемы"} ] } ] ``` 6. После загрузки тикетов введи вопрос в поле поиска и нажми «Найти» Система найдёт похожие тикеты, а LLM сформирует ответ на их основе. ### Остановка ```bash docker compose down # С данными: docker compose down -v ``` --- ## Архитектура ``` Пользователь (браузер) │ ▼ ┌─────────────┐ ┌─────────────┐ │ Traefik │────▶│ Nginx │ (frontend, React SPA) │ (Ingress) │ └─────────────┘ │ │ ┌─────────────┐ │ │────▶│ FastAPI │ (backend) └─────────────┘ └──────┬──────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │PostgreSQL│ │ Qdrant │ │ Redis │ │(пользова-│ │(векторная│ │(кэш/сес- │ │ тели, │ │ БД) │ │ сии) │ │коллекции)│ └──────────┘ └──────────┘ └──────────┘ │ ▼ ┌──────────────┐ │ OpenRouter │ │(эмбеддинги + │ │ LLM) │ └──────────────┘ ``` ### Компоненты | Компонент | Технология | |-----------|-----------| | **Фронтенд** | React + Vite + TypeScript, Nginx | | **Бэкенд** | FastAPI (Python 3.12) + SQLAlchemy async | | **База данных** | PostgreSQL (пользователи, коллекции) | | **Векторная БД** | Qdrant (точки с эмбеддингами) | | **Кэш** | Redis | | **Эмбеддинги** | `nvidia/llama-nemotron-embed-vl-1b-v2:free` через OpenRouter | | **LLM** | `openrouter/owl-alpha` через OpenRouter | | **Ingress** | Traefik v3.7 | | **Оркестрация** | Kubernetes (k0s v1.36.1) | | **CI** | GitLab CI (Forgejo) | --- ## REST API v1 Аутентификация: `Authorization: Bearer ` API-ключ можно посмотреть и сбросить на странице **Настройки** в веб-интерфейсе. **Базовый URL:** `http://sovet.itoservice.ru/api/v1` ### Получить информацию об аккаунте ```bash curl -H "Authorization: Bearer " http://sovet.itoservice.ru/api/v1/me ``` Ответ: ```json { "id": "uuid", "email": "user@example.com", "api_key": "d469cee785e444cf9abd24a3e709108c", "plan": "free", "created_at": "2026-06-20T12:00:00Z" } ``` ### Сбросить API-ключ ```bash curl -X PATCH -H "Authorization: Bearer " http://sovet.itoservice.ru/api/v1/me/api-key ``` Ответ: ```json { "api_key": "новый-ключ" } ``` ### Список коллекций ```bash curl -H "Authorization: Bearer " http://sovet.itoservice.ru/api/v1/collections ``` Ответ: ```json [ { "id": "49a3f88e-...", "user_id": "uuid", "name": "OFRS", "ticket_count": 2901, "created_at": "2026-06-20T..." } ] ``` ### Загрузить тикеты в коллекцию ```bash curl -X POST "http://sovet.itoservice.ru/api/v1/tickets?collection_id=" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "tickets": [ { "ticket_id": "12345", "category": "Сбой работы", "description": "Описание проблемы", "client": "Имя клиента", "messages": [ {"role": "client", "text": "У меня проблема"}, {"role": "support", "text": "Решение проблемы"} ] } ] }' ``` Ответ: ```json { "processed": 1, "skipped": 0, "collection_ticket_count": 2902 } ``` ### Поиск с AI-ответом ```bash curl -X POST http://sovet.itoservice.ru/api/v1/search \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "collection_id": "", "query": "не могу начать смену в приложении", "generate_answer": true }' ``` Параметры: - `collection_id` — ID коллекции (обязательно) - `query` — текст проблемы (обязательно) - `generate_answer` — true/false, генерировать ответ LLM (опционально, по умолчанию true) Ответ: ```json { "answer": "Добрый день! Для решения проблемы...", "sources": [ { "ticket_id": "38446", "score": 0.514, "category": "Вопрос по работе мобильного приложения", "full_text": "Категория: ...\nПроблема: ...\nРешение: ..." } ] } ``` --- ## Как это работает ### 1. Загрузка тикетов Пользователь загружает JSON-файл с тикетами через веб-интерфейс или API. Бэкенд: - Проверяет дубликаты (по `tenant_id:ticket_id` через md5) - Очищает персональные данные: телефоны → `[ТЕЛЕФОН]`, email → `[EMAIL]`, имена → `[ИМЯ]` - Строит чанк: выделяет сообщения поддержки (роли `support/assistant/operator/agent`) как решение - Фильтрует бесполезные тикеты: - последнее сообщение поддержки — просьба уточнить / прислать скрин - слишком короткие (< 100 символов) - нет описания и нет сообщений клиента - Векторизует `search_text` (описание + сообщения клиента) через OpenRouter - Сохраняет точку в Qdrant с полями: `tenant_id`, `collection_id`, `ticket_id`, `category`, `client`, `search_text`, `full_text` ### 2. Поиск и генерация Пользователь вводит вопрос. Система: 1. Векторизует вопрос той же моделью эмбеддингов 2. Ищет 20 похожих точек в Qdrant (фильтр по `tenant_id` + `collection_id`) 3. Сортирует по скорингу (косинусная близость), берёт топ-5 4. Формирует контекст из `full_text` найденных тикетов 5. Отправляет в LLM с системным промптом 6. Возвращает ответ + исходные тикеты ### Очистка персональных данных Перед сохранением в Qdrant из текста удаляются: - Номера телефонов: `+7xxxxxxxxxx`, `8xxxxxxxxxx` → `[ТЕЛЕФОН]` - Email-адреса → `[EMAIL]` - Русские имена в обращениях → `[ИМЯ]` --- ## Разработка ### Локальный запуск бэкенда ```bash cd backend python -m venv venv venv\Scripts\activate # Windows pip install -r requirements.txt # Настройки в .env: # openrouter_api_key=... # database_url=postgresql+asyncpg://postgres:postgres@localhost:5432/support_bot # qdrant_url=http://localhost:6333 uvicorn app.main:app --reload ``` ### Локальный запуск фронтенда ```bash cd frontend npm install npm run dev ``` ### Сборка Docker-образов ```bash docker build -t git.itoservice.ru/dosnav/support-bot-saas/backend:latest ./backend docker build -t git.itoservice.ru/dosnav/support-bot-saas/frontend:latest ./frontend docker push git.itoservice.ru/dosnav/support-bot-saas/backend:latest docker push git.itoservice.ru/dosnav/support-bot-saas/frontend:latest ``` ### Деплой в K8s ```bash kubectl apply -f k8s/00-namespace.yaml kubectl apply -f k8s/01-postgres.yaml kubectl apply -f k8s/02-redis.yaml kubectl apply -f k8s/03-qdrant.yaml kubectl apply -f k8s/04-backend.yaml kubectl apply -f k8s/05-frontend.yaml kubectl apply -f k8s/traefik/ kubectl apply -f k8s/06-ingress.yaml ``` --- ## Структура проекта ``` support-bot-saas/ ├── backend/ │ └── app/ │ ├── auth/ # JWT + API-ключ аутентификация │ ├── models/ # SQLAlchemy модели + Pydantic схемы │ ├── routers/ # FastAPI роутеры │ │ ├── auth.py # регистрация, логин, коллекции │ │ ├── tickets.py # загрузка тикетов │ │ ├── search.py # поиск (для фронтенда) │ │ └── api_v1.py # внешний REST API v1 │ ├── services/ # бизнес-логика │ │ ├── chunking.py # нарезка чанков, очистка ПД │ │ ├── embedding.py # эмбеддинги через OpenRouter │ │ ├── vector_store.py# Qdrant: upsert, scroll, search │ │ ├── reranking.py # сортировка результатов │ │ └── llm.py # генерация ответа через OpenRouter │ ├── config.py # настройки из .env │ ├── database.py # подключение к БД │ └── main.py # точка входа FastAPI ├── frontend/ │ └── src/ │ ├── api/client.ts # API-клиент │ ├── pages/ # страницы React │ │ ├── Login.tsx / Register.tsx │ │ ├── Dashboard.tsx │ │ ├── CollectionView.tsx │ │ └── Settings.tsx │ └── nginx.conf # конфиг Nginx ├── k8s/ # манифесты Kubernetes │ ├── 00-namespace.yaml │ ├── 01-postgres.yaml ... 06-ingress.yaml │ └── traefik/ # Traefik CRDs + Deployment ├── docker-compose.yml # локальный запуск (Docker) ├── tickets*.json # тестовые данные ├── README.md └── backend/.env.example # шаблон .env для бэкенда ```