Detecta intents con transmisión de entrada de audio

En esta página, se muestra cómo transmitir una entrada de audio a una solicitud de intent de detección mediante la API. Dialogflow procesa el audio y lo convierte en texto antes de buscar una coincidencia del intent. Esta conversión se conoce como entrada de audio, reconocimiento de voz, voz a texto o STT.

Antes de comenzar

Esta función solo es aplicable cuando se usa la API para las interacciones del usuario final. Si usas una integración, puedes omitir esta guía.

Debes hacer lo siguiente antes de leer esta guía:

  1. Lee los conceptos básicos de Dialogflow.
  2. Realiza los pasos de configuración.

Crea un agente

Si aún no creaste un agente, sigue estos pasos para crearlo:

  1. Ve a la consola de Dialogflow.
  2. Si se te solicita, accede a la consola de Dialogflow. Consulta Descripción general de la consola de Dialogflow para obtener más información.
  3. Haz clic en Create agent, en el menú de la barra lateral izquierda. (Si ya tienes otros agentes, haz clic en el nombre del agente, desplázate hacia la parte inferior y haz clic en Create new agent).
  4. Ingresa el nombre del agente, y el idioma y la zona horaria predeterminados.
  5. Si ya creaste un proyecto, ingrésalo. Si quieres que la consola de Dialogflow cree el proyecto, selecciona Create a new Google project.
  6. Haz clic en el botón Create.

Importa el archivo de ejemplo al agente

En los pasos que se brindan en esta guía, se hacen suposiciones sobre tu agente, por lo que debes importar un agente preparado para esta guía. Cuando realizas la importación, en estos pasos se usa la opción restablecer, que reemplaza todas las opciones de configuración, intents y entidades del agente.

Para importar el archivo, sigue estos pasos:

  1. Descarga el archivo room-booking-agent.zip.
  2. Ve a la consola de Dialogflow.
  3. Selecciona el agente.
  4. Haz clic en el botón de configuración  que se encuentra junto al nombre del agente.
  5. Selecciona la pestaña Export and Import.
  6. Selecciona Restore From Zip y sigue las instrucciones para restablecer el archivo ZIP que descargaste.

Conceptos básicos de transmisión

El método streamingDetectIntent del tipo Session muestra un objeto de transmisión de gRPC bidireccional. Los métodos disponibles para este objeto varían según el lenguaje. Por lo tanto, si quieres obtener más información, consulta la documentación de referencia de tu biblioteca cliente.

El objeto de transmisión se usa para enviar y recibir datos de forma simultánea. Con este objeto, tu cliente transmite contenido de audio a Dialogflow, mientras escucha StreamingDetectIntentResponse de forma simultánea.

El método streamingDetectIntent tiene un parámetro query_input.audio_config.single_utterance que afecta el reconocimiento de voz de las siguientes maneras:

  • Si es false (predeterminado), el reconocimiento de voz no se detiene hasta que el cliente cierre la transmisión.
  • Si es true, Dialogflow detectará una sola declaración de voz en el audio de entrada. Cuando Dialogflow detecta que la voz del audio se detuvo o se pausó, cesa el reconocimiento de voz y envía una StreamingDetectIntentResponse con un resultado de reconocimiento de END_OF_SINGLE_UTTERANCE a tu cliente. Dialogflow ignora todos los archivos de audio que recibe después de la recepción de END_OF_SINGLE_UTTERANCE.

En la transmisión bidireccional, un cliente puede cerrar de forma parcial el objeto de transmisión para indicarle al servidor que no enviará más datos. Por ejemplo, en Java y Go, este método se llama closeSend. Es importante cerrar las transmisiones de forma parcial (pero no cancelarlas) en los siguientes casos:

  • Tu cliente ya terminó de enviar datos.
  • Tu cliente está configurado con single_utterance establecido en verdadero y recibe una StreamingDetectIntentResponse con un resultado de reconocimiento de END_OF_SINGLE_UTTERANCE.

Después de cerrar una transmisión, tu cliente debe iniciar otra solicitud con una transmisión nueva según sea necesario.

Transmite intents de detección

En las siguientes muestras, se usa el método streamingDetectIntent del tipo Session para transmitir audio.

C#

