Dialogflow Essentials を使用して Android 用音声ボットを構築するフラッター

1. 始める前に

この Codelab では、シンプルな Dialogflow Essentials(ES)テキスト ボットと音声ボットを Flutter アプリに統合する方法を学びます。Dialogflow ES は、会話型 UI を構築するための開発スイートです。チャットボット、音声ボット、電話ゲートウェイなどです。同じツールで構築でき、20 以上の言語で複数のチャネルをサポートすることもできます。Dialogflow は、Google アシスタント、Slack、Facebook メッセンジャーなどの一般的な会話プラットフォームと統合されます。このようなプラットフォームのエージェントを作成する場合は、さまざまな統合オプションのうちの 1 つを使用する必要があります。ただし、モバイル デバイス用の chatbot を作成するには、カスタム インテグレーションを作成する必要があります。インテントを作成するには、基盤となる ML モデルをトレーニングするためのトレーニング フレーズを指定します。

このラボでは、一般的なクラウド デベロッパーが行う手順に沿って次の順序で演習を進めます。

  1. 環境の設定
  • Dialogflow: 新しい Dialogflow ES エージェントを作成する
  • Dialogflow: Dialogflow を構成する
  • Google Cloud: サービス アカウントを作成する
  1. Flutter: チャット アプリケーションの構築
  • Flutter プロジェクトを作成する
  • 設定と権限の構成
  • 依存関係を追加する
  • サービス アカウントへのリンク。
  • 仮想デバイスまたは物理デバイスでアプリを実行する
  1. Flutter: 音声文字変換をサポートするチャット インターフェースの構築
  • チャット インターフェースの作成
  • チャット インターフェースをリンクする
  • Dialogflow gRPC パッケージをアプリに統合する
  1. Dialogflow: Dialogflow エージェントのモデリング
  • ウェルカム インテントとフォールバック インテントを構成する
  • よくある質問のナレッジベースを活用する

前提条件

  • Dart/Flutter の基本的な経験
  • Google Cloud Platform の基本的な使用経験
  • Dialogflow ES の基本的な使用経験

作成するアプリの概要

この Codelab では、Dialogflow ツールに関するよくある質問に回答できるモバイル FAQ bot を作成する方法について説明します。エンドユーザーは、テキスト インターフェースを操作するか、モバイル デバイスの内蔵マイクから音声をストリーミングして、回答を得ることができます。

学習内容

  • Dialogflow Essentials で chatbot を作成する方法
  • Dialogflow gRPC パッケージを使用して Dialogflow を Flutter アプリに統合する方法
  • Dialogflow でテキスト インテントを検出する方法
  • マイクから Dialogflow に音声をストリーミングする方法
  • ナレッジベース コネクタを使用して公開 FAQ をインポートする方法
  • 仮想デバイスまたは物理デバイスのテキスト インターフェースと音声インターフェースを使用してチャットボットをテストする

必要なもの

  • Dialogflow エージェントを作成するには、Google ID または Gmail アドレスが必要です。
  • サービス アカウントをダウンロードするには、Google Cloud Platform へのアクセス権が必要です。
  • Flutter 開発環境

Flutter 開発環境をセットアップする

  1. Flutter をインストールするオペレーティング システムを選択します。
  1. Flutter でアプリを構築する際は、任意のテキスト エディタを Google のコマンドライン ツールと組み合わせて使用できます。ただし、このワークショップでは Android Studio を使用します。Android Studio 用の Flutter プラグインと Dart プラグインは、コード補完、構文のハイライト表示、ウィジェット編集支援、実行とデバッグのサポートなどを備えています。https://flutter.dev/docs/get-started/editor の手順に沿って操作します。

2. 環境設定

Dialogflow: 新しい Dialogflow ES エージェントを作成する

  1. を開きます。
  2. 左側のバーのロゴのすぐ下にあるプルダウンで、[Create New Agent] を選択します。([Global] と表示されているプルダウンはクリックしないでください。FAQ ナレッジベースを使用するには、グローバルな Dialogflow インスタンスが必要です)。
  3. エージェント名 yourname-dialogflow を指定します(自分の名前を使用します)。
  4. デフォルトの言語として [英語 - en] を選択します。
  5. デフォルトのタイムゾーンとして、最も近いタイムゾーンを選択します。
  6. [Mega Agent] は選択しないでください。(この機能を使用すると、サブエージェント間のオーケストレーションを行う包括的なエージェントを作成できます。ここでは必要ありません)。
  7. [作成] をクリックします。

[Create new project] 画面

