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
108
README.md
108
README.md
@ -7,8 +7,7 @@
|
|||||||
Watchdog-демон, который:
|
Watchdog-демон, который:
|
||||||
- **Проверяет доступность** SES API каждые 30 секунд
|
- **Проверяет доступность** SES API каждые 30 секунд
|
||||||
- **Имитирует диалоги пользователей** по сценариям, описанным в YAML-файлах
|
- **Имитирует диалоги пользователей** по сценариям, описанным в YAML-файлах
|
||||||
- **Анализирует архив** диалогов на ошибки (unknown, not found, no data)
|
- **Отправляет метрики в Zabbix** через zabbix_sender или UserParameter
|
||||||
- **Отправляет метрики в Zabbix** через `zabbix_sender`
|
|
||||||
|
|
||||||
SES — универсальная сценарная машина, поэтому мониторинг не привязан к конкретному проекту. Один и тот же демон может обслуживать любой сценарий: голосового ассистента, чат-бота, IVR, диалогов МФЦ, медорганизаций и любых других.
|
SES — универсальная сценарная машина, поэтому мониторинг не привязан к конкретному проекту. Один и тот же демон может обслуживать любой сценарий: голосового ассистента, чат-бота, IVR, диалогов МФЦ, медорганизаций и любых других.
|
||||||
|
|
||||||
@ -18,19 +17,19 @@ SES — универсальная сценарная машина, поэтом
|
|||||||
┌─────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────┐
|
||||||
│ ses-monitor (watchdog-демон) │
|
│ ses-monitor (watchdog-демон) │
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────────┐ ┌────────────────┐ ┌──────────┐│
|
│ ┌──────────────┐ ┌────────────────┐ │
|
||||||
│ │ LivenessCheck │ │ ScenarioRunner │ │ Archive ││
|
│ │ LivenessCheck │ │ ScenarioRunner │ │
|
||||||
│ │ (30 сек) │ │ (по YAML) │ │ Auditor ││
|
│ │ (30 сек) │ │ (по YAML) │ │
|
||||||
│ │ │ │ │ │ (10 мин) ││
|
│ │ │ │ │ │
|
||||||
│ │ GET /hostname│ │ POST /ses/ask │ │ /archive ││
|
│ │ GET /hostname│ │ POST /ses/ask │ │
|
||||||
│ │ GET /license │ │ шаги+expect │ │ /list ││
|
│ │ GET /license │ │ шаги+expect │ │
|
||||||
│ └──────┬───────┘ └───────┬─────────┘ └────┬─────┘│
|
│ └──────┬───────┘ └───────┬─────────┘ │
|
||||||
│ │ │ │ │
|
│ │ │ │
|
||||||
│ ┌──────▼──────────────────▼──────────────────▼─────┐│
|
│ ┌──────▼──────────────────▼──────┐ │
|
||||||
│ │ ZabbixSender (zabbix_sender) ││
|
│ │ Zabbix (sender / UP) │ │
|
||||||
│ └──────────────────────┬───────────────────────────┘│
|
│ └──────────────┬─────────────────┘ │
|
||||||
└─────────────────────────┼───────────────────────────┘
|
└─────────────────┼────────────────────────────────────┘
|
||||||
│ TCP 10051
|
│ TCP 10051 / JSON file
|
||||||
┌───────────▼───────────┐
|
┌───────────▼───────────┐
|
||||||
│ Zabbix Server │
|
│ Zabbix Server │
|
||||||
│ (триггеры, алерты) │
|
│ (триггеры, алерты) │
|
||||||
@ -69,7 +68,8 @@ Zabbix агент по запросу сервера запускает скри
|
|||||||
|
|
||||||
```
|
```
|
||||||
/opt/ses-monitor/
|
/opt/ses-monitor/
|
||||||
├── ses_monitor/ # Python-пакет
|
├── src/
|
||||||
|
│ └── ses_monitor/ # Python-пакет (src-layout)
|
||||||
│ ├── __init__.py # Версия, метаданные
|
│ ├── __init__.py # Версия, метаданные
|
||||||
│ ├── __main__.py # Точка входа: python -m ses_monitor
|
│ ├── __main__.py # Точка входа: python -m ses_monitor
|
||||||
│ ├── main.py # Главный asyncio watchdog-цикл
|
│ ├── main.py # Главный asyncio watchdog-цикл
|
||||||
@ -77,21 +77,26 @@ Zabbix агент по запросу сервера запускает скри
|
|||||||
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
|
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
|
||||||
│ ├── session_client.py # HTTP-клиент для SES API
|
│ ├── session_client.py # HTTP-клиент для SES API
|
||||||
│ ├── scenario_runner.py # Движок выполнения сценариев
|
│ ├── scenario_runner.py # Движок выполнения сценариев
|
||||||
│ ├── log_client.py # Чтение логов SES
|
│ ├── log_client.py # Чтение логов SES (задел на будущее)
|
||||||
│ ├── zabbix.py # Интеграция с Zabbix (zabbix_sender)
|
│ ├── zabbix.py # Интеграция с Zabbix (sender / UserParameter)
|
||||||
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
|
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
|
||||||
├── config/
|
├── config/
|
||||||
│ ├── config.yaml # Основная конфигурация
|
│ ├── config.yaml.example # Шаблон конфигурации (скопировать в config.yaml)
|
||||||
│ └── scenarios/
|
│ └── scenarios/
|
||||||
│ ├── example_liveness.yaml # Пример: быстрая проверка
|
│ ├── example_liveness.yaml # Пример: быстрая проверка
|
||||||
│ └── example_dialog.yaml # Пример: многошаговый диалог
|
│ ├── example_dialog.yaml # Пример: многошаговый диалог
|
||||||
|
│ └── mfc_dialog.yaml # Production: диалог с МФЦ
|
||||||
├── zabbix/
|
├── zabbix/
|
||||||
│ ├── zbx_ses_template.xml # Шаблон Zabbix (импорт в frontend)
|
│ ├── zbx_ses_template_5.0.xml # Шаблон для sender (trapper)
|
||||||
│ └── zbx_ses_monitor.conf # UserParameter для Zabbix Agent (резерв)
|
│ ├── zbx_ses_template_5.0_up.xml # Шаблон для UserParameter
|
||||||
|
│ └── zbx_ses_monitor.conf # UserParameter для Zabbix агента
|
||||||
|
├── scripts/
|
||||||
|
│ └── zbx_get.py # Скрипт чтения JSON для UserParameter
|
||||||
├── systemd/
|
├── systemd/
|
||||||
│ └── ses-monitor.service # Systemd unit
|
│ └── ses-monitor.service # Systemd unit
|
||||||
├── tests/
|
├── tests/
|
||||||
│ └── test_scenario_runner.py
|
│ ├── __init__.py
|
||||||
|
│ └── test_scenario_runner.py # 48 тестов
|
||||||
├── pyproject.toml # Зависимости и метаданные пакета
|
├── pyproject.toml # Зависимости и метаданные пакета
|
||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
@ -139,7 +144,6 @@ nano config/config.yaml
|
|||||||
```yaml
|
```yaml
|
||||||
ses:
|
ses:
|
||||||
base_url: "http://10.101.60.31:6190" # URL вашего SES API
|
base_url: "http://10.101.60.31:6190" # URL вашего SES API
|
||||||
robot_id: "" # ID робота из GET /ses/robot/listall
|
|
||||||
channel: "monitoring" # Имя канала в архиве
|
channel: "monitoring" # Имя канала в архиве
|
||||||
request_timeout: 30
|
request_timeout: 30
|
||||||
|
|
||||||
@ -206,8 +210,8 @@ interval: 300
|
|||||||
# Таймаут HTTP-запроса в секундах
|
# Таймаут HTTP-запроса в секундах
|
||||||
timeout: 30
|
timeout: 30
|
||||||
|
|
||||||
# ID робота (если не указан — берётся из config.yaml)
|
# ID робота (обязательно). Узнать: GET /ses/robot/listall
|
||||||
robot_id: "ваш-uuid-робота"
|
robot_id: "uuid-вашего-робота"
|
||||||
|
|
||||||
# Канал (если не указан — берётся из config.yaml)
|
# Канал (если не указан — берётся из config.yaml)
|
||||||
channel: "monitoring"
|
channel: "monitoring"
|
||||||
@ -263,10 +267,10 @@ path: "request.data[0].reply.confidence"
|
|||||||
|
|
||||||
1. **Узнайте robot_id** вашего сценария:
|
1. **Узнайте robot_id** вашего сценария:
|
||||||
```bash
|
```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. **Создайте файл сценария**, описав шаги диалога:
|
3. **Создайте файл сценария**, описав шаги диалога:
|
||||||
```bash
|
```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"
|
curl -X POST "http://<ses-host>:6190/ses/ask/<robot_id>?text=Привет&channel=monitoring"
|
||||||
```
|
```
|
||||||
|
|
||||||
5. **Добавьте item в Zabbix** для вашего сценария (см. комментарий в `zbx_ses_template.xml`)
|
5. **Запустите демон** — метрики агрегируются автоматически (шаблон Zabbix универсален)
|
||||||
|
|
||||||
6. **Запустите демон** и проверьте что метрики идут в Zabbix
|
|
||||||
|
|
||||||
## Расширение системы
|
## Расширение системы
|
||||||
|
|
||||||
@ -295,48 +297,8 @@ path: "request.data[0].reply.confidence"
|
|||||||
### Добавление новой метрики
|
### Добавление новой метрики
|
||||||
|
|
||||||
1. В нужном модуле добавить поле в датакласс-результат
|
1. В нужном модуле добавить поле в датакласс-результат
|
||||||
2. В методе `to_zabbix_metrics()` добавить соответствующий ключ
|
2. В `main.py` вызвать `self._zabbix.add(key, value)`
|
||||||
3. В `main.py` вызвать `self._zabbix.add(key, value)`
|
3. Добавить item в Zabbix шаблон
|
||||||
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
|
|
||||||
# Косвенная проверка через тестовые сценарии,
|
|
||||||
# которые отправляют аудио и проверяют текстовую расшифровку
|
|
||||||
```
|
|
||||||
|
|
||||||
### Prometheus/Grafana
|
### Prometheus/Grafana
|
||||||
|
|
||||||
|
|||||||
@ -12,11 +12,6 @@ ses:
|
|||||||
# Базовый URL SES API
|
# Базовый URL SES API
|
||||||
base_url: "http://YOUR_SES_HOST:6190"
|
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/list — только опубликованные
|
||||||
# /ses/robot/listall — все (включая черновики)
|
# /ses/robot/listall — все (включая черновики)
|
||||||
|
|||||||
@ -16,6 +16,7 @@ description: >
|
|||||||
enabled: false # Пример выключен по умолчанию
|
enabled: false # Пример выключен по умолчанию
|
||||||
interval: 600
|
interval: 600
|
||||||
timeout: 45
|
timeout: 45
|
||||||
|
# robot_id: "uuid-вашего-робота" # ОБЯЗАТЕЛЬНО: ID робота из /ses/robot/listall
|
||||||
restart_closed: false # Не переоткрывать закрытые сессии
|
restart_closed: false # Не переоткрывать закрытые сессии
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
# Пример сценария: базовая проверка liveness робота
|
# Пример сценария: базовая проверка liveness робота
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Проверяет что робот отвечает на приветствие и не возвращает ошибок API.
|
# Проверяет что робот отвечает на приветствие и не возвращает ошибок API.
|
||||||
# Подходит для любого сценария SES — достаточно заменить robot_id в config.yaml.
|
# robot_id указывается в этом же файле (не наследуется из глобального конфига).
|
||||||
#
|
#
|
||||||
# ИНСТРУКЦИЯ: скопируйте этот файл, переименуйте и адаптируйте под ваш проект.
|
# ИНСТРУКЦИЯ: скопируйте этот файл, переименуйте и адаптируйте под ваш проект.
|
||||||
# cp example_liveness.yaml my_project_liveness.yaml
|
# cp example_liveness.yaml my_project_liveness.yaml
|
||||||
# # отредактируйте: name, steps, expect
|
# # отредактируйте: name, robot_id, steps, expect
|
||||||
|
|
||||||
name: "example: liveness"
|
name: "example: liveness"
|
||||||
description: >
|
description: >
|
||||||
@ -17,6 +17,7 @@ description: >
|
|||||||
enabled: false # Пример выключен по умолчанию
|
enabled: false # Пример выключен по умолчанию
|
||||||
interval: 60
|
interval: 60
|
||||||
timeout: 15
|
timeout: 15
|
||||||
|
# robot_id: "uuid-вашего-робота" # ОБЯЗАТЕЛЬНО: ID робота из /ses/robot/listall
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- send: "Здравствуйте"
|
- send: "Здравствуйте"
|
||||||
|
|||||||
@ -13,6 +13,7 @@ description: >
|
|||||||
enabled: true
|
enabled: true
|
||||||
interval: 60
|
interval: 60
|
||||||
timeout: 15
|
timeout: 15
|
||||||
|
robot_id: "8ae503dc-ee33-4a6c-a6cf-3dcad3900986_prod"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- send: "Здравствуйте"
|
- send: "Здравствуйте"
|
||||||
|
|||||||
@ -39,7 +39,6 @@ class SESConfig:
|
|||||||
"""Настройки подключения к SES серверу."""
|
"""Настройки подключения к SES серверу."""
|
||||||
|
|
||||||
base_url: str = "http://10.101.60.31:6190"
|
base_url: str = "http://10.101.60.31:6190"
|
||||||
robot_id: str = "" # ID робота задаётся в config.yaml под конкретный проект
|
|
||||||
channel: str = "monitoring"
|
channel: str = "monitoring"
|
||||||
request_timeout: int = 30
|
request_timeout: int = 30
|
||||||
robot_list_endpoint: str = "/ses/robot/list" # или "/ses/robot/listall"
|
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_data = data.get("ses", {})
|
||||||
ses = SESConfig(
|
ses = SESConfig(
|
||||||
base_url=ses_data.get("base_url", SESConfig.base_url),
|
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),
|
channel=ses_data.get("channel", SESConfig.channel),
|
||||||
request_timeout=ses_data.get("request_timeout", SESConfig.request_timeout),
|
request_timeout=ses_data.get("request_timeout", SESConfig.request_timeout),
|
||||||
robot_list_endpoint=ses_data.get("robot_list_endpoint", SESConfig.robot_list_endpoint),
|
robot_list_endpoint=ses_data.get("robot_list_endpoint", SESConfig.robot_list_endpoint),
|
||||||
|
|||||||
@ -137,7 +137,6 @@ class SESClient:
|
|||||||
|
|
||||||
def __init__(self, config: SESConfig):
|
def __init__(self, config: SESConfig):
|
||||||
self._base_url = config.base_url.rstrip("/")
|
self._base_url = config.base_url.rstrip("/")
|
||||||
self._robot_id = config.robot_id
|
|
||||||
self._channel = config.channel
|
self._channel = config.channel
|
||||||
self._timeout = config.request_timeout
|
self._timeout = config.request_timeout
|
||||||
self._robot_list_endpoint = config.robot_list_endpoint
|
self._robot_list_endpoint = config.robot_list_endpoint
|
||||||
@ -221,8 +220,8 @@ class SESClient:
|
|||||||
self,
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
*,
|
*,
|
||||||
|
robot_id: str,
|
||||||
session_id: Optional[str] = None,
|
session_id: Optional[str] = None,
|
||||||
robot_id: Optional[str] = None,
|
|
||||||
channel: Optional[str] = None,
|
channel: Optional[str] = None,
|
||||||
restart_closed: bool = False,
|
restart_closed: bool = False,
|
||||||
) -> AskResponse:
|
) -> AskResponse:
|
||||||
@ -230,15 +229,16 @@ class SESClient:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
text: Текст сообщения.
|
text: Текст сообщения.
|
||||||
|
robot_id: ID робота (обязательно, не наследуется из конфига).
|
||||||
session_id: UUID сессии. Если None — генерируется новый.
|
session_id: UUID сессии. Если None — генерируется новый.
|
||||||
robot_id: ID робота. Если None — из конфигурации.
|
|
||||||
channel: Канал. Если None — из конфигурации.
|
channel: Канал. Если None — из конфигурации.
|
||||||
restart_closed: Переоткрывать закрытую сессию.
|
restart_closed: Переоткрывать закрытую сессию.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AskResponse с ответом робота.
|
AskResponse с ответом робота.
|
||||||
"""
|
"""
|
||||||
rid = robot_id or self._robot_id
|
if not robot_id:
|
||||||
|
raise ValueError("robot_id обязателен для POST /ses/ask")
|
||||||
ch = channel or self._channel
|
ch = channel or self._channel
|
||||||
|
|
||||||
if session_id is None:
|
if session_id is None:
|
||||||
@ -251,7 +251,7 @@ class SESClient:
|
|||||||
"restartClosed": int(restart_closed),
|
"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)
|
return AskResponse.from_json(data)
|
||||||
|
|
||||||
# --------------- Архив диалогов ---------------
|
# --------------- Архив диалогов ---------------
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user