从音频输出中检测意图

应用通常需要聊天机器人来回复最终用户。Dialogflow 可以使用由 DeepMind WaveNet 提供支持的 Cloud Text-to-Speech 来生成代理的语音响应。将意图文本响应转换为音频的操作称为音频输出、语音合成、文字转语音,或 TTS

本指南提供了一个在检测意图时使用音频进行输入和输出的示例。在开发通过纯音频接口与用户沟通的应用时,可以经常见到这种用例。

如需查看受支持语言的列表,请参阅语言页面上的 TTS 列。

准备工作

此功能仅适用于使用 API 与最终用户互动的情况。如果您使用的是集成服务,则可以跳过本指南。

在阅读本指南之前,请先完成以下事项:

  1. 阅读 Dialogflow 基础知识
  2. 执行设置步骤

创建代理

如果尚未创建代理,请立即创建一个:

  1. 转到 Dialogflow ES 控制台
  2. 如果系统要求登录 Dialogflow 控制台,请登录。如需了解详情,请参阅 Dialogflow 控制台概览
  3. 点击左侧边栏菜单中的创建代理 (Create Agent)。如果您已有其他代理,请点击代理名称,滚动到底部,然后点击创建新代理 (Create new agent)。
  4. 输入您的代理名称、默认语言和默认时区。
  5. 如果您已经创建了项目,请输入该项目。如果要允许 Dialogflow 控制台创建项目,请选择创建新 Google 项目 (Create a new Google project)。
  6. 点击创建 (Create) 按钮。

将示例文件导入代理

本指南中的步骤对您的代理进行了假设,因此您需要导入为本指南准备的代理。 导入时,这些步骤使用“恢复”(restore) 选项,该选项会覆盖所有代理设置、意图和实体。

如需导入文件,请按以下步骤操作:

  1. 下载 room-booking-agent.zip 文件。
  2. 转到 Dialogflow ES 控制台
  3. 选择您的代理。
  4. 点击代理名称旁边的设置 按钮。
  5. 选择导出和导入 (Export and Import) 标签页。
  6. 选择从 ZIP 文件恢复 (Restore from ZIP),然后按照说明恢复下载的 zip 文件。

检测意图

如需检测意图,请对 Sessions 类型调用 detectIntent 方法。

REST 和命令行

1. 准备音频内容

下载 book-a-room.wav 示例 input_audio 文件,其中有语音提示“book a room”。此示例中的音频文件必须采用 base64 编码,以便能够在下面的 JSON 请求中添加该音频文件。 下面是 Linux 平台上的一个示例:

wget https://cloud.google.com/dialogflow/docs/data/book-a-room.wav
base64 -w 0 book-a-room.wav > book-a-room.b64

如需查看其他平台上的示例,请参阅 Cloud Speech API 文档中的嵌入采用 Base64 编码的音频

2. 发出检测意图请求

Sessions 类型调用 detectIntent 方法,并指定采用 base64 编码的音频。

在使用下面的请求数据之前,请先进行以下替换:

  • project-id:您的 GCP 项目 ID
  • base64-audio:上述输出文件中的 base64 内容

HTTP 方法和网址:

POST https://dialogflow.googleapis.com/v2/projects/project-id/agent/sessions/123456789:detectIntent

请求 JSON 正文:

{
  "queryInput": {
    "audioConfig": {
      "languageCode": "en-US"
    }
  },
  "outputAudioConfig" : {
    "audioEncoding": "OUTPUT_AUDIO_ENCODING_LINEAR_16"
  },
  "inputAudio": "base64-audio"
}

如需发送您的请求,请展开以下选项之一:

您应会收到如下所示的 JSON 响应:

{
  "responseId": "b7405848-2a3a-4e26-b9c6-c4cf9c9a22ee",
  "queryResult": {
    "queryText": "book a room",
    "speechRecognitionConfidence": 0.8616504,
    "action": "room.reservation",
    "parameters": {
      "time": "",
      "date": "",
      "duration": "",
      "guests": "",
      "location": ""
    },
    "fulfillmentText": "I can help with that. Where would you like to reserve a room?",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "I can help with that. Where would you like to reserve a room?"
          ]
        }
      }
    ],
    "intent": {
      "name": "projects/project-id/agent/intents/e8f6a63e-73da-4a1a-8bfc-857183f71228",
      "displayName": "room.reservation"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": "en-us"
  },
  "outputAudio": "UklGRs6vAgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AA..."
}

请注意,queryResult.action 字段的值为 room.reservation,而 outputAudio 字段则包含一个较长的 base64 音频字符串。

3.播放输出音频

复制 outputAudio 字段中的文本并将其保存在一个名为 output_audio.b64 的文件中。您需要将此文件转换为音频格式。下面是 Linux 平台上的一个示例:

base64 -d output_audio.b64 > output_audio.wav

如需查看其他平台上的示例,请参阅 Text-to-Speech API 文档中的解码采用 Base64 编码的音频内容

您现在可以播放 output_audio.wav 音频文件,并听到它与上述 queryResult.fulfillmentMessages[1].text.text[0] 字段中的文本相匹配。系统会选择第二个 fulfillmentMessages 元素,因为它是默认平台的文本响应。

Java


