Observaciones en la elaboración de un asistente conversacional de voz mediante llamada telefónica

Presento aquí ciertos aspectos generales de mi trabajo con interfaces de voz automáticas

Una conversación tiene estos niveles (ISI):

Componentes del sistema:

  1. establecimiento de llamada entre participantes (infraestructura que permita alternancia de participantes: bot-usuario, técnico/agente-usuario)
  2. audio a texto en directo (streaming)
  3. texto a intención (clusterización con semejanza semántica)
  4. flujo conversacional del bot
  5. guión específico del dominio

Existen servicios en la nube que permiten implementar estas tareas. A continuación indicaré cómo implementé los tres últimos desde cero (en Python y con un editor de grafos).

En una interacción ideal el usuario se mantiene dentro del dominio que definen los objetivos de la interacción, mantiene un rol pasivo, no es ambigüo y cada expresión suya se corresponde con una intención ya anticipada en el diseño. Como quiera que tales escenarios no son frecuentes y que se complican especialmente si el usuario se siente confuso sobre la interacción o sus contenidos, indicaré a continuación algunas problemáticas de la automatización de las conversaciones.

2. Nivel de audio

Dificultades:

  1. latencias
  2. distinguir adecuadamente entre ruido y voz
  3. distinguir adecuadamente entre la voz de nuestro interlocutor y la de terceras personas cuya voz se oye.
    • Esto puede hacerse con biometría/diarización tras identificar al usuario
  4. Reconocimiento de voz preciso. Ver cómo responde a las ineficiencias del canal o de la articulación del usuario y a variedades de una lengua infrarrepresentadas (como probablemente las variedades no estándar) en el entrenamiento de su red neuronal.
  5. tener, lo más inmediatamente posible, reconocimientos parciales del usuario. De este modo se puede anticipar la intención del usuario según va profiriendo. Así sabemos hacia dónde va y la repercusión posible de su intervención para gestionar el solapamiento.

3. nivel de intención

La intención se obtiene usando una red neuronal entrenada para corresponder un corpus anotado de pares expresión-intención.

Dificultades:

4. nivel de flujo de la conversación

Dificultades:

El progreso en la interacción puede implementarse mediante un autómata finito. Y este se implementa a través de un grafo, que puede constituir un lenguaje de programación en sí mismo (con asignación, condición, user I/O, módulos...).

Este autómata debe definir contextos: aparte del contexto inmediato de cada nodo constituido por sus arcos, debe haber otros generales como por ejemplo los módulos de preguntas+respuestas o de gestión general de la interacción, etc... Y el contexto inmediato debe tener preferencia en la toma de decisiones sobre los generales en caso de competencia de arcos.

La secuencia de avance es:

  1. arco a nodo (el primer nodo START es de sección y este tipo de nodos no se ejecutan)
  2. arco nodo nodo: en caso de salto a sección

Los nodos de la propia interfaz de voz pueden definirse mediante lenguaje de marcado. En la siguiente notación se expresan metadatos <>, alternativas textuales / y variables {}. Las alternativas son importantes para que se sienta el bot menos artificial en caso de repeticiones, especialmente tras una interrupción.

<greeting>
encantad@ de saludarle, {user_name}
/
es un placer saludarle, {user_name}

Si usamos grabaciones de voz y, por eficiencia, concatenamos segmentos de voz la posición de la variable importa. Por ejemplo, en el caso de nombres resulta mucho más conveniente concatenar vocativos (posición inicial) que ubicar el nombre en posición final; esto es así porque el vocativo posee una entonación propia e interpone una breve pausa con el resto del texto por lo que, bien realizado, no hay que afinar la continuidad prosódica.

A continuación se enumeran los tipos de nodos y arcos del grafo:

nodos equivalente en python
información print()
pregunta input()
asignación (individual y múltiple mediante yaml) a = X
condición if elif ... else
sección y salto a sección def, return
directrices que alteran el funcionamiento del programa  
arcos  
no condicionados Se ejecutan en ausencia de otros (son equivalentes a else) o si los arcos condicionados, al ser evaluados, no superan cierto umbral mínimo
condicionados Las condiciones pueden ser múltiples y de distintos tipos: intenciones, comparaciones algebraicas, simples valores de verdad, etc...
temporizadores son arcos condicionados pero se inician sólo tras ejecutarse un paso arco-nodo. Sólo si expiran avanzan a su nodo de destino; se cancelan si antes de expirar se materializa algún otro avance. El objetivo de estos arcos es limitar el periodo de silencio del usuario, permitiendo efectos como preguntarle si sigue ahí y finalizar la llamada tras un tiempo mayor.
con punto de retorno establece un punto de retorno, en caso de que una rama finalice
sin nodo de destino finaliza la rama y retornan el cursor a un punto anterior (como un salto u otro nodo establecido explícitamente). Si la lista de puntos de retorno está vacía, se finalizará el bot.
colectores permite que varios nodos compartan parcial o totalmente la lógica de la toma de decisiones.