Как бы вы спроектировали систему для 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 (логи)]
Компоненты
- API Gateway — nginx: балансировка, rate limiting, SSL termination.
- FastAPI — асинхронный веб-сервер: принимает запросы, валидирует, отправляет в очередь.
- Очередь задач — RabbitMQ (брокер) + Celery (воркеры): буферизация, retry, backpressure.
- vLLM — инференс-сервер для LLM: continuous batching, поддержка PagedAttention.
- Qdrant — векторная БД: кластер из 3+ нод, шардирование, репликация.
- Redis — кэш частых запросов и результатов retrieval.
- Prometheus + Grafana — мониторинг метрик.
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 собирает метрики со всех компонентов:
- FastAPI request count, latency, error rate.
- Celery queue length, task duration, retries.
- vLLM throughput, batch size, GPU utilisation.
- Qdrant search latency, number of points, disk usage.
- Redis hit ratio, memory usage.
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 (для нагрузочного тестирования).
Шаги:
- Написать FastAPI-приложение с эндпоинтом
/chat, которое отправляет задачу в Celery. - Настроить Celery воркер, который вызывает vLLM (через OpenAI-совместимый API) и Qdrant.
- Поднять Qdrant, загрузить тестовые документы (например, 100 статей).
- Написать Locust-скрипт для симуляции 10 concurrent users.
- Измерить latency и throughput, добавить Redis-кэш, замерить улучшение.
- Настроить Prometheus и Grafana для визуализации.
Ожидаемый результат Работающий чат-бот, выдерживающий 10 concurrent users с p95 latency < 1 с (на маленькой модели). Понимание, как каждый компонент влияет на производительность.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 80 | Как бы вы спроектировали RAG-систему для 10 000 документов с разной структурой? |
| 82 | Как вы уменьшаете latency RAG-системы (время ответа)? |
| 83 | Как вы обрабатываете запросы, на которые нет ответа в документах? |
| 84 | Как вы обновляете документы в существующей RAG-системе? |
| 85 | Что такое Self-RAG и когда его использовать? |
| 86 | Как вы оцениваете качество retrieval'а в RAG-системе? |
Навигация
- Предыдущий: 80
- Следующий: 82
- Индекс: 00. Индекс разборов