Dialogflow を構成する

  1. 左側のメニューで、プロジェクト名の横にある歯車アイコンをクリックします。

新しいプロジェクトのプルダウンを作成する

  1. エージェントの説明として「Dialogflow FAQ Chatbot」と入力します。
  2. [ベータ版の機能を有効にする] のスイッチをオンにします。

Dialogflow Essentials V2Beta1

  1. [音声] タブをクリックし、[自動音声適応] ボックスがオンになっていることを確認します。
  2. 必要に応じて、最初のスイッチをオンにすることもできます。これにより、音声モデルが改善されますが、Dialogflow トライアルをアップグレードした場合にのみ使用できます。
  3. [保存] をクリックします。

Google Cloud: サービス アカウントを取得する

Dialogflow でエージェントを作成すると、Google Cloud コンソールに Google Cloud プロジェクトが作成されます。

  1. Google Cloud コンソールを開きます。
  2. Dialogflow と同じ Google アカウントでログインしていることを確認し、上部の青いバーでプロジェクト: yourname-dialogflow を選択します。
  3. 次に、上部のツールバーで Dialogflow API を検索し、プルダウンの [Dialogflow API] の結果をクリックします。

Dialogflow API の有効化

  1. 青い [管理] ボタンをクリックし、左側のメニューバーで [認証情報] をクリックします。(Dialogflow がまだ有効になっていない場合は、まず [有効にする] をクリックします)。

認証情報 GCP コンソール

  1. [認証情報を作成](画面上部)をクリックし、[サービス アカウント] を選択します。

認証情報を作成

  1. サービス アカウント名(flutter_dialogflow)、ID、説明を指定して、[作成] をクリックします。

サービス アカウントを作成する

  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 アプリケーションを作成する

これにより、マテリアル コンポーネントを含むサンプル アプリケーションが作成されます。

Android Studio により SDK がインストールされ、プロジェクトが作成されるまで待ちます。

設定と権限

  1. 使用する音声レコーダー ライブラリ sound_stream には、少なくとも 21 の minSdk が必要です。android/app/build.gradle の defaultConfig ブロックでこれを変更しましょう。(android フォルダには 2 つの build.gradle ファイルがありますが、app フォルダにあるものが正しいファイルです)。
defaultConfig {
   applicationId "com.myname.flutter_dialogflow_agent"
   minSdkVersion 21
   targetSdkVersion 30
   versionCode flutterVersionCode.toInteger()
   versionName flutterVersionName
}
  1. マイクへの権限を付与し、アプリがクラウドで実行されている Dialogflow エージェントにアクセスできるようにするには、app/src/main/AndroidManifest.xml ファイルに INTERNET 権限と RECORD_AUDIO 権限を追加する必要があります。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 ブロックにサービス アカウントを追加します。
flutter:
  uses-material-design: true
  assets:
    - assets/credentials.json

実機でアプリケーションを実行する

Android デバイスがある場合は、USB ケーブルでスマートフォンを接続して、デバイスでデバッグできます。Android デバイスの [開発者向けオプション] 画面で設定するには、こちらの手順に沿って操作します。

仮想デバイスでアプリを実行する

仮想デバイスでアプリケーションを実行する場合は、次の手順を行います。

  1. [Tools] > [AVD Manager] をクリックします。(または、下の図でピンク色でハイライト表示されているように、上部のツールバーから AVD Manager を選択します)。

Android Studio の上部ツールバー

  1. ターゲット Android 仮想デバイスを作成して、実機なしでアプリケーションをテストできるようにします。詳しくは、AVD の管理をご覧ください。新しい仮想デバイスを選択したら、ダブルクリックして起動します。

AVD を管理する

仮想デバイス

  1. Android Studio のメイン ツールバーで、プルダウンから ターゲットとして Android デバイスを選択し、main.dart が選択されていることを確認します。[実行] ボタン(緑色の三角形)を押します。

IDE の下部に、コンソールにログが表示されます。Android とスターター Flutter アプリがインストールされていることがわかります。これには 1 分ほどかかります。仮想デバイスの準備が整うと、変更が非常に高速になります。すべて完了すると、スターター Flutter アプリが開きます。

ボイラープレート アプリ

  1. チャットボット アプリのマイクを有効にしましょう。仮想デバイスのオプション ボタンをクリックして、オプションを開きます。[マイク] タブで、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] > [Stateful 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),
      ),
    );
  }
}

