Transcription vocale

La transcription vocale vous permet de convertir vos données audio en streaming en texte transcrit en temps réel. L'assistance de l'agent fait des suggestions basées sur du texte. Les données audio doivent donc être converties avant de pouvoir être utilisées. Vous pouvez également utiliser des données audio en streaming transcrites avec Customer Experience Insights pour collecter des données en temps réel sur les conversations des agents (par exemple, la modélisation de sujets).

Il existe deux façons de transcrire des données audio en streaming pour les utiliser avec l'assistance de l'agent : à l'aide de la fonctionnalité SIPREC ou en effectuant des appels gRPC avec des données audio comme charge utile. Cette page décrit le processus de transcription de données audio en streaming à l'aide d'appels gRPC.

La transcription vocale fonctionne à l'aide de la reconnaissance vocale en streaming Speech-to-Text streaming speech recognition. Speech-to-Text propose plusieurs modèles de reconnaissance, standards et améliorés. L'assistance de l'agent ne limite pas les modèles que vous pouvez utiliser avec la transcription vocale, mais celle-ci n'est compatible au niveau de la disponibilité générale que lorsqu'elle est utilisée avec le modèle de téléphonie ou Chirp 3. Pour une qualité de transcription optimale, le modèle Chirp 3 est recommandé, sous réserve de disponibilité régionale.

Prérequis

Créer un profil de conversation

Pour créer un profil de conversation, utilisez la console d'assistance de l'agent ou appelez directement la méthode create sur la ConversationProfile ressource.

Pour la transcription vocale, nous vous recommandons de configurer ConversationProfile.stt_config comme InputAudioConfig par défaut lorsque vous envoyez des données audio dans une conversation.

Obtenir des transcriptions lors de l'exécution de la conversation

Pour obtenir des transcriptions lors de l'exécution de la conversation, vous devez créer des participants pour la conversation et envoyer des données audio pour chaque participant.

Créer des participants

Il existe trois types de participant. Pour en savoir plus sur leurs rôles, consultez la documentation de référence. Appelez la méthode create sur le participant et spécifiez le role. Seul un participant END_USER ou HUMAN_AGENT peut appeler StreamingAnalyzeContent, ce qui est nécessaire pour obtenir une transcription.

Envoyer des données audio et obtenir une transcription

Vous pouvez utiliser StreamingAnalyzeContent pour envoyer l'audio d'un participant à Google et obtenir une transcription, avec les paramètres suivants :

  • La première requête du flux doit être InputAudioConfig. (Les champs configurés ici remplacent les paramètres correspondants dans ConversationProfile.stt_config.) N'envoyez aucune entrée audio avant la deuxième requête.

    • audioEncoding doit être défini sur AUDIO_ENCODING_LINEAR_16 ou AUDIO_ENCODING_MULAW.
    • model : modèle Speech-to-Text que vous souhaitez utiliser pour transcrire votre contenu audio. Définissez ce champ sur chirp_3. La variante n'a pas d'incidence sur la qualité de la transcription. Vous pouvez donc laisser Speech model variant (Variante du modèle de reconnaissance vocale) non spécifié ou choisir Use best available (Utiliser la meilleure option disponible).
    • singleUtterance doit être défini sur false pour une qualité de transcription optimale. Vous ne devez pas vous attendre à END_OF_SINGLE_UTTERANCE si singleUtterance est false, mais vous pouvez compter sur isFinal==true dans StreamingAnalyzeContentResponse.recognition_result pour fermer le flux à moitié.
    • Paramètres facultatifs supplémentaires : les paramètres suivants sont facultatifs. Pour accéder à ces paramètres, contactez votre représentant Google.
      • languageCode: language_code de l'audio. La valeur par défaut est en-US.
      • alternativeLanguageCodes: cette fonctionnalité n'est disponible au niveau de la disponibilité générale que pour le modèle Chirp 3. Autres langues susceptibles d'être détectées dans le contenu audio. L'assistance de l'agent utilise le champ language_code pour détecter automatiquement la langue au début du contenu audio et l'utilise par défaut dans tous les tours de conversation suivants. Le champ alternativeLanguageCodes vous permet de spécifier d'autres options parmi lesquelles l'assistance de l'agent peut choisir.
      • phraseSets : nom de ressource Speech-to-Text model adaptation phraseSet.
        • Pour configurer l'adaptation du modèle Chirp 3, ajoutez des expressions intégrées séparées par des sauts de ligne, sans virgules.
        • Pour utiliser l'adaptation de modèle avec d'autres modèles tels que telephony pour la transcription vocale, vous devez d'abord créer le phraseSet à l'aide de l'API Speech-to-Text et spécifier le nom de ressource ici.
  • Une fois que vous avez envoyé la deuxième requête avec la charge utile audio, vous devriez commencer à recevoir des StreamingAnalyzeContentResponses du flux.

    • Vous pouvez fermer le flux à moitié (ou arrêter l'envoi dans certaines langues comme Python) lorsque vous voyez is_final défini sur true dans StreamingAnalyzeContentResponse.recognition_result.
    • Une fois que vous avez fermé le flux à moitié, le serveur renvoie la réponse contenant la transcription finale, ainsi que des suggestions Dialogflow ou des suggestions d'assistance de l'agent potentielles.
  • Vous trouverez la transcription finale aux emplacements suivants :

  • Démarrez un nouveau flux une fois le flux précédent fermé.

    • Renvoyer l'audio : les données audio générées après le dernier speech_end_offset de la réponse avec is_final=true au nouvel horaire de début du flux doivent être renvoyées à StreamingAnalyzeContent pour une qualité de transcription optimale.
  • Le schéma suivant illustre le fonctionnement du flux.

Exemple de code de requête de reconnaissance en streaming

L'exemple de code suivant montre comment envoyer une requête de transcription en streaming.

Python

Pour vous authentifier auprès de l'assistance de l'agent, configurez les identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement 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()

Procédez comme suit pour afficher les fichiers Python pour conversation_management et participant_management.

  1. Accédez au dépôt GitHub pour les documents Python.

    Accéder à GitHub

  2. Cliquez sur Go to file (Accéder au fichier), puis saisissez le nom du fichier : conversation_management ou participant_management.

  3. Cliquez sur Entrée.

Bonnes pratiques

L'heure d'envoi du message correspond au début d'une énonciation. Utilisez l'heure d'envoi du message pour déterminer l'ordre dans lequel votre centre de contact affiche ou analyse les messages vocaux .