feat: ui + docs: для жюри
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
# Инструкция для жюри по проверке
|
||||
|
||||
Модуль принимает текст, находит в нём персональные данные, подменяет их и по тому же идентификатору возвращает исходный текст. Ниже — как это воспроизвести и где смотреть журнал и метрики.
|
||||
|
||||
Сервис слушает `http://localhost:8080`.
|
||||
|
||||
## Запуск
|
||||
|
||||
Нужны Java 21 и Maven 3.8+ (или обёртка `./mvnc`). Для дашборда — Docker Compose.
|
||||
|
||||
Локально:
|
||||
|
||||
```bash
|
||||
./mvnc package -DskipTests
|
||||
java -jar target/pd-guard-spring-1.0.0.jar
|
||||
```
|
||||
|
||||
Контейнер, Prometheus и Grafana (`compose.yaml` ссылается на уже собранный образ `pd-guard-spring:jvm`, сам его не строит):
|
||||
|
||||
```bash
|
||||
docker build -f src/main/docker/Dockerfile -t pd-guard-spring:jvm .
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Если локальной Java нет — тот же образ собирается полностью внутри Docker, `mvn package` идёт в отдельной стадии сборки (дольше первого запуска, зато не требует ничего на хосте кроме Docker):
|
||||
|
||||
```bash
|
||||
docker build -f src/main/docker/Dockerfile.build -t pd-guard-spring:jvm .
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Готовность: `GET http://localhost:8080/health` отвечает `OK`.
|
||||
|
||||
Действующие системы читаются из `config/systems.json`. Это не встроенный файл в jar: при запуске из каталога проекта используется именно он.
|
||||
|
||||
## Как устроен запрос
|
||||
|
||||
`POST /process`
|
||||
|
||||
Тело:
|
||||
|
||||
```json
|
||||
{"payload": "текст", "payload_id": "устойчивый-идентификатор"}
|
||||
```
|
||||
|
||||
Заголовки:
|
||||
|
||||
| Заголовок | Смысл |
|
||||
|-----------|--------|
|
||||
| `X-System-Id` | имя системы из `config/systems.json`. Нет заголовка или имя неизвестно — применяется политика `default` |
|
||||
| `X-System-Key` | нужен только если у системы в конфиге задано поле `key` |
|
||||
|
||||
Направление выбирается по `payload_id`, а не по отдельному флагу:
|
||||
|
||||
1. Идентификатор ещё не встречался — текст маскируется, пара «исходник ↔ маска» запоминается.
|
||||
2. Пришёл ранее выданный текст маски и у системы включено `demask` — возвращается исходный текст.
|
||||
3. Пришёл тот же исходный текст — возвращается та же маска, что и в первый раз.
|
||||
|
||||
Ответ: `{"result": "..."}`.
|
||||
|
||||
Коды: `200` успех, `400` нет `payload` или `payload_id`, `403` система выключена или неверный ключ, `429` перегрузка (заголовок `Retry-After: 1`). При внутреннем сбое сервис отвечает `200` и текстом `[обработка недоступна]`, чтобы исходные персональные данные не ушли наружу.
|
||||
|
||||
## Маскирование в браузере
|
||||
|
||||
Откройте `http://localhost:8080/`.
|
||||
|
||||
Четыре режима на странице — это четыре системы из конфига:
|
||||
|
||||
| Кнопка | Заголовок | Что увидите |
|
||||
|--------|-----------|-------------|
|
||||
| MASK | `default` | звёздочки с открытыми краями номера, ФИО инициалами |
|
||||
| STRICT | `strict` | сплошные звёздочки, длина сохраняется |
|
||||
| TOKEN | `crm` | токены вида `[FIO_1]`, `[PASSPORT_1]` |
|
||||
| SYNTHETIC | `analytics` | правдоподобная подмена (вымышленное ФИО, номер, адрес) |
|
||||
|
||||
Вставьте текст, например:
|
||||
|
||||
```text
|
||||
Клиент Иванов Иван Иванович, паспорт 4509 123456, тел +7 916 123-45-67
|
||||
```
|
||||
|
||||
Нажмите «Обработать». Результат появится в блоке под кнопкой.
|
||||
|
||||
Страница каждый раз создаёт новый `payload_id`. Она показывает только маскирование. Демаскирование проверяется запросом к API с тем же идентификатором.
|
||||
|
||||
## Маскирование через API
|
||||
|
||||
Система `default` маскирует все известные типы и умеет демаскировать. Режим — звёздочки с открытыми краями номера (`MASK`).
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8080/process \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-System-Id: default" \
|
||||
-d "{\"payload\":\"Клиент Иванов Иван Иванович, паспорт 4509 123456, тел +7 916 123-45-67\",\"payload_id\":\"doc-1\"}"
|
||||
```
|
||||
|
||||
Ожидаемый вид ответа: в `result` ФИО заменено на инициалы (`И. И. И.`), середина паспорта и телефона закрыта звёздочками, края видны.
|
||||
|
||||
Сплошные звёздочки — тот же запрос с заголовком `X-System-Id: strict`.
|
||||
|
||||
Токены вида `[FIO_1]`, `[PASSPORT_1]`, `[PHONE_1]` — тот же запрос с заголовком `X-System-Id: crm` (но у `crm` в политике нет `PASSPORT`, см. ниже).
|
||||
|
||||
Синтетика — `X-System-Id: analytics`. У этой системы обратное преобразование выключено.
|
||||
|
||||
Система `crm` маскирует только ФИО, телефон, email и части адреса (город, улица, дом, квартира). Паспорт в её политике не входит и в ответе останется открытым. Демаскирование у `crm` выключено.
|
||||
|
||||
Система `legacy-billing` выключена: тот же запрос с `X-System-Id: legacy-billing` даёт `403`.
|
||||
|
||||
## Демаскирование
|
||||
|
||||
Нужны три условия одновременно:
|
||||
|
||||
- тот же `payload_id`, что при маскировании (`doc-1` в примере выше);
|
||||
- в `payload` — точная строка из поля `result` предыдущего ответа;
|
||||
- система с `"demask": true`. Сейчас это `default` и `strict`. У `crm` и `analytics` демаскирование выключено намеренно.
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8080/process \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-System-Id: default" \
|
||||
-d "{\"payload\":\"<сюда строка result из маскирования>\",\"payload_id\":\"doc-1\"}"
|
||||
```
|
||||
|
||||
В `result` вернётся исходная фраза с ФИО, паспортом и телефоном.
|
||||
|
||||
Соответствие живёт в памяти процесса 30 минут и пропадает после перезапуска. Повторный запрос после перезапуска будет обработан как новое маскирование.
|
||||
|
||||
Хранилище разделено по системам: маска, полученная от `default`, не раскрывается запросом от `strict` с тем же `payload_id`.
|
||||
|
||||
## Цепочка до модели
|
||||
|
||||
`POST /proxy` показывает, что ушло бы во внешнюю модель и что вернулось бы потребителю. Адрес модели по умолчанию пуст, поэтому отвечает заглушка: она возвращает присланный (уже замаскированный) текст. В модель подставляются токены, даже если у системы выбран другой режим: по звёздочкам однозначное восстановление невозможно.
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8080/proxy \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-System-Id: default" \
|
||||
-d "{\"prompt\":\"Клиент Иванов Иван Иванович, карта 4111 1111 1111 1111. Кратко опишите профиль.\"}"
|
||||
```
|
||||
|
||||
В ответе:
|
||||
|
||||
| Поле | Смысл |
|
||||
|------|--------|
|
||||
| `prompt_masked` | текст, который ушёл бы в модель |
|
||||
| `llm_response_masked` | ответ модели (или заглушки) ещё с токенами |
|
||||
| `response` | текст потребителю; при `demask: true` токены заменены обратно |
|
||||
| `replaced` | таблица «токен → исходное значение» |
|
||||
| `llm` | `заглушка` либо имя модели |
|
||||
|
||||
В `prompt_masked` не должно остаться открытых ФИО и номера карты.
|
||||
|
||||
## Где смотреть логи
|
||||
|
||||
Отдельного файла журнала нет. Строки пишет процесс в стандартный вывод.
|
||||
|
||||
Локальный запуск — окно, где выполнена команда `java -jar`.
|
||||
|
||||
Docker:
|
||||
|
||||
```bash
|
||||
docker compose logs -f pd-guard
|
||||
```
|
||||
|
||||
На каждое маскирование есть строка уровня INFO. В ней идентификатор, длина текста и счётчики по типам. Сами значения персональных данных в журнал не пишутся:
|
||||
|
||||
```text
|
||||
payload_id=doc-1 символов=72 найдено={FIO=1, PASSPORT=1, PHONE=1}
|
||||
```
|
||||
|
||||
Рядом по смыслу, если они случаются:
|
||||
|
||||
- `Системе … обращение в модуль запрещено настройками` — выключенная система;
|
||||
- `Системе … отказано: неверный ключ`;
|
||||
- `демаскирование не нашло соответствие` — идентификатор и отпечаток маски неизвестны, текст обработан как новый;
|
||||
- `Настройки систем перечитаны из …` — после правки `config/systems.json`.
|
||||
|
||||
Для прокси: `proxy: система=… заменено=… модель=…`. Число замен есть, значения — нет.
|
||||
|
||||
## Где смотреть метрики
|
||||
|
||||
Сырые метрики сервиса, без Docker:
|
||||
|
||||
| Адрес | Что это |
|
||||
|-------|---------|
|
||||
| `GET /actuator/prometheus` | все метрики в формате Prometheus |
|
||||
| `GET /actuator/metrics` | список имён Micrometer |
|
||||
| `GET /actuator/metrics/pdguard.process` | длительность обработки |
|
||||
| `GET /actuator/metrics/pdguard.pd.detected` | сколько фрагментов найдено, в разрезе типа и системы |
|
||||
| `GET /actuator/health` | проба Spring Boot |
|
||||
|
||||
Имеет смысл смотреть после одного-двух вызовов `/process`:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/actuator/prometheus | findstr pdguard
|
||||
```
|
||||
|
||||
На Linux и macOS вместо `findstr` — `grep pdguard`.
|
||||
|
||||
Основные ряды:
|
||||
|
||||
| Метрика | О чём |
|
||||
|---------|--------|
|
||||
| `pdguard_process_seconds` | длительность, метки `direction` (`mask` / `unmask`) и `system` |
|
||||
| `pdguard_pd_detected_total` | найденные фрагменты, метки `type` и `system` |
|
||||
| `pdguard_requests_rejected_total` | отказы: `overload`, `malformed`, `system_disabled`, `internal_error` |
|
||||
| `pdguard_concurrency_limit` | текущий потолок одновременных запросов |
|
||||
| `pdguard_concurrency_in_flight` | сколько запросов в работе |
|
||||
| `pdguard_store_chars` | объём хранилища соответствий в символах |
|
||||
| `pdguard_tokens_processed_total` | оценка числа обработанных токенов |
|
||||
| `pdguard_demask_unresolved_total` | демаскирование без найденного соответствия |
|
||||
| `pdguard_ner_*` | вторая ступень (модель). В поставке без скачанных моделей ступень выключена, ряды почти не растут |
|
||||
|
||||
Дашборд появляется вместе с `docker compose`:
|
||||
|
||||
- Grafana: `http://localhost:3000` (анонимный просмотр включён). Дашборд «Модуль безопасности персональных данных»: `http://localhost:3000/d/pd-guard`. Обновление раз в 5 секунд. Блоки: обращения и типы ПДн, задержка и доля ответов быстрее 0,5 с, отказы, вторая ступень, ресурсы узла.
|
||||
- Prometheus: `http://localhost:9090`. Цель сбора — `pd-guard:8080`, интервал 5 секунд.
|
||||
|
||||
## Что поменять без перезапуска
|
||||
|
||||
Отредактируйте `config/systems.json` и вызовите:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://localhost:8080/admin/reload
|
||||
```
|
||||
|
||||
Файл также перечитывается сам, если изменилось время модификации (проверка не чаще раза в секунду). Текущие политики: `GET /admin/config`. Список типов, которые умеет распознавать сборка: `GET /admin/types`.
|
||||
|
||||
Проверка набора типов: в политике `crm` нет `PASSPORT`. Текст с паспортом и телефоном под `X-System-Id: crm` скроет телефон и оставит номер паспорта. Под `default` скроет оба.
|
||||
Reference in New Issue
Block a user