REJCOB
← Todo el trabajo

Pipeline de inteligencia de llamadas

De un webhook de telefonía a un registro buscable: transcripción, extracción validada por esquema y búsqueda full-text sobre cada conversación.

Rol
Arquitectura y diseño
Período
2026
Empresa
Proyecto personal

Async

el webhook se confirma antes de cualquier procesamiento

Schema

salida del LLM validada — campos tipados, no texto libre

Staged

cada etapa re-ejecutable sin repetir las demás

#Contexto

Los equipos que trabajan por teléfono — soporte, ventas, clínicas, inmobiliarias — generan horas de audio que nadie vuelve a escuchar. Una transcripción simple apenas mejora: es larga y no se puede buscar por significado. Lo útil es un registro corto y estructurado por llamada que se pueda filtrar y buscar. Este diseño es un pipeline sobre telefonía hospedada por el proveedor que produce exactamente eso, usando grabaciones sintéticas como datos de prueba.

#Problema y restricciones

  • La transcripción tarda más de lo que permite el timeout de un webhook, así que el trabajo no puede ocurrir en el request.
  • Los resúmenes de LLM en texto libre no se pueden filtrar, contar, ni se puede confiar en que tengan la misma forma dos veces.
  • La transcripción es el paso caro; mejorar el prompt de extracción no debería implicar pagarla otra vez.

#Arquitectura

Twiliorecording callbackBullMQack firstWhispertimestamped segmentsLLM extractionJSON schemaPostgreSQLstages + full-textCall explorerReact · filter & search
Un callback de grabación se abre en tres etapas persistidas: transcribir, extraer, indexar.

Cuando la grabación de una llamada está lista, Twilio envía un status callback; el endpoint verifica la firma, guarda el evento y encola un job en BullMQ. El pipeline corre como tres etapas independientes, cada una persistiendo su salida en PostgreSQL: (1) descargar la grabación y transcribirla con Whisper, conservando timestamps por segmento; (2) enviar la transcripción a un LLM con un esquema JSON — tema, intención del cliente, objeciones planteadas, compromisos asumidos, siguiente paso, sentimiento — y rechazar cualquier salida que no pase la validación; (3) indexar la transcripción con búsqueda full-text. Como cada etapa guarda su resultado, cambiar el esquema de extracción significa re-ejecutar la etapa 2 sobre las transcripciones guardadas sin tocar el audio. Una vista en React lista las llamadas con sus campos estructurados, filtra por cualquiera de ellos, y salta de un resultado de búsqueda al momento correspondiente de la transcripción.

#Decisiones clave y tradeoffs

01Extracción validada por esquema sobre resúmenes

Decisión
Pedir al LLM un objeto JSON tipado y validarlo, reintentando ante fallo, en vez de guardar un resumen en prosa.
Alternativas
Un párrafo de resumen por llamada, o reglas de keywords sobre la transcripción.
Por qué
Los campos tipados se pueden filtrar, agregar y verificar; un párrafo solo se puede leer. La validación convierte una respuesta mal formada en un reintento en vez de datos malos.
Costo de equivocarse
El esquema hay que diseñarlo de entrada y versionarlo; cambiarlo implica reprocesar llamadas antiguas.

02Etapas independientes y persistidas

Decisión
Separar transcripción, extracción e indexado en jobs distintos que guardan cada uno su salida.
Alternativas
Un solo job que hace todo desde el audio hasta el registro final.
Por qué
Un fallo en una etapa tardía no repite la etapa cara inicial, y los prompts se pueden iterar barato contra transcripciones guardadas.
Costo de equivocarse
Más piezas móviles y estado intermedio que seguir por llamada.

#Resultados

6 fields

por llamada: tema, intención, objeciones, compromisos, siguiente paso, sentimiento

Search

full-text sobre transcripciones, enlazado al timestamp

Re-run

de extracción sobre transcripciones guardadas sin re-transcribir

#Qué haría diferente

El punto débil es la separación de hablantes: sin ella, 'quién se comprometió a qué' es una suposición del LLM. El siguiente paso son grabaciones de doble canal para transcribir cada lado por separado, y luego medir la tasa de error de palabras y la precisión de extracción sobre un set etiquetado de llamadas sintéticas.

#Stack

TwilioWhisperOpenAIBullMQPostgreSQLNode.jsTypeScriptReact