chat.dart ファイルで Widget build を検索します。これにより、chatbot インターフェースがビルドされます。このインターフェースには次のものが含まれます。

  • ユーザーと chatbot のすべてのチャット バルーンを含む ListView。この例では、アバターとテキストを含むチャット メッセージを作成する ChatMessage クラスを使用しています。
  • テキスト クエリを入力するための TextField
  • Dialogflow にテキストクエリを送信するための送信アイコン付きの IconButton
  • Dialogflow に音声ストリームを送信するためのマイク付きの IconButton。押すと状態が変わります。

チャット インターフェースをリンクする

  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] タブにテキスト クエリが記録されます。マイクボタンをタップして停止すると、[実行] タブに音声ストリームが記録されます。

Audio Stream ログ

これで、アプリケーションを Dialogflow と統合する準備が整いました。

Flutter アプリと Dialogflow_gRPC の統合

  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 インスタンスが作成されます。(credentials.jsonassets フォルダにあることを確認してください)。

Dialogflow gRPC の操作方法のデモではこれで問題ありませんが、本番環境のアプリでは、credentials.json ファイルをアセット フォルダに保存しないでください。これは安全とは言えません。

detectIntent 呼び出しを行う

  1. handleSubmitted() メソッドを見つけます。ここが魔法の場所です。TODO コメントのすぐ下に、次のコードを追加します。このコードは、ユーザーが入力したメッセージを ListView に追加します。
ChatMessage message = ChatMessage(
 text: text,
 name: "You",
 type: true,
);

setState(() {
 _messages.insert(0, message);
});
  1. 次に、前のコードのすぐ下に detectIntent 呼び出しを作成し、入力からテキストと 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デフォルトのウェルカム メッセージが表示されます。別の文字を入力すると、デフォルトのフォールバックが返されます。

streamingDetectIntent 呼び出しを行う

  1. 次に、handleStream() メソッドを見つけます。ここで、音声ストリーミングの魔法が起こります。最初の TODO のすぐ下に、音声モデルをバイアスする biasList を含む音声 InputConfigV2beta1 を作成します。スマートフォン(仮想デバイス)を使用しているため、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 セッションを保持する dialogflow オブジェクトの streamingDetectIntent メソッドを呼び出します。
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 との統合に最適なので、chatbot の会話の作成を開始できます。

5. Dialogflow: Dialogflow エージェントのモデリング

Dialogflow Essentials は、会話型 UI を構築するための開発スイートです。チャットボット、音声ボット、電話ゲートウェイなどです。同じツールで構築でき、20 以上の言語で複数のチャネルをサポートすることもできます。Dialogflow UX デザイナー(エージェント モデラー、言語学者)またはデベロッパーは、基盤となる ML モデルをトレーニングするためのトレーニング フレーズを指定してインテントを作成します。

インテントはユーザーの意図を分類します。Dialogflow ES エージェントごとに多数のインテントを定義することで、複数のインテントを結合して会話全体を処理できるようになります。各インテントには、パラメータとレスポンスを含めることができます。

インテントのマッチングは、インテント分類またはインテント マッチングとも呼ばれます。これは Dialogflow ES の主なコンセプトです。インテントが一致すると、レスポンスを返す、パラメータを収集する(エンティティ抽出)、Webhook コードをトリガーする(フルフィルメント)ことができます。たとえば、データベースからデータを取得できます。

エンドユーザーがチャットボットで何かを書いたり読んだりすると(ユーザー表現または発話と呼ばれます)、Dialogflow ES はトレーニング フレーズに基づいて、その表現を Dialogflow エージェントの最適なインテントと照合します。Dialogflow ES の基盤となる ML モデルは、これらのトレーニング フレーズでトレーニングされています。

Dialogflow ES は、コンテキストと呼ばれるコンセプトを使用します。人間と同じように、Dialogflow ES は 2 回目と 3 回目のターンでコンテキストを記憶できます。これにより、以前のユーザーの発話を追跡できます。

Dialogflow インテントの詳細はこちら

Default Welcome Intent を変更する

新しい Dialogflow エージェントを作成すると、2 つのデフォルト インテントが自動的に作成されます。Default Welcome Intent は、エージェントとの会話を開始したときに最初に到達するフローです。Default Fallback Intent は、エージェントがユーザーの発話を理解できない場合や、発話内容と一致するインテントが見つからない場合に実行されるフローです。

Default Welcome Intent のウェルカム メッセージは次のとおりです。

ユーザー

エージェント

お客様

「こんにちは。Dialogflow FAQ ボットです。Dialogflow に関する質問にお答えします。」

