Руководство администратора Platform RAG
Архитектура, эксплуатация и диагностика сервиса Platform RAG.
Назначение и область применения
Настоящее руководство определяет порядок развёртывания, настройки, сопровождения и проверки сервиса Platform RAG в контуре заказчика. Документ предназначен для администратора платформы и предполагает владение основами администрирования Linux, Docker, Kubernetes, Helm и HTTP API.
Platform RAG — общий платформенный сервис уровня COMMON. Он предоставляет управляемое извлечение знаний (retrieval-augmented generation): реестр коллекций, приём нормализованных документов, гибридный поиск с обязательной фильтрацией по правам доступа и выдачу фрагментов с цитатами.
Потребители обращаются к сервису только через санкционированные пути: платформенный SDK (platform-rag) или кастом-компонент low-code. Построение собственного retrieval внутри флоу (generic HTTP, ключи моделей во флоу, захардкоженные URL) не допускается.
Порядок работы разработчика с SDK и компонентом retrieval в настоящем документе не рассматривается и приведён в руководстве пользователя. Контракты API зафиксированы в platform-rag-specs/contracts.md, продуктовые требования — в platform-rag-specs/rag-service-prd.md, спецификация — в platform-rag-specs/platform_rag_specification.md.
Условные обозначения
Параметры, зависящие от конкретной поставки (реестр образов, доменные имена, адреса PostgreSQL, OpenSearch, RabbitMQ, S3 и AI Gateway), приведены как типовые и сопровождаются пометкой «уточняется при внедрении».
Конфигурация задаётся файлом config.yaml (переменная окружения RAG_CONFIG) и может быть переопределена переменными с префиксом RAG__ (вложенность — через __, например RAG__POSTGRES__DSN). Секреты в Kubernetes не размещаются в values и передаются через Secret.
Чарт Helm устанавливает только прикладные нагрузки api и worker. PostgreSQL, RabbitMQ, OpenSearch, объектное хранилище (S3/MinIO), AI Gateway и SpiceDB — внешние зависимости и в чарт не входят.
Термины и сокращения
| Термин, сокращение | Определение |
|---|---|
| Контур заказчика | Сетевая и вычислительная среда, в которой развёрнут сервис |
| Коллекция | Управляемый индекс знаний: профиль эмбеддинга, модель ACL, стратегия чанкинга, параметры поиска |
| Канонический документ | Нормализованный документ приёма (content/content_ref, acl, content_hash, idempotency_key) |
| Ingest | Асинхронный приём: регистрация задания → чанкинг → эмбеддинг → индексация |
| Job store | Таблица platform_rag.ingest_job в PostgreSQL — источник истины по заданиям приёма |
| Retrieval | Синхронный поиск: гибридный kNN+BM25, слияние RRF, фильтр tenant+ACL, цитаты |
| RAG API | Stateless HTTP-сервис (platform-rag-api), порты поверхностей collections, ingest, query |
| Worker | Процесс platform-rag-worker, обрабатывающий задания ingest из RabbitMQ |
| AI Gateway | Единственный разрешённый путь к моделям эмбеддинга и реранка. У сервиса нет собственных ключей инференса вне шлюза |
| SpiceDB | Сервис авторизации (ReBAC): права на коллекцию (read/write/owner) |
| ACL | Список групп и субъектов на документе; перепроверяется в момент запроса |
| Act-цепочка | Делегированная идентичность пользователя (JWT / заголовки), по которой строится фильтр выдачи |
| Fail-closed | При недоступности AuthZ сервис отвечает 503 и не выдаёт данные «на всякий случай» |
| OpenSearch | Векторный и полнотекстовый индекс чанков (knn_vector + BM25) |
| RRF | Reciprocal Rank Fusion — слияние ранжированных списков vector/BM25 |
| Helm / чарт / релиз | Пакетный менеджер Kubernetes; пакет приложения; установленный экземпляр |
| KEDA | Автомасштабирование воркеров по числу заданий PENDING в PostgreSQL |
1. Архитектура сервиса
1.1. Общая схема
Сервис состоит из двух прикладных нагрузок без локального состояния: API и воркер ingest. Всё состояние хранится во внешних сервисах данных. Модели вызываются только через AI Gateway. Авторизация коллекций — SpiceDB; видимость чанков — ACL документа, выведенный из act-цепочки вызывающего.

