feature/escalation-no-answer-retry-and-agent-state
Phase 2 of the L1->L2 routing engine (Phase 1: MR!4). - ami_loop() now also captures native AMI DialEnd/Hangup frames (not only UserEvent), needed to detect that an escalated agent did not answer. No dialplan change required - Redirect already routes the client channel into an existing Dial()-based transfer context, so Asterisk emits these events on its own; the listener just wasn't reading them before. - retry_escalation_no_answer(): on NOANSWER/BUSY/CANCEL/CHANUNAVAIL/ CONGESTION, releases the non-answering agent, excludes it, and reserves+redirects to the next available agent via the routing engine's existing exclude_agent_ids support. Exhausted pool marks the escalation failed and leaves the call with the AI instead of dropping the client (ТЗ §32). - Agent status now actually moves through RESERVED -> RINGING -> TALKING -> AFTER_CALL_WORK -> AVAILABLE instead of staying stuck on RESERVED for the whole call; a new acw_sweep_loop background thread (same pattern as the existing failed_retry_loop) times out AFTER_CALL_WORK back to AVAILABLE. - escalations gains attempt_count/real_agent_id/attempted_agent_ids_json (migration 0033); fixes a latent bug where assigned_agent_id stored the SIP extension instead of the real agent_id despite routing-service already returning it in RoutingAgentReserveOut. - Every transition now records an interaction timeline entry and publishes the ТЗ §25 event catalog (AgentReserved/AgentRinging/ AgentNoAnswer/AgentConnected/TransferCompleted/TransferFailed) through the existing emit_voice_event/EventOutboxRow idempotent path. Not in this MR (see plan): SLA config, Callback, L3 (needs real technical agents from the business), metrics.
Merge pull request 'feat: L1->L2 agent pool and routing engine for voice escalation' (#4) from feature/l1-l2-routing-engine into main
call-center
Омниканальный контакт-центр: телефония (Asterisk), Telegram, WhatsApp, веб-чат, email — плюс встроенный голосовой ИИ-оператор (распознавание речи → LLM → синтез речи). Написан на Python (FastAPI), ~25 сервисов вокруг одной SQLAlchemy-схемы, локально работает на SQLite, в проде — на PostgreSQL.
Документация
| Документ | Про что |
|---|---|
docs/architecture/overview.md |
Обзор микросервисов и их ролей |
docs/architecture/voice-ai.md |
Голосовой ИИ-оператор подробно |
docs/architecture/real-time-voice-service.md |
Рантайм голосового моста (AudioSocket, TTS/ASR) |
docs/architecture/event-schemas.md |
Схемы событий шины (RabbitMQ) |
docs/runbooks/local-setup.md |
Как поднять проект локально (демо-режим и ручной запуск) |
docs/runbooks/deployment.md |
Как разворачивать (Docker Compose / Kubernetes) |
longread.md |
Сквозной архитектурный ревью репозитория — что реально есть, где технический долг |
Быстрый старт
Подробности — в docs/runbooks/local-setup.md. Коротко:
python -m pip install -r requirements.txt
cp .env.example .env # по умолчанию SQLite, без Docker
python scripts/migrate_core_db.py
uvicorn gateway.app:app --reload --port 8080
Точки входа (после старта gateway): /operator, /supervisor, /admin, /analyst на http://localhost:8080.
Демо-режим одной командой (поднимает весь стек + тестовые данные): scripts/prepare_demo.ps1.
Тесты
pytest -q
Частые задачи
- Поменять greeting/system prompt голосового бота Айнур — правится в коде:
services/shared/ai_operator_config.py→ai_operator_default_config(), коммит и деплой; сервис сам синкает значение в БД при старте.
Languages
Python
74.2%
JavaScript
18.8%
HTML
3.4%
CSS
2.9%
PowerShell
0.4%
Other
0.3%