English translation is not available yet. Showing Russian content.

Как бы вы спроектировали систему для 1000 одновременных пользователей чат-бота с RAG?

Краткий тезис

Проектирование системы для 1000 одновременных пользователей чат-бота с RAG требует асинхронной архитектуры с очередью задач, кэшированием и горизонтальным масштабированием. Ключевые компоненты: API Gateway (nginx), FastAPI с async-обработчиками, очередь Celery/RabbitMQ, инференс LLM через vLLM с batching|continuous batching, векторная БД Qdrant в кластерной конфигурации и мониторинг через Prometheus/Grafana. Основной вызов — LLM медленная, поэтому очередь обязательна, чтобы не потерять запросы при пиковых нагрузках.


1. Термины и контекст

Одновременные пользователи (concurrent users) — количество активных сессий, которые отправляют запросы в течение одной секунды. 1000 concurrent users ≠ 1000 RPS (requests per second), так как каждый пользователь может делать паузы между сообщениями. Обычно RPS = concurrent users / среднее время ответа. При целевом latency 500 мс RPS ≈ 2000.

RAG (Retrieval-Augmented Generation)пайплайн: запрос → retrieval (поиск релевантных чанков в векторной БД) → генерация ответа LLM с контекстом.

Latency (задержка) — время от отправки запроса до получения ответа. Для чат-бота критично: p95 < 500 мс.

Throughput (пропускная способность) — количество запросов в секунду, которое система может обработать. При 1000 concurrent users throughput должен быть не менее 2000 RPS.

SLO (Service Level Objective) — целевые показатели: latency p95 < 500 мс, availability 99.9%.


2. Архитектура высокого уровня

Система состоит из следующих слоёв:

[Пользователи] → [CDN/Cloudflare] → [API Gateway (nginx)] → [FastAPI (async)]
                                                              ↓
                                                    [Очередь задач (Celery + RabbitMQ)]
                                                              ↓
                                          ┌───────────────────┼───────────────────┐
                                          ↓                   ↓                   ↓
                                    [Worker 1]          [Worker 2]          [Worker N]
                                          ↓                   ↓                   ↓
                                    [vLLM replica]      [vLLM replica]      [vLLM replica]
                                          ↓                   ↓                   ↓
                                    [Qdrant cluster]    [Redis cache]       [PostgreSQL (логи)]

Компоненты


3. API Gateway и балансировка

nginx выступает как reverse proxy. Конфигурация:

upstream fastapi_backend {
    least_conn;
    server fastapi1:8000;
    server fastapi2:8000;
    server fastapi3:8000;
}

server {
    listen 443 ssl;
    location /api/ {
        proxy_pass http://fastapi_backend;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 30s;
    }
}

Почему nginx — лёгкий, проверенный, встроенный rate limiting (limit_req_zone), балансировка по наименьшему числу соединений (least_conn).

FastAPI использует async/await, чтобы не блокировать потоки при ожидании ответа от очереди. Каждый запрос быстро отправляется в RabbitMQ и возвращает пользователю ID задачи. Затем клиент опрашивает статус (polling) или получает ответ через WebSocket.


4. Очередь задач — обязательный компонент

LLM медленная — даже с vLLM инференс занимает 200–500 мс на один запрос. Если 1000 пользователей одновременно отправят запросы, без очереди все воркеры будут перегружены, запросы начнут падать по таймауту.

Очередь решает

  • Буферизация — запросы накапливаются, не теряются.
  • Backpressure — если воркеры заняты, очередь растёт, но система не падает.
  • Retry — при ошибке LLM задача автоматически повторяется (с ограничением).
  • Приоритезация — можно настроить очереди с разным приоритетом (например, платные пользователи быстрее).

Технологии RabbitMQ (надёжный, persistent queues) + Celery (гибкий, интеграция с FastAPI). Пример задачи:

# tasks.py
from celery import Celery

app = Celery('rag_tasks', broker='pyamqp://guest@rabbitmq//')

@app.task(bind=True, max_retries=3, default_retry_delay=10)
def process_rag_query(self, query: str, user_id: str):
    try:
        # 1. Retrieval из Qdrant
        # 2. Генерация через vLLM
        # 3. Сохранение в Redis
        return result
    except Exception as exc:
        raise self.retry(exc=exc)

Масштабирование очереди при росте нагрузки добавляем воркеры Celery (горизонтально). RabbitMQ кластеризуется для отказоустойчивости.


5. Кэширование Redis

Зачем многие пользователи задают одинаковые вопросы (например, «Как сбросить пароль?»). Кэш на уровне retrieval и генерации снижает нагрузку на LLM.

Стратегия

  • Кэш частых запросов — храним (query → ответ) с TTL 1 час.
  • Кэш retrieval — для одинаковых запросов retrieval даёт одинаковые чанки. Храним (query → list of chunk_ids) с TTL 10 минут.
  • LRU eviction — Redis автоматически вытесняет старые записи.

Пример использования в FastAPI

from redis import Redis
cache = Redis(host='redis', decode_responses=True)

async def get_cached_response(query: str):
    return cache.get(f"rag:{query}")

async def set_cached_response(query: str, response: str):
    cache.setex(f"rag:{query}", 3600, response)

