Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
222 lines
9.9 KiB
Markdown
222 lines
9.9 KiB
Markdown
# Voice Incidents Runbook
|
||
**Version:** 1.0 | **Node:** NODA2 | **SLO doc:** `config/slo_policy.yml`
|
||
|
||
---
|
||
|
||
## Перший крок для БУДЬ-ЯКОГО алерту (30 секунд)
|
||
|
||
```bash
|
||
# 1. Репро пакет — весь контекст в одному запиті
|
||
curl -s http://localhost:8002/api/voice/degradation_status | python3 -m json.tool
|
||
|
||
# 2. Canary живий синтез
|
||
python3 ops/scripts/voice_canary.py --mode preflight --memory-url http://localhost:8000
|
||
|
||
# 3. Логи останніх 2 хвилин
|
||
docker logs sofiia-console --since 2m 2>&1 | grep -E "ERROR|WARNING|TTS|LLM|502|429|503"
|
||
docker logs dagi-memory-service-node2 --since 2m 2>&1 | grep -E "ERROR|403|edge.tts|synthesiz"
|
||
```
|
||
|
||
**Поля `repro` у відповіді** дають: `last_5_tts_errors`, `last_5_llm_errors`, `node_id`, `last_model`, `concurrent_tts_slots_free`.
|
||
|
||
---
|
||
|
||
## Alert 1: `VoiceTTFA_P95_Breach_Fast`
|
||
**Умова:** TTFA p95 > 5000ms за 10 хвилин | **Severity:** warning
|
||
|
||
**Що значить:** LLM відповідає повільно — черга Ollama переповнена, модель cold-start, або qwen3.5 вибрана замість gemma3.
|
||
|
||
### Крок 1 — Діагностика (2 хв)
|
||
```bash
|
||
# Ollama поточний стан
|
||
curl -s http://localhost:11434/api/ps | python3 -m json.tool
|
||
# Метрики LLM по моделях (якщо є Prometheus)
|
||
# promql: histogram_quantile(0.95, rate(voice_llm_ms_bucket[5m])) by (model)
|
||
|
||
# Деградаційний стан
|
||
curl -s http://localhost:8002/api/voice/degradation_status | python3 -c \
|
||
"import sys,json; d=json.load(sys.stdin); print(d['repro']['last_model'], d['p95'])"
|
||
```
|
||
|
||
### Крок 2 — Mitigation
|
||
```bash
|
||
# A. Примусово переключити на gemma3 (якщо qwen3.5 завантажений)
|
||
# В UI: зняти галочку "Якісно" → fast profile автоматично обере gemma3
|
||
|
||
# B. Якщо Ollama завантажений запитами — зупинити важкі моделі
|
||
curl -s -X POST http://localhost:11434/api/generate \
|
||
-d '{"model":"qwen3.5:35b-a3b","keep_alive":0}' # вивантажити з GPU
|
||
|
||
# C. Якщо Ollama не відповідає — перезапуск
|
||
docker restart ollama && sleep 10
|
||
curl -s http://localhost:11434/api/tags | python3 -m json.tool
|
||
```
|
||
|
||
### Крок 3 — Verify
|
||
```bash
|
||
python3 ops/scripts/voice_canary.py --mode runtime --memory-url http://localhost:8000
|
||
# Очікування: overall=ok, Polina/Ostap < 3000ms
|
||
```
|
||
|
||
---
|
||
|
||
## Alert 2: `VoiceTTFA_P95_Breach_Quality`
|
||
**Умова:** quality profile TTFA p95 > 7000ms | **Severity:** warning
|
||
|
||
**Що значить:** qwen3.5 або qwen3:14b надто повільні. Часто — конкурентні запити або cold token generation.
|
||
|
||
### Дії
|
||
1. Перевірити `degradation_status.repro.last_model` — підтвердити що це quality profile.
|
||
2. Якщо це ізольована сесія — ігнорувати (quality SLO м'якший).
|
||
3. Якщо 5+ хвилин стабільно → переключити всіх на fast: в `router-config.yml` тимчасово видалити `voice_quality_uk` з `agent_voice_profiles.sofiia.quality_option`.
|
||
4. Після нормалізації — повернути.
|
||
|
||
```bash
|
||
# Підтвердити що fast profile нормальний
|
||
curl -s -X POST http://localhost:8002/api/voice/chat/stream \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"message":"ping","model":"ollama:gemma3:latest","voice_profile":"voice_fast_uk"}' \
|
||
| python3 -c "import sys,json; d=json.load(sys.stdin); print('llm_ms:', d['meta']['llm_ms'])"
|
||
```
|
||
|
||
---
|
||
|
||
## Alert 3: `VoiceQueueUnderflow_Spike`
|
||
**Умова:** underflow rate > 1/хв за 5 хвилин | **Severity:** warning
|
||
|
||
**Що значить:** браузер відтворює аудіо швидше ніж BFF синтезує `rest_chunks`. Користувач чує тишу між реченнями.
|
||
|
||
### Діагностика
|
||
```bash
|
||
# Перевірити TTS latency (чи сповільнилось edge-tts?)
|
||
curl -s http://localhost:8000/voice/health | python3 -c \
|
||
"import sys,json; d=json.load(sys.stdin); [print(v['voice'], v['ms'],'ms') for v in d['voices']]"
|
||
|
||
# Перевірити concurrent TTS slots
|
||
curl -s http://localhost:8002/api/voice/degradation_status | python3 -c \
|
||
"import sys,json; d=json.load(sys.stdin); print('free slots:', d['repro']['concurrent_tts_slots_free'])"
|
||
```
|
||
|
||
### Mitigation
|
||
- **Якщо TTS slow** (> 2s) → Alert 4 (edge-tts). Дивись нижче.
|
||
- **Якщо concurrent slots = 0** → TTS DOS. Перевірити `docker stats dagi-memory-service-node2`. Збільшити `MAX_CONCURRENT_TTS` або перезапустити memory-service.
|
||
- **Якщо slots OK** → перший чанк надто короткий (~1 речення). Тимчасове рішення — зменшити `MIN_CHUNK_CHARS` у `voice_utils.py` щоб більше тексту йшло у перший чанк.
|
||
|
||
---
|
||
|
||
## Alert 4: `VoiceTTS_P95_Degraded`
|
||
**Умова:** TTS synthesis p95 > 2000ms за 10 хвилин | **Severity:** **critical**
|
||
|
||
**Що значить:** edge-tts сповільнився або починає отримувати 403. Типова причина — Microsoft endpoint зміна auth або rate limiting.
|
||
|
||
### Крок 1 — Визначити тип помилки (1 хв)
|
||
```bash
|
||
# Подивитись last_5_tts_errors
|
||
curl -s http://localhost:8002/api/voice/degradation_status | python3 -c \
|
||
"import sys,json; d=json.load(sys.stdin); [print(e) for e in d['repro']['last_5_tts_errors']]"
|
||
|
||
# Живий тест
|
||
python3 ops/scripts/voice_canary.py --mode preflight --memory-url http://localhost:8000
|
||
```
|
||
|
||
### Якщо 403 errors:
|
||
```bash
|
||
# Перевірити версію edge-tts
|
||
docker exec dagi-memory-service-node2 pip show edge-tts | grep Version
|
||
# Очікується: 7.2.7
|
||
|
||
# Якщо версія не 7.2.7 — оновити
|
||
docker exec dagi-memory-service-node2 pip install edge-tts==7.2.7
|
||
docker restart dagi-memory-service-node2
|
||
sleep 10 && python3 ops/scripts/voice_canary.py --mode preflight
|
||
```
|
||
|
||
### Якщо timeout / network:
|
||
```bash
|
||
# Тест від сервера до Microsoft endpoint
|
||
docker exec dagi-memory-service-node2 python3 -c \
|
||
"import asyncio, edge_tts; asyncio.run(edge_tts.list_voices())"
|
||
|
||
# Якщо мережева проблема — тимчасово переключити на espeak (fallback)
|
||
# В memory-service env: TTS_FALLBACK_ENGINE=espeak
|
||
# Увага: якість значно гірша, але голос є
|
||
```
|
||
|
||
### Нотувати в incident log:
|
||
```bash
|
||
curl -s -X POST http://localhost:9102/v1/tools/execute \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tool":"oncall_tool","action":"incident_log_append","params":{"severity":"sev2","title":"TTS degraded — edge-tts","body":"VoiceTTS_P95_Degraded alert fired. Last errors: ..."}}'
|
||
```
|
||
|
||
---
|
||
|
||
## Alert 5: `VoiceTTS_ErrorRate_High`
|
||
**Умова:** TTS errors > 0.05/s за 3 хвилини | **Severity:** **critical**
|
||
|
||
**Що значить:** масові відмови TTS синтезу. Користувачі або не чують нічого, або чують espeak-fallback.
|
||
|
||
### Перший крок (30 секунд)
|
||
```bash
|
||
# Скільки помилок і якого типу
|
||
docker logs dagi-memory-service-node2 --since 5m 2>&1 | grep -c "ERROR\|403\|edge.tts"
|
||
docker logs dagi-memory-service-node2 --since 5m 2>&1 | grep "ERROR" | tail -5
|
||
```
|
||
|
||
### Mitigation tree:
|
||
```
|
||
error_type = 403 → Крок "Якщо 403 errors" з Alert 4
|
||
error_type = timeout → Перевірити мережу, перезапустити memory-service
|
||
error_type = synthesis → pip install edge-tts==7.2.7 --force-reinstall
|
||
error_type = OOM → docker stats → перезапустити memory-service з більшим RAM limit
|
||
```
|
||
|
||
### Аварійний fallback (якщо нічого не допомогло):
|
||
```bash
|
||
# Вимкнути автоспік у UI — щоб не показувало помилки
|
||
# Або тимчасово вимкнути streaming
|
||
docker exec sofiia-console env VOICE_STREAM_ENABLED=false \
|
||
uvicorn app.main:app --host 0.0.0.0 --port 8002 &
|
||
# (не рекомендовано на prod без rebuild, але як аварійний захід)
|
||
```
|
||
|
||
### Повідомити користувачів (якщо > 10 хвилин):
|
||
- Додати banner у UI: змінна `VOICE_DEGRADED_BANNER` у env → відобразити через degradation badge "🔴 TTS DEGRADED"
|
||
|
||
---
|
||
|
||
## Escalation
|
||
|
||
| Тривалість | Дія |
|
||
|------------|-----|
|
||
| < 10 хв | Автоматичний деградаційний badge у UI, моніторинг |
|
||
| 10–30 хв | Mitigation з цього runbook, canary preflight |
|
||
| > 30 хв | Escalate до @IvanTytar, записати incident в ops/incidents.jsonl |
|
||
| > 2 год | Post-mortem draft (Sofiia-supervisor `postmortem_draft_graph`) |
|
||
|
||
```bash
|
||
# Записати incident
|
||
echo '{"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","sev":"sev2","title":"Voice TTS degraded","status":"open"}' \
|
||
>> ops/incidents.jsonl
|
||
```
|
||
|
||
---
|
||
|
||
## Корисні команди (bookmark)
|
||
|
||
```bash
|
||
# Швидкий статус всього voice стеку
|
||
curl -s http://localhost:8002/api/voice/degradation_status | python3 -m json.tool
|
||
curl -s http://localhost:8000/voice/health | python3 -c "import sys,json; d=json.load(sys.stdin); print('TTS:', d['edge_tts'], '| Polina:', [v for v in d['voices'] if 'Polina' in v['voice']][0]['ms'], 'ms')"
|
||
python3 ops/scripts/voice_canary.py --mode preflight
|
||
|
||
# Browser console для активних сесій
|
||
# _voiceStats() — p50/p95 по останніх 20 турнах
|
||
# _voice_degradation_sm — поточний стан на сервері
|
||
|
||
# Prometheus queries (якщо є)
|
||
# histogram_quantile(0.95, rate(voice_ttfa_ms_bucket[5m])) by (voice_profile)
|
||
# rate(voice_tts_errors_total[5m])
|
||
# rate(voice_queue_underflows_total[5m]) * 60
|
||
```
|