Saltar al contenido principal

Arquitectura

VOCALS esta organizado en tres capas: un panel de configuracion (frontend), una API orquestadora (backend) y una capa de telefonia (SIP). Esta pagina describe como encajan entre si y como fluye el audio a traves del sistema.

Vision General del Sistema

Twilio SIP ─────────── WebSocket Media Streams ───► Orchestrator API
|
Generic SIP Provider ── SIP trunk ── Asterisk |
| |
AudioSocket |
| |
Bridge ─── WebSocket ┘
|
STT Provider
|
LLM Provider
|
TTS Provider
|
audio response
back to caller

Admin Panel ──► Orchestrator API (config endpoints)

VOCALS soporta dos rutas de telefonia:

  1. Twilio -- WebSocket Media Streams directo al orquestador (mulaw/8kHz)
  2. SIP Generico -- SIP trunk → Asterisk → AudioSocket → Bridge → WebSocket al orquestador (alaw o ulaw, auto-detectado)

Panel de Configuracion (Frontend)

Una aplicacion de pagina unica (SPA) en React + TypeScript servida por Nginx. Proporciona el dashboard para gestionar agentes, proveedores, configuraciones SIP, registros de llamadas, analiticas, webhooks y API keys. Se comunica con la API del orquestador a traves de HTTPS. Autenticado mediante Supabase Auth (email/contrasena, OAuth).

API Orquestadora (Backend)

Una aplicacion Python FastAPI que maneja dos responsabilidades distintas:

  1. API REST -- Operaciones CRUD para agentes, proveedores, llamadas, webhooks, analiticas y API keys. Montada en /api/v1/.
  2. Handler WebSocket -- Recibe audio en tiempo real desde Twilio Media Streams. Cada llamada activa mantiene una conexion WebSocket que impulsa el pipeline STT -> LLM -> TTS.

El backend se ejecuta sobre Uvicorn con uvloop para I/O asincrono de alto rendimiento.

Capa de Telefonia

VOCALS soporta dos transportes de telefonia. Ambos alimentan el mismo pipeline de voz.

Twilio: Twilio SIP maneja la conexion PSTN. Cuando llega una llamada (o se inicia), Twilio abre un WebSocket Media Stream hacia el backend del orquestador, transmitiendo audio en formato mulaw/8kHz. El backend lo convierte a PCM y lo pasa por el pipeline.

SIP Generico (Asterisk): Para proveedores no-Twilio (Netelip, Zadarma, etc.), VOCALS ejecuta Asterisk como gateway SIP. Asterisk se registra con el proveedor SIP, maneja la senalizacion SIP y conecta el audio mediante el protocolo AudioSocket a un servicio bridge ligero en Python. El bridge convierte el audio (alaw/ulaw/slin) y lo reenvia a traves de WebSocket al backend, donde entra al mismo pipeline que las llamadas de Twilio.

Componentes clave:

  • Asterisk -- Gateway SIP. Maneja el registro de trunks, enrutamiento de dialplan, media RTP y deteccion de contestador automatico (AMD) para llamadas salientes.
  • Bridge -- Servicio Python que traduce entre el protocolo AudioSocket de Asterisk (TCP binario) y el protocolo WebSocket del backend. Auto-detecta el codec por el tamano del frame (160 bytes = alaw/ulaw, 320 bytes = slin). Se ejecuta en el puerto 9092 (audio) y 9093 (health check).
  • Volumen compartido -- El backend genera la configuracion de trunks PJSIP y la escribe en un volumen Docker compartido. Asterisk lee la configuracion mediante symlink. La recarga en vivo se activa via ARI cuando se crean, actualizan o eliminan configuraciones SIP en el dashboard.

Pipeline de Voz

Cada llamada fluye a traves de un pipeline en tiempo real con cuatro etapas:

Twilio Media Stream (mulaw/8kHz)     Asterisk AudioSocket (alaw/ulaw/slin 8kHz)
| |
v v
Audio Conversion Bridge Service
(mulaw -> PCM 16kHz) (codec detect + WebSocket)
| |
└──────────────┬───────────────────────┘
v
STT Provider (streaming transcription)
|
v
LLM Provider (streaming text generation)
|
v
TTS Provider (streaming speech synthesis)
|
v
Audio Conversion (PCM -> caller codec)
|
v
Back to caller

Conversion de Audio

El audio llega en diferentes formatos dependiendo de la ruta de telefonia:

  • Twilio: muestras mulaw a 8kHz
  • Asterisk: alaw (G.711a, comun en Europa), ulaw (G.711u, comun en Norteamerica), o linear con signo de 16 bits a 8kHz. El bridge auto-detecta el codec a partir del tamano del primer frame de AudioSocket.

La mayoria de los proveedores de STT y TTS esperan PCM 16-bit 16kHz mono. El orquestador realiza remuestreo en tiempo real en ambas direcciones:

  • Entrante: codec origen/8kHz -> PCM 16kHz 16-bit mono (para STT)
  • Saliente: PCM 16kHz 16-bit mono -> codec origen/8kHz (de vuelta al llamante)

Speech-to-Text (STT)

El proveedor STT recibe un iterador asincrono de fragmentos de audio PCM y produce fragmentos de texto transcritos. Se emiten tanto resultados parciales (provisionales) como finales. El orquestador utiliza los resultados de transcripcion finales para activar la generacion del LLM.

