Пользовательские детекторы¶
Аудитория: SRE / AppSec-инженер OnPremise-клиента, который подключает к AppSec.AIGate собственный ML-детектор — отраслевой classifier, корпоративный DLP, кастомный jailbreak-фильтр, и т.п. Время на onboarding: 10–15 минут от написания детектора до первого блокирующего запроса. Что нужно уметь: разворачивать HTTP-сервис в той же сети что и AppSec.AIGate, читать-писать JSON, иметь админский доступ к Admin UI.
Что это и для чего¶
AppSec.AIGate из коробки идёт с двумя встроенными ML-детекторами — атак на промпт и категоризации контента. Они закрывают типовые угрозы, но не покрывают специфичные для клиента требования:
| Индустрия / Use case | Что хочется детектировать |
|---|---|
| Финтех / Банк | Номера договоров, ИНН контрагентов, внутренние коды продуктов, упоминания клиентских счетов |
| Энергетика / ТЭК | Названия объектов инфраструктуры, технологические параметры, корпоративные кодовые имена проектов |
| Здравоохранение | Диагнозы по МКБ-10, лекарства из конкретных списков, имена пациентов в формате «Фамилия И.О.» |
| Юридический департамент | Номера дел, имена адвокатов, упоминания контрагентов под NDA |
| Госсектор / Оборонка | Номера форм отчётности, гриф «ДСП», коды объектов, перечни критических субъектов |
| Корпоративный IT | Имена внутренних сервисов в облаке, IP-диапазоны staging, креды от внутренних API |
Раньше для каждого такого кейса требовался релиз вендора. Custom Detector Plug-in Framework позволяет вам:
- Написать свой HTTP-сервис, реализующий минимальный контракт ответа.
- Зарегистрировать его в Admin UI без правок кода вендора.
- Привязать к одному или нескольким профилям с per-profile настройками.
- Платформа автоматически вызывает ваш детектор на каждом запросе и/или ответе LLM, применяет правила блокировки / монитора / разрешения и записывает события для аудита.
Высокоуровневая архитектура¶

