AI Platform Docs
Администрирование

Руководство администратора 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 APIStateless 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)
RRFReciprocal Rank Fusion — слияние ранжированных списков vector/BM25
Helm / чарт / релизПакетный менеджер Kubernetes; пакет приложения; установленный экземпляр
KEDAАвтомасштабирование воркеров по числу заданий PENDING в PostgreSQL

1. Архитектура сервиса

1.1. Общая схема

Сервис состоит из двух прикладных нагрузок без локального состояния: API и воркер ingest. Всё состояние хранится во внешних сервисах данных. Модели вызываются только через AI Gateway. Авторизация коллекций — SpiceDB; видимость чанков — ACL документа, выведенный из act-цепочки вызывающего.

rag-architecture.drawio.png

Разделение ответственности: интеграционные сервисы извлекают данные из источников (Confluence, S3, БД и т.д.) и приводят их к каноническому виду; Platform RAG выполняет чанкинг, эмбеддинг, индексацию, авторизацию и поиск. Прямая запись интеграций в OpenSearch, S3 staging или RabbitMQ не допускается.

1.2. Состав компонентов

КомпонентНазначениеПримечание
platform-rag-apiHTTP API: коллекции, приём документов, статус заданий, поиск, /healthStateless, горизонтально масштабируется. Порт 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 / MinIOStaging канонического JSON на время задания (payload_uri)В локальном Compose допускается s3.dry_run: true
AI GatewayЭмбеддинг и реранкИдентификаторы моделей задаются профилями, не хардкодом в коде
SpiceDBПрава на коллекцию: owner / reader / writerFail-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 ГБ
OpenSearch2 ГБ heapотдельный кластер, реплики индекса
PostgreSQL1 ГБвнешний отказоустойчивый кластер
Диск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 version

2.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:

СервисПорт на хостеНазначение
API8095 → 8090HTTP API
PostgreSQL5433 → 5432БД сервиса
RabbitMQ5672, 15672AMQP и management UI
OpenSearch9201 → 9200Поиск / индекс
SpiceDB50051, 8443gRPC / 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 -v

2.5. Развёртывание в Kubernetes (Helm)

Чарт: helm/platform-rag. Канонические команды также приведены в helm/platform-rag/README.md и docs/helm-deployment.md.

Подготовка

  1. Собрать и опубликовать образ.
  2. Проверить сетевую доступность PostgreSQL, RabbitMQ, S3, OpenSearch, AI Gateway и SpiceDB из целевого пространства имён.
  3. Создать 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).

  1. Подготовить 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-key

SpiceDB и 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-worker

3. Подключение моделей

Сервис не содержит собственной языковой модели. Все вызовы эмбеддинга и реранка идут через 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)
AuthorizationBearer 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 / 443HTTP/HTTPSRAG API
API / worker5432PostgreSQLРеестр и job store
API / worker5672AMQPОчередь ingest
API / worker9200HTTPSOpenSearch
API / worker50051gRPCSpiceDB
Worker / API443HTTPSAI Gateway, S3
Администраторы22 / API KubernetesSSH / 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. Восстановление

  1. Развернуть ту же версию сервиса (раздел 2) с прежними секретами и профилями эмбеддинга.
  2. Восстановить PostgreSQL (схема platform_rag).
  3. Восстановить снимки OpenSearch (алиас rag-kb-<env>).
  4. Запустить 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 10m

6.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/health

Kubernetes

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 pods

GET /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_unavailableSpiceDBШлюз авторизации недоступен — не «обходить»
Submit 202, job завис в PENDINGWorker, 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. Недоступность сервиса

  1. Проверить /health и Ingress/Service.
  2. По details определить отказавший компонент (БД, очередь, S3, OpenSearch).
  3. Проверить поды API и worker, события Kubernetes, журналы.
  4. Перезапуск:
# 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
  1. При переполнении диска — очистка журналов/образов, расширение PVC OpenSearch/PostgreSQL.
  2. При повреждении данных — восстановление из копии (п. 5.5).
  3. Зафиксировать время, признаки, 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-check

10.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-Id401
Нет JWT при auth.enabledбез Authorization401
Чужая коллекцияGET /v1/collections/{id} без права403
Несуществующая коллекция (при наличии права на id)GET /v1/collections/{id}404
Невалидный upsertнет content и content_ref400
AuthZ downSpiceDB выключен при spicedb.enabled: true503 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_policyreject или strictest
postgres.dsnpostgresql+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_limit16 / 5
s3.endpoint_url / bucket_name / prefixStaging
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 / overlap800 / 120
spicedb.enabled / endpoint / insecure / manage_schemaAuthZ
auth.enabled / keycloak_server_url / keycloak_realmJWT
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.

На этой странице

Назначение и область примененияУсловные обозначенияТермины и сокращения1. Архитектура сервиса1.1. Общая схема1.2. Состав компонентов1.3. Потоки данных1.4. Минимальные ресурсы2. Установка и развёртывание2.1. Предварительные требования2.2. Получение дистрибутива2.3. Настройка конфигурации2.4. Развёртывание в Docker ComposeВариант A. Инфраструктура в Docker, приложение на хостеВариант B. Полный стек в Compose2.5. Развёртывание в Kubernetes (Helm)ПодготовкаУстановкаОсобенности промышленного контура2.6. Автомасштабирование воркеров (KEDA)3. Подключение моделей4. Доступ и авторизация4.1. Две плоскости идентичности4.2. SpiceDB4.3. ACL документа5. Безопасность5.1. Секреты5.2. Сетевые ограничения5.3. Хранение данных5.4. Резервное копирование5.5. Восстановление6. Обновление сервиса6.1. Docker Compose6.2. Kubernetes (Helm)6.3. Схема базы данных6.4. Откат7. Мониторинг и журналы7.1. Состояние сервисов7.2. Журналы7.3. Типовые нарушения8. Производительность и масштабирование8.1. Воркеры ingest8.2. API8.3. OpenSearch8.4. Ограничения9. Аварийные ситуации9.1. Недоступность сервиса9.2. Эскалация10. Примеры запросов для проверки системы10.1. Живучесть10.2. Создание коллекции10.3. Список и чтение коллекции10.4. Выдача права на чтение агенту10.5. Приём документа (ingest)10.6. Статус задания10.7. Поиск (query)10.8. Удаление документа10.9. Негативные проверки10.10. Что не вызывать на текущем релизеПриложение А. Ключевые параметры конфигурацииПриложение Б. Контрольный перечень развёртывания