Transcripción de voz

La transcripción de voz te permite convertir tus datos de audio de transmisión en texto transcrito en tiempo real. Agent Assist hace sugerencias basadas en texto, por lo que los datos de audio se deben convertir antes de que se puedan usar. También puedes usar audio de transmisión transcrito con Customer Experience Insights para recopilar datos en tiempo real sobre las conversaciones de los agentes (por ejemplo, el modelado de temas).

Existen dos formas de transcribir audio de transmisión para usarlo con Agent Assist: con la función SIPREC o con llamadas gRPC con datos de audio como carga útil. En esta página, se describe el proceso de transcripción de datos de audio de transmisión con llamadas gRPC.

La transcripción de voz funciona con el reconocimiento de voz de transmisión de Speech-to-Text streaming speech recognition. Speech-to-Text ofrece varios modelos de reconocimiento, estándar y mejorados. Agent Assist no restringe los modelos que puedes usar con la transcripción de voz, pero esta se admite en el nivel de GA solo cuando se usa con el modelo de telefonía o Chirp 3. Para una calidad de transcripción óptima, se recomienda el modelo Chirp 3, sujeto a la disponibilidad regional.

Requisitos previos

Crea un perfil de conversación

Para crear un perfil de conversación, usa la consola de Agent Assist o llama directamente al método create en el ConversationProfile recurso.

Para la transcripción de voz, te recomendamos que configures ConversationProfile.stt_config como el InputAudioConfig predeterminado cuando envíes datos de audio en una conversación.

Obtén transcripciones en el tiempo de ejecución de la conversación

Para obtener transcripciones en el tiempo de ejecución de la conversación, debes crear participantes para la conversación y enviar datos de audio para cada uno de ellos.

Crea participantes

Existen tres tipos de participante. Consulta la documentación de referencia para obtener más detalles sobre sus funciones. Llama al método create en el participant y especifica el role. Solo un participante END_USER o HUMAN_AGENT puede llamar a StreamingAnalyzeContent, que es necesario para obtener una transcripción.

Envía datos de audio y obtén una transcripción

Puedes usar StreamingAnalyzeContent para enviar el audio de un participante a Google y obtener la transcripción con los siguientes parámetros:

  • La primera solicitud en la transmisión debe ser InputAudioConfig. (Los campos configurados aquí anulan la configuración correspondiente en ConversationProfile.stt_config). No envíes ninguna entrada de audio hasta la segunda solicitud.

    • audioEncoding debe establecerse en AUDIO_ENCODING_LINEAR_16 o AUDIO_ENCODING_MULAW.
    • model: Es el modelo de Speech-to-Text que quieres usar para transcribir tu audio. Establece este campo en chirp_3. La variante no afecta la calidad de la transcripción, por lo que puedes dejar Variante del modelo de voz sin especificar o elegir Usar la mejor disponible.
    • singleUtterance debe establecerse en false para obtener la mejor calidad de transcripción. No debes esperar END_OF_SINGLE_UTTERANCE si singleUtterance es false, pero puedes depender de isFinal==true dentro de StreamingAnalyzeContentResponse.recognition_result para cerrar la transmisión a la mitad.
    • Parámetros adicionales opcionales: Los siguientes parámetros son opcionales. Para obtener acceso a estos parámetros, comunícate con tu representante de Google.
      • languageCode: language_code del audio. El valor predeterminado es en-US.
      • alternativeLanguageCodes: Esta función solo está lista en el nivel de GA para el modelo Chirp 3. Idiomas adicionales que se pueden detectar en el audio. Agent Assist usa el campo language_code para detectar automáticamente el idioma al comienzo del audio y lo establece como predeterminado en todos los turnos de conversación siguientes. El campo alternativeLanguageCodes te permite especificar más opciones para que Agent Assist elija.
      • phraseSets: El nombre del recurso Speech-to-Text model adaptation phraseSet.
        • Para configurar la adaptación del modelo Chirp 3, agrega frases intercaladas separadas por líneas nuevas, sin comas.
        • Para usar la adaptación de modelos con otros modelos como telephony para la transcripción de voz, primero debes crear el phraseSet con la API de Speech-to-Text y especificar el nombre del recurso aquí.
  • Después de enviar la segunda solicitud con la carga útil de audio, deberías comenzar a recibir algunas StreamingAnalyzeContentResponses de la transmisión.

    • Puedes cerrar la transmisión a la mitad (o dejar de enviar en algunos idiomas como Python) cuando veas is_final establecido en true en StreamingAnalyzeContentResponse.recognition_result.
    • Después de cerrar la transmisión a la mitad, el servidor enviará la respuesta que contiene la transcripción final, junto con posibles sugerencias de Dialogflow o Agent Assist.
  • Puedes encontrar la transcripción final en las siguientes ubicaciones:

  • Inicia una transmisión nueva después de que se cierre la anterior.

    • Reenvío de audio: Los datos de audio generados después del último speech_end_offset de la respuesta con is_final=true a la nueva hora de inicio de la transmisión deben volver a enviarse a StreamingAnalyzeContent para obtener la mejor calidad de transcripción.
  • En el siguiente diagrama, se ilustra cómo funciona la transmisión.