Шаги пайплайна:
- SRE регистрирует детектор через Admin UI или REST API.
- Profiles Registry перепаковывает OPA bundle и заливает в MinIO. OPA подтягивает за 2–5 секунд.
- LLM-клиент отправляет обычный chat-completion-запрос в API Gateway.
- Gateway параллельно вызывает встроенные детекторы + ваш через HTTP.
- Все вердикты передаются в PDP, generic правило применяет ваш
policy_config_schema. - По решению BLOCK / MONITOR / ALLOW gateway возвращает 403/451 (с описанием) или прокидывает запрос в реальный LLM.
Стадии конвейера. Детектор может работать на request-side (на входящий промпт до отправки в LLM), на response-side (на ответ от LLM до выдачи клиенту), или на обеих стадиях одновременно. Каждая стадия настраивается независимо в профиле.
Контракт детектора¶
Что приходит на вход¶
Gateway отправляет на POST <ваш-endpoint>:
{
"prompt": "<нормализованный текст пользовательского промпта>",
"context": {
"tenant_id": "default",
"profile_id": "98e4be8c-378a-41e6-aa7d-897250c147ae",
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"detector_config": {
"enabled": true,
"threshold": 0.85,
"...": "ваши per-profile параметры из configuration_schema"
}
}
}
| Поле | Что в нём |
|---|---|
prompt |
Уже нормализованный текст. PII замаскирован (если профиль включает PII detection), homoglyph-нормализация применена. На response-side здесь приходит assistant-текст из ответа LLM. |
context.tenant_id |
Устаревшее поле. В текущей версии (single-tenant) содержит константу (default); оставлено для обратной совместимости. |
context.profile_id |
UUID активного профиля. Полезно для аналитики и rate-limiting на вашей стороне. |
context.trace_id |
Сквозной OpenTelemetry trace ID. Используйте для логирования внутри вашего детектора — это позволит cross-сервисную трассировку. |
context.detector_config |
Per-profile конфигурация, которую SRE выставил в Admin UI или через API. Структура определяется вашей configuration_schema при регистрации. |
Независимость от провайдера. Детектор НЕ видит провайдер-специфичные обёртки (
messages[],result.alternatives[], etc.). Платформа извлекает текст и подаёт plain string. Не пишите код вродеif "messages" in body— это не сработает.
Что вы должны вернуть¶
Каноническая форма ответа (минимум):
Расширенная форма (всё, что поддерживается):
ℹ️ В примере здесь и далее используется вымышленное кодовое имя
Полярнаяи idinternal-codename-leak— пример; замените на свои значения.
{
"verdict": "unsafe",
"score": 0.93,
"reason": "Обнаружена утечка кодового имени проекта 'Полярная'",
"categories": ["internal_codenames"],
"details": {
"matched_terms": ["Полярная"],
"match_confidence": 0.93,
"model_version": "v2.3.1",
"rule_id": "codenames-rev-117"
}
}
| Поле | Тип | Обязательное | Что в нём |
|---|---|---|---|
verdict |
string | да | safe / unsafe / unknown |
score |
number | да | от 0.0 (safe) до 1.0 (unsafe). Числовая мера уверенности. |
reason |
string | нет | Человеко-читаемое объяснение. Попадает в security_events.reason для аудита. |
categories |
array<string> | нет | Список таксономий, которые сработали. Для content-safety-подобных детекторов. |
details |
object | нет | Произвольный JSON. Не интерпретируется автоматически, доступен через mini-DSL по dot-path (details.match_confidence и т.п.) для тонкой настройки правил. |
Что должен делать ваш сервис ещё¶
Помимо основного POST <path>, мы рекомендуем экспонировать health-эндпоинты (проверки состояния) для наблюдаемости:
| Endpoint | Назначение |
|---|---|
GET /healthz |
Liveness probe — всегда 200 если процесс живой. Никаких проверок зависимостей. |
GET /readyz |
Readiness probe — 503 если ML-модель не загружена, БД недоступна и т.п. Полезно для Kubernetes deployment'ов. |
Эти эндпоинты НЕ обязательны — gateway не пингует их. Но клиент будет переживать сам себя гораздо легче, если они есть.
Полный пример детектора на Python¶
Минимальный рабочий код для детектора утечек внутренних кодовых имён. Скопируйте, замените INTERNAL_CODENAMES на ваш список, и сервис готов к запуску.
detector_app.py¶
"""
Internal Codename Leak Detector — пример custom-детектора для AppSec.AIGate.
Проверяет промпт на упоминание корпоративных кодовых имён.
В реальной интеграции вместо substring-match используйте TF-IDF,
fuzzy matching, или transformer-классификатор.
"""
from __future__ import annotations
import os
from typing import Any
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
app = FastAPI(title="internal-codename-detector", version="1.0.0")
# Конфиденциальный список кодовых имён.
# В проде загружайте из Kubernetes Secret или Vault, не из исходника.
CODENAMES = {
name.strip().lower()
for name in os.environ.get(
"INTERNAL_CODENAMES",
"Полярная,Каскад,Гелиос,Аврора"
).split(",")
if name.strip()
}
# Опциональная защита: если задан AUTH_TOKEN, требуем заголовок
# Authorization: Bearer <token>. Этот же токен вы укажете при
# регистрации детектора — gateway шифрует его Fernet'ом и присылает
# обратно в каждом вызове.
EXPECTED_TOKEN = os.environ.get("DETECTOR_AUTH_TOKEN", "")
class DetectContext(BaseModel):
tenant_id: str | None = None
profile_id: str | None = None
trace_id: str | None = None
detector_config: dict[str, Any] = {}
class DetectRequest(BaseModel):
prompt: str
context: DetectContext = DetectContext()
class DetectResponse(BaseModel):
verdict: str # "safe" | "unsafe" | "unknown"
score: float # 0.0 .. 1.0
reason: str | None = None
categories: list[str] | None = None
details: dict[str, Any] | None = None
@app.get("/healthz")
async def healthz() -> dict[str, str]:
return {"status": "ok"}
@app.get("/readyz")
async def readyz() -> dict[str, str]:
# Если у вас есть ML-модель, проверьте что она загружена.
return {"status": "ok"}
@app.post("/api/v1/detect", response_model=DetectResponse)
async def detect(
req: DetectRequest,
authorization: str | None = Header(default=None),
) -> DetectResponse:
# Опциональная проверка Bearer-токена.
if EXPECTED_TOKEN:
expected = f"Bearer {EXPECTED_TOKEN}"
if authorization != expected:
raise HTTPException(status_code=401, detail="invalid auth_token")
# Per-profile конфиг.
cfg = req.context.detector_config or {}
threshold = float(cfg.get("threshold", 0.7))
# Простой substring-match. В реальной модели здесь TF-IDF или transformer.
lowered = req.prompt.lower()
hits: list[str] = sorted(name for name in CODENAMES if name in lowered)
if not hits:
return DetectResponse(verdict="safe", score=0.05)
# Уверенность растёт с количеством совпадений.
confidence = min(1.0, 0.6 + 0.1 * len(hits))
if confidence < threshold:
return DetectResponse(
verdict="unknown",
score=confidence,
reason=f"Найдено {len(hits)} совпадение(ий), но ниже порога {threshold}",
categories=["internal_codenames"],
details={"matched_terms": hits, "threshold_applied": threshold},
)
return DetectResponse(
verdict="unsafe",
score=confidence,
reason=f"Обнаружена утечка внутреннего кодового имени: {', '.join(hits)}",
categories=["internal_codenames"],
details={"matched_terms": hits, "match_count": len(hits)},
)
Dockerfile¶
FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir fastapi uvicorn[standard]
COPY detector_app.py .
EXPOSE 9000
CMD ["uvicorn", "detector_app:app", "--host", "0.0.0.0", "--port", "9000"]
Развёртывание¶
Подключите контейнер к той же сети, что и AppSec.AIGate (llm-firewall-network для Docker, нужный namespace для Kubernetes):
Docker Compose фрагмент:
services:
internal-codename-detector:
build: ./detector
container_name: internal-codename-detector
networks:
- llm-firewall-network
environment:
INTERNAL_CODENAMES: "Полярная,Каскад,Гелиос,Аврора"
DETECTOR_AUTH_TOKEN: "secret-from-vault-or-k8s-secret"
restart: unless-stopped
networks:
llm-firewall-network:
external: true
Kubernetes — обычный Deployment + Service. Имя сервиса = host, который вы укажете в base_url при регистрации (например, http://internal-codename-detector для service-name в том же namespace).
Правила разработки детекторов (обязательные)¶
Производительность¶
| Параметр | Рекомендация |
|---|---|
| Время ответа (P95) | ≤ 200 мс. Gateway вызывает детектор синхронно — каждый запрос пользователя ждёт ответа. |
| Жёсткий таймаут | По умолчанию 5 секунд. Можно поднять до 30 секунд при регистрации, но НЕ рекомендуем — пользователи воспринимают LLM-запросы дольше 3 секунд как «не работает». |
| Холодный старт | Предзагружайте модель в startup-хуке (@app.on_event("startup") для FastAPI). Первый запрос НЕ должен прогревать модель. |
| Память (RAM/VRAM) | Если вы хостите модель сами — следите за RAM/VRAM. Платформа не задаёт лимитов, но при OOM-killer'ах вы будете флапать в healthcheck'е. |
| Параллельность | Сервис должен выдерживать ≥ N RPS где N = ваш пиковый LLM-трафик. Все детекторы вызываются в параллель на каждый запрос. |
Идемпотентность и отсутствие состояния¶
- Детектор не должен менять состояние между запросами. Один и тот же
promptвсегда должен давать тот жеverdict(или хотя бы тот же набор категорий). - Не пишите в БД, не отправляйте email, не вызывайте внешние API «по дороге». Это побочные эффекты — gateway повторит запрос если что-то сломается, и вы получите дубликаты.
- Логирование локально — нормально (для observability). Но НЕ сохраняйте plain-text промпты в свою БД — это нарушит принцип минимизации обработки ПДн (см. раздел «Непрерывная регистрация событий (audit-trail) и 152-ФЗ»).
Безопасность¶
auth_tokenобязателен в проде. Без него любой, кто имеет сетевой доступ к вашему детектору, может его вызвать.- TLS — если вы выставляете детектор за пределы локальной сети, оборачивайте в TLS (изменяйте
protocol: "https"при регистрации). - Rate limiting на вашей стороне — gateway не ограничивает обращения к детектору. Если кто-то стрельнёт по gateway 10k RPS, ваш детектор получит 10k RPS. Защищайтесь, например, через nginx limit_req или Envoy.
- Никогда не возвращайте plain PII в
reason/details. SecurityEvent сохраняет эти поля вsecurity_events, и они видны операторам через UI. Если детектор находит email, вернитеcategories: ["email"], а неreason: "Found user@example.com".
Корректность ответа¶
verdictстрого один изsafe/unsafe/unknown. Любое другое значение — gateway отбросит ответ как malformed.score ∈ [0, 1]. Значение вне диапазона = malformed.- Если ваш детектор не уверен — возвращайте
unknownс конкретнымscore. Не возвращайтеsafeсо score 0.5 — это семантически противоречиво. - При внутренней ошибке возвращайте HTTP 500 с пустым телом или
{detail: "..."}. НЕ возвращайтеverdict: "safe"чтобы притвориться что всё хорошо — это сломает fail-safe пользователя.
Независимость от провайдера¶
Платформа поддерживает любые LLM-провайдеры — OpenAI, Anthropic, GigaChat, YandexGPT, Mistral, локальные модели через OpenAI-совместимый API. Ваш детектор не должен знать, какой провайдер. Платформа извлекает текст из любого формата и подаёт plain string в prompt.
Если вашему детектору нужны провайдер-специфичные сигналы (которых нет в plain prompt) — это, скорее всего, признак того, что вы пытаетесь сделать что-то не так. Свяжитесь с командой AppSec.AIGate для обсуждения.
Регистрация детектора¶
Два пути: через Admin UI (быстрее на первый раз) или через REST API (лучше для IaC / Terraform / Ansible).
Способ A — через Admin UI¶
- Откройте Admin UI → Система → Реестр детекторов.
- Нажмите «Зарегистрировать детектор» в правом верхнем углу.
- В модалке заполните 5 секций. Параметры — в следующих разделах.
- Нажмите «Зарегистрировать».
После сохранения детектор появится в таблице. Дальше его нужно привязать к профилю (см. раздел «Привязка детектора к профилю»).
Превью формы. В секции «Configuration Schema» модалки есть раскрывающийся блок «Превью формы». Он показывает, как ваша JSON Schema будет выглядеть в редакторе профиля для оператора. Это позволяет проверить читабельность настроек ДО регистрации.
Способ B — через REST API¶
Получите OAuth2-токен с ролью admin или operator:
TOKEN=$(curl -s -X POST \
https://<your-stand>/auth/realms/llm-firewall/protocol/openid-connect/token \
-d 'client_id=admin-api' \
-d 'grant_type=password' \
-d "username=<your-keycloak-user>" \
-d "password=<your-password>" | jq -r .access_token)
Зарегистрируйте детектор:
curl -X POST https://<your-stand>/admin/api/v1/detector-registry \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d @- <<'JSON'
{
"id": "internal-codename-leak",
"name": "Internal Codename Leak Detector",
"description": "Detects mentions of confidential project codenames.",
"role": "custom",
"applies_to": ["request"],
"endpoint_metadata": {
"base_url": "http://internal-codename-detector",
"path": "/api/v1/detect",
"port": 9000,
"protocol": "http",
"request_timeout_ms": 3000,
"auth_token": "secret-from-vault-or-k8s-secret"
},
"configuration_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"title": "Включить детектор"
},
"threshold": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.7,
"title": "Порог уверенности"
}
}
},
"policy_config_schema": {
"block_when": [
{ "field": "verdict", "op": "eq", "value": "unsafe" }
],
"decision": "BLOCK",
"reason": "Internal codename leak detected"
},
"supports_audit_only": true
}
JSON
Ожидаемый ответ: HTTP 201 с полным телом записи. Поле endpoint_metadata.auth_token в ответе отсутствует — оно write-only.
Параметры регистрации — детально¶
Базовые поля¶
| Поле | Обязательное | Описание |
|---|---|---|
id |
да | Уникальный идентификатор. Regex: ^[a-z][a-z0-9-]{0,63}$. Lowercase ASCII, цифры, дефисы. Иммутабельный после создания. Зарезервированные id: threat-detector, content-safety (встроенные). |
name |
да | Человекочитаемое имя для UI. До 255 символов. |
description |
нет | Описание для UI. |
role |
нет | Всегда custom для пользовательских детекторов. Зарезервировано. |
applies_to |
да | Массив стадий. Допустимые значения: ["request"], ["response"], ["request", "response"]. Регистрация без стадий невозможна — детектор должен где-то работать. |
endpoint_metadata¶
| Поле | Обязательное | Описание |
|---|---|---|
base_url |
да | Только host, БЕЗ path/query/fragment. Например: http://internal-detector или https://detector.internal.acme.local. Если указать path тут — регистрация отклоняется с 422. |
path |
да | Путь, который gateway будет POST'ить. Например /api/v1/detect. |
port |
да | TCP port. 1–65535. |
protocol |
нет | http (по умолчанию) или https. |
request_timeout_ms |
нет | Жёсткий таймаут на вызов детектора. По умолчанию 5000. Диапазон 100–30000. После таймаута срабатывает fail-safe режим профиля. |
auth_token |
нет | Plaintext bearer-токен. Платформа шифрует его Fernet'ом перед сохранением в БД. В GET-ответах НЕ возвращается (write-only). Передаётся вашему детектору в заголовке Authorization: Bearer <token>. |
response_field_mapping |
нет | Если ваш детектор отвечает не в канонической форме, а вкладывает поля внутрь (например {result: {payload: {verdict: ...}}}), укажите dot-path для каждого поля. См. раздел «Неканоническая форма ответа». |
configuration_schema¶
JSON Schema (Draft 2020-12), описывающая per-profile настройки. Admin UI автоматически рендерит форму из этой схемы в редакторе профиля — оператор настраивает там значения, которые потом попадают в ваш детектор в context.detector_config.
См. раздел Схема конфигурации: поддержка в UI.
policy_config_schema¶
Mini-DSL правила, как платформа интерпретирует ответ вашего детектора. См. раздел «Правила блокировки (mini-DSL)».
Метаданные (опциональные)¶
| Поле | Описание |
|---|---|
model_name |
Название ML-модели. Появится в реестре, но не в публичных событиях. |
model_version |
Версия модели. Полезно при A/B-тестах. |
license |
Лицензия модели (Apache-2.0, MIT, и т.п.). Для compliance-аудита. |
hardware_requirements.requires_gpu |
Boolean. Информационно. |
hardware_requirements.min_gpu_memory_gb |
Минимум VRAM. Информационно. |
supports_audit_only |
Если true, оператор может включить детектор только для аудита (через profile.monitor_mode). Рекомендуем оставлять true всегда. |
Неканоническая форма ответа¶
Если ваш детектор возвращает не каноническую {verdict, score} структуру, а свою — например, обёрнутую в envelope:
{
"status": "ok",
"result": {
"classification": {
"label": "unsafe",
"confidence": 0.93
},
"tags": ["internal_codenames"]
}
}
— укажите response_field_mapping, и платформа извлечёт нужные поля по dot-path:
{
"endpoint_metadata": {
"base_url": "http://your-detector",
"path": "/api/v1/detect",
"port": 9000,
"response_field_mapping": {
"verdict_path": "result.classification.label",
"score_path": "result.classification.confidence",
"categories_path": "result.tags"
}
}
}
Платформа возьмёт значения по этим путям и проверит, что они соответствуют контракту:
- verdict — строка из {safe, unsafe, unknown} (после lowercase);
- score — число в [0, 1].
Если по пути не найдено значение или оно не соответствует контракту — детектор считается ответившим malformed (см. раздел «Fail-safe режимы»).
Схема конфигурации: поддержка в UI¶
Admin UI рендерит per-profile форму из вашей configuration_schema через библиотеку @rjsf/core с валидатором AJV (Draft 2020-12).
Поддерживается напрямую¶
| Конструкция | UI-виджет |
|---|---|
type: "string" |
text input |
type: "string" + enum: [...] |
dropdown select |
type: "string" + format: "textarea" |
многострочный textarea |
type: "string" + format: "date" / "date-time" / "email" / "uri" |
специализированные виджеты |
type: "number" / "integer" |
numeric input |
type: "number" + minimum + maximum |
slider + numeric input |
type: "boolean" |
toggle / checkbox |
type: "array" + items.enum |
multi-select (чекбоксы) |
type: "array" + items.type: "string" |
tag-input |
default |
пресет при первом открытии |
title |
label рядом с полем |
description |
tooltip / подсказка под полем |
pattern |
regex-валидация на сохранении |
minLength / maxLength |
валидация длины строки |
required |
звёздочка, валидация на сохранении |
Откат на ручной JSON¶
Если в схеме встречаются:
- oneOf / allOf / anyOf с нетривиальными ветвлениями
- $ref на внешние схемы
- dependentRequired / if-then-else
UI не падает, а показывает раскрытый textarea для редактирования JSON-конфига вручную с подсветкой ошибок валидации. Это покрывает edge case и сохраняет полную выразительность Draft 2020-12, но оператор теряет красивую форму.
Рекомендация: держите configuration_schema плоской и без conditional-конструкций. Если нужна разветвлённая логика — лучше зарегистрируйте два детектора с разными id.
Пример хорошо оформленной схемы¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"title": "Codename Leak Detector Settings",
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"title": "Включить детектор"
},
"threshold": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.7,
"title": "Порог уверенности",
"description": "Confidence ниже порога → verdict=unknown вместо unsafe"
},
"monitored_departments": {
"type": "array",
"items": {
"type": "string",
"enum": ["finance", "legal", "rnd", "marketing", "ops"]
},
"uniqueItems": true,
"title": "Контролируемые отделы",
"description": "Чекать промпты только от перечисленных команд"
},
"language": {
"type": "string",
"enum": ["ru", "en", "auto"],
"default": "auto",
"title": "Язык анализа"
}
},
"required": ["enabled", "threshold"]
}
Оператор в UI увидит: toggle, slider 0–1, multi-select с 5 опциями, dropdown с 3 значениями.
Что попадает в детектор¶
Значения, выбранные оператором, передаются в каждом вызове в context.detector_config. Например, если SRE настроил threshold: 0.85 и language: "ru", ваш детектор получит:
{
"prompt": "...",
"context": {
"...": "...",
"detector_config": {
"enabled": true,
"threshold": 0.85,
"monitored_departments": ["finance", "legal"],
"language": "ru"
}
}
}
Привязка детектора к профилю¶
После регистрации детектор по умолчанию не активен ни в одном профиле.
Через Admin UI¶
- Профили → Создать профиль или открыть существующий и нажать «Редактировать».
- Заполнить базовые поля профиля.
- Прокрутить до секции «📥 Анализ запросов» (слева) или «📤 Анализ ответов» (справа) — в зависимости от стадии, где работает детектор.
- В подсекции «Пользовательские детекторы» найти карточку нужного детектора.
- Включить toggle. Раскроется форма с per-profile настройками — заполнить значения.
- Сохранить профиль и активировать (нажать «Активировать» в диалоге сохранения).
Per-stage независимость. Если детектор зарегистрирован с
applies_to: ["request", "response"], его карточка появится в обеих секциях профиля. Toggle и настройки в каждой стадии независимые — можно включить детектор только на запросах, или только на ответах, или на обеих сторонах с разными порогами.
Через REST API¶
curl -X POST https://<your-stand>/admin/api/v1/profiles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Profile with Codename Leak Detector",
"provider_id": "<existing-provider-id>",
"detectors": {
"threat_detector_enabled": true,
"pii_enabled": true,
"pii_mask_mode": "mask",
"pii_types": ["email", "phone"]
},
"fail_safe": { "mode": "fail-open" },
"enabled_detectors": {
"internal-codename-leak": {
"request": {
"enabled": true,
"policy_config": { "threshold": 0.75 }
}
}
},
"created_by": "ops@example.com"
}'
Активация:
curl -X POST "https://<your-stand>/admin/api/v1/profiles/<profile-id>/activate?performed_by=ops@example.com" \
-H "Authorization: Bearer $TOKEN"
Формат enabled_detectors¶
"enabled_detectors": {
"<detector-id>": {
"request": {
"enabled": true,
"policy_config": { ...ваши настройки... }
},
"response": {
"enabled": false,
"policy_config": { ... }
}
}
}
Каждая стадия — независимая. Если детектор работает только на одной стадии, можно опустить вторую секцию.
Профиль monitor_mode (важно)¶
На уровне профиля есть глобальный флаг monitor_mode: true/false:
- false (по умолчанию) — обычная работа: BLOCK блокирует запрос с HTTP 403.
- true — все BLOCK-решения понижаются до MONITOR_ONLY: запрос проходит, в audit-log пишется событие.
Используйте monitor_mode: true для теневого подключения (shadow-deploy) нового детектора: смотрите 1-2 недели, насколько часто срабатывает, сколько false-positive'ов, потом снимаете режим и реально блокируете.
Правила блокировки (mini-DSL)¶
policy_config_schema — это правило, как платформа должна интерпретировать ответ вашего детектора.
Структура¶
{
"block_when": [
{ "field": "verdict", "op": "eq", "value": "unsafe" },
{ "field": "score", "op": "gte", "value": 0.85 }
],
"decision": "BLOCK",
"reason": "Внутреннее кодовое имя обнаружено с высокой уверенностью"
}
Семантика: платформа применяет каждое условие из block_when к ответу вашего детектора. Если все условия выполнились (AND-логика), срабатывает decision.
Операторы (закрытый набор)¶
| Оператор | Семантика | Пример |
|---|---|---|
eq |
строгое равенство | {"field": "verdict", "op": "eq", "value": "unsafe"} |
neq |
строгое неравенство | {"field": "verdict", "op": "neq", "value": "safe"} |
gt |
строго больше (число) | {"field": "score", "op": "gt", "value": 0.85} |
gte |
больше либо равно | {"field": "score", "op": "gte", "value": 0.5} |
lt |
строго меньше | {"field": "score", "op": "lt", "value": 0.1} |
lte |
меньше либо равно | {"field": "score", "op": "lte", "value": 0.3} |
in |
значение принадлежит массиву | {"field": "verdict", "op": "in", "value": ["unsafe", "unknown"]} |
contains |
массив (поле) содержит значение | {"field": "categories", "op": "contains", "value": "violence"} |
Путь к полю (dot-path)¶
Можно вложиться внутрь details:
Платформа извлекает значение по пути. Если ключа нет — условие считается невыполненным (graceful unknown). Это безопасно — отсутствие данных не приводит к ложному BLOCK.
Решение (одно из трёх)¶
| Значение | Семантика |
|---|---|
BLOCK |
Запрос отбит с HTTP 403 (request-side) или HTTP 451 (response-side). Любой BLOCK от любого детектора немедленно прерывает обработку. |
MONITOR |
Запись MONITOR_ALERT в audit-log. Запрос пропускается — клиент получает обычный ответ от LLM. Для теневого подключения (shadow-deploy) и тестов. |
ALLOW |
Информационная запись. Не вето-блокирует BLOCK от других детекторов. |
Приоритет блокировки (any-BLOCK-wins). Если в профиле включено несколько детекторов и хотя бы один из них вернул BLOCK по своему правилу — запрос блокируется, остальные детекторы продолжают выполняться (для аудита), но их вердикты не меняют итог.
ALLOWот другого детектора НЕ отменяет BLOCK.
Логика «ИЛИ» (OR)¶
Mini-DSL поддерживает только AND. Если вам нужно «БЛОК если verdict=unsafe ИЛИ score>0.9» — есть три варианта:
-
Логика на стороне детектора: ваш детектор сам решает, выдавать ли
На платформе достаточно простогоunsafe. Например, в коде:verdict==unsafe → BLOCK. -
Регистрация двух детекторов с одинаковым endpoint но разными
policy_config_schema. Каждый со своим BLOCK-правилом. Активируйте оба в профиле — сработает «OR» через any-BLOCK-wins. -
Оператор
in: вместо двух условийeqиспользоватьinдля дискретных значений:{"field": "verdict", "op": "in", "value": ["unsafe", "unknown"]}— то же самое что(verdict == "unsafe" OR verdict == "unknown").
Режимы fail-safe¶
Что происходит, когда детектор ошибся: вернул HTTP 500, упал в timeout, вернул битый JSON, недоступен по сети.
Profile fail_safe.mode |
Поведение при сбое детектора |
|---|---|
fail-open |
Сбойный детектор исключается из решения. Если есть другие детекторы — они отрабатывают нормально. Если был один — запрос проходит в LLM. В audit пишется warning. |
fail-closed |
Сбойный детектор синтезирует unsafe вердикт. Через обычный mini-DSL это приводит к BLOCK запроса с HTTP 503/403 + явной причиной в reason. |
Когда использовать какой¶
| Сценарий | Рекомендация |
|---|---|
| Новый детектор первые 1-2 недели | fail-open — если что-то не так с самим детектором, не блокировать пользователей |
| Production детектор, прошедший SRE-аудит | fail-closed — детектор критичен для безопасности, лучше отказать в обслуживании чем пропустить опасный запрос |
| Compliance / regulated workload (банкинг, медицина) | fail-closed — регулятор должен видеть, что мы НЕ пропустили проверку |
| Внутренний инструмент с low risk | fail-open — производительность важнее редких пропусков |
Тип сбоя НЕ важен для fail-safe — timeout, HTTP 500, malformed body, connection refused, DNS fail — всё это обрабатывается одинаково по правилу профиля.
Стадии конвейера (request / response / both)¶
Когда использовать request-stage¶
- Детектор анализирует то, что пользователь хочет отправить в LLM.
- Цель: не дать конфиденциальной информации УТЕЧЬ к external LLM-провайдеру.
- Примеры:
- Утечка кодовых имён («Поляну сейчас тестим в стейджинге»)
- Запрос с упоминанием реальных клиентов («что лучше сказать акционеру компании Acme?»)
- Промпт со скрытыми инструкциями (попытка jailbreak)
Когда использовать response-stage¶
- Детектор анализирует то, что LLM выдал — до того, как клиент это увидит.
- Цель: не дать LLM-у выдать опасный/некорректный/вредящий контент.
- Примеры:
- LLM сгенерировал инструкции по обходу безопасности
- Модель выдала ложную информацию, выдающую её за фактическую
- Ответ содержит ругательства или нарушает корпоративный tone-of-voice
- Важно: для response-stage в профиле должен быть включён
response_scanning.enabled: true(Admin UI выставит автоматически когда вы включите хотя бы один response-side детектор).
Когда оба¶
Регистрируйте applies_to: ["request", "response"], если один и тот же детектор имеет смысл и до, и после LLM. Например, leak-детектор: пользователь мог сам написать кодовое имя в промпте, и LLM мог его выдать в ответе.
В профиле для такого детектора независимо включается request-стадия (левая колонка редактора) и response-стадия (правая колонка). Можно включить только одну, обе, или ни одной — с независимыми порогами.
Потоковые ответы (stream: true запросы)¶
Если LLM-клиент шлёт "stream": true:
response_scanning.streaming.mode |
Что делает response-side детектор |
|---|---|
passthrough (по умолчанию) |
Response scanner не запускается. Все ваши response-side детекторы НЕ вызываются. Подходит для UX-критичных стримов где latency важнее scanning. |
buffer_and_scan |
Gateway собирает полный ответ от LLM в буфер, ваши детекторы вызываются. Если BLOCK — клиенту приходит SSE-event с reason, поток заканчивается [DONE]. HTTP-статус остаётся 200 (он уже отправлен в headers), но контент чётко сигнализирует блокировку. |
reject |
Потоковые запросы получают HTTP 400 с ошибкой — клиент должен переотправить без stream: true. |
Рекомендация: для production-профилей с response-side детекторами включайте buffer_and_scan. Для типовых чат-интерфейсов задержка в 200–500 мс на буферизацию незаметна.
Жизненный цикл детектора¶
| Состояние | Поведение |
|---|---|
enabled |
Активный — обрабатывает трафик, виден в реестре и редакторе профиля. |
deprecated |
Активный, но помечен в UI как «не для нового использования». Существующие профили продолжают работать. |
archived |
Soft-deleted: новые профили НЕ могут на него ссылаться. Существующие профили перестают вызывать детектор (он исключён из бандла OPA). Запись остаётся в БД для исторических event'ов. |
Переходы между состояниями свободные — любое из трёх состояний достижимо из любого другого. В Admin UI на строке архивированного детектора появляется иконка ↺ Восстановить.
Quota guard при восстановлении: восстанавливаемый детектор занимает обычный слот в квоте. Если все 50 слотов заняты — PATCH возвращает HTTP 422 с явной причиной. Сначала архивируйте один из активных.
Built-in детекторы immutable. Попытка PATCH/DELETE на
threat-detector/content-safetyвозвращает HTTP 403. Они конфигурятся через legacy boolean-поля профиля (threat_detector_enabled,content_safety_enabled).
Непрерывная регистрация событий (audit-trail) и 152-ФЗ¶
Каждое срабатывание детектора (BLOCK или MONITOR) записывается в security_events. Поля:
| Поле | Что в нём |
|---|---|
trace_id |
Сквозной OpenTelemetry trace ID запроса |
tenant_id |
Устаревшее поле (single-tenant): содержит константу default, оставлено для совместимости |
profile_id |
Профиль, который выдал решение |
event_type |
REQUEST_BLOCKED / MONITOR_ALERT / RESPONSE_BLOCKED |
action |
BLOCK / MONITOR_ONLY |
reason |
Текст вида Custom detector blocked: ["<id>"] или ваш reason из policy_config_schema |
matched_rules |
Массив сработавших правил: custom_detector_block, custom_detector_monitor |
user_prompt |
Текст запроса (с маскировкой PII, если включена) |
metadata.custom_detectors |
Полный verdict-map от всех ваших детекторов на этот запрос |
timestamp |
UTC ISO 8601 |
Просмотр событий¶
В Admin UI → События безопасности → секция событий. Используйте фильтр «Детектор» (dropdown справа от «Действие») чтобы оставить только события от вашего детектора. Клик на строку открывает панель деталей с полным metadata.custom_detectors.
Можно фильтровать по trace_id, периоду, action (BLOCK / MONITOR_ONLY).
Соответствие 152-ФЗ (для российских клиентов)¶
Если ваш детектор обрабатывает персональные данные (паспортные данные, СНИЛС, медицинская информация, банковские реквизиты), учитывайте:
- Согласие субъекта на обработку ПДн вашим детектором должно быть получено до начала эксплуатации.
- Инфраструктура детектора должна находиться в Локализованной Зоне — обычно тот же сегмент сети, что и остальной AppSec.AIGate, размещённый в РФ.
endpoint_metadata.auth_tokenобязательно используйте — он Fernet-шифруется at rest, что вписывается в общие требования к организационно-техническим мерам защиты ПДн по ст.19 152-ФЗ и приказу ФСТЭК №21 (защита от несанкционированного доступа к учётным данным, открывающим путь к ПДн).- Retention аудит-логов настраивается в Admin UI → Настройки → Retention. Минимум 1095 дней (3 года) для соответствия требованиям о хранении журналов событий безопасности (см. ст.18.1 152-ФЗ и Приказ ФСТЭК №21 по защите ИСПДн).
- Маскирование PII в промпте: включайте
pii_enabled: trueиpii_mask_mode: "mask"или"vault_tokenize"в профиле. Детектор увидит[EMAIL]/[PHONE]плейсхолдеры или Vault-токены вместо исходных значений.
⚠ НИКОГДА не логируйте plain-text ПДн внутри детектора (в свой stdout, в свою БД, в свои метрики). Дублирование чувствительных данных вне AppSec.AIGate противоречит принципу минимизации обработки ПДн, закреплённому в ст.5 ч.2 152-ФЗ («объём и характер обрабатываемых данных должен соответствовать заявленным целям»).
GDPR / HIPAA / PCI DSS¶
Аналогично: audit_log + RBAC + Fernet-encryption at rest = baseline для compliance. Для PHI (HIPAA) / Article 9 GDPR / PCI DSS добавьте pii_mask_mode: "vault_tokenize" — исходные значения не покинут ваш периметр даже к Cloud LLM-провайдеру.
Квоты и лимиты¶
| Лимит | Значение | Что происходит при превышении |
|---|---|---|
| Активных custom-детекторов на инсталляцию | 50 | 51-я регистрация — HTTP 422 с явной причиной. Архивируйте неиспользуемые. Восстановление архивированного также упирается в эту квоту. |
Размер configuration_schema |
~256 KB (JSON) | Больший не сериализуется в bundle |
| Размер ответа детектора | 256 KB | Больший body — клиент трактует как malformed |
| Запросов в секунду | Не ограничено платформой | Защищайтесь сами на стороне детектора |
Квота единая на всю инсталляцию (одна организация — одна инсталляция). Защищает от случайного DoS-флуда регистраций и от роста bundle size в MinIO.
Сценарии с несколькими детекторами¶
Несколько детекторов на одной стадии¶
В профиле можно включить несколько custom-детекторов одновременно. Все они вызываются в параллель на каждом запросе. Решения агрегируются по правилу any-BLOCK-wins:
- Если хотя бы один вернул BLOCK → запрос блокируется.
- Если ни один не BLOCK, но хотя бы один MONITOR → пишется MONITOR_ALERT, запрос проходит.
- Если все ALLOW (или ни одно правило не сработало) → запрос проходит без событий.
Детекторы на разных стадиях¶
Можно зарегистрировать: - 1 детектор только на request-side - 1 детектор только на response-side - 1 детектор на обеих стадиях с разными настройками per-stage
Запрос проходит сначала через request-стадию (если там BLOCK → 403, response-стадия НЕ вызывается, экономия API costs). Если request пропустил → LLM-call → response-стадия. Если response BLOCK → клиент получает 451 с причиной.
Кастомные и встроенные детекторы¶
Custom-детекторы НЕ заменяют встроенные. Они работают параллельно. Профиль может одновременно использовать:
- Встроенный threat-detector (атаки на промпт)
- Встроенный content-safety (категоризация)
- Ваши custom-детекторы
Все вердикты агрегируются. Built-in BLOCK имеет приоритет над custom BLOCK в matched_rules, но семантически разницы нет — запрос всё равно отбит.
Сквозная ручная проверка (10 минут)¶
Полный сценарий на свежем стенде.
Шаг 1 — Поднимите свой детектор¶
Возьмите пример из раздела «Полный пример детектора на Python», разверните через docker-compose up -d. Проверьте напрямую:
curl -X POST http://internal-codename-detector:9000/api/v1/detect \
-H "Content-Type: application/json" \
-d '{"prompt": "обсуждение проекта Полярная", "context": {}}'
# → {"verdict":"unsafe","score":0.7,"reason":"...","categories":["internal_codenames"]}
Шаг 2 — Зарегистрируйте через Admin UI¶
- Admin UI → Система → Реестр детекторов.
- Зарегистрировать детектор.
- Заполните:
- id:
internal-codename-leak - name:
Internal Codename Leak - applies_to: ✓ Сканирование запросов
- Endpoint:
http://internal-codename-detector/ port9000/ path/api/v1/detect - Configuration Schema: пример из раздела «Пример хорошо оформленной схемы»
- Policy Config: builder →
verdict eq "unsafe"→ decisionBLOCK→ reasonCodename leak - Сохранить. Toast «Detector registered».
Шаг 3 — Привяжите к профилю¶
- Профили → Создать профиль.
- Задайте имя и выберите OpenAI-провайдер.
- fail_safe →
fail-open(для первичного теста). - Прокрутите до «Пользовательские детекторы (запросы)».
- Включите toggle на карточке
Internal Codename Leak. - Выставите threshold =
0.7. - Сохраните → активируйте профиль.
Шаг 4 — Тестовый запрос¶
Подождите ~5 секунд (OPA bundle resync). Отправьте запрос через gateway:
curl -X POST https://<your-stand>/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <openai-key>" \
-d '{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "Каков статус проекта Полярная?"}
]
}'
Ожидаемое:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": {
"code": "POLICY_VIOLATION",
"message": "Your request was blocked due to security policy",
"type": "policy_violation",
"trace_id": "<UUID>"
}
}
Запрос не дошёл до OpenAI — заблокирован на firewall.
Шаг 5 — Контр-кейс¶
Отправьте тот же запрос без кодового имени:
Должен пройти насквозь (HTTP 200, реальный ответ от OpenAI). Подтверждает, что детектор не false-positive'ит на безопасном промпте.
Шаг 6 — Проверка события в UI¶
Admin UI → События безопасности → фильтр «Детектор» → Internal Codename Leak. Найдите строку с REQUEST_BLOCKED, action=BLOCK, ваш trace_id из Шага 4. Клик → панель деталей покажет:
- reason:
Custom detector blocked: ["internal-codename-leak"] - matched_rules:
custom_detector_block - user_prompt: исходный текст
- metadata.custom_detectors.internal-codename-leak:
{verdict: "unsafe", score: 0.7, ...}
Шаг 7 — Cleanup (если был тестовый)¶
Admin UI → Реестр детекторов → 🗑 архивировать. Все ссылающиеся профили перестанут вызывать детектор за 2-5 секунд.
Устранение неполадок¶
Регистрация: HTTP 422 «id must match …»¶
Регулярка id: ^[a-z][a-z0-9-]{0,63}$. Только lowercase, цифры, дефис. Длина до 64. Никаких подчёркиваний, точек, заглавных.
Корректно: my-detector-v2, corp-leak-2026.
Некорректно: My-Detector, corp.leak, corp_leak, MyDet.
Регистрация: HTTP 422 «policy_config_schema has unknown keys: ['properties', 'type']»¶
Вы передали в policy_config_schema общую JSON Schema вместо mini-DSL. policy_config_schema — это правило применения, не схема. Правильная форма:
{
"block_when": [{"field": "verdict", "op": "eq", "value": "unsafe"}],
"decision": "BLOCK",
"reason": "optional human-readable string"
}
Регистрация: HTTP 409 «detector already exists»¶
Этот id уже занят. Имя реестра — глобальное (одна инсталляция = один реестр). Выберите другой id или восстановите архивированную запись через PATCH.
Регистрация: HTTP 422 «quota exceeded»¶
Достигнут лимит 50 активных детекторов. Архивируйте неиспользуемые (через UI «🗑» или REST DELETE) и повторите.
Регистрация: HTTP 403 «Built-in detectors are immutable»¶
Попытка PATCH/DELETE на threat-detector или content-safety. Эти id зарезервированы за встроенными детекторами.
Детектор зарегистрирован, профиль активен, но запрос проходит без BLOCK¶
Чек-лист:
- OPA bundle подтянулся? Подождите 5 секунд после регистрации/PATCH.
- Поле
enabled_detectors[<id>].request.enabledточноtrueна профиле? Проверьте через UI (toggle должен быть включён) или черезGET /admin/api/v1/profiles/<id>. - Профиль точно в статусе
ACTIVE, неDRAFT? Проверьте в реестре профилей. - Gateway увидел детектор в кеше? Через 30 секунд после регистрации gateway обновляет свой локальный кеш реестра.
- Детектор реально возвращает
verdict: "unsafe"? Дёрните его напрямуюcurl-ом (см. Шаг 1 из раздела «End-to-end ручная проверка»). policy_config_schema.block_whenдействительно matches ответ детектора? Если детектор возвращаетdetails.confidence, а вы написалиconfidence(без префикса), не совпадёт.fail_safe.mode? Если детектор отвечает HTTP 500/timeout и профильfail-open— детектор тихо исключается из решения, BLOCK не сработает. Переключите профиль наfail-closedдля отладки.
Детектор падает по таймауту¶
В audit-логе появляется warning вида custom_detector_failed: context deadline exceeded. Варианты:
- Поднимите
endpoint_metadata.request_timeout_ms(максимум 30000). - Предзагружайте ML-модель в startup-хуке вместо первого запроса.
- Профилируйте детектор —
/api/v1/detectдолжен укладываться в 200мс на p95.
Gateway возвращает 200, но я ожидал BLOCK¶
Возможные причины:
profile.monitor_mode: true— все BLOCK понижаются до MONITOR_ONLY. Запрос проходит, но в audit-log естьMONITOR_ALERTсобытие.- Detector toggle off — детектор зарегистрирован и привязан, но
enabled: false. Включите toggle. - Несовпадение стадии — детектор
applies_to: ["request"], а вы ждёте блокировки на ответе. Зарегистрируйте также на response-side. - Потоковая передача + passthrough — для
stream: trueзапросов response-side не запускается. Переключитеresponse_scanning.streaming.modeнаbuffer_and_scan.
«Detector response missing required field 'verdict'»¶
Ваш детектор отвечает не по контракту. Два решения:
- Возвращайте каноническую форму
{verdict, score}на верхнем уровне. - Опишите свою shape через
response_field_mappingпри регистрации (см. раздел «Неканоническая форма ответа»).
Fernet-ключ не расшифровывает токен¶
Ошибка custom_detector_decrypt_failed в gateway-логах. Скорее всего, ключ env-var DETECTOR_AUTH_TOKEN_KEY отличается на разных сервисах. Свяжитесь с администратором инсталляции — он должен убедиться, что ключ синхронизирован.
Если вы меняете ключ — нужно пере-зарегистрировать все детекторы с auth_token (ciphertext не миграбелен между разными ключами).
UI: форма Configuration Schema пустая или сломана¶
Проверьте configuration_schema детектора через UI «Просмотр» (иконка глаза). Если она содержит oneOf / $ref / dependentRequired — UI переключился в fallback-режим (raw-JSON textarea). Это нормально. Упростите схему до плоского properties: {...} без conditional-конструкций, если хотите красивую форму (см. раздел «Fallback на raw-JSON»).
Архивированный детектор не возвращается в реестре¶
В UI на строке archived есть иконка ↺ Восстановить. Если её нет — проверьте, что вы не пытаетесь восстановить встроенный детектор. Custom-детекторы восстанавливаются всегда (если есть свободный слот в квоте).
Часто задаваемые вопросы¶
Можно ли изменить applies_to после регистрации?
Да, через PATCH. Изменится поведение для всех привязанных профилей. Существующие настройки enabled_detectors[id] для исчезнувшей стадии будут проигнорированы (gateway не дёрнет детектор, даже если в БД остались данные).
Можно ли использовать один детектор в нескольких профилях?
Да. Реестр детекторов один на инсталляцию, и любой профиль может ссылаться на зарегистрированный детектор. Индивидуальные настройки задаются через enabled_detectors[id].request.policy_config в каждом профиле.
Что если у меня два детектора с одинаковым endpoint?
Зарегистрируйте оба с разными id и разными policy_config_schema. Платформа сделает два независимых HTTP-вызова в одной параллельной фазе — детектор справится, если он без состояния (stateless).
Как сделать «детектор для определённой группы пользователей»? Платформа не имеет per-user условий. Создайте отдельный профиль для группы и привяжите детектор только к нему. Маршрутизация группа→профиль идёт через provider routing (например, отдельный API-ключ на группу).
Можно ли в policy_config_schema использовать score детектора как threshold per-profile?
Не напрямую. policy_config_schema фиксирует threshold при регистрации детектора. Если нужен per-profile threshold — экспонируйте его в configuration_schema, передавайте в context.detector_config, и применяйте логику внутри детектора (см. пример в разделе «detector_app.py»: threshold = float(cfg.get("threshold", 0.7))).
Что происходит с детектором, если LLM-провайдер недоступен? Для request-side — ничего не меняется. Детектор отрабатывает на промпте до вызова LLM. Если детектор blocked → 403; если allow → gateway пытается вызвать LLM и возвращает ошибку провайдера. Response-side детекторы не вызываются (нет ответа для сканирования).
Сколько event'ов записывается, если несколько детекторов сработали?
В текущей версии — один event с агрегированной причиной. Если был BLOCK от одного детектора и MONITOR от другого — записывается только BLOCK-event. Все вердикты доступны в metadata.custom_detectors. Расширение audit-видимости для multi-detector сценариев — в roadmap.
Влияет ли мой детектор на встроенные? Нет. Custom и built-in работают параллельно и независимо. Действует агрегация any-BLOCK-wins: первый сработавший — победил.
Можно ли обращаться к моему детектору НЕ из gateway? Например, для отладки.
Технически да, если есть сетевой доступ. Если вы используете auth_token, входной запрос должен нести Authorization: Bearer <plaintext-token>. Полезно для отладки контракта.
Что если мой детектор зависит от внешнего сервиса (Vault, LDAP, БД)? Платформа этого не запрещает, но критично следить за p95 latency — gateway ждёт ответа синхронно. Кешируйте всё что можно, используйте connection-pooling.
Чек-лист готовности к production¶
Перед тем как переключить профиль на ваш детектор в production:
- Healthcheck endpoints
/healthzи/readyzреализованы и возвращают корректно. - p95 латентности
/api/v1/detect< 200 мс под пиковой нагрузкой. - Холодный старт модели вынесен в startup-хук.
-
auth_tokenсгенерирован, сохранён в Vault / K8s Secret, передан при регистрации. - HTTPS включен (если детектор за пределами доверенной сети).
- Rate limiting на стороне детектора настроен.
- Логи детектора НЕ содержат личные данные в открытом незашифрованном виде (в формате простого текста, plain-text PII).
- Схема конфигурации проходит превью в UI без отката на ручной JSON.
- Policy Config протестирован на реальных промптах (TP/FP rate измерены).
- Профиль с детектором запущен в
monitor_mode: trueхотя бы 1-2 недели для калибровки порогов. -
fail_safe.modeвыбран соответственно требованиям compliance. - Retention аудит-логов настроен ≥ 1095 дней для регулируемых workload'ов.
- Команда оператора знает, как просмотреть события в Admin UI.