Разделение ответственности: интеграционные сервисы извлекают данные из источников (Confluence, S3, БД и т.д.) и приводят их к каноническому виду; Platform RAG выполняет чанкинг, эмбеддинг, индексацию, авторизацию и поиск. Прямая запись интеграций в OpenSearch, S3 staging или RabbitMQ не допускается.
1.2. Состав компонентов
| Компонент | Назначение | Примечание |
|---|---|---|
platform-rag-api | HTTP API: коллекции, приём документов, статус заданий, поиск, /health | Stateless, горизонтально масштабируется. Порт 8090 |
platform-rag-worker | Обработка ingest: staging → chunk → embed → index | Масштабируется независимо, в т.ч. через KEDA |
| PostgreSQL | Реестр коллекций и источник истины по заданиям ingest | Схема platform_rag |
| RabbitMQ | Уведомления о заданиях (exchange: rag.ingest, quorum-очередь rag.ingest.jobs, DLX/DLQ) | Не хранит состояние задания |
| OpenSearch | Индекс чанков: embedding (kNN), текст BM25, tenant_id, acl, version | Алиас вида rag-kb-{env} |
| S3 / MinIO | Staging канонического JSON на время задания (payload_uri) | В локальном Compose допускается s3.dry_run: true |
| AI Gateway | Эмбеддинг и реранк | Идентификаторы моделей задаются профилями, не хардкодом в коде |
| SpiceDB | Права на коллекцию: owner / reader / writer | Fail-closed при недоступности |
| Keycloak | Проверка JWT при auth.enabled: true | В dev идентичность задаётся заголовками |
Helm-чарт helm/platform-rag создаёт Deployment *-api, Deployment *-worker, Service (ClusterIP, порт 8090), ConfigMap, ServiceAccount и опционально Ingress и KEDA ScaledObject. Зависимости чартом не устанавливаются.
1.3. Потоки данных
Приём документа. Клиент вызывает POST /v1/documents:submit. API проверяет право записи в коллекцию, регистрирует задание в PostgreSQL (PENDING), сохраняет канонический JSON в S3 и публикует уведомление в RabbitMQ. Воркер захватывает задание (RUNNING), выполняет чанкинг, эмбеддинг через AI Gateway и bulk-индексацию в OpenSearch, затем переводит задание в DONE или FAILED. Повторная отправка с тем же (tenant, collection, doc_id, content_hash, idempotency_key) возвращает DEDUPLICATED.
Поиск. Клиент вызывает POST /v1/query. API авторизует доступ к коллекции (SEC prior), строит эмбеддинг запроса через AI Gateway, выполняет гибридный поиск (kNN + BM25) со слиянием RRF, обязательно фильтрует выдачу по tenant_id и ACL вызывающего (SEC posterior) и возвращает цитаты (doc_id, chunk_id, фрагмент, оценка).
Идемпотентность и надёжность очереди. Источник истины — PostgreSQL. RabbitMQ доставляет уведомление; при потере сообщения фоновая сверка выбирает зависшие PENDING и публикует повторно. Воркеры идемпотентны.
1.4. Минимальные ресурсы
Значения ориентировочные и уточняются по нагрузке (раздел 8). Для пилотного стенда (одна коллекция, проверка API):
| Ресурс | Минимум (пилот) | Рекомендуется (пром) |
|---|---|---|
| Процессор (API + worker) | 2 ядра | 4–8 ядер |
| Оперативная память | 4 ГБ | 8–16 ГБ |
| OpenSearch | 2 ГБ heap | отдельный кластер, реплики индекса |
| PostgreSQL | 1 ГБ | внешний отказоустойчивый кластер |
| Диск | 20 ГБ (индекс + WAL) | по объёму коллекций |
| ОС | Linux x86-64, Docker Compose v2 или Kubernetes 1.27+ | — |
Запросы чарта по умолчанию: API — 100m CPU / 256Mi RAM (лимит 1Gi); worker — 250m CPU / 512Mi RAM (лимит 2Gi).
Языковые модели размещаются за AI Gateway, вне сервиса. Требования к GPU определяются выбранной моделью, а не Platform RAG.
2. Установка и развёртывание
Основной способ пилотных и локальных стендов — Docker Compose. Для промышленных сред — Kubernetes + Helm.
2.1. Предварительные требования
- Linux x86-64 (см. 1.4).
- Docker Engine и Docker Compose версии 2 или кластер Kubernetes с Helm 3.
- Доступ к реестру образов либо возможность собрать образ из исходного кода.
- Для полного цикла ingest/query: доступные PostgreSQL 16, RabbitMQ 3, OpenSearch 2.x, объектное хранилище S3-совместимое, AI Gateway, SpiceDB.
Проверка Docker:
docker --version
docker compose versionПроверка Kubernetes / Helm:
kubectl version --client
helm version2.2. Получение дистрибутива
Исходный код и чарт поставляются репозиторием сервиса. Сборка образа:
docker build -t platform-rag:<tag> .В контуре заказчика используется согласованный реестр (пример dev-поставки: образ platform-rag в корпоративном registry, тег уточняется при внедрении). После сборки образ публикуется в реестр, доступный узлам кластера.
2.3. Настройка конфигурации
Образец — config/config_template.yaml. Рабочий файл — config/config.yaml (путь задаётся RAG_CONFIG). Секреты (DSN, пароли, ключи) не хранятся в Git; для локального стенда допустимы dev-значения только во внутреннем контуре.
Обязательные группы параметров до запуска:
| Группа | Назначение | Действие |
|---|---|---|
postgres.dsn | Подключение к БД (postgresql+asyncpg://…) | Задать устойчивый пароль; схема platform_rag |
rabbitmq.* | Брокер ingest | Хост, пользователь, пароль, exchange/queue/dlx/dlq |
opensearch.hosts | Индекс чанков | URL кластера; index_alias вида rag-kb-<env> |
s3.* | Staging документов | Endpoint, bucket, ключи; для пилота без MinIO — dry_run: true |
ai_gateway.url / api_key | Эмбеддинг и реранк | URL шлюза; ключ только в Secret |
embedding_profiles | Профиль модели и размерность | Закрепляется за коллекцией; смена — через переиндексацию |
spicedb.* | Авторизация коллекций | В проде enabled: true, TLS, не insecure |
auth.* | Проверка JWT | В проде enabled: true и параметры Keycloak |
Переопределение из окружения (примеры):
export RAG_CONFIG=/app/config/config.yaml
export RAG__POSTGRES__DSN='postgresql+asyncpg://user:pass@postgres:5432/platform_rag'
export RAG__RABBITMQ__PASSWORD='<password>'
export RAG__S3__ACCESS_KEY_ID='<key>'
export RAG__S3__SECRET_ACCESS_KEY='<secret>'
export RAG__OPENSEARCH__USERNAME='<user>'
export RAG__OPENSEARCH__PASSWORD='<password>'
export RAG__AI_GATEWAY__API_KEY='<api-key>'
export RAG__SPICEDB__ENABLED=true
export RAG__AUTH__ENABLED=trueПри app.create_schema_on_startup: true API при старте создаёт схему PostgreSQL. Для промышленного контура порядок миграций согласовывается отдельно; откат Helm не откатывает схему БД.
Внимание. Профиль эмбеддинга (model + dimension) закрепляется за коллекцией. Смена профиля без версионируемой переиндексации сделает векторный поиск несовместимым.
2.4. Развёртывание в Docker Compose
Файл docker-compose.yml поднимает прикладные сервисы и зависимости: PostgreSQL, RabbitMQ, OpenSearch, SpiceDB (с отдельной БД и миграцией схемы). MinIO и AI Gateway в Compose не входят: для пилота без объектного хранилища используется s3.dry_run: true; эмбеддинг требует доступного AI Gateway либо ai_gateway.dry_run: true (синтетические векторы, только дымовая проверка).
Публикуемые порты Compose:
| Сервис | Порт на хосте | Назначение |
|---|---|---|
| API | 8095 → 8090 | HTTP API |
| PostgreSQL | 5433 → 5432 | БД сервиса |
| RabbitMQ | 5672, 15672 | AMQP и management UI |
| OpenSearch | 9201 → 9200 | Поиск / индекс |
| SpiceDB | 50051, 8443 | gRPC / HTTP |
Вариант A. Инфраструктура в Docker, приложение на хосте
Этот вариант соответствует config/config.yaml (хосты localhost и порты таблицы выше).
docker compose up -d postgres rabbitmq opensearch spicedb-postgres
docker compose up spicedb-migrate
docker compose up -d spicedb
# из корня репозитория, Python 3.11+
pip install -e .
export RAG_CONFIG=config/config.yaml
platform-rag-api
# во втором терминале:
platform-rag-workerПроверка:
curl -fsS http://127.0.0.1:8090/healthОжидается "status": "ok" по компонентам postgres, rabbitmq, s3, opensearch. При s3.dry_run: true проверка S3 считается успешной без реального бакета.
Вариант B. Полный стек в Compose
Контейнеры api и worker должны ходить к зависимостям по именам сервисов Compose (postgres, rabbitmq, opensearch, spicedb), а не через localhost. Задайте переопределения в environment (или в отдельном .env):
# фрагмент environment для сервисов api и worker
environment:
RAG_CONFIG: /app/config/config.yaml
RAG__POSTGRES__DSN: postgresql+asyncpg://postgres:postgres@postgres:5432/platform_rag
RAG__RABBITMQ__HOST: rabbitmq
RAG__RABBITMQ__PASSWORD: guest
RAG__OPENSEARCH__HOSTS: '["http://opensearch:9200"]'
RAG__SPICEDB__ENABLED: "true"
RAG__SPICEDB__ENDPOINT: spicedb:50051
RAG__S3__DRY_RUN: "true"
RAG__AI_GATEWAY__URL: https://<ai-gateway>/v1
RAG__AI_GATEWAY__API_KEY: <secret>Запуск:
docker compose up -d --build
docker compose ps
docker compose logs -f api worker
curl -fsS http://127.0.0.1:8095/healthИнтерактивная спецификация API доступна по адресу http://127.0.0.1:8095/docs (Swagger UI) и http://127.0.0.1:8095/redoc.
Остановка:
docker compose down
# с удалением томов данных:
docker compose down -v2.5. Развёртывание в Kubernetes (Helm)
Чарт: helm/platform-rag. Канонические команды также приведены в helm/platform-rag/README.md и docs/helm-deployment.md.
Подготовка
- Собрать и опубликовать образ.
- Проверить сетевую доступность PostgreSQL, RabbitMQ, S3, OpenSearch, AI Gateway и SpiceDB из целевого пространства имён.
- Создать Secret с учётными данными (не помещать их в values):
kubectl -n <namespace> create secret generic platform-rag-secrets \
--from-literal=postgres-dsn='postgresql+asyncpg://user:pass@host:5432/platform_rag' \
--from-literal=postgres-dsn-sync='postgresql://user:pass@host:5432/platform_rag' \
--from-literal=rabbitmq-password='<password>' \
--from-literal=s3-access-key-id='<access-key>' \
--from-literal=s3-secret-access-key='<secret-key>' \
--from-literal=opensearch-username='<username>' \
--from-literal=opensearch-password='<password>' \
--from-literal=ai-gateway-api-key='<api-key>'Ключ postgres-dsn-sync (postgresql://…, не +asyncpg) нужен только при включённом KEDA (п. 2.6).
- Подготовить values окружения (образцы:
helm/platform-rag/values.yaml,values-dev.yaml,values-prod.example.yaml).
Сопоставление ключей Secret в values:
secretEnv:
existingSecret: platform-rag-secrets
variables:
RAG__POSTGRES__DSN: postgres-dsn
RAG__RABBITMQ__PASSWORD: rabbitmq-password
RAG__S3__ACCESS_KEY_ID: s3-access-key-id
RAG__S3__SECRET_ACCESS_KEY: s3-secret-access-key
RAG__OPENSEARCH__USERNAME: opensearch-username
RAG__OPENSEARCH__PASSWORD: opensearch-password
RAG__AI_GATEWAY__API_KEY: ai-gateway-api-keySpiceDB и JWT в текущем чарте не вынесены в блок config по умолчанию; для промышленного контура задайте их через extraEnv / extraEnvFrom, например RAG__SPICEDB__ENABLED, RAG__SPICEDB__ENDPOINT, RAG__AUTH__ENABLED, RAG__AUTH__KEYCLOAK_SERVER_URL, RAG__AUTH__KEYCLOAK_REALM.
Установка
helm lint helm/platform-rag
helm template platform-rag helm/platform-rag -n <namespace> -f values-<env>.yaml
helm upgrade --install platform-rag helm/platform-rag \
-n <namespace> --create-namespace \
-f values-<env>.yaml \
--atomic --wait --timeout 10m
kubectl -n <namespace> rollout status deployment/platform-rag-api --timeout=5m
kubectl -n <namespace> rollout status deployment/platform-rag-worker --timeout=5m
kubectl -n <namespace> get pods,svcПроверка с порта кластера:
kubectl -n <namespace> port-forward svc/platform-rag 8090:8090
curl -fsS http://127.0.0.1:8090/healthПри включённом Ingress (ingress.enabled: true) задайте ingress.hosts[].host и при необходимости TLS. Снаружи публикуется только API; воркер наружу не выставляется.
Особенности промышленного контура
- Образы запускаются от UID/GID
10001,runAsNonRoot, без привилегий, read-only root filesystem,/tmp—emptyDir. - Конфиг монтируется ConfigMap по пути
/app/config/config.yaml. - Probes API: startup/liveness/readiness на
GET /health. terminationGracePeriodSeconds: 60— чтобы воркер успел завершить текущее задание.- При полной замене блока
configсохраните секцииapi,embedding_profiles,rerank_profiles,chunking,paths,logиз значений чарта, если не меняете их намеренно.
2.6. Автомасштабирование воркеров (KEDA)
Источник истины backlog — SQL, не глубина очереди RabbitMQ:
SELECT COUNT(*) FROM platform_rag.ingest_job WHERE status = 'PENDING'Включение в values (см. values-prod.example.yaml):
worker:
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 10
targetQueryValue: "4"
authentication:
existingSecret: platform-rag-secrets
connectionKey: postgres-dsn-syncТребования: KEDA в кластере; ключ Secret с libpq-строкой postgresql://… (SQLAlchemy postgresql+asyncpg://… скейлер не принимает). При включённом autoscaling поле replicas у Deployment воркера не задаётся — число реплик принадлежит KEDA.
Проверка:
kubectl -n <namespace> get scaledobject,triggerauthentication
kubectl -n <namespace> describe scaledobject platform-rag-worker3. Подключение моделей
Сервис не содержит собственной языковой модели. Все вызовы эмбеддинга и реранка идут через AI Gateway. Ключи инференса хранятся у шлюза; в конфигурации RAG задаётся только URL шлюза и ключ доступа к нему.
Профили задаются в конфигурации (в перспективе — из Control Plane, OPS-3):
embedding_profiles:
text-default-v1:
profile_id: text-default-v1
model: bge-m3 # идентификатор модели у AI Gateway
dimension: 1024
normalize: true
rerank_profiles:
rerank-default-v1:
profile_id: rerank-default-v1
model: bge-m3
candidates: 20| Назначение | Где задаётся | Примечание |
|---|---|---|
| Эмбеддинг | embedding_profile_id коллекции | Изменение требует новой версии индекса |
| Реранк | retrieval_defaults.rerank_profile_id и флаг rerank в запросе | Только из числа одобренных профилей |
| Размерность вектора | embedding_profiles.*.dimension | Должна совпадать с mapping OpenSearch (knn_vector) |
Для закрытого контура используется шлюз в контуре заказчика. Передача содержимого документов во внешние сетевые сервисы без правовых оснований недопустима.
ai_gateway.dry_run: true возвращает нулевые векторы фиксированной размерности — только для проверки конвейера без модели. Для приёмки качества поиска dry-run не используется.
4. Доступ и авторизация
4.1. Две плоскости идентичности
Каждый вызов несёт тенант и идентичность вызывающего. В режиме разработки (auth.enabled: false) идентичность передаётся заголовками. В промышленном режиме (auth.enabled: true) обязателен Authorization: Bearer <JWT>; user_id / groups / agent_id берутся из проверенного токена Keycloak, заголовки X-User-Id / X-Agent-Id не доверяются.
| Заголовок | Назначение |
|---|---|
X-Tenant-Id | Тенант (обязателен всегда) |
X-User-Id | Пользователь (dev; в проде — из JWT sub / user_id) |
X-Groups | Группы через запятую (dev; в проде — claim groups) |
X-Principals | Дополнительные principals (dev) |
X-Agent-Id | Идентификатор агента (dev; в проде — claim agent_id) |
Authorization | Bearer JWT при auth.enabled: true |
X-Trace-Id | Корреляция (поиск прокидывает в meta.trace_id) |
Фильтр выдачи строится по правам пользователя, а не агента: один и тот же агент для разных пользователей возвращает разный набор чанков (RET-2).
4.2. SpiceDB
Модель прав (файл config/schema.zed):
collection.owner— пользователь-владелец;collection.reader— пользователь, группа или агент (чтение / поиск);collection.writer— интеграционный сервис (запись /submit_documents);document.view— членviewer_groupилиparent->read.
Создание коллекции (POST /v1/collections) требует X-User-Id (или user в JWT): вызывающий регистрируется как owner. Выдача права reader: POST /v1/collections/{id}/readers (только owner) с ровно одним из полей agent_id или user_id.
При недоступности SpiceDB сервис отвечает 503 authz_unavailable (fail-closed), а не «пропускает» запрос.
4.3. ACL документа
В приватной коллекции пустой ACL при upsert недопустим: документ отклоняется (empty_acl_policy: reject) либо индексируется с самой строгой видимостью (strictest). Политика задаётся на коллекции и значением app.empty_acl_policy.
Отзыв прав действует на следующем запросе: ACL перепроверяются в момент retrieval, переиндексация для отзыва не требуется.
5. Безопасность
5.1. Секреты
В Secret / защищённом хранилище: DSN PostgreSQL, пароль RabbitMQ, ключи S3, учётные данные OpenSearch, ключ AI Gateway, preshared key SpiceDB. Файл values и ConfigMap не содержат паролей.
Права на локальный config.yaml с секретами:
chmod 600 config/config.yamlКлючи приложений и интеграций выдаются по принципу наименьших полномочий; при компрометации отзываются и перевыпускаются.
5.2. Сетевые ограничения
Наружу публикуется только API (ClusterIP + Ingress, порт 8090 / TLS). Воркер, PostgreSQL, RabbitMQ, OpenSearch, SpiceDB во внешнюю сеть не публикуются.
| Источник | Порт | Протокол | Назначение |
|---|---|---|---|
| Потребители (SDK, low-code, интеграции) | 8090 / 443 | HTTP/HTTPS | RAG API |
| API / worker | 5432 | PostgreSQL | Реестр и job store |
| API / worker | 5672 | AMQP | Очередь ingest |
| API / worker | 9200 | HTTPS | OpenSearch |
| API / worker | 50051 | gRPC | SpiceDB |
| Worker / API | 443 | HTTPS | AI Gateway, S3 |
| Администраторы | 22 / API Kubernetes | SSH / kubectl | Сопровождение |
5.3. Хранение данных
Документы и чанки хранятся в контуре: PostgreSQL (метаданные, jobs), OpenSearch (текст и векторы), S3 (staging). Учётные данные источников данных в путь запроса и во флоу не попадают — только в Secrets интеграционных сервисов.
Каждая операция ingest и query подлежит аудиту (минимум: trace_id, операция, идентичность обеих плоскостей, тенант, коллекция). Полнота полей аудита зависит от релиза (R1 с MVP).
5.4. Резервное копирование
Объекты копирования: PostgreSQL (реестр и job store), индексы OpenSearch, при необходимости бакет staging, файл/Secret конфигурации. RabbitMQ некритичен как SoT.
Логическая выгрузка БД (Compose):
docker compose exec -T postgres \
pg_dump -U postgres platform_rag > platform_rag_db_$(date +%F).sqlВ Kubernetes:
kubectl -n <namespace> exec -T <под_postgresql> -- \
pg_dump -U <user> platform_rag > platform_rag_db_$(date +%F).sqlСнимки OpenSearch выполняются штатными средствами кластера (snapshot repository). Копии хранятся отдельно от контура приложения; периодически проверяется восстановление.
5.5. Восстановление
- Развернуть ту же версию сервиса (раздел 2) с прежними секретами и профилями эмбеддинга.
- Восстановить PostgreSQL (схема
platform_rag). - Восстановить снимки OpenSearch (алиас
rag-kb-<env>). - Запустить API и worker; проверить
/health, список коллекций и контрольныйquery.
6. Обновление сервиса
Перед обновлением — резервное копирование (п. 5.4) и окно обслуживания.
6.1. Docker Compose
docker compose down
# обновить код / тег образа
docker compose build
docker compose up -d
docker compose logs -f api workerСверить config_template.yaml с рабочим конфигом на предмет новых параметров.
6.2. Kubernetes (Helm)
helm upgrade --install platform-rag helm/platform-rag \
-n <namespace> \
-f values-<env>.yaml \
--atomic --wait --timeout 10m6.3. Схема базы данных
При app.create_schema_on_startup: true API создаёт/догоняет объекты схемы при старте. Эксплуатация начинается после успешного /health и отсутствия ошибок создания схемы в журнале API.
Откат Helm не отменяет изменения схемы и данных внешних хранилищ.
6.4. Откат
helm -n <namespace> history platform-rag
helm -n <namespace> rollback platform-rag <REVISION> --wait --timeout 10m
kubectl -n <namespace> rollout status deployment/platform-rag-api --timeout=5m
kubectl -n <namespace> rollout status deployment/platform-rag-worker --timeout=5mПри несовместимых миграциях откат версии выполняется вместе с восстановлением БД из копии, снятой до обновления.
7. Мониторинг и журналы
7.1. Состояние сервисов
Docker Compose
docker compose ps
docker compose logs -f api
docker compose logs -f worker
curl -fsS http://127.0.0.1:8095/healthKubernetes
kubectl -n <namespace> get pods -l app.kubernetes.io/instance=platform-rag
kubectl -n <namespace> logs deploy/platform-rag-api --tail=200
kubectl -n <namespace> logs deploy/platform-rag-worker --tail=200
kubectl -n <namespace> top podsGET /health возвращает status: ok | degraded и статусы postgres, rabbitmq, s3, opensearch. Probes Kubernetes используют этот путь.
Дополнительно контролируются: число PENDING/FAILED в ingest_job, глубина DLQ rag.ingest.dlq, латентность query, ошибки AI Gateway.
7.2. Журналы
Уровень задаётся log.level (по умолчанию INFO). Воркер пишет о старте listener, захвате job, ошибках эмбеддинга и индексации. Корреляция — trace_id.
7.3. Типовые нарушения
| Признак | Источник | Вероятная причина и действие |
|---|---|---|
/health → degraded | Тело ответа details | Недоступен Postgres, RabbitMQ, S3 или OpenSearch |
401 Missing X-Tenant-Id | Журнал API | Нет заголовка тенанта |
401 Missing bearer token | Журнал API | Включён auth.enabled, нет JWT |
403 на коллекции | SpiceDB | Нет read/write/owner |
503 authz_unavailable | SpiceDB | Шлюз авторизации недоступен — не «обходить» |
Submit 202, job завис в PENDING | Worker, RabbitMQ | Воркер не запущен; нет потребителя; сбой публикации |
Job FAILED | Журнал worker, поле error | Ошибка AI Gateway, OpenSearch, S3; неверный профиль |
| Query пустой при наличии документов | ACL, группы | Вызывающий не в acl.groups; неверный тенант |
| Индексация медленная | KEDA, PENDING count | Нехватка воркеров; лимит AI Gateway |
8. Производительность и масштабирование
8.1. Воркеры ingest
Основной потребитель ресурсов при загрузке знаний. Число реплик — worker.replicaCount или KEDA по PENDING (целевое значение targetQueryValue, по умолчанию 4 задания на реплику). Параллелизм внутри процесса — app.worker_concurrency (по умолчанию 4). Prefetch RabbitMQ — rabbitmq.prefetch (16).
8.2. API
Stateless: увеличивайте api.replicaCount. Пул БД — postgres.pool_size. Эмбеддинг запроса на каждый query идёт в AI Gateway — фактический предел латентности часто определяется шлюзом, а не API.
8.3. OpenSearch
Индексу выделяются CPU/RAM; mapping knn_vector требователен к памяти. Шаблон индекса: resources/opensearch/index-template.json (паттерн rag-kb-*, HNSW cosine, dimension 1024 по умолчанию). Смена размерности эмбеддинга требует нового шаблона и переиндексации.
8.4. Ограничения
| Параметр | Назначение |
|---|---|
empty_acl_policy | Отклонение документов без ACL в private-коллекции |
s3.presigned_expires_in | Срок presigned URL для content_ref |
opensearch.bulk_batch_size | Размер bulk при индексации |
ai_gateway.timeout / batch_size | Таймаут и пачки эмбеддинга |
rabbitmq.delivery_limit | После 5 неуспешных доставок — DLQ |
| Лимиты ресурсов контейнеров | Защита узла |
| Пропускная способность AI Gateway | Фактический предел ingest и query |
9. Аварийные ситуации
9.1. Недоступность сервиса
- Проверить
/healthи Ingress/Service. - По
detailsопределить отказавший компонент (БД, очередь, S3, OpenSearch). - Проверить поды API и worker, события Kubernetes, журналы.
- Перезапуск:
# Compose
docker compose restart api worker
# Kubernetes
kubectl -n <namespace> rollout restart deploy/platform-rag-api
kubectl -n <namespace> rollout restart deploy/platform-rag-worker- При переполнении диска — очистка журналов/образов, расширение PVC OpenSearch/PostgreSQL.
- При повреждении данных — восстановление из копии (п. 5.5).
- Зафиксировать время, признаки,
trace_id, выдержки журналов, выполненные действия.
9.2. Эскалация
| Категория | Признаки | Ответственный |
|---|---|---|
| Функциональное | Некорректная выдача, ACL, качество поиска | Владелец знаний / функциональная команда |
| Инфраструктурное | Поды, сеть, диски, Helm, зависимости | Администратор контура |
| Платформенное | Дефект API/worker, контракт, SpiceDB-схема | Команда Platform RAG |
Перед эскалацией: версия образа/чарта, описание, затронутые коллекции, trace_id, журналы, время, уже выполненные действия. Контакты — приложение В.
10. Примеры запросов для проверки системы
Базовый URL локального Compose: http://127.0.0.1:8095. При port-forward Helm: http://127.0.0.1:8090. Ниже $BASE — выбранный URL.
Примеры даны для dev-режима (auth.enabled: false). В проде замените заголовки пользователя на Authorization: Bearer <jwt> (тенант по-прежнему передаётся).
export BASE=http://127.0.0.1:8095
export TENANT=acme
export USER=admin-check10.1. Живучесть
curl -fsS "$BASE/health"Ожидается HTTP 200 и JSON вида:
{
"status": "ok",
"components": {
"postgres": "ok",
"rabbitmq": "ok",
"s3": "ok",
"opensearch": "ok"
},
"details": {}
}Если status равен degraded, смотрите details.<компонент>.error — сервис запущен, но зависимость недоступна.
OpenAPI: $BASE/docs, $BASE/openapi.json.
10.2. Создание коллекции
curl -sS -X POST "$BASE/v1/collections" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER" \
-d '{
"name": "handbook",
"description": "Проверка стенда",
"embedding_profile_id": "text-default-v1",
"acl_model": "document",
"visibility": "private",
"chunking": { "strategy": "recursive_text", "size": 800, "overlap": 120 },
"retrieval_defaults": { "mode": "hybrid", "top_k": 8 }
}'Ожидается 201 и тело коллекции с collection_id, status: created, active_version: 1. Вызывающий становится owner в SpiceDB.
Сохраните идентификатор:
export COLLECTION_ID='<uuid из ответа>'Без X-User-Id создание возвращает 401.
10.3. Список и чтение коллекции
curl -sS "$BASE/v1/collections" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER"
curl -sS "$BASE/v1/collections/$COLLECTION_ID" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER"Ожидается 200. Список отфильтрован по праву read: чужие коллекции тенанта не возвращаются.
10.4. Выдача права на чтение агенту
curl -sS -o /dev/null -w "%{http_code}\n" \
-X POST "$BASE/v1/collections/$COLLECTION_ID/readers" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER" \
-d '{"agent_id": "assistant-check"}'Ожидается 204. Тело должно содержать ровно одно из agent_id | user_id. Не-owner получает 403.
10.5. Приём документа (ingest)
curl -sS -X POST "$BASE/v1/documents:submit" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER" \
-H "X-Groups: group:hr" \
-d '{
"documents": [
{
"op": "upsert",
"collection": "handbook",
"tenant_id": "acme",
"doc_id": "confluence:HR:12345",
"content": {
"title": "Отпуск",
"sections": [
{ "heading": "Правила", "text": "До 28 календарных дней в год." }
]
},
"metadata": {
"source": "confluence",
"url": "https://wiki.example/HR/12345",
"labels": ["policy"]
},
"acl": {
"groups": ["group:hr"],
"principals": [],
"scope": { "confluence_space": "HR" }
},
"content_hash": "check-hash-001",
"idempotency_key": "confluence:12345:rev-1"
}
]
}'Ожидается 202:
{
"jobs": [
{
"doc_id": "confluence:HR:12345",
"job_id": "<uuid>",
"status": "PENDING"
}
]
}Повтор того же запроса даёт status: DEDUPLICATED (ING-4). Поле collection может быть именем или UUID. tenant_id в теле сверяется с идентичностью, вслепую не доверяется.
Пустой acl в private-коллекции при политике reject даёт 400.
export JOB_ID='<uuid из ответа>'10.6. Статус задания
curl -sS "$BASE/v1/jobs/$JOB_ID" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER"Ожидается 200. Цепочка статусов: PENDING → RUNNING → DONE | FAILED. Дождитесь DONE перед поиском (эмбеддинг зависит от AI Gateway).
# ожидание завершения (пример)
while true; do
st=$(curl -sS "$BASE/v1/jobs/$JOB_ID" \
-H "X-Tenant-Id: $TENANT" -H "X-User-Id: $USER" \
| python -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "status=$st"
case "$st" in DONE|FAILED) break ;; esac
sleep 2
doneПри FAILED смотрите поле error и журнал worker.
10.7. Поиск (query)
Пользователь с правом на документ (group:hr):
curl -sS -X POST "$BASE/v1/query" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER" \
-H "X-Groups: group:hr" \
-H "X-Trace-Id: admin-check-1" \
-d '{
"collection": "handbook",
"text": "сколько дней отпуска",
"mode": "hybrid",
"top_k": 8,
"rerank": false
}'Ожидается 200, в items — цитаты с doc_id = confluence:HR:12345, непустым content и score. В meta — mode, top_k, trace_id.
Пользователь без группы group:hr (тот же агент/сервис, другая идентичность):
curl -sS -X POST "$BASE/v1/query" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: other-user" \
-H "X-Groups: group:sales" \
-d '{
"collection": "handbook",
"text": "сколько дней отпуска",
"mode": "hybrid",
"top_k": 8
}'Ожидается 200 с пустым items (или без закрытого документа) — проверка RET-2. Если коллекция недоступна субъекту — 403. Если SpiceDB недоступна — 503.
Допустимые mode: hybrid, vector, bm25. rerank: true требует настроенного rerank_profile_id у коллекции.
10.8. Удаление документа
curl -sS -X POST "$BASE/v1/documents:submit" \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: $TENANT" \
-H "X-User-Id: $USER" \
-d '{
"documents": [
{
"op": "delete",
"collection": "handbook",
"tenant_id": "acme",
"doc_id": "confluence:HR:12345",
"content_hash": "check-hash-001",
"idempotency_key": "confluence:12345:rev-1-delete"
}
]
}'После DONE повторный query из п. 10.7 не должен возвращать этот doc_id.
10.9. Негативные проверки
| Проверка | Запрос | Ожидание |
|---|---|---|
| Нет тенанта | любой путь без X-Tenant-Id | 401 |
Нет JWT при auth.enabled | без Authorization | 401 |
| Чужая коллекция | GET /v1/collections/{id} без права | 403 |
| Несуществующая коллекция (при наличии права на id) | GET /v1/collections/{id} | 404 |
| Невалидный upsert | нет content и content_ref | 400 |
| AuthZ down | SpiceDB выключен при spicedb.enabled: true | 503 authz_unavailable |
10.10. Что не вызывать на текущем релизе
Контракт описывает POST /v1/retrieve_and_pack и POST /v1/collections/{id}:reindex. В текущей версии API эти пути не реализованы. Упаковка контекста выполняется на стороне потребителя по результатам /v1/query; переиндексация — отдельным релизом (COL-3).
Проверка через SDK (имена методов сверять с пакетом platform-rag):
from platform_rag import RagClient
rag = RagClient(base_url="http://127.0.0.1:8095", env="dev")
hits = rag.query(collection="handbook", text="сколько дней отпуска", mode="hybrid", top_k=8)
job = rag.ingest_status(job_id)Приложение А. Ключевые параметры конфигурации
Перечень типовой; имена сверяются с config/config_template.yaml и helm/platform-rag/values.yaml. Переопределение — RAG__<СЕКЦИЯ>__<ПОЛЕ>.
| Параметр | Назначение |
|---|---|
RAG_CONFIG | Путь к YAML (в контейнере /app/config/config.yaml) |
api.host / api.port | Слушатель API (0.0.0.0:8090) |
app.environment | Метка среды (dev / prod) |
app.worker_concurrency | Параллелизм обработки jobs в процессе worker |
app.create_schema_on_startup | Создание схемы PostgreSQL при старте API |
app.auto_create_collection | Автосоздание коллекции при submit (осторожно в проде) |
app.empty_acl_policy | reject или strictest |
postgres.dsn | postgresql+asyncpg://… |
postgres.schema_name | По умолчанию platform_rag |
postgres.pool_size | Пул соединений |
rabbitmq.host / port / username / password | Брокер |
rabbitmq.exchange / queue / dlx / dlq | Топология ingest (rag.ingest / rag.ingest.jobs / rag.ingest.dlx / rag.ingest.dlq) |
rabbitmq.prefetch / delivery_limit | 16 / 5 |
s3.endpoint_url / bucket_name / prefix | Staging |
s3.dry_run | Пилот без MinIO |
opensearch.hosts / index_alias | Кластер и алиас (rag-kb-dev, rag-kb-prod) |
opensearch.index_template_path | /app/resources/opensearch/index-template.json |
ai_gateway.url / timeout / batch_size / dry_run | Шлюз моделей |
embedding_profiles / rerank_profiles | Одобренные модели |
chunking.size / overlap | 800 / 120 |
spicedb.enabled / endpoint / insecure / manage_schema | AuthZ |
auth.enabled / keycloak_server_url / keycloak_realm | JWT |
log.level | Подробность журналов |
ingress.host | Внешний DNS (Helm) |
worker.autoscaling.* | KEDA по PENDING |
Приложение Б. Контрольный перечень развёртывания
- Подготовлены ресурсы и доступ к реестру образов.
- Собраны и опубликованы образы API/worker одной версии.
- Заданы (или подняты) PostgreSQL, RabbitMQ, OpenSearch, S3, AI Gateway, SpiceDB.
- Создан Secret; в values нет паролей.
- Профили эмбеддинга и размерность согласованы с шаблоном OpenSearch.
- Для Compose: проверен
docker compose psи/health. - Для Kubernetes:
helm lint/helm template; релиз установлен с--atomic --wait. - Поды
platform-rag-apiиplatform-rag-workerв Ready; probes зелёные. - В проде включены
auth.enabledиspicedb.enabled(не insecure без TLS). - Выполнены запросы раздела 10: health, коллекция, submit, job
DONE, query с ACL и без. - Настроены снимки PostgreSQL и OpenSearch; проверена процедура восстановления.
- Закрыты лишние порты; наружу только API.
- Зафиксированы версия образа, версия чарта, контакты эскалации.
- При KEDA: libpq DSN, ScaledObject в Ready.