No description
Find a file
ed0ss 14025ae30d
Some checks are pending
CI / lint-backend (push) Waiting to run
CI / lint-frontend (push) Waiting to run
CI / build-backend (push) Blocked by required conditions
CI / build-frontend (push) Blocked by required conditions
fix: передача реальной ошибки LLM в ответ (вместо null)
2026-06-25 12:15:45 +03:00
.github/workflows Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
.opencode/plans Initial commit 2026-06-20 16:07:21 +03:00
backend fix: передача реальной ошибки LLM в ответ (вместо null) 2026-06-25 12:15:45 +03:00
frontend feat: внешний REST API v1 с аутентификацией по API-ключу 2026-06-25 11:43:51 +03:00
helm/support-bot-saas Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
k8s chore: убран отдельный IngressRoute для api.sovet.itoservice.ru, API доступен через sovet.itoservice.ru 2026-06-25 12:11:43 +03:00
.gitignore Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
app.py Initial commit 2026-06-20 16:07:21 +03:00
docker-compose.monitoring.yml Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
docker-compose.yml Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
prepare_data.py Initial commit 2026-06-20 16:07:21 +03:00
prometheus.yml Реструктуризация проекта в SaaS: FastAPI бэкенд, React SPA фронтенд, Helm-чарт, CI/CD 2026-06-20 17:40:27 +03:00
README.md chore: убран отдельный IngressRoute для api.sovet.itoservice.ru, API доступен через sovet.itoservice.ru 2026-06-25 12:11:43 +03:00
requirements.txt Initial commit 2026-06-20 16:07:21 +03:00
test_upload.py fix: масштабные исправления и улучшения бэкенда и фронтенда 2026-06-20 19:51:58 +03:00
tickets-202511.json Initial commit 2026-06-20 16:07:21 +03:00
tickets-202512.json Initial commit 2026-06-20 16:07:21 +03:00
tickets-202601.json Initial commit 2026-06-20 16:07:21 +03:00
tickets-202602.json Initial commit 2026-06-20 16:07:21 +03:00
tickets-202603.json Initial commit 2026-06-20 16:07:21 +03:00
tickets.json Initial commit 2026-06-20 16:07:21 +03:00

Support Bot SaaS — RAG-ассистент техподдержки

Система поиска решений по истории обращений в техподдержку. Основана на RAG (Retrieval-Augmented Generation) — находит похожие решённые тикеты в векторной БД и формирует ответ на их основе через LLM.

Развёрнута на Kubernetes (k0s), используются OpenRouter для эмбеддингов и LLM.


Архитектура

Пользователь (браузер)
      │
      ▼
┌─────────────┐     ┌─────────────┐
│   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-ключ>

API-ключ можно посмотреть и сбросить на странице Настройки в веб-интерфейсе.

Базовый URL: http://sovet.itoservice.ru/api/v1

Получить информацию об аккаунте

curl -H "Authorization: Bearer <API_KEY>" http://sovet.itoservice.ru/api/v1/me

Ответ:

{
  "id": "uuid",
  "email": "user@example.com",
  "api_key": "d469cee785e444cf9abd24a3e709108c",
  "plan": "free",
  "created_at": "2026-06-20T12:00:00Z"
}

Сбросить API-ключ

curl -X PATCH -H "Authorization: Bearer <API_KEY>" http://sovet.itoservice.ru/api/v1/me/api-key

Ответ:

{
  "api_key": "новый-ключ"
}

Список коллекций

curl -H "Authorization: Bearer <API_KEY>" http://sovet.itoservice.ru/api/v1/collections

Ответ:

[
  {
    "id": "49a3f88e-...",
    "user_id": "uuid",
    "name": "OFRS",
    "ticket_count": 2901,
    "created_at": "2026-06-20T..."
  }
]

Загрузить тикеты в коллекцию

curl -X POST "http://sovet.itoservice.ru/api/v1/tickets?collection_id=<ID_КОЛЛЕКЦИИ>" \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "tickets": [
      {
        "ticket_id": "12345",
        "category": "Сбой работы",
        "description": "Описание проблемы",
        "client": "Имя клиента",
        "messages": [
          {"role": "client", "text": "У меня проблема"},
          {"role": "support", "text": "Решение проблемы"}
        ]
      }
    ]
  }'

Ответ:

{
  "processed": 1,
  "skipped": 0,
  "collection_ticket_count": 2902
}

Поиск с AI-ответом

curl -X POST http://sovet.itoservice.ru/api/v1/search \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "collection_id": "<ID_КОЛЛЕКЦИИ>",
    "query": "не могу начать смену в приложении",
    "generate_answer": true
  }'

Параметры:

  • collection_id — ID коллекции (обязательно)
  • query — текст проблемы (обязательно)
  • generate_answer — true/false, генерировать ответ LLM (опционально, по умолчанию true)

Ответ:

{
  "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]
  • Русские имена в обращениях → [ИМЯ]

Разработка

Локальный запуск бэкенда

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

Локальный запуск фронтенда

cd frontend
npm install
npm run dev

Сборка Docker-образов

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

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
├── tickets*.json              # тестовые данные
└── README.md