public static async Task<object> DetectIntentFromStreamAsync(
    string projectId,
    string sessionId,
    string filePath)
{
    var sessionsClient = SessionsClient.Create();
    var sessionName = SessionName.FromProjectSession(projectId, sessionId).ToString();

    // Initialize streaming call, retrieving the stream object
    var streamingDetectIntent = sessionsClient.StreamingDetectIntent();

    // Define a task to process results from the API
    var responseHandlerTask = Task.Run(async () =>
    {
        var responseStream = streamingDetectIntent.GetResponseStream();
        while (await responseStream.MoveNextAsync())
        {
            var response = responseStream.Current;
            var queryResult = response.QueryResult;

            if (queryResult != null)
            {
                Console.WriteLine($"Query text: {queryResult.QueryText}");
                if (queryResult.Intent != null)
                {
                    Console.Write("Intent detected:");
                    Console.WriteLine(queryResult.Intent.DisplayName);
                }
            }
        }
    });

    // Instructs the speech recognizer how to process the audio content.
    // Note: hard coding audioEncoding, sampleRateHertz for simplicity.
    var queryInput = new QueryInput
    {
        AudioConfig = new InputAudioConfig
        {
            AudioEncoding = AudioEncoding.Linear16,
            LanguageCode = "en-US",
            SampleRateHertz = 16000
        }
    };

    // The first request must **only** contain the audio configuration:
    await streamingDetectIntent.WriteAsync(new StreamingDetectIntentRequest
    {
        QueryInput = queryInput,
        Session = sessionName
    });

    using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
    {
        // Subsequent requests must **only** contain the audio data.
        // Following messages: audio chunks. We just read the file in
        // fixed-size chunks. In reality you would split the user input
        // by time.
        var buffer = new byte[32 * 1024];
        int bytesRead;
        while ((bytesRead = await fileStream.ReadAsync(
            buffer, 0, buffer.Length)) > 0)
        {
            await streamingDetectIntent.WriteAsync(new StreamingDetectIntentRequest
            {
                Session = sessionName,
                InputAudio = Google.Protobuf.ByteString.CopyFrom(buffer, 0, bytesRead)
            });
        };
    }

    // Tell the service you are done sending data
    await streamingDetectIntent.WriteCompleteAsync();

    // This will complete once all server responses have been processed.
    await responseHandlerTask;

    return 0;
}

Go

func DetectIntentStream(projectID, sessionID, audioFile, languageCode string) (string, error) {
	ctx := context.Background()

	sessionClient, err := dialogflow.NewSessionsClient(ctx)
	if err != nil {
		return "", err
	}
	defer sessionClient.Close()

	if projectID == "" || sessionID == "" {
		return "", errors.New(fmt.Sprintf("Received empty project (%s) or session (%s)", projectID, sessionID))
	}

	sessionPath := fmt.Sprintf("projects/%s/agent/sessions/%s", projectID, sessionID)

	// In this example, we hard code the encoding and sample rate for simplicity.
	audioConfig := dialogflowpb.InputAudioConfig{AudioEncoding: dialogflowpb.AudioEncoding_AUDIO_ENCODING_LINEAR_16, SampleRateHertz: 16000, LanguageCode: languageCode}

	queryAudioInput := dialogflowpb.QueryInput_AudioConfig{AudioConfig: &audioConfig}

	queryInput := dialogflowpb.QueryInput{Input: &queryAudioInput}

	streamer, err := sessionClient.StreamingDetectIntent(ctx)
	if err != nil {
		return "", err
	}

	f, err := os.Open(audioFile)
	if err != nil {
		return "", err
	}

	defer f.Close()

	go func() {
		audioBytes := make([]byte, 1024)

		request := dialogflowpb.StreamingDetectIntentRequest{Session: sessionPath, QueryInput: &queryInput}
		err = streamer.Send(&request)
		if err != nil {
			log.Fatal(err)
		}

		for {
			_, err := f.Read(audioBytes)
			if err == io.EOF {
				streamer.CloseSend()
				break
			}
			if err != nil {
				log.Fatal(err)
			}

			request = dialogflowpb.StreamingDetectIntentRequest{InputAudio: audioBytes}
			err = streamer.Send(&request)
			if err != nil {
				log.Fatal(err)
			}
		}
	}()

	var queryResult *dialogflowpb.QueryResult

	for {
		response, err := streamer.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		recognitionResult := response.GetRecognitionResult()
		transcript := recognitionResult.GetTranscript()
		log.Printf("Recognition transcript: %s\n", transcript)

		queryResult = response.GetQueryResult()
	}

	fulfillmentText := queryResult.GetFulfillmentText()
	return fulfillmentText, nil
}

Java


