Files
microdao-daarion/ops/runbook-voice-incidents.md
Apple 67225a39fa docs(platform): add policy configs, runbooks, ops scripts and platform documentation
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
2026-03-03 07:14:53 -08:00

222 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, моніторинг |
| 1030 хв | 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
```