使用 Dialogflow Essentials 建構 Android 專用 Voice 機器人飄逸袖

1. 事前準備

在本程式碼研究室中,您將瞭解如何將簡單的 Dialogflow Essentials (ES) 文字和語音機器人整合至 Flutter 應用程式。Dialogflow ES 是可用於建構對話式 UI 的開發套件。例如聊天機器人、語音機器人、電話閘道。你可以使用同一個工具建構所有版本,甚至可以支援超過 20 種語言的多個頻道。Dialogflow 與許多常見的對話平台整合,例如 Google 助理、Slack 和 Facebook Messenger。如要為其中一個平台建構代理程式,建議您從眾多整合功能選項中擇一使用。不過,如要建立行動裝置適用的聊天機器人,就必須建立自訂整合項目。您必須指定訓練詞組來建立基礎機器學習模型,藉此建立意圖。

本研究室經過特別安排,以反映常見的雲端開發人員體驗:

  1. 環境設定
  • Dialogflow:建立新的 Dialogflow ES 虛擬服務專員
  • Dialogflow:設定 Dialogflow
  • Google Cloud:建立服務帳戶
  1. Flutter:建構即時通訊應用程式
  • 建立 Flutter 專案
  • 調整設定和權限
  • 新增依附元件
  • 連結至服務帳戶。
  • 在虛擬裝置或實體裝置上執行應用程式
  1. Flutter:運用 Speech to Text 支援功能建構即時通訊介面
  • 建立即時通訊介面
  • 連結即時通訊介面
  • 將 Dialogflow gRPC 套件整合至應用程式
  1. Dialogflow:建立 Dialogflow 虛擬服務專員模型
  • 設定歡迎頁面,備用意圖
  • 善用常見問題知識庫

修課條件

  • 基本的 Dart/Flutter 體驗
  • 基本的 Google Cloud Platform 體驗
  • 具備 Dialogflow ES 的基本經驗

建構項目

本程式碼研究室將說明如何建構行動常見問題機器人,能夠回答 Dialogflow 工具最常見的問題。使用者可以透過行動裝置內建的麥克風與文字介面互動,也可以串流播放語音訊息。

課程內容

  • 如何使用 Dialogflow Essentials 建立聊天機器人
  • 如何透過 Dialogflow gRPC 套件將 Dialogflow 整合至 Flutter 應用程式
  • 如何使用 Dialogflow 偵測文字意圖
  • 如何透過麥克風將語音串流至 Dialogflow
  • 如何使用知識庫連接器匯入公開常見問題
  • 進入虛擬或實體裝置,透過文字和語音介面測試聊天機器人

軟硬體需求

  • 您需要有 Google 身分 / Gmail 地址,才能建立 Dialogflow 虛擬服務專員。
  • 您必須具備 Google Cloud Platform 存取權,才能下載服務帳戶
  • Flutter 開發環境

設定 Flutter 開發環境

  1. 請選取要安裝 Flutter 的作業系統。
  1. 您可以使用任何文字編輯器搭配我們的指令列工具,透過 Flutter 建構應用程式。不過,本研討會將說明如何使用 Android Studio。Android Studio 的 Flutter 和 Dart 外掛程式提供程式碼補全、語法醒目顯示、小工具編輯輔助、執行和執行偵錯支援等按照 https://flutter.dev/docs/get-started/editor 中的步驟操作

2. 環境設定

Dialogflow:建立新的 Dialogflow ES 虛擬服務專員

  1. 開啟
  2. 在左側導覽列中,選取標誌下方的 [Create New Agent] (建立新代理程式)。(請注意,請勿點選「Global」(全域) 的下拉式選單,因為我們需要全域的 Dialogflow 執行個體,才能使用 FAQ 知識庫。)
  3. 指定虛擬服務專員名稱 yourname-dialogflow (使用自己的名稱)
  4. 將預設語言設為「英文 - en」
  5. 將時區設為預設時區,請選擇離你最近的時區。
  6. 「請勿」選取 Mega Agent。(有了這項功能,您就可以建立整體代理程式,並在「子」代理程式之間進行自動化調度管理,我們現在不需要這項資訊)。
  7. 按一下「建立」

建立新專案畫面

設定 Dialogflow

  1. 在左側選單中,按一下專案名稱旁邊的「齒輪」圖示。