Muestra de código de la solicitud de reconocimiento de transmisión

En la siguiente muestra de código, se ilustra cómo enviar una solicitud de transcripción de transmisión.

Python

Para autenticarte en Agent Assist, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Google Cloud Dialogflow API sample code using the StreamingAnalyzeContent
API.

Also please contact Google to get credentials of this project and set up the
credential file json locations by running:
export GOOGLE_APPLICATION_CREDENTIALS=<cred_json_file_location>

Example usage:
    export GOOGLE_CLOUD_PROJECT='cloud-contact-center-ext-demo'
    export CONVERSATION_PROFILE='FnuBYO8eTBWM8ep1i-eOng'
    export GOOGLE_APPLICATION_CREDENTIALS='/Users/ruogu/Desktop/keys/cloud-contact-center-ext-demo-78798f9f9254.json'
    python streaming_transcription.py

Then started to talk in English, you should see transcription shows up as you speak.

Say "Quit" or "Exit" to stop.
"""

import os
import re
import sys

from google.api_core.exceptions import DeadlineExceeded, OutOfRange

import pyaudio

from six.moves import queue

import conversation_management
import participant_management

PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT")
CONVERSATION_PROFILE_ID = os.getenv("CONVERSATION_PROFILE")

# Audio recording parameters
SAMPLE_RATE = 16000
CHUNK_SIZE = int(SAMPLE_RATE / 10)  # 100ms
RESTART_TIMEOUT = 160  # seconds
MAX_LOOKBACK = 3  # seconds
HALF_CLOSE_DURATION_MS = 90 * 1000  # milliseconds

YELLOW = "\033[0;33m"


class ResumableMicrophoneStream:
    """Opens a recording stream as a generator yielding the audio chunks."""

    def __init__(self, rate, chunk_size):
        self._rate = rate
        self.chunk_size = chunk_size
        self._num_channels = 1
        self._buff = queue.Queue()
        self.is_final = False
        self.closed = True
        # Count the number of times the stream analyze content restarts.
        self.restart_counter = 0
        self.last_start_time = 0
        # Time end of the last is_final in millisec since last_start_time.
        self.is_final_offset = 0
        # Save the audio chunks generated from the start of the audio stream for
        # replay after restart.
        self.audio_input_chunks = []
        self.new_stream = True
        self._audio_interface = pyaudio.PyAudio()
        self._audio_stream = self._audio_interface.open(
            format=pyaudio.paInt16,
            channels=self._num_channels,
            rate=self._rate,
            input=True,
            frames_per_buffer=self.chunk_size,
            # Run the audio stream asynchronously to fill the buffer object.
            # This is necessary so that the input device's buffer doesn't
            # overflow while the calling thread makes network requests, etc.
            stream_callback=self._fill_buffer,
        )

    def __enter__(self):
        self.closed = False
        return self

    def __exit__(self, type, value, traceback):
        self._audio_stream.stop_stream()
        self._audio_stream.close()
        self.closed = True
        # Signal the generator to terminate so that the client's
        # streaming_recognize method will not block the process termination.
        self._buff.put(None)
        self._audio_interface.terminate()

    def _fill_buffer(self, in_data, *args, **kwargs):
        """Continuously collect data from the audio stream, into the buffer in
        chunksize."""

        self._buff.put(in_data)
        return None, pyaudio.paContinue

    def generator(self):
        """Stream Audio from microphone to API and to local buffer"""
        try:
            # Handle restart.
            print("restart generator")
            # Flip the bit of is_final so it can continue stream.
            self.is_final = False
            total_processed_time = self.last_start_time + self.is_final_offset
            processed_bytes_length = (
                int(total_processed_time * SAMPLE_RATE * 16 / 8) / 1000
            )
            self.last_start_time = total_processed_time
            # Send out bytes stored in self.audio_input_chunks that is after the
            # processed_bytes_length.
            if processed_bytes_length != 0:
                audio_bytes = b"".join(self.audio_input_chunks)
                # Lookback for unprocessed audio data.
                need_to_process_length = min(
                    int(len(audio_bytes) - processed_bytes_length),
                    int(MAX_LOOKBACK * SAMPLE_RATE * 16 / 8),
                )
                # Note that you need to explicitly use `int` type for substring.
                need_to_process_bytes = audio_bytes[(-1) * need_to_process_length :]
                yield need_to_process_bytes

            while not self.closed and not self.is_final:
                data = []
                # Use a blocking get() to ensure there's at least one chunk of
                # data, and stop iteration if the chunk is None, indicating the
                # end of the audio stream.
                chunk = self._buff.get()

                if chunk is None:
                    return
                data.append(chunk)
                # Now try to the rest of chunks if there are any left in the _buff.
                while True:
                    try:
                        chunk = self._buff.get(block=False)

                        if chunk is None:
                            return
                        data.append(chunk)

                    except queue.Empty:
                        break
                self.audio_input_chunks.extend(data)
                if data:
                    yield b"".join(data)
        finally:
            print("Stop generator")


def main():
    """start bidirectional streaming from microphone input to Dialogflow API"""
    # Create conversation.
    conversation = conversation_management.create_conversation(
        project_id=PROJECT_ID, conversation_profile_id=CONVERSATION_PROFILE_ID
    )

    conversation_id = conversation.name.split("conversations/")[1].rstrip()

    # Create end user participant.
    end_user = participant_management.create_participant(
        project_id=PROJECT_ID, conversation_id=conversation_id, role="END_USER"
    )
    participant_id = end_user.name.split("participants/")[1].rstrip()

    mic_manager = ResumableMicrophoneStream(SAMPLE_RATE, CHUNK_SIZE)
    print(mic_manager.chunk_size)
    sys.stdout.write(YELLOW)
    sys.stdout.write('\nListening, say "Quit" or "Exit" to stop.\n\n')
    sys.stdout.write("End (ms)       Transcript Results/Status\n")
    sys.stdout.write("=====================================================\n")

    with mic_manager as stream:
        while not stream.closed:
            terminate = False
            while not terminate:
                try:
                    print(f"New Streaming Analyze Request: {stream.restart_counter}")
                    stream.restart_counter += 1
                    # Send request to streaming and get response.
                    responses = participant_management.analyze_content_audio_stream(
                        conversation_id=conversation_id,
                        participant_id=participant_id,
                        sample_rate_herz=SAMPLE_RATE,
                        stream=stream,
                        timeout=RESTART_TIMEOUT,
                        language_code="en-US",
                        single_utterance=False,
                        # Uncomment to process multiple utterances detected in the audio stream
                        # individually instead of stitching together to form a single utterance.
                        # output_multiple_utterances=True,
                    )

                    # Now, print the final transcription responses to user.
                    for response in responses:
                        if response.message:
                            print(response)
                        if response.recognition_result.is_final:
                            print(response)
                            # offset return from recognition_result is relative
                            # to the beginning of audio stream.
                            offset = response.recognition_result.speech_end_offset
                            stream.is_final_offset = int(
                                offset.seconds * 1000 + offset.microseconds / 1000
                            )
                            transcript = response.recognition_result.transcript
                            # Half-close upon final results for better streaming experiences
                            # (in Python just stop yielding requests)
                            if stream.is_final_offset > HALF_CLOSE_DURATION_MS:
                                stream.is_final = True
                            # Exit recognition if any of the transcribed phrase could be
                            # one of our keywords.
                            if re.search(r"\b(exit|quit)\b", transcript, re.I):
                                sys.stdout.write(YELLOW)
                                sys.stdout.write("Exiting...\n")
                                terminate = True
                                stream.closed = True
                                break
                except OutOfRange:
                    print("Maximum audio duration exceeded in the stream, restarting.")
                except DeadlineExceeded:
                    print("Deadline Exceeded, restarting.")

            if terminate:
                conversation_management.complete_conversation(
                    project_id=PROJECT_ID, conversation_id=conversation_id
                )
                break


if __name__ == "__main__":
    main()

Sigue estos pasos para ver los archivos de Python para conversation_management y participant_management.

  1. Navega al repositorio de GitHub para documentos de Python.

    Ir a GitHub

  2. Haz clic en Ir al archivo y, luego, ingresa el nombre del archivo: conversation_management o participant_management.

  3. Haz clic en Intro.

Prácticas recomendadas

La hora de envío del mensaje es cuando comienza una expresión. Usa la hora de envío del mensaje para determinar el orden en que tu centro de contacto muestra o analiza los mensajes de voz messages.