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

1. 始める前に

この Codelab では、シンプルな Dialogflow Essentials(ES)テキスト bot と音声 bot を Flutter アプリに統合する方法を学びます。Dialogflow ES は、会話型 UI を構築するための開発スイートです。chatbot、voicebot、Phone Gateway がこれに該当します。すべて同じツールで作成でき、20 以上の言語で複数のチャンネルをサポートすることも可能です。Dialogflow は、Google アシスタント、Slack、Facebook Messenger などの一般的な会話プラットフォームと統合できます。これらのプラットフォームのいずれか用のエージェントを構築する場合は、多数の統合オプションのいずれかを使用する必要があります。ただし、モバイル デバイス用の chatbot を作成するには、カスタム統合を作成する必要があります。インテントを作成するには、基盤となる ML モデルをトレーニングするためのトレーニング フレーズを指定します。

このラボは、一般的なクラウド デベロッパー向けの

  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 に関する基本的な経験

作成するアプリの概要

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

学習内容

  • Dialogflow Essentials を使用して chatbot を作成する方法
  • Dialogflow gRPC パッケージを使用して Dialogflow を Flutter アプリに統合する方法
  • Dialogflow でテキスト インテントを検出する方法
  • マイク経由で Dialogflow に音声をストリーミングする方法
  • ナレッジベース コネクタを使用して一般公開のよくある質問をインポートする方法
  • 仮想デバイスまたは実機でテキストと音声のインターフェースを使用して chatbot をテストする

必要なもの

  • 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. デフォルトの言語として [English - en] を選択します。
  5. デフォルトのタイムゾーンとして、最も近いタイムゾーンを選択します。
  6. [Mega Agent] は選択しないでください。(この機能を使用すると、「サブ」エージェント間のオーケストレーションが可能な包括的なエージェントを作成できます。今は必要ありません)。
  7. [作成] をクリックします。

新しいプロジェクトの作成画面

Dialogflow を構成する

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

[新しいプロジェクトを作成] プルダウン

  1. エージェントの説明として「Dialogflow FAQ Chatbot」を入力します。
  2. ベータ版の機能を有効にする場合は、スイッチを切り替えます。