建立新專案的下拉式選單

  1. 輸入下列虛擬服務專員說明:Dialogflow 常見問題聊天機器人
  2. 啟用 Beta 版功能:切換切換鈕。

Dialogflow Essentials V2Beta 版 1

  1. 點選「語音」分頁標籤,確認「自動語音調整」方塊已啟用。
  2. 您也可以視需要切換第一個開關,藉此改善語音模型,但只有在您升級 Dialogflow 試用版時才能使用此功能。
  3. 點選「儲存」

Google Cloud:取得服務帳戶

在 Dialogflow 中建立代理程式後,您應在 Google Cloud 控制台中建立 Google Cloud 專案。

  1. 開啟 Google Cloud 控制台:
  2. 請確認您用來登入的 Google 帳戶與 Dialogflow 相同,並且選取頂端藍色列中的「yourname-dialogflow」專案。
  3. 接著,在頂端工具列中搜尋 Dialogflow API,然後點選下拉式選單中的「Dialogflow API」結果。

啟用 Dialogflow API

  1. 按一下藍色的「管理」按鈕,然後點選左側選單列中的「憑證」。(如果尚未啟用 Dialogflow,請先按一下「啟用」)。

憑證 GCP 主控台

  1. 按一下畫面頂端的「建立憑證」,然後選擇「服務帳戶」

建立憑證

  1. 指定服務帳戶名稱:flutter_dialogflow、ID 和說明,然後按一下「Create」(建立)

建立服務帳戶

  1. 在步驟 2 中,您必須選取角色:Dialogflow API Admin、依序點選「繼續」和「完成」
  2. 按一下flutter_dialogflow服務帳戶,按一下「金鑰」金鑰分頁標籤,然後點選「新增金鑰」建立新的金鑰

建立金鑰

  1. 建立 JSON 金鑰。將其重新命名為 credentials.json,並儲存在硬碟的安全位置。稍後會用到這個名稱

JSON 金鑰

太好了,我們所有必要的工具都設定正確,現在我們可以開始在應用程式中整合 Dialogflow!

3. Flutter:建構即時通訊應用程式

建立樣板應用程式

  1. 開啟 Android Studio,然後選取「Start a new Flutter project」。
  2. 選取「Flutter Application」做為專案類型。然後點選「下一步」。
  3. 確認 Flutter SDK 路徑指定了 SDK 的位置 (若文字欄位空白,請選取「Install SDK...」)。
  4. 輸入專案名稱 (例如 flutter_dialogflow_agent)。然後點選「下一步」
  5. 修改套件名稱,然後按一下「Finish」

建立新的 Flutter 應用程式

這項操作會使用 Material 元件建立範例應用程式。

等待 Android Studio 安裝 SDK 並建立專案。

設定與權限

  1. 我們要使用的錄音工具庫 sound_stream,需要至少 21 的 minSdk。現在我們要在 defaultConfig 區塊的 android/app/build.gradle 中變更這一點。(請注意,android 資料夾中有 2 個 build.gradle 檔案,但應用程式資料夾中的檔案是正確的)。
defaultConfig {
   applicationId "com.myname.flutter_dialogflow_agent"
   minSdkVersion 21
   targetSdkVersion 30
   versionCode flutterVersionCode.toInteger()
   versionName flutterVersionName
}
  1. 如要授予麥克風權限,也允許應用程式連線至在雲端執行的 Dialogflow 代理程式,我們必須將 INTERNET 和 RECORD_AUDIO 權限新增至 app/src/main/AndroidManifest.xml 檔案。Flutter 專案中有多個 AndroidManifest.xml 檔案,但你需要在主資料夾中。您可以在資訊清單標記內新增這行程式碼。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />

新增依附元件

我們會使用 sound_streamrxdartdialogflow_grpc 套件。

  1. 新增 sound_stream 依附元件
$ flutter pub add sound_stream
Resolving dependencies...
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
  matcher 0.12.10 (0.12.11 available)
+ sound_stream 0.3.0
  test_api 0.4.2 (0.4.5 available)
  vector_math 2.1.0 (2.1.1 available)
Downloading sound_stream 0.3.0...
Changed 1 dependency!
  1. 新增 dialogflow_grpc 依附元件