import com.google.api.gax.rpc.ApiException;
import com.google.cloud.dialogflow.v2.DetectIntentRequest;
import com.google.cloud.dialogflow.v2.DetectIntentResponse;
import com.google.cloud.dialogflow.v2.OutputAudioConfig;
import com.google.cloud.dialogflow.v2.OutputAudioEncoding;
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.TextInput;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class DetectIntentWithTextToSpeechResponse {

  public static Map<String, QueryResult> detectIntentWithTexttoSpeech(
      String projectId, List<String> texts, String sessionId, String languageCode)
      throws IOException, ApiException {
    Map<String, QueryResult> queryResults = Maps.newHashMap();
    // 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);
      System.out.println("Session Path: " + session.toString());

      // Detect intents for each text input
      for (String text : texts) {
        // Set the text (hello) and language code (en-US) for the query
        TextInput.Builder textInput =
            TextInput.newBuilder().setText(text).setLanguageCode(languageCode);

        // Build the query with the TextInput
        QueryInput queryInput = QueryInput.newBuilder().setText(textInput).build();

        //
        OutputAudioEncoding audioEncoding = OutputAudioEncoding.OUTPUT_AUDIO_ENCODING_LINEAR_16;
        int sampleRateHertz = 16000;
        OutputAudioConfig outputAudioConfig =
            OutputAudioConfig.newBuilder()
                .setAudioEncoding(audioEncoding)
                .setSampleRateHertz(sampleRateHertz)
                .build();

        DetectIntentRequest dr =
            DetectIntentRequest.newBuilder()
                .setQueryInput(queryInput)
                .setOutputAudioConfig(outputAudioConfig)
                .setSession(session.toString())
                .build();

        // Performs the detect intent request
        DetectIntentResponse response = sessionsClient.detectIntent(dr);

        // Display the query result
        QueryResult queryResult = response.getQueryResult();

        System.out.println("====================");
        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());

        queryResults.put(text, queryResult);
      }
    }
    return queryResults;
  }
}

Node.js

// Imports the Dialogflow client library
const dialogflow = require('@google-cloud/dialogflow').v2;

// Instantiate a DialogFlow client.
const sessionClient = new dialogflow.SessionsClient();

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'ID of GCP project associated with your Dialogflow agent';
// const sessionId = `user specific ID of session, e.g. 12345`;
// const query = `phrase(s) to pass to detect, e.g. I'd like to reserve a room for six people`;
// const languageCode = 'BCP-47 language code, e.g. en-US';
// const outputFile = `path for audio output file, e.g. ./resources/myOutput.wav`;

// Define session path
const sessionPath = sessionClient.projectAgentSessionPath(
  projectId,
  sessionId
);
const fs = require('fs');
const util = require('util');

async function detectIntentwithTTSResponse() {
  // The audio query request
  const request = {
    session: sessionPath,
    queryInput: {
      text: {
        text: query,
        languageCode: languageCode,
      },
    },
    outputAudioConfig: {
      audioEncoding: 'OUTPUT_AUDIO_ENCODING_LINEAR_16',
    },
  };
  sessionClient.detectIntent(request).then(responses => {
    console.log('Detected intent:');
    const audioFile = responses[0].outputAudio;
    util.promisify(fs.writeFile)(outputFile, audioFile, 'binary');
    console.log(`Audio content written to file: ${outputFile}`);
  });
}
detectIntentwithTTSResponse();

Python

def detect_intent_with_texttospeech_response(project_id, session_id, texts,
                                             language_code):
    """Returns the result of detect intent with texts as inputs and includes
    the response in an audio format.

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

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

    for text in texts:
        text_input = dialogflow.types.TextInput(
            text=text, language_code=language_code)

        query_input = dialogflow.types.QueryInput(text=text_input)

        # Set the query parameters with sentiment analysis
        output_audio_config = dialogflow.types.OutputAudioConfig(
            audio_encoding=dialogflow.enums.OutputAudioEncoding
            .OUTPUT_AUDIO_ENCODING_LINEAR_16)

        response = session_client.detect_intent(
            session=session_path, query_input=query_input,
            output_audio_config=output_audio_config)

        print('=' * 20)
        print('Query text: {}'.format(response.query_result.query_text))
        print('Detected intent: {} (confidence: {})\n'.format(
            response.query_result.intent.display_name,
            response.query_result.intent_detection_confidence))
        print('Fulfillment text: {}\n'.format(
            response.query_result.fulfillment_text))
        # The response's audio_content is binary.
        with open('output.wav', 'wb') as out:
            out.write(response.output_audio)
            print('Audio content written to file "output.wav"')

请参阅检测意图响应部分,了解相关响应字段的说明。

检测意图响应

检测意图请求的响应为 DetectIntentResponse 类型。

一般的检测意图处理功能会控制 DetectIntentResponse.queryResult.fulfillmentMessages 字段的内容。

DetectIntentResponse.outputAudio 字段所填充的音频取决于 DetectIntentResponse.queryResult.fulfillmentMessages 字段中所包含的默认平台文本响应的值。如果存在多个默认文本响应,在生成音频时将连接这些响应。如果不存在默认平台文本响应,则生成的音频内容将为空。

DetectIntentResponse.outputAudioConfig 字段使用用于生成输出音频的音频设置填充。

从音频流中检测意图

从音频流中检测意图时,您发出的请求类似于不使用输出音频的示例:从音频流中检测意图。不过,您需要在请求中提供 OutputAudioConfig 字段。在您从 Dialogflow API 服务器获得的最后一个流式传输响应中,output_audiooutput_audio_config 字段会填入相关值。如需了解详情,请参阅 StreamingDetectIntentRequestStreamingDetectIntentResponse

语音的代理设置

您可以控制语音合成的各个方面。请参阅代理语音设置

使用 Dialogflow 模拟器

您可以通过 Dialogflow 模拟器与代理进行互动并接收音频响应:

  1. 按照上述步骤启用自动文字转语音
  2. 在模拟器中输入或说出“预订房间”。
  3. 查看模拟器底部的输出音频 (output audio) 部分。