refactor: remove global robot_id, require it per-scenario
- config.py: remove robot_id field from SESConfig (no global default)
- session_client.py: robot_id is now REQUIRED in ask(), no fallback
- mfc_dialog.yaml: add explicit robot_id
- example_*.yaml: add commented robot_id placeholder, fix outdated comments
- config.yaml.example: remove robot_id from ses section
- README: full audit — remove archive references, fix deployment docs,
fix scenario format docs, remove outdated sections
This commit is contained in:
parent
9505742b96
commit
bec273d7ac
134
README.md
134
README.md
@ -7,8 +7,7 @@
|
||||
Watchdog-демон, который:
|
||||
- **Проверяет доступность** SES API каждые 30 секунд
|
||||
- **Имитирует диалоги пользователей** по сценариям, описанным в YAML-файлах
|
||||
- **Анализирует архив** диалогов на ошибки (unknown, not found, no data)
|
||||
- **Отправляет метрики в Zabbix** через `zabbix_sender`
|
||||
- **Отправляет метрики в Zabbix** через zabbix_sender или UserParameter
|
||||
|
||||
SES — универсальная сценарная машина, поэтому мониторинг не привязан к конкретному проекту. Один и тот же демон может обслуживать любой сценарий: голосового ассистента, чат-бота, IVR, диалогов МФЦ, медорганизаций и любых других.
|
||||
|
||||
@ -18,23 +17,23 @@ SES — универсальная сценарная машина, поэтом
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ ses-monitor (watchdog-демон) │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌────────────────┐ ┌──────────┐│
|
||||
│ │ LivenessCheck │ │ ScenarioRunner │ │ Archive ││
|
||||
│ │ (30 сек) │ │ (по YAML) │ │ Auditor ││
|
||||
│ │ │ │ │ │ (10 мин) ││
|
||||
│ │ GET /hostname│ │ POST /ses/ask │ │ /archive ││
|
||||
│ │ GET /license │ │ шаги+expect │ │ /list ││
|
||||
│ └──────┬───────┘ └───────┬─────────┘ └────┬─────┘│
|
||||
│ │ │ │ │
|
||||
│ ┌──────▼──────────────────▼──────────────────▼─────┐│
|
||||
│ │ ZabbixSender (zabbix_sender) ││
|
||||
│ └──────────────────────┬───────────────────────────┘│
|
||||
└─────────────────────────┼───────────────────────────┘
|
||||
│ TCP 10051
|
||||
┌───────────▼───────────┐
|
||||
│ Zabbix Server │
|
||||
│ (триггеры, алерты) │
|
||||
└───────────────────────┘
|
||||
│ ┌──────────────┐ ┌────────────────┐ │
|
||||
│ │ LivenessCheck │ │ ScenarioRunner │ │
|
||||
│ │ (30 сек) │ │ (по YAML) │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ GET /hostname│ │ POST /ses/ask │ │
|
||||
│ │ GET /license │ │ шаги+expect │ │
|
||||
│ └──────┬───────┘ └───────┬─────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────▼──────────────────▼──────┐ │
|
||||
│ │ Zabbix (sender / UP) │ │
|
||||
│ └──────────────┬─────────────────┘ │
|
||||
└─────────────────┼────────────────────────────────────┘
|
||||
│ TCP 10051 / JSON file
|
||||
┌───────────▼───────────┐
|
||||
│ Zabbix Server │
|
||||
│ (триггеры, алерты) │
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
@ -69,29 +68,35 @@ Zabbix агент по запросу сервера запускает скри
|
||||
|
||||
```
|
||||
/opt/ses-monitor/
|
||||
├── ses_monitor/ # Python-пакет
|
||||
│ ├── __init__.py # Версия, метаданные
|
||||
│ ├── __main__.py # Точка входа: python -m ses_monitor
|
||||
│ ├── main.py # Главный asyncio watchdog-цикл
|
||||
│ ├── config.py # Загрузка YAML-конфигурации
|
||||
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
|
||||
│ ├── session_client.py # HTTP-клиент для SES API
|
||||
│ ├── scenario_runner.py # Движок выполнения сценариев
|
||||
│ ├── log_client.py # Чтение логов SES
|
||||
│ ├── zabbix.py # Интеграция с Zabbix (zabbix_sender)
|
||||
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
|
||||
├── src/
|
||||
│ └── ses_monitor/ # Python-пакет (src-layout)
|
||||
│ ├── __init__.py # Версия, метаданные
|
||||
│ ├── __main__.py # Точка входа: python -m ses_monitor
|
||||
│ ├── main.py # Главный asyncio watchdog-цикл
|
||||
│ ├── config.py # Загрузка YAML-конфигурации
|
||||
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
|
||||
│ ├── session_client.py # HTTP-клиент для SES API
|
||||
│ ├── scenario_runner.py # Движок выполнения сценариев
|
||||
│ ├── log_client.py # Чтение логов SES (задел на будущее)
|
||||
│ ├── zabbix.py # Интеграция с Zabbix (sender / UserParameter)
|
||||
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
|
||||
├── config/
|
||||
│ ├── config.yaml # Основная конфигурация
|
||||
│ ├── config.yaml.example # Шаблон конфигурации (скопировать в config.yaml)
|
||||
│ └── scenarios/
|
||||
│ ├── example_liveness.yaml # Пример: быстрая проверка
|
||||
│ └── example_dialog.yaml # Пример: многошаговый диалог
|
||||
│ ├── example_dialog.yaml # Пример: многошаговый диалог
|
||||
│ └── mfc_dialog.yaml # Production: диалог с МФЦ
|
||||
├── zabbix/
|
||||
│ ├── zbx_ses_template.xml # Шаблон Zabbix (импорт в frontend)
|
||||
│ └── zbx_ses_monitor.conf # UserParameter для Zabbix Agent (резерв)
|
||||
│ ├── zbx_ses_template_5.0.xml # Шаблон для sender (trapper)
|
||||
│ ├── zbx_ses_template_5.0_up.xml # Шаблон для UserParameter
|
||||
│ └── zbx_ses_monitor.conf # UserParameter для Zabbix агента
|
||||
├── scripts/
|
||||
│ └── zbx_get.py # Скрипт чтения JSON для UserParameter
|
||||
├── systemd/
|
||||
│ └── ses-monitor.service # Systemd unit
|
||||
│ └── ses-monitor.service # Systemd unit
|
||||
├── tests/
|
||||
│ └── test_scenario_runner.py
|
||||
│ ├── __init__.py
|
||||
│ └── test_scenario_runner.py # 48 тестов
|
||||
├── pyproject.toml # Зависимости и метаданные пакета
|
||||
└── README.md
|
||||
```
|
||||
@ -139,7 +144,6 @@ nano config/config.yaml
|
||||
```yaml
|
||||
ses:
|
||||
base_url: "http://10.101.60.31:6190" # URL вашего SES API
|
||||
robot_id: "" # ID робота из GET /ses/robot/listall
|
||||
channel: "monitoring" # Имя канала в архиве
|
||||
request_timeout: 30
|
||||
|
||||
@ -206,8 +210,8 @@ interval: 300
|
||||
# Таймаут HTTP-запроса в секундах
|
||||
timeout: 30
|
||||
|
||||
# ID робота (если не указан — берётся из config.yaml)
|
||||
robot_id: "ваш-uuid-робота"
|
||||
# ID робота (обязательно). Узнать: GET /ses/robot/listall
|
||||
robot_id: "uuid-вашего-робота"
|
||||
|
||||
# Канал (если не указан — берётся из config.yaml)
|
||||
channel: "monitoring"
|
||||
@ -263,10 +267,10 @@ path: "request.data[0].reply.confidence"
|
||||
|
||||
1. **Узнайте robot_id** вашего сценария:
|
||||
```bash
|
||||
curl -s http://<ses-host>:6190/ses/robot/list
|
||||
curl -s http://<ses-host>:6190/ses/robot/listall
|
||||
```
|
||||
|
||||
2. **Пропишите robot_id** в `config/config.yaml`
|
||||
2. **Пропишите robot_id** в файле сценария (поле `robot_id` в YAML)
|
||||
|
||||
3. **Создайте файл сценария**, описав шаги диалога:
|
||||
```bash
|
||||
@ -279,9 +283,7 @@ path: "request.data[0].reply.confidence"
|
||||
curl -X POST "http://<ses-host>:6190/ses/ask/<robot_id>?text=Привет&channel=monitoring"
|
||||
```
|
||||
|
||||
5. **Добавьте item в Zabbix** для вашего сценария (см. комментарий в `zbx_ses_template.xml`)
|
||||
|
||||
6. **Запустите демон** и проверьте что метрики идут в Zabbix
|
||||
5. **Запустите демон** — метрики агрегируются автоматически (шаблон Zabbix универсален)
|
||||
|
||||
## Расширение системы
|
||||
|
||||
@ -295,48 +297,8 @@ path: "request.data[0].reply.confidence"
|
||||
### Добавление новой метрики
|
||||
|
||||
1. В нужном модуле добавить поле в датакласс-результат
|
||||
2. В методе `to_zabbix_metrics()` добавить соответствующий ключ
|
||||
3. В `main.py` вызвать `self._zabbix.add(key, value)`
|
||||
4. Добавить item в Zabbix шаблон
|
||||
|
||||
### Мониторинг SEE, SMC, TTS, QAS, SPR
|
||||
|
||||
Заложена возможность расширения для мониторинга внутренних сервисов SES.
|
||||
|
||||
**SEE (распознавание намерений/сущностей):**
|
||||
```python
|
||||
for entry in entries:
|
||||
for model in entry.models:
|
||||
if model.startswith("see:"):
|
||||
see_model = model.split(":", 1)[1]
|
||||
# Анализ точности распознавания
|
||||
```
|
||||
|
||||
**SMC (классификация):**
|
||||
```python
|
||||
if model.startswith("smc:"):
|
||||
# Отслеживание дрифта классификации
|
||||
```
|
||||
|
||||
**TTS (синтез речи):**
|
||||
```python
|
||||
# Проверка наличия голосовых ответов в реплаях
|
||||
if any(a.get("voice") for a in entry.raw.get("reply", {}).get("answers", [])):
|
||||
tts_ok_count += 1
|
||||
```
|
||||
|
||||
**QAS (вопросно-ответная система):**
|
||||
```python
|
||||
# Поле qas в archive entry — проверка наполненности
|
||||
if not entry.raw.get("current", {}).get("qas"):
|
||||
qas_empty_count += 1
|
||||
```
|
||||
|
||||
**SPR (распознавание речи):**
|
||||
```python
|
||||
# Косвенная проверка через тестовые сценарии,
|
||||
# которые отправляют аудио и проверяют текстовую расшифровку
|
||||
```
|
||||
2. В `main.py` вызвать `self._zabbix.add(key, value)`
|
||||
3. Добавить item в Zabbix шаблон
|
||||
|
||||
### Prometheus/Grafana
|
||||
|
||||
|
||||
@ -12,11 +12,6 @@ ses:
|
||||
# Базовый URL SES API
|
||||
base_url: "http://YOUR_SES_HOST:6190"
|
||||
|
||||
# ID робота (сценария) для проверок.
|
||||
# Узнать ID: GET /ses/robot/list или /ses/robot/listall
|
||||
# Каждый сценарий может переопределить robot_id в своём YAML-файле
|
||||
robot_id: "YOUR_ROBOT_ID"
|
||||
|
||||
# Эндпоинт для получения списка роботов:
|
||||
# /ses/robot/list — только опубликованные
|
||||
# /ses/robot/listall — все (включая черновики)
|
||||
|
||||
@ -16,6 +16,7 @@ description: >
|
||||
enabled: false # Пример выключен по умолчанию
|
||||
interval: 600
|
||||
timeout: 45
|
||||
# robot_id: "uuid-вашего-робота" # ОБЯЗАТЕЛЬНО: ID робота из /ses/robot/listall
|
||||
restart_closed: false # Не переоткрывать закрытые сессии
|
||||
|
||||
steps:
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
# Пример сценария: базовая проверка liveness робота
|
||||
# =============================================================================
|
||||
# Проверяет что робот отвечает на приветствие и не возвращает ошибок API.
|
||||
# Подходит для любого сценария SES — достаточно заменить robot_id в config.yaml.
|
||||
# robot_id указывается в этом же файле (не наследуется из глобального конфига).
|
||||
#
|
||||
# ИНСТРУКЦИЯ: скопируйте этот файл, переименуйте и адаптируйте под ваш проект.
|
||||
# cp example_liveness.yaml my_project_liveness.yaml
|
||||
# # отредактируйте: name, steps, expect
|
||||
# # отредактируйте: name, robot_id, steps, expect
|
||||
|
||||
name: "example: liveness"
|
||||
description: >
|
||||
@ -17,6 +17,7 @@ description: >
|
||||
enabled: false # Пример выключен по умолчанию
|
||||
interval: 60
|
||||
timeout: 15
|
||||
# robot_id: "uuid-вашего-робота" # ОБЯЗАТЕЛЬНО: ID робота из /ses/robot/listall
|
||||
|
||||
steps:
|
||||
- send: "Здравствуйте"
|
||||
|
||||
@ -13,6 +13,7 @@ description: >
|
||||
enabled: true
|
||||
interval: 60
|
||||
timeout: 15
|
||||
robot_id: "8ae503dc-ee33-4a6c-a6cf-3dcad3900986_prod"
|
||||
|
||||
steps:
|
||||
- send: "Здравствуйте"
|
||||
|
||||
@ -39,7 +39,6 @@ class SESConfig:
|
||||
"""Настройки подключения к SES серверу."""
|
||||
|
||||
base_url: str = "http://10.101.60.31:6190"
|
||||
robot_id: str = "" # ID робота задаётся в config.yaml под конкретный проект
|
||||
channel: str = "monitoring"
|
||||
request_timeout: int = 30
|
||||
robot_list_endpoint: str = "/ses/robot/list" # или "/ses/robot/listall"
|
||||
@ -190,7 +189,6 @@ def _build_config(data: Dict[str, Any]) -> AppConfig:
|
||||
ses_data = data.get("ses", {})
|
||||
ses = SESConfig(
|
||||
base_url=ses_data.get("base_url", SESConfig.base_url),
|
||||
robot_id=ses_data.get("robot_id", SESConfig.robot_id),
|
||||
channel=ses_data.get("channel", SESConfig.channel),
|
||||
request_timeout=ses_data.get("request_timeout", SESConfig.request_timeout),
|
||||
robot_list_endpoint=ses_data.get("robot_list_endpoint", SESConfig.robot_list_endpoint),
|
||||
|
||||
@ -137,7 +137,6 @@ class SESClient:
|
||||
|
||||
def __init__(self, config: SESConfig):
|
||||
self._base_url = config.base_url.rstrip("/")
|
||||
self._robot_id = config.robot_id
|
||||
self._channel = config.channel
|
||||
self._timeout = config.request_timeout
|
||||
self._robot_list_endpoint = config.robot_list_endpoint
|
||||
@ -221,8 +220,8 @@ class SESClient:
|
||||
self,
|
||||
text: str,
|
||||
*,
|
||||
robot_id: str,
|
||||
session_id: Optional[str] = None,
|
||||
robot_id: Optional[str] = None,
|
||||
channel: Optional[str] = None,
|
||||
restart_closed: bool = False,
|
||||
) -> AskResponse:
|
||||
@ -230,15 +229,16 @@ class SESClient:
|
||||
|
||||
Args:
|
||||
text: Текст сообщения.
|
||||
robot_id: ID робота (обязательно, не наследуется из конфига).
|
||||
session_id: UUID сессии. Если None — генерируется новый.
|
||||
robot_id: ID робота. Если None — из конфигурации.
|
||||
channel: Канал. Если None — из конфигурации.
|
||||
restart_closed: Переоткрывать закрытую сессию.
|
||||
|
||||
Returns:
|
||||
AskResponse с ответом робота.
|
||||
"""
|
||||
rid = robot_id or self._robot_id
|
||||
if not robot_id:
|
||||
raise ValueError("robot_id обязателен для POST /ses/ask")
|
||||
ch = channel or self._channel
|
||||
|
||||
if session_id is None:
|
||||
@ -251,7 +251,7 @@ class SESClient:
|
||||
"restartClosed": int(restart_closed),
|
||||
}
|
||||
|
||||
data = await self._post(f"/ses/ask/{rid}", params=params)
|
||||
data = await self._post(f"/ses/ask/{robot_id}", params=params)
|
||||
return AskResponse.from_json(data)
|
||||
|
||||
# --------------- Архив диалогов ---------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user