flutter pub add dialogflow_grpc
Resolving dependencies...
+ archive 3.1.5
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
+ crypto 3.0.1
+ dialogflow_grpc 0.2.9
+ fixnum 1.0.0
+ googleapis_auth 1.1.0
+ grpc 3.0.2
+ http 0.13.4
+ http2 2.0.0
+ http_parser 4.0.0
  matcher 0.12.10 (0.12.11 available)
+ protobuf 2.0.0
  test_api 0.4.2 (0.4.5 available)
+ uuid 3.0.4
  vector_math 2.1.0 (2.1.1 available)
Downloading dialogflow_grpc 0.2.9...
Downloading grpc 3.0.2...
Downloading http 0.13.4...
Downloading archive 3.1.5...
Changed 11 dependencies!
  1. 新增 rxdart 依附元件
$ flutter pub add rxdart
Resolving dependencies...
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
  matcher 0.12.10 (0.12.11 available)
+ rxdart 0.27.2
  test_api 0.4.2 (0.4.5 available)
  vector_math 2.1.0 (2.1.1 available)
Downloading rxdart 0.27.2...
Changed 1 dependency!

正在載入服務帳戶和 Google Cloud 專案資訊

  1. 在專案中建立目錄,並命名為 assets
  2. 將您從 Google Cloud 控制台下載的 credentials.json 檔案移至 assets 資料夾。
  3. 開啟 pubspec.yaml 並將服務帳戶新增至流暢區塊。
flutter:
  uses-material-design: true
  assets:
    - assets/credentials.json

在實體裝置上執行應用程式

如果您有 Android 裝置,可以透過 USB 傳輸線將手機連接到手機,並在裝置上偵錯。如要設定這項功能,請按照這些步驟在 Android 裝置的「開發人員選項」畫面中操作。

在虛擬裝置上執行應用程式

如果您在虛擬裝置上執行應用程式,請按照下列步驟操作:

  1. 按一下 [工具] > AVD Manager:或者,從頂端工具列中選取 AVD Manager,如下圖中醒目顯示的粉紅色)

Android Studio 頂端工具列

  1. 我們將建立目標 Android 虛擬裝置,以便在沒有實體裝置的情況下測試應用程式。詳情請參閱「管理 AVD」。選取新的虛擬裝置後,請按兩下裝置以啟動該裝置。

管理 AVD

虛擬裝置

  1. 在 Android Studio 主要工具列中,透過下拉式選單選取 Android 裝置做為目標,並確認已選取「main.dart」。然後按下「Run」按鈕 (綠色三角形)。

IDE 底部會顯示主控台中的記錄檔。您會注意到安裝 Android 和入門 Flutter 應用程式。請稍候片刻,虛擬裝置準備就緒後,您就能快速進行變更。完成所有作業後,系統會開啟範例應用程式的 Flutter 應用程式。

樣板應用程式

  1. 現在請啟用聊天機器人應用程式的麥克風。按一下虛擬裝置的「options」按鈕,即可開啟選項。請前往麥克風標籤,啟用這 3 個開關。

AVD 選項

  1. 接著,我們來試用「熱門重新載入」功能,示範如何快速做出變更。

lib/main.dart 中,將 MyApp 類別中的 MyHomePage 標題變更為 Flutter Dialogflow Agent。將 primarySwatch 變更為 Colors.orange。

第一個程式碼

儲存檔案,或按一下 Android Studio 工具列中的閃動圖示。您應該會在虛擬裝置中看見直接所做的變更。

4. Flutter:透過 STT 支援功能建構即時通訊介面

建立即時通訊介面

  1. lib 資料夾中建立新的 Flutter 小工具檔案。(在 lib 資料夾上按一下滑鼠右鍵,依序點選「New」>「Flutter Widget」>「有狀態小工具」),呼叫這個檔案:chat.dart

將下列程式碼貼入這個檔案。這個 Dart 檔案會建立即時通訊介面。Dialogflow 目前尚未支援,只是所有元件的版面配置。整合麥克風元件以允許串流。檔案中的註解會指出,我們稍後要整合 Dialogflow。

// Copyright 2021 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.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sound_stream/sound_stream.dart';

// TODO import Dialogflow


class Chat extends StatefulWidget {
  Chat({Key key}) : super(key: key);

  @override
  _ChatState createState() => _ChatState();
}

class _ChatState extends State<Chat> {
  final List<ChatMessage> _messages = <ChatMessage>[];
  final TextEditingController _textController = TextEditingController();

  bool _isRecording = false;