import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.BidiStream;
import com.google.cloud.dialogflow.v2.AudioEncoding;
import com.google.cloud.dialogflow.v2.InputAudioConfig;
import com.google.cloud.dialogflow.v2.QueryInput;
import com.google.cloud.dialogflow.v2.QueryResult;
import com.google.cloud.dialogflow.v2.SessionName;
import com.google.cloud.dialogflow.v2.SessionsClient;
import com.google.cloud.dialogflow.v2.StreamingDetectIntentRequest;
import com.google.cloud.dialogflow.v2.StreamingDetectIntentResponse;
import com.google.protobuf.ByteString;
import java.io.FileInputStream;
import java.io.IOException;

class DetectIntentStream {

  // DialogFlow API Detect Intent sample with audio files processes as an audio stream.
  static void detectIntentStream(String projectId, String audioFilePath, String sessionId)
      throws IOException, ApiException {
    // String projectId = "YOUR_PROJECT_ID";
    // String audioFilePath = "path_to_your_audio_file";
    // Using the same `sessionId` between requests allows continuation of the conversation.
    // String sessionId = "Identifier of the DetectIntent session";

    // Instantiates a client
    try (SessionsClient sessionsClient = SessionsClient.create()) {
      // Set the session name using the sessionId (UUID) and projectID (my-project-id)
      SessionName session = SessionName.of(projectId, sessionId);

      // Instructs the speech recognizer how to process the audio content.
      // Note: hard coding audioEncoding and sampleRateHertz for simplicity.
      // Audio encoding of the audio content sent in the query request.
      InputAudioConfig inputAudioConfig =
          InputAudioConfig.newBuilder()
              .setAudioEncoding(AudioEncoding.AUDIO_ENCODING_LINEAR_16)
              .setLanguageCode("en-US") // languageCode = "en-US"
              .setSampleRateHertz(16000) // sampleRateHertz = 16000
              .build();

      // Build the query with the InputAudioConfig
      QueryInput queryInput = QueryInput.newBuilder().setAudioConfig(inputAudioConfig).build();

      // Create the Bidirectional stream
      BidiStream<StreamingDetectIntentRequest, StreamingDetectIntentResponse> bidiStream =
          sessionsClient.streamingDetectIntentCallable().call();

      // The first request must **only** contain the audio configuration:
      bidiStream.send(
          StreamingDetectIntentRequest.newBuilder()
              .setSession(session.toString())
              .setQueryInput(queryInput)
              .build());

      try (FileInputStream audioStream = new FileInputStream(audioFilePath)) {
        // Subsequent requests must **only** contain the audio data.
        // Following messages: audio chunks. We just read the file in fixed-size chunks. In reality
        // you would split the user input by time.
        byte[] buffer = new byte[4096];
        int bytes;
        while ((bytes = audioStream.read(buffer)) != -1) {
          bidiStream.send(
              StreamingDetectIntentRequest.newBuilder()
                  .setInputAudio(ByteString.copyFrom(buffer, 0, bytes))
                  .build());
        }
      }

      // Tell the service you are done sending data
      bidiStream.closeSend();

      for (StreamingDetectIntentResponse response : bidiStream) {
        QueryResult queryResult = response.getQueryResult();
        System.out.println("====================");
        System.out.format("Intent Display Name: %s\n", queryResult.getIntent().getDisplayName());
        System.out.format("Query Text: '%s'\n", queryResult.getQueryText());
        System.out.format(
            "Detected Intent: %s (confidence: %f)\n",
            queryResult.getIntent().getDisplayName(), queryResult.getIntentDetectionConfidence());
        System.out.format("Fulfillment Text: '%s'\n", queryResult.getFulfillmentText());
      }
    }
  }
}

Node.js

const fs = require('fs');
const util = require('util');
const {Transform, pipeline} = require('stream');
const {struct} = require('pb-util');

const pump = util.promisify(pipeline);
// Imports the Dialogflow library
const dialogflow = require('@google-cloud/dialogflow');

// Instantiates a session client
const sessionClient = new dialogflow.SessionsClient();

// The path to the local file on which to perform speech recognition, e.g.
// /path/to/audio.raw const filename = '/path/to/audio.raw';

// The encoding of the audio file, e.g. 'AUDIO_ENCODING_LINEAR_16'
// const encoding = 'AUDIO_ENCODING_LINEAR_16';

// The sample rate of the audio file in hertz, e.g. 16000
// const sampleRateHertz = 16000;

// The BCP-47 language code to use, e.g. 'en-US'
// const languageCode = 'en-US';
const sessionPath = sessionClient.projectAgentSessionPath(
  projectId,
  sessionId
);

const initialStreamRequest = {
  session: sessionPath,
  queryInput: {
    audioConfig: {
      audioEncoding: encoding,
      sampleRateHertz: sampleRateHertz,
      languageCode: languageCode,
    },
    singleUtterance: true,
  },
};