「何について知りたいですか?」

  1. [Intents > Default Welcome Intent] をクリックします。
  2. [Responses] まで下にスクロールします。
  3. すべてのテキスト回答をクリアします。
  4. デフォルトのタブで、次の 2 つのレスポンスを作成します。
  • こんにちは。Dialogflow FAQ ボットです。Dialogflow に関する質問にお答えします。知りたいトピックは何ですか?
  • こんにちは。Dialogflow FAQ ボットです。Dialogflow についてご質問はありますか?

構成は次のスクリーンショットのようになります。

Default Welcome Intent を編集する

  1. [保存] をクリックします。
  2. このインテントをテストしてみましょう。まず、Dialogflow シミュレータでテストできます。「Hello」と入力します。次のいずれかのメッセージが返されます。
  • こんにちは。Dialogflow FAQ ボットです。Dialogflow に関する質問にお答えします。知りたいトピックは何ですか?
  • こんにちは。Dialogflow FAQ ボットです。Dialogflow についてご質問はありますか?

デフォルトのフォールバック インテントを変更する

  1. [Intents] > [Default Fallback Intent] をクリックします。
  2. [Responses] まで下にスクロールします。
  3. すべてのテキスト回答をクリアします。
  4. デフォルトのタブで、次のレスポンスを作成します。
  • 申し訳ございませんが、この質問にはお答えできません。ウェブサイト(http://www.dialogflow.com)をご確認いただけましたでしょうか?
  1. [保存] をクリックします。

オンライン ナレッジベースへの接続

ナレッジ コネクタは、定義済みのインテントを補完します。ナレッジ ドキュメントを解析して、自動化レスポンスを見つけます。(たとえば、CSV ファイル、オンライン ウェブサイト、PDF ファイルの FAQ や記事など)。ナレッジ コネクタを構成するには、ナレッジ ドキュメントの集合であるナレッジベースを定義します。

ナレッジ コネクタの詳細

それでは作業を始めましょう。

  1. メニューで [ナレッジ](ベータ版)を選択します。

ナレッジベース

  1. 右側の青いボタン [Create Knowledge Base] をクリックします。
  2. ナレッジベース名として「Dialogflow FAQ」と入力し、[保存] をクリックします。
  3. [最初の 1 つを作成] リンクをクリックします。

ナレッジベースの最初の 1 つ

  1. ウィンドウが開きます。

次の構成を使用します。

ドキュメント名: DialogflowFAQ ナレッジの種類: FAQ MIME タイプ: text/html

  1. データを読み込む URL は次のとおりです。

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

  1. [作成] をクリックします。

ナレッジベースが作成されました。

ナレッジベースが作成されました

  1. [Responses] セクションまで下にスクロールし、[Add Response] をクリックします。

次の回答を作成して、[保存] をクリックします。

$Knowledge.Answer[1]
  1. [詳細を表示] をクリックします。

詳細を表示

  1. [自動再読み込みを有効にする] を選択して、FAQ ウェブページが更新されたときに変更を自動的に取得し、[保存] をクリックします。

これで、Dialogflow に実装したすべての FAQ が表示されます。簡単ですね。

よくある質問を含むオンライン HTML ウェブサイトを指定して、よくある質問をエージェントにインポートすることもできます。テキストのブロックを含む PDF をアップロードすることもできます。Dialogflow が質問を自動的に作成します。

FAQ は、インテント フローの横にエージェントに追加する「追加」と見なされるようになりました。ナレッジベースのよくある質問ではモデルをトレーニングできません。そのため、まったく異なる方法で質問すると、自然言語理解(ML モデル)が使用されないため、一致しない可能性があります。そのため、FAQ をインテントに変換する価値がある場合があります。

  1. 右側のシミュレータで質問をテストします。
  2. すべてが機能したら、Flutter アプリに戻り、この新しいコンテンツでチャットボットと音声ボットをテストします。Dialogflow に読み込んだ質問をします。

結果

6. 完了

おつかれさまです。これで、Dialogflow チャットボットを統合した最初の Flutter アプリを作成できました。

学習した内容

  • Dialogflow Essentials で chatbot を作成する方法
  • Dialogflow を Flutter アプリに統合する方法
  • Dialogflow でテキスト インテントを検出する方法
  • マイクから Dialogflow に音声をストリーミングする方法
  • ナレッジベース コネクタの使用方法

次のステップ

この Codelab はお役に立ちましたか?Dialogflow の優れたラボをご覧ください。

Dart/Flutter 用の Dialogflow gRPC パッケージの作成方法にご興味をお持ちですか?