Large Language Model (LLM)

El proveedor LLM recibe el historial de conversacion (system prompt + todos los turnos anteriores) y genera una respuesta de texto en streaming. El orquestador detecta limites de oracion en el flujo y envia cada oracion completa al TTS inmediatamente, en lugar de esperar la respuesta completa. Esto reduce la latencia percibida.

El system prompt se enriquece con instrucciones de control de llamada. El LLM puede emitir marcadores especiales:

  • [END_CALL] -- Senala al orquestador que cuelgue la llamada
  • [VOICEMAIL] -- Senala que se detecto un contestador automatico (para llamadas salientes)

Text-to-Speech (TTS)

El proveedor TTS recibe texto y produce fragmentos de audio PCM en streaming. Estos se convierten de vuelta a mulaw y se envian a Twilio en tiempo real.

Barge-In

VOCALS soporta la interrupcion del llamante (barge-in). Un Detector de Actividad de Voz (VAD) monitorea el audio entrante. Cuando se detecta voz mientras el agente esta hablando, el orquestador:

  1. Detiene la reproduccion TTS actual
  2. Cancela los fragmentos TTS pendientes
  3. Reanuda el procesamiento STT para el habla del llamante
  4. Enruta la nueva transcripcion a traves del LLM

La sensibilidad del barge-in es configurable por agente (very_low, low, medium, high, very_high). El mensaje de bienvenida puede configurarse opcionalmente como no interrumpible.

Gestion de Sesiones

Sesiones Activas (Redis)

Mientras una llamada esta en curso, su estado se gestiona en Redis:

  • Historial de conversacion -- La lista en ejecucion de turnos de mensajes usuario/asistente
  • Metadatos de llamada -- Call SID, agent ID, referencias de proveedores, datos de tiempo
  • Cola de llamadas -- Llamadas salientes en espera de ser iniciadas (procesadas a 1 llamada por segundo)
  • Contadores de limite de tasa -- Conteos de solicitudes por API key (RPM y RPD)
  • Cache de configuracion -- Credenciales de Twilio y configuraciones de agente cacheadas por 60 segundos para evitar consultas a la base de datos en solicitudes concurrentes

Persistencia (PostgreSQL)

Despues de que una llamada termina, el orquestador escribe el registro final de la llamada en PostgreSQL (via Supabase):

  • Registros de llamadas -- Duracion, estado, resultado, transcripcion, uso de proveedores, metricas de latencia, ruta de grabacion, razon de error
  • Eventos STT -- Eventos del ciclo de vida de la conexion (abierta, cerrada, rechazada, timeout, error)
  • Registros de auditoria -- Registros con marca de tiempo de cada cambio de configuracion (operaciones de agente, proveedor, webhook, API key)

Todos los datos estan limitados a un tenant. El multi-tenancy se aplica a nivel de consulta con filtros tenant_id en cada operacion de base de datos.

Abstraccion de Proveedores

Cada etapa del pipeline (STT, LLM, TTS) esta definida por una clase base abstracta con implementaciones intercambiables:

class STTProvider(BaseProvider):
async def transcribe(self, audio_stream) -> AsyncIterator[str]: ...

class LLMProvider(BaseProvider):
async def generate(self, messages, system_prompt) -> AsyncIterator[str]: ...

class TTSProvider(BaseProvider):
async def synthesize(self, text) -> AsyncIterator[bytes]: ...

Los proveedores se registran en un registro central y se instancian en tiempo de ejecucion basandose en la configuracion del agente. Cada proveedor necesita:

  • Una API key (cifrada en reposo)
  • Un identificador de modelo
  • Configuracion especifica del proveedor (opcional)

Este patron te permite:

  • Cambiar proveedores por agente sin cambios de codigo
  • Hacer pruebas A/B con diferentes configuraciones de proveedores
  • Agregar nuevos proveedores implementando la interfaz base

Proveedores Registrados Actualmente

TipoProveedores
STTDeepgram, OpenAI, Whisper, ElevenLabs, Qwen, Fish Audio
LLMOpenAI, Claude, Google Gemini, Kimi
TTSDeepgram, OpenAI, ElevenLabs, Qwen, Resemble, Fish Audio

Manejo de Errores

El orquestador incluye registro estructurado de fallos y logica de reintentos:

  • Fallos de proveedor se registran con tenant ID, call ID, tipo/nombre del proveedor, mensaje de error y si se intento un reintento.
  • Degradacion elegante -- Si un proveedor falla durante una llamada, el sistema intenta continuar con un mensaje de error al llamante en lugar de cortar la llamada silenciosamente.
  • Deteccion de contestador automatico -- Para llamadas salientes, se utilizan multiples capas de deteccion:
    • Asterisk AMD (solo SIP generico) -- Analiza los primeros segundos de audio antes de que la llamada llegue al pipeline. Si se detecta una maquina, Asterisk cuelga inmediatamente.
    • Deteccion basada en LLM -- El LLM puede emitir un marcador [VOICEMAIL] si detecta patrones de contestador durante la conversacion.
    • Coincidencia de patrones regex -- Las transcripciones se verifican contra patrones comunes de saludos de contestador.