// Create a stream for the streaming request.
const detectStream = sessionClient
  .streamingDetectIntent()
  .on('error', console.error)
  .on('data', data => {
    if (data.recognitionResult) {
      console.log(
        `Intermediate transcript: ${data.recognitionResult.transcript}`
      );
    } else {
      console.log('Detected intent:');

      const result = data.queryResult;
      // Instantiates a context client
      const contextClient = new dialogflow.ContextsClient();

      console.log(`  Query: ${result.queryText}`);
      console.log(`  Response: ${result.fulfillmentText}`);
      if (result.intent) {
        console.log(`  Intent: ${result.intent.displayName}`);
      } else {
        console.log('  No intent matched.');
      }
      const parameters = JSON.stringify(struct.decode(result.parameters));
      console.log(`  Parameters: ${parameters}`);
      if (result.outputContexts && result.outputContexts.length) {
        console.log('  Output contexts:');
        result.outputContexts.forEach(context => {
          const contextId = contextClient.matchContextFromProjectAgentSessionContextName(
            context.name
          );
          const contextParameters = JSON.stringify(
            struct.decode(context.parameters)
          );
          console.log(`    ${contextId}`);
          console.log(`      lifespan: ${context.lifespanCount}`);
          console.log(`      parameters: ${contextParameters}`);
        });
      }
    }
  });

// Write the initial stream request to config for audio input.
detectStream.write(initialStreamRequest);

// Stream an audio file from disk to the Conversation API, e.g.
// "./resources/audio.raw"
await pump(
  fs.createReadStream(filename),
  // Format the audio stream into the request format.
  new Transform({
    objectMode: true,
    transform: (obj, _, next) => {
      next(null, {inputAudio: obj});
    },
  }),
  detectStream
);

PHP

namespace Google\Cloud\Samples\Dialogflow;

use Google\Cloud\Dialogflow\V2\SessionsClient;
use Google\Cloud\Dialogflow\V2\AudioEncoding;
use Google\Cloud\Dialogflow\V2\InputAudioConfig;
use Google\Cloud\Dialogflow\V2\QueryInput;
use Google\Cloud\Dialogflow\V2\StreamingDetectIntentRequest;

/**
* Returns the result of detect intent with streaming audio as input.
* Using the same `session_id` between requests allows continuation
* of the conversation.
*/
function detect_intent_stream($projectId, $path, $sessionId, $languageCode = 'en-US')
{
    // need to use gRPC
    if (!defined('Grpc\STATUS_OK')) {
        throw new \Exception('Install the grpc extension ' .
            '(pecl install grpc)');
    }

    // new session
    $sessionsClient = new SessionsClient();
    $session = $sessionsClient->sessionName($projectId, $sessionId ?: uniqid());
    printf('Session path: %s' . PHP_EOL, $session);

    // hard coding audio_encoding and sample_rate_hertz for simplicity
    $audioConfig = new InputAudioConfig();
    $audioConfig->setAudioEncoding(AudioEncoding::AUDIO_ENCODING_LINEAR_16);
    $audioConfig->setLanguageCode($languageCode);
    $audioConfig->setSampleRateHertz(16000);

    // create query input
    $queryInput = new QueryInput();
    $queryInput->setAudioConfig($audioConfig);

    // first request contains the configuration
    $request = new StreamingDetectIntentRequest();
    $request->setSession($session);
    $request->setQueryInput($queryInput);
    $requests = [$request];

    // we are going to read small chunks of audio data from
    // a local audio file. in practice, these chunks should
    // come from an audio input device.
    $audioStream = fopen($path, 'rb');
    while (true) {
        $chunk = stream_get_contents($audioStream, 4096);
        if (!$chunk) {
            break;
        }
        $request = new StreamingDetectIntentRequest();
        $request->setInputAudio($chunk);
        $requests[] = $request;
    }

    // intermediate transcript info
    print(PHP_EOL . str_repeat("=", 20) . PHP_EOL);
    $stream = $sessionsClient->streamingDetectIntent();
    foreach ($requests as $request) {
        $stream->write($request);
    }
    foreach ($stream->closeWriteAndReadAll() as $response) {
        $recognitionResult = $response->getRecognitionResult();
        if ($recognitionResult) {
            $transcript = $recognitionResult->getTranscript();
            printf('Intermediate transcript: %s' . PHP_EOL, $transcript);
        }
    }

    // get final response and relevant info
    if ($response) {
        print(str_repeat("=", 20) . PHP_EOL);
        $queryResult = $response->getQueryResult();
        $queryText = $queryResult->getQueryText();
        $intent = $queryResult->getIntent();
        $displayName = $intent->getDisplayName();
        $confidence = $queryResult->getIntentDetectionConfidence();
        $fulfilmentText = $queryResult->getFulfillmentText();

        // output relevant info
        printf('Query text: %s' . PHP_EOL, $queryText);
        printf('Detected intent: %s (confidence: %f)' . PHP_EOL, $displayName,
            $confidence);
        print(PHP_EOL);
        printf('Fulfilment text: %s' . PHP_EOL, $fulfilmentText);
    }

    $sessionsClient->close();
}