Dialogflow Essentials V2 ベータ版 1

  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 が必要です。そこで、defaultConfig ブロックの android/app/build.gradle でこれを変更してみましょう。(android フォルダには build.gradle ファイルが 2 つありますが、app フォルダにある 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 ファイルがありますが、メインフォルダには 1 つ必要です。この行は、マニフェスト タグの内側に追加できます。
<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. [ツール >AVD Manager。(または、下の図でピンクでハイライト表示されている上部のツールバーから AVD Manager を選択します)。

Android Studio 上部のツールバー

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

AVD を管理する

仮想デバイス

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

IDE の下部にあるコンソールにログが表示されます。Android とスターター Flutter アプリがインストールされることがわかります。この処理には 1 分ほどかかります。仮想デバイスの準備ができると、すぐに変更を加えることができます。すべて完了すると、スターターの Flutter アプリが開きます。

ボイラープレート アプリ

  1. chatbot アプリのマイクを有効にしてみましょう。仮想デバイスの [options] ボタンをクリックしてオプションを開きます。[Microphone] タブで 3 つのスイッチをすべて有効にします。

AVD オプション

  1. ホットリロードを使用して、変更がどれだけ速く行えるかを見てみましょう。

lib/main.dart で、MyApp クラスの MyHomePage titleFlutter 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 ファイルでウィジェット ビルドを検索します。これにより、以下を含む chatbot インターフェースが作成されます。

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

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

  1. main.dart を開き、Chat() インターフェースのみをインスタンス化するように Widget build を変更します。他のすべてのデモコードは削除できます。
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] タブにログが記録されたテキストクエリが表示されます。マイクボタンをタップして停止すると、[Run] タブに記録された音声ストリームが表示されます。

音声ストリームのログ

これで、アプリケーションを 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 インスタンスが作成されます。(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. では、前のコードのすぐ下で、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 を構築するための開発スイートです。chatbot、voicebot、Phone Gateway がこれに該当します。すべて同じツールで作成でき、20 以上の言語で複数のチャンネルをサポートすることも可能です。Dialogflow UX デザイナー(エージェント モデリング担当者、言語学者)やデベロッパーは、基盤となる ML モデルをトレーニングするためのトレーニング フレーズを指定することで、インテントを作成します。

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

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

エンドユーザーが chatbot で何かを書いたり話したりすると、ユーザー表現や発話と呼ばれます。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 に関するよくある質問 bot です。Dialogflow に関する質問に答えることができます。」

「知りたいことは何ですか?」

  1. [Intents(インテント)] >Default Welcome Intent(デフォルトのウェルカム インテント)
  2. [Responses] までスクロールします。
  3. すべての返信テキストをクリアします。
  4. デフォルト タブで、次の 2 つのレスポンスを作成します。
  • こんにちは。Dialogflow FAQ bot です。Dialogflow に関する質問にお答えします。どのようなことを知りたいですか?
  • やあ、私は Dialogflow に関するよくある質問 bot です。Dialogflow について質問はございますか?どのようなご用件でしょうか?

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

Default Welcome Intent を編集する

  1. [保存] をクリックします。
  2. このインテントをテストしましょう。まず、Dialogflow シミュレータでテストします。Type: Hello を指定します。次のいずれかのメッセージが返されます。
  • こんにちは。Dialogflow FAQ bot です。Dialogflow に関する質問にお答えします。どのようなことを知りたいですか?
  • やあ、私は Dialogflow に関するよくある質問 bot です。Dialogflow について質問はございますか?どのようなご用件でしょうか?

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

  1. [Intents(インテント)] >デフォルトのフォールバック インテント
  2. [Responses] までスクロールします。
  3. すべての返信テキストをクリアします。
  4. デフォルトタブで、次のレスポンスを作成します。
  • 残念ながら、この質問の答えがわかりません。弊社のウェブサイトはもうご覧になりましたか?http://www.dialogflow.com?
  1. [保存] をクリックします。

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

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

ナレッジ コネクタの詳細

試してみましょう。

  1. メニューから [Knowledge](ベータ版)を選択します。

ナレッジベース

  1. 右側の青いボタン [Create Knowledge Base] をクリックします。
  2. ナレッジベース名として入力します。Dialogflow のよくある質問] をクリックし、[保存] をクリックします。
  3. [Create the first one] リンクをクリックします。

まずはナレッジベース

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

次の構成を使用します。

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

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

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

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

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

ナレッジベースの作成

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

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

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

詳細を表示

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

Dialogflow に実装したよくある質問がすべて表示されます。簡単に行えます。

よくある質問を掲載したオンラインの HTML ウェブサイトをエージェントに提示して、よくある質問をインポートすることもできます。テキスト ブロックを含む PDF をアップロードすることも可能で、Dialogflow 自体が質問を生成します。

よくある質問を「補足情報」と見なす必要性追加することもできます。ナレッジベースに関するよくある質問ではモデルをトレーニングできません。そのため、まったく異なる方法で質問すると、自然言語理解(ML モデル)を活用しないため、適合しない可能性があります。そのため、FAQ をインテントに変換するメリットもあります。

  1. 右側のシミュレータで問題をテストします。
  2. すべて問題なく動作したら、Flutter アプリに戻り、この新しいコンテンツで chat bot と voicebot をテストします。Dialogflow に読み込んだ質問をします。

結果

6. 完了

これで、Dialogflow chatbot を統合した最初の Flutter アプリを作成できました。

学習した内容

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

次のステップ

この Codelab はいかがでしたか?優れた Dialogflow ラボをご覧ください。

Dart/Flutter 用の Dialogflow gRPC パッケージの構築についてご関心をお持ちの方は、