Ожидаемый hit ratio 30–50% для популярных тем, что снижает latency для этих запросов до < 50 мс.


6. Масштабирование LLM через vLLM

vLLM — высокопроизводительный инференс-сервер с continuous batching (динамическое объединение запросов в батч). Поддерживает PagedAttention для эффективного управления KV-кэшем.

Горизонтальное масштабирование запускаем несколько реплик vLLM (например, 4–8 на GPU A100). Балансировка через nginx или встроенный load balancer vLLM (round-robin).

Конфигурация запуска

vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --tensor-parallel-size 1 \
    --pipeline-parallel-size 1 \
    --max-num-batched-tokens 8192 \
    --enable-chunked-prefill \
    --port 8001

Метрики для мониторинга request throughput, average latency, queue length в vLLM.

Почему не OpenAI API — для 1000 concurrent users API-ключи дороги и latency выше. Локальный vLLM даёт контроль и низкую задержку.


7. Векторная БД Qdrant cluster

Qdrant — специализированная векторная БД с поддержкой шардирования и репликации.

Кластер из 3 нод

  • Шардирование — данные распределяются по шардам (например, 6 шардов) для параллельного поиска.
  • Репликация — каждый шард имеет 2 реплики для отказоустойчивости.
  • Consistency — настраивается (strong или eventual). Для чат-бота достаточно eventual.

Пример конфигурации (config.yaml):

cluster:
  enabled: true
  node_type: normal
  peers:
    - qdrant-node1:6335
    - qdrant-node2:6335
    - qdrant-node3:6335

Retrieval pipeline: запрос → эмбеддинг (через embedding model, например, BGE-M3) → поиск top-k в Qdrant → возврат чанков.

Latency retrieval < 50 мс при правильной настройке индекса (HNSW).


8. Мониторинг и SLO

Prometheus собирает метрики со всех компонентов:

Grafana дашборд с панелями:

  • Latency p50, p95, p99 — тревога при превышении 500 мс.
  • Availability — процент успешных запросов (цель 99.9%).
  • Queue depth — если > 1000, добавляем воркеры.

Alerting через Alertmanager — уведомления в Slack/Telegram при нарушении SLO.


9. Обработка ошибок и fallback

Rate limiting — на уровне nginx (limit_req) и FastAPI (slowapi) для защиты от DDoS.

Retry с exponential backoff — в Celery задача повторяется через 10, 30, 90 секунд.

Circuit breaker — если vLLM или Qdrant недоступны, перестаём отправлять запросы на 30 секунд, возвращаем пользователю fallback-ответ («Сервис временно недоступен»).

Fallback-ответы — для случаев, когда retrieval не нашёл ничего или LLM выдала ошибку. Храним в Redis статические ответы для частых ошибок.


10. Пример конфигурации (docker-compose)

version: '3.8'
services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
  fastapi:
    build: ./app
    environment:
      - CELERY_BROKER=pyamqp://guest:guest@rabbitmq:5672//
    depends_on:
      - rabbitmq
  rabbitmq:
    image: rabbitmq:3-management
  celery_worker:
    build: ./app
    command: celery -A tasks worker --concurrency=4
    depends_on:
      - rabbitmq
      - vllm
  vllm:
    image: vllm/vllm-openai:latest
    command: --model meta-llama/Llama-3.1-8B-Instruct --port 8001
    deploy:
      replicas: 4
  qdrant:
    image: qdrant/qdrant:latest
    volumes:
      - ./qdrant_storage:/qdrant/storage
  redis:
    image: redis:7-alpine
  prometheus:
    image: prom/prometheus
  grafana:
    image: grafana/grafana

Пет-проект для закрепления

Задача Развернуть локально прототип системы для 10 одновременных пользователей с теми же принципами.

Инструменты Docker, FastAPI, Celery, RabbitMQ, vLLM (можно использовать маленькую модель, например, Qwen2.5-0.5B), Qdrant (один узел), Redis, Locust (для нагрузочного тестирования).

Шаги:

  1. Написать FastAPI-приложение с эндпоинтом /chat, которое отправляет задачу в Celery.
  2. Настроить Celery воркер, который вызывает vLLM (через OpenAI-совместимый API) и Qdrant.
  3. Поднять Qdrant, загрузить тестовые документы (например, 100 статей).
  4. Написать Locust-скрипт для симуляции 10 concurrent users.
  5. Измерить latency и throughput, добавить Redis-кэш, замерить улучшение.
  6. Настроить Prometheus и Grafana для визуализации.

Ожидаемый результат Работающий чат-бот, выдерживающий 10 concurrent users с p95 latency < 1 с (на маленькой модели). Понимание, как каждый компонент влияет на производительность.


Связь с другими вопросами

ВопросТема
80Как бы вы спроектировали RAG-систему для 10 000 документов с разной структурой?
82Как вы уменьшаете latency RAG-системы (время ответа)?
83Как вы обрабатываете запросы, на которые нет ответа в документах?
84Как вы обновляете документы в существующей RAG-системе?
85Что такое Self-RAG и когда его использовать?
86Как вы оцениваете качество retrieval'а в RAG-системе?

Навигация