Python

def detect_intent_stream(project_id, session_id, audio_file_path,
                         language_code):
    """Returns the result of detect intent with streaming audio as input.

    Using the same `session_id` between requests allows continuation
    of the conversation."""
    import dialogflow_v2 as dialogflow
    session_client = dialogflow.SessionsClient()

    # Note: hard coding audio_encoding and sample_rate_hertz for simplicity.
    audio_encoding = dialogflow.enums.AudioEncoding.AUDIO_ENCODING_LINEAR_16
    sample_rate_hertz = 16000

    session_path = session_client.session_path(project_id, session_id)
    print('Session path: {}\n'.format(session_path))

    def request_generator(audio_config, audio_file_path):
        query_input = dialogflow.types.QueryInput(audio_config=audio_config)

        # The first request contains the configuration.
        yield dialogflow.types.StreamingDetectIntentRequest(
            session=session_path, query_input=query_input)

        # Here we are reading small chunks of audio data from a local
        # audio file.  In practice these chunks should come from
        # an audio input device.
        with open(audio_file_path, 'rb') as audio_file:
            while True:
                chunk = audio_file.read(4096)
                if not chunk:
                    break
                # The later requests contains audio data.
                yield dialogflow.types.StreamingDetectIntentRequest(
                    input_audio=chunk)

    audio_config = dialogflow.types.InputAudioConfig(
        audio_encoding=audio_encoding, language_code=language_code,
        sample_rate_hertz=sample_rate_hertz)

    requests = request_generator(audio_config, audio_file_path)
    responses = session_client.streaming_detect_intent(requests)

    print('=' * 20)
    for response in responses:
        print('Intermediate transcript: "{}".'.format(
                response.recognition_result.transcript))

    # Note: The result from the last response is the final transcript along
    # with the detected content.
    query_result = response.query_result

    print('=' * 20)
    print('Query text: {}'.format(query_result.query_text))
    print('Detected intent: {} (confidence: {})\n'.format(
        query_result.intent.display_name,
        query_result.intent_detection_confidence))
    print('Fulfillment text: {}\n'.format(
        query_result.fulfillment_text))

Ruby

# project_id = "Your Google Cloud project ID"
# session_id = "mysession"
# audio_file_path = "resources/book_a_room.wav"
# language_code = "en-US"

require "google/cloud/dialogflow"
require "monitor"

session_client = Google::Cloud::Dialogflow.sessions
session = session_client.session_path project: project_id,
                                      session: session_id
puts "Session path: #{session}"

audio_config = {
  audio_encoding:    :AUDIO_ENCODING_LINEAR_16,
  sample_rate_hertz: 16_000,
  language_code:     language_code
}
query_input = { audio_config: audio_config }
streaming_config = { session: session, query_input: query_input }

# Set up a stream of audio data
request_stream = Gapic::StreamInput.new

# Initiate the call
response_stream = session_client.streaming_detect_intent request_stream

# Process the response stream in a separate thread
response_thread = Thread.new do
  response_stream.each do |response|
    if response.recognition_result
      puts "Intermediate transcript: #{response.recognition_result.transcript}\n"
    else
      # the last response has the actual query result
      query_result = response.query_result
      puts "Query text:        #{query_result.query_text}"
      puts "Intent detected:   #{query_result.intent.display_name}"
      puts "Intent confidence: #{query_result.intent_detection_confidence}"
      puts "Fulfillment text:  #{query_result.fulfillment_text}\n"
    end
  end
end

# The first request needs to be the configuration.
request_stream.push streaming_config

# Send chunks of audio data in the request stream
begin
  audio_file = File.open audio_file_path, "rb"
  loop do
    chunk = audio_file.read 4096
    break unless chunk
    request_stream.push input_audio: chunk
    sleep 0.5
  end
ensure
  audio_file.close
  # Close the request stream to signal that you are finished sending data.
  request_stream.close
end

# Wait until the response processing thread is complete.
response_thread.join

Ejemplos

Consulta la página de muestras para conocer las prácticas recomendadas sobre la transmisión desde un micrófono del navegador a Dialogflow.