  RecorderStream _recorder = RecorderStream();
  StreamSubscription _recorderStatus;
  StreamSubscription<List<int>> _audioStreamSubscription;
  BehaviorSubject<List<int>> _audioStream;

  // TODO DialogflowGrpc class instance

  @override
  void initState() {
    super.initState();
    initPlugin();
  }

  @override
  void dispose() {
    _recorderStatus?.cancel();
    _audioStreamSubscription?.cancel();
    super.dispose();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlugin() async {
    _recorderStatus = _recorder.status.listen((status) {
      if (mounted)
        setState(() {
          _isRecording = status == SoundStreamStatus.Playing;
        });
    });

    await Future.wait([
      _recorder.initialize()
    ]);



    // TODO Get a Service account

  }

  void stopStream() async {
    await _recorder.stop();
    await _audioStreamSubscription?.cancel();
    await _audioStream?.close();
  }

  void handleSubmitted(text) async {
    print(text);
    _textController.clear();

    //TODO Dialogflow Code

  }

  void handleStream() async {
    _recorder.start();

    _audioStream = BehaviorSubject<List<int>>();
    _audioStreamSubscription = _recorder.audioStream.listen((data) {
      print(data);
      _audioStream.add(data);
    });


    // TODO Create SpeechContexts
    // Create an audio InputConfig

    // TODO Make the streamingDetectIntent call, with the InputConfig and the audioStream
    // TODO Get the transcript and detectedIntent and show on screen

  }

  // The chat interface
  //
  //------------------------------------------------------------------------------------
  @override
  Widget build(BuildContext context) {
    return Column(children: <Widget>[
      Flexible(
          child: ListView.builder(
            padding: EdgeInsets.all(8.0),
            reverse: true,
            itemBuilder: (_, int index) => _messages[index],
            itemCount: _messages.length,
          )),
      Divider(height: 1.0),
      Container(
          decoration: BoxDecoration(color: Theme.of(context).cardColor),
          child: IconTheme(
            data: IconThemeData(color: Theme.of(context).accentColor),
            child: Container(
              margin: const EdgeInsets.symmetric(horizontal: 8.0),
              child: Row(
                children: <Widget>[
                  Flexible(
                    child: TextField(
                      controller: _textController,
                      onSubmitted: handleSubmitted,
                      decoration: InputDecoration.collapsed(hintText: "Send a message"),
                    ),
                  ),
                  Container(
                    margin: EdgeInsets.symmetric(horizontal: 4.0),
                    child: IconButton(
                      icon: Icon(Icons.send),
                      onPressed: () => handleSubmitted(_textController.text),
                    ),
                  ),
                  IconButton(
                    iconSize: 30.0,
                    icon: Icon(_isRecording ? Icons.mic_off : Icons.mic),
                    onPressed: _isRecording ? stopStream : handleStream,
                  ),
                ],
              ),
            ),
          )
      ),
    ]);
  }
}


//------------------------------------------------------------------------------------
// The chat message balloon
//
//------------------------------------------------------------------------------------
class ChatMessage extends StatelessWidget {
  ChatMessage({this.text, this.name, this.type});

  final String text;
  final String name;
  final bool type;

  List<Widget> otherMessage(context) {
    return <Widget>[
      new Container(
        margin: const EdgeInsets.only(right: 16.0),
        child: CircleAvatar(child: new Text('B')),
      ),
      new Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(this.name,
                style: TextStyle(fontWeight: FontWeight.bold)),
            Container(
              margin: const EdgeInsets.only(top: 5.0),
              child: Text(text),
            ),
          ],
        ),
      ),
    ];
  }

  List<Widget> myMessage(context) {
    return <Widget>[
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            Text(this.name, style: Theme.of(context).textTheme.subtitle1),
            Container(
              margin: const EdgeInsets.only(top: 5.0),
              child: Text(text),
            ),
          ],
        ),
      ),
      Container(
        margin: const EdgeInsets.only(left: 16.0),
        child: CircleAvatar(
            child: Text(
              this.name[0],
              style: TextStyle(fontWeight: FontWeight.bold),
            )),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: this.type ? myMessage(context) : otherMessage(context),
      ),
    );
  }
}

在 Widget build 的 chat.dart 檔案中搜尋 這個聊天機器人介面建構出以下內容:

  • ListView,包含使用者和聊天機器人傳送的所有即時通訊說明框。並透過 ChatMessage 類別建立包含顯示圖片和文字的即時通訊訊息。
  • 用於輸入文字查詢的 TextField
  • IconButton (含有傳送圖示),用於將文字查詢傳送至 Dialogflow
  • IconButton 和用於將音訊串流傳送至 Dialogflow 的麥克風,會在使用者按下按鈕後變更狀態。

連結即時通訊介面

  1. 開啟 main.dart 並變更 Widget build,只將 Chat() 介面例項化。其他示範代碼則可移除。
import 'package:flutter/material.dart';
import 'chat.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.orange,
      ),
      home: MyHomePage(title: 'Flutter Dialogflow Agent'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Chat())
    );
  }
}
  1. 執行應用程式。(如果應用程式先前啟動過,停止虛擬裝置,然後重新執行 main.dart)。首次使用即時通訊介面執行應用程式時。系統會顯示權限彈出式視窗,詢問你是否要授予麥克風權限。按一下「使用應用程式時」

權限

  1. 使用文字區域和按鈕玩遊戲。當您輸入文字查詢並按下 Enter 鍵或輕觸傳送按鈕時,您會在 Android Studio 的「Run」分頁中看到記錄的文字查詢。輕觸麥克風按鈕並停止時,您會在「執行」分頁中看到記錄的音訊串流。

音訊串流記錄

很好,現在可以將應用程式與 Dialogflow 整合了!

使用 Dialogflow_gRPC 整合 Flutter 應用程式

  1. 開啟 chat.dart 並新增下列匯入內容:
import 'package:dialogflow_grpc/dialogflow_grpc.dart';
import 'package:dialogflow_grpc/generated/google/cloud/dialogflow/v2beta1/session.pb.dart';
  1. 在檔案頂端的 // TODO DialogflowGrpcV2Beta1 class instance 下方,新增下列程式碼,來保存 Dialogflow 類別執行個體:
DialogflowGrpcV2Beta1 dialogflow;
  1. 搜尋 initPlugin() 方法,然後在 TODO 註解的下方新增下列程式碼:
    // Get a Service account
    final serviceAccount = ServiceAccount.fromString(
        '${(await rootBundle.loadString('assets/credentials.json'))}');
    // Create a DialogflowGrpc Instance
    dialogflow = DialogflowGrpcV2Beta1.viaServiceAccount(serviceAccount);

這會透過服務帳戶建立已授權 Google Cloud 專案的 Dialogflow 執行個體。(確認已在 assets 資料夾中建立 credentials.json

再次提醒,讓我們以示範瞭解如何使用 Dialogflow gRPC 進行操作,但以正式版應用程式來說,請勿將 credentials.json 檔案儲存在資產資料夾中,因為系統並不安全。

發出 DetectionIntent 呼叫

  1. 現在請找到 handleSubmitted() 方法,接著就能運用神奇指令。在 TODO 註解的正下方加入下列程式碼,此程式碼會將使用者輸入的訊息新增至 ListView:
ChatMessage message = ChatMessage(
 text: text,
 name: "You",
 type: true,
);

setState(() {
 _messages.insert(0, message);
});
  1. 現在,在先前的程式碼下方,我們會發出 DetectionIntent 呼叫、輸入內容中的文字和 languageCode。- 結果 (在 data.queryResult.fulfillment 內) 會顯示在 ListView 中:
DetectIntentResponse data = await dialogflow.detectIntent(text, 'en-US');
String fulfillmentText = data.queryResult.fulfillmentText;
if(fulfillmentText.isNotEmpty) {
  ChatMessage botMessage = ChatMessage(
    text: fulfillmentText,
    name: "Bot",
    type: false,
  );

  setState(() {
    _messages.insert(0, botMessage);
  });
}
  1. 啟動虛擬裝置,並測試偵測意圖呼叫。類型:hi應用程式應該以預設的歡迎訊息問候你。當你輸入其他內容時,系統會將預設備用方式傳回。

發出 streamDetectIntent 呼叫

  1. 現在請找到 handleStream() 方法,接下來就是音訊串流的重要功能。在第一個 TODO 下方,建立音訊 InputConfigV2beta1 並加入 bipList 調整語音模型。因為我們使用的是手機 (虛擬裝置),sampleHertz 為 16000,編碼為 Linear 16。這取決於你使用的電腦硬體 / 麥克風。我使用 Macbook 內建的麥克風,16000 顆星都很好看。(請參閱 https://pub.dev/packages/sound_stream 套件相關資訊)。
var biasList = SpeechContextV2Beta1(
    phrases: [
      'Dialogflow CX',
      'Dialogflow Essentials',
      'Action Builder',
      'HIPAA'
    ],
    boost: 20.0
);

    // See: https://cloud.google.com/dialogflow/es/docs/reference/rpc/google.cloud.dialogflow.v2#google.cloud.dialogflow.v2.InputAudioConfig
var config = InputConfigV2beta1(
    encoding: 'AUDIO_ENCODING_LINEAR_16',
    languageCode: 'en-US',
    sampleRateHertz: 16000,
    singleUtterance: false,
    speechContexts: [biasList]
);
  1. 接下來,我們會在 dialogflow 物件上呼叫 streamingDetectIntent 方法,該物件會保留我們的 Dialogflow 工作階段:
final responseStream = dialogflow.streamingDetectIntent(config, _audioStream);
  1. 透過 responseStream,我們最終可以監聽傳入的轉錄稿、偵測到的使用者查詢,以及偵測到的相符意圖回應。我們會在螢幕上的 ChatMessage 中列印此內容:
// Get the transcript and detectedIntent and show on screen
responseStream.listen((data) {
  //print('----');
  setState(() {
    //print(data);
    String transcript = data.recognitionResult.transcript;
    String queryText = data.queryResult.queryText;
    String fulfillmentText = data.queryResult.fulfillmentText;

    if(fulfillmentText.isNotEmpty) {

      ChatMessage message = new ChatMessage(
        text: queryText,
        name: "You",
        type: true,
      );

      ChatMessage botMessage = new ChatMessage(
        text: fulfillmentText,
        name: "Bot",
        type: false,
      );

      _messages.insert(0, message);
      _textController.clear();
      _messages.insert(0, botMessage);

    }
    if(transcript.isNotEmpty) {
      _textController.text = transcript;
    }

  });
},onError: (e){
  //print(e);
},onDone: () {
  //print('done');
});

這樣就完成了,請啟動您的應用程式,並在虛擬裝置上測試應用程式,按下麥克風按鈕,然後說出:「Hello」(你好)

這會觸發 Dialogflow 預設歡迎意圖。結果會顯示在螢幕上。現在 Flutter 能夠與 Dialogflow 整合搭配運作,我們就能開始開發聊天機器人的對話。

5. Dialogflow:建立 Dialogflow 虛擬服務專員模型

Dialogflow Essentials 是用於建構對話式 UI 的開發套件。例如聊天機器人、語音機器人、電話閘道。你可以使用同一個工具建構所有版本,甚至可以支援超過 20 種語言的多個頻道。Dialogflow 使用者體驗設計人員 (代理程式建模者、語言學家) 或開發人員藉由指定訓練詞組來訓練基礎機器學習模型,藉此建立意圖。

意圖會將使用者的意圖分類。針對每個 Dialogflow ES 代理程式,您可以定義多個意圖,讓合併的意圖可以處理完整對話。每個意圖都可以包含參數和回應。

比對意圖的方式也稱為意圖分類或意圖比對。這是 Dialogflow ES 的主要概念。意圖比對相符後,就會傳回回應、收集參數 (實體擷取) 或觸發 Webhook 程式碼 (執行要求),以便從資料庫擷取資料。

使用者在聊天機器人中撰寫或說話的內容時,稱為使用者運算式或語音內容,Dialogflow ES 會根據訓練詞組,將運算式與 Dialogflow 代理程式的最佳意圖配對。一切就是運用這些訓練詞組訓練 Dialogflow ES 機器學習模型。

Dialogflow ES 使用稱為「背景資訊」的概念。Dialogflow ES 就如同人類,在第 2 回合和第 3 次回合中記住背景資訊。也藉此追蹤先前的使用者話語。

進一步瞭解 Dialogflow 意圖

修改預設歡迎意圖

建立新的 Dialogflow 代理程式時,系統會自動建立兩個預設意圖。預設歡迎意圖是您與代理程式開始對話時進入的第一個流程。「預設備用意圖」是指在代理程式無法理解您,或無法將意圖與您剛剛說的內容配對時,您所使用的流程。

以下是預設歡迎意圖的歡迎訊息:

使用者

代理程式

你好

「您好,我是 Dialogflow 常見問題機器人,我可以回答 Dialogflow 中的問題。」

「您想瞭解什麼資訊?」

  1. 按一下意圖 >預設歡迎意圖
  2. 向下捲動至「回應」
  3. 清除所有簡訊回應。
  4. 在預設分頁中,建立下列 2 個回應:
  • 您好,我是 Dialogflow 常見問題機器人,我可以回答 Dialogflow 中的問題。您想瞭解什麼資訊?
  • 你好,我是 Dialogflow 常見問題機器人,對 Dialogflow 有任何疑問嗎?請問我能幫上什麼忙?

設定應類似於這張螢幕截圖。

編輯預設歡迎意圖

  1. 點選「儲存」
  2. 我們來測試這項意圖。首先,我們可以在 Dialogflow emulator.Type: Hello 中測試模型。瀏覽器應該會傳回下列其中一則訊息:
  • 您好,我是 Dialogflow 常見問題機器人,我可以回答 Dialogflow 中的問題。您想瞭解什麼資訊?
  • 你好,我是 Dialogflow 常見問題機器人,對 Dialogflow 有任何疑問嗎?請問我能幫上什麼忙?

修改預設備用意圖

  1. 按一下意圖 >預設備用意圖
  2. 向下捲動至「回應」
  3. 清除所有簡訊回應。
  4. 在預設分頁中,建立以下回應:
  • 很抱歉,我不知道這個問題的答案。您是否查看過我們的網站?http://www.dialogflow.com?
  1. 點選「儲存」

連線至線上知識庫

知識連接器可與已定義的意圖互補。專家會剖析知識文件,找出自動回覆。(例如 CSV 檔案、線上網站,甚至是 PDF 檔案的常見問題或文章)。如要設定知識庫,您必須定義一或多個知識庫 (即知識文件集合)。

進一步瞭解知識連接器

我們來試試。

  1. 選取選單中的「Knowledge」(Beta 版)

知識庫

  1. 按一下右側藍色按鈕:Create Knowledge Base
  2. 輸入知識庫名稱;Dialogflow 常見問題,然後按一下「儲存」
  3. 按一下「建立第一個連結」連結。

知識庫第一

  1. 開啟視窗後,

請使用下列設定:

文件名稱:DialogflowFAQ知識類型:FAQ Mime 類型:text/html

  1. 我們載入資料的來源網址為:

https://www.leeboonstra.dev/faqs/

  1. 按一下 [建立]

已建立知識庫:

已建立知識庫

  1. 向下捲動至「Responses」(回應) 部分,然後按一下「Add Response」(新增回應)

建立下列答案,然後按一下「儲存」

$Knowledge.Answer[1]
  1. 按一下「查看詳細資料」

查看詳細資料

  1. 選取「啟用自動重新載入」,即可在常見問題網頁更新時自動擷取變更,然後按一下「儲存」

這裡會顯示你在 Dialogflow 中實作的所有常見問題。很簡單,

請注意,您也可以連結至線上 HTML 網站,查看常見問題,將常見問題匯入代理程式。你甚至可以上傳含有一段文字的 PDF 檔案,Dialogflow 會提出問題。

現在常見問題應顯示為「額外項目」新增至代理程式,就在意圖流程旁邊。知識庫常見問題無法訓練模型。因此,以截然不同的方式提問,可能會因為沒有使用自然語言理解 (機器學習模型) 而成為相符項目。因此,有時建議你將常見問題轉化為意圖。

  1. 請在右側的模擬工具中測試問題。
  2. 確認一切順利後,請返回 Flutter 應用程式,利用這些新內容測試即時通訊和語音機器人!詢問您載入至 Dialogflow 的問題。

結果

6. 恭喜

恭喜!您已成功透過 Dialogflow 聊天機器人整合功能建立第一個 Flutter 應用程式!

涵蓋內容

  • 如何使用 Dialogflow Essentials 建立聊天機器人
  • 如何將 Dialogflow 整合至 Flutter 應用程式
  • 如何使用 Dialogflow 偵測文字意圖
  • 如何透過麥克風將語音串流至 Dialogflow
  • 如何使用知識庫連接器

後續步驟

喜歡這個程式碼研究室嗎?快去看看這些出色的 Dialogflow 研究室吧!

想知道我如何為 Dart/Flutter 建構 Dialogflow gRPC 套件嗎?