สร้างบ็อตเสียงสำหรับ Android ด้วย Dialogflow Essentials & แขนระบาย

1. ก่อนเริ่มต้น

ใน Codelab นี้ คุณจะได้เรียนรู้วิธีผสานรวมข้อความและบ็อตเสียงจาก Dialogflow Essentials (ES) แบบง่ายๆ เข้ากับแอป Flutter Dialogflow ES เป็นชุดโปรแกรมการพัฒนาสำหรับการสร้าง UI แบบสนทนา ไม่ว่าจะเป็นแชทบ็อต บ็อตเสียง เกตเวย์โทรศัพท์ คุณสามารถสร้างแอปด้วยเครื่องมือเดียวกัน หรือรองรับช่องหลายช่องในภาษาต่างๆ กว่า 20 ภาษา Dialogflow ผสานรวมกับแพลตฟอร์มการสนทนายอดนิยมมากมาย เช่น Google Assistant, Slack และ Facebook Messenger หากต้องการสร้าง Agent สำหรับแพลตฟอร์มใดแพลตฟอร์มหนึ่งดังกล่าว คุณควรใช้ตัวเลือกการผสานรวมที่มีอยู่หลายรายการ อย่างไรก็ตาม หากต้องการสร้างแชทบ็อตสำหรับอุปกรณ์เคลื่อนที่ คุณจะต้องสร้างการผสานรวมที่กำหนดเอง คุณจะสร้าง Intent ได้โดยระบุวลีการฝึกเพื่อฝึกโมเดลแมชชีนเลิร์นนิงที่สำคัญ

ห้องทดลองนี้เรียงลำดับตามประสบการณ์ทั่วไปของนักพัฒนาซอฟต์แวร์ระบบคลาวด์

  1. การตั้งค่าสภาพแวดล้อม
  • Dialogflow: สร้างตัวแทน Dialogflow ES ใหม่
  • Dialogflow: กำหนดค่า Dialogflow
  • Google Cloud: สร้างบัญชีบริการ
  1. Flutter: การสร้างแอปพลิเคชันแชท
  • กำลังสร้างโปรเจ็กต์ Flutter
  • การกำหนดการตั้งค่าและสิทธิ์
  • การเพิ่มทรัพยากร Dependency
  • การลิงก์กับบัญชีบริการ
  • การเรียกใช้แอปพลิเคชันบนอุปกรณ์เสมือนหรืออุปกรณ์จริง
  1. Flutter: การสร้างอินเทอร์เฟซแชทด้วยการรองรับการแปลงเสียงพูดเป็นข้อความ
  • การสร้างอินเทอร์เฟซแชท
  • การลิงก์อินเทอร์เฟซแชท
  • การผสานรวมแพ็กเกจ gRPC ของ Dialogflow ในแอป
  1. Dialogflow: การสร้างโมเดล Agent ของ Dialogflow
  • กำหนดค่าการต้อนรับและ Intent สำรอง
  • ใช้ฐานความรู้สำหรับคำถามที่พบบ่อย

วิชาบังคับก่อน

  • ประสบการณ์การใช้งาน Dart/Flutter พื้นฐาน
  • ประสบการณ์การใช้งาน Google Cloud Platform พื้นฐาน
  • ประสบการณ์การใช้งาน Dialogflow ES ขั้นพื้นฐาน

สิ่งที่คุณจะสร้าง

Codelab นี้จะแสดงวิธีสร้างบ็อตคำถามที่พบบ่อยบนอุปกรณ์เคลื่อนที่ ซึ่งจะตอบคำถามที่พบบ่อยที่สุดเกี่ยวกับเครื่องมือ Dialogflow ผู้ใช้ปลายทางสามารถโต้ตอบกับอินเทอร์เฟซข้อความหรือสตรีมเสียงผ่านไมโครโฟนในตัวของอุปกรณ์เคลื่อนที่เพื่อหาคำตอบได้

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างแชทบ็อตด้วย Dialogflow Essentials
  • วิธีผสานรวม Dialogflow ในแอป Flutter ด้วยแพ็กเกจ gRPC ของ Dialogflow
  • วิธีตรวจหา Intent ข้อความด้วย Dialogflow
  • วิธีสตรีมเสียงผ่านไมโครโฟนไปยัง Dialogflow
  • วิธีใช้เครื่องมือเชื่อมต่อฐานความรู้เพื่อนำเข้าคำถามที่พบบ่อยแบบสาธารณะ
  • ทดสอบแชทบ็อตผ่านอินเทอร์เฟซข้อความและเสียงในอุปกรณ์เสมือนหรืออุปกรณ์จริง

สิ่งที่ต้องมี

  • คุณต้องมีอีเมลของ Google / Gmail เพื่อสร้าง Agent ของ Dialogflow
  • คุณจะต้องมีสิทธิ์เข้าถึง Google Cloud Platform เพื่อดาวน์โหลดบัญชีบริการ
  • สภาพแวดล้อมสำหรับนักพัฒนา Flutter

ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์ Flutter

  1. เลือกระบบปฏิบัติการที่จะติดตั้ง Flutter
  1. คุณสร้างแอปด้วย Flutter โดยใช้เครื่องมือแก้ไขข้อความร่วมกับเครื่องมือบรรทัดคำสั่งของเราได้ อย่างไรก็ตาม เวิร์กช็อปนี้จะใช้ Android Studio ปลั๊กอิน Flutter และ Dart สำหรับ Android Studio มีการเติมโค้ด การไฮไลต์ไวยากรณ์ ความช่วยเหลือในการแก้ไขวิดเจ็ต การเรียกใช้ & การสนับสนุนการแก้ไขข้อบกพร่อง และอื่นๆ โปรดทำตามขั้นตอนใน https://flutter.dev/docs/get-started/editor

2. การตั้งค่าสภาพแวดล้อม

Dialogflow: สร้างตัวแทน Dialogflow ES ใหม่

  1. เปิด
  2. ในแถบด้านซ้ายใต้โลโก้ ให้เลือก "Create New Agent" ในเมนูแบบเลื่อนลง (หมายเหตุ อย่าคลิกเมนูแบบเลื่อนลงที่มีคำว่า "ทั่วโลก" เราจะต้องใช้อินสแตนซ์ของ Dialogflow ใน "ทั่วโลก" เพื่อใช้ประโยชน์จากฐานความรู้ของคำถามที่พบบ่อย)
  3. ระบุชื่อตัวแทน yourname-dialogflow (ใช้ชื่อของคุณเอง)
  4. เลือกอังกฤษ - en เป็นภาษาเริ่มต้น
  5. สำหรับเขตเวลาเริ่มต้น ให้เลือกเขตเวลาที่อยู่ใกล้คุณมากที่สุด
  6. อย่าเลือก Mega Agent (ฟีเจอร์นี้ช่วยให้คุณสามารถสร้าง Agent หลัก ซึ่งจัดการเป็นกลุ่มระหว่าง Agent "ย่อย" ได้ เรายังไม่ต้องใช้อีกในตอนนี้)
  7. คลิกสร้าง

สร้างหน้าจอโปรเจ็กต์ใหม่

กำหนดค่า Dialogflow

  1. คลิกไอคอนรูปเฟืองถัดจากชื่อโครงการในเมนูด้านซ้าย

เมนูแบบเลื่อนลงสำหรับสร้างโปรเจ็กต์ใหม่

  1. ป้อนคำอธิบาย Agent ต่อไปนี้ Chatbot คำถามที่พบบ่อยเกี่ยวกับ Dialogflow
  2. เปิดใช้ฟีเจอร์เบต้า แล้วเลื่อนสวิตช์

Dialogflow Essentials เวอร์ชัน 2 เบต้า1

  1. คลิกแท็บคำพูด และตรวจสอบว่าช่องการปรับคำพูดอัตโนมัติทำงานอยู่
  2. หรือคุณจะสลับสวิตช์แรกก็ได้ ซึ่งจะปรับปรุงโมเดลคำพูด แต่จะใช้ได้เมื่อคุณอัปเกรดช่วงทดลองใช้ Dialogflow เท่านั้น
  3. คลิกบันทึก

Google Cloud: รับบัญชีบริการ

หลังจากสร้าง Agent ใน Dialogflow แล้ว ควรสร้างโปรเจ็กต์ Google Cloud ในคอนโซล Google Cloud

  1. เปิด Google Cloud Console:
  2. ตรวจสอบว่าคุณลงชื่อเข้าสู่ระบบด้วยบัญชี Google เดียวกับใน Dialogflow แล้วเลือกโปรเจ็กต์: yourname-dialogflow ในแถบสีน้ำเงินด้านบน
  3. ถัดไป ให้ค้นหา Dialogflow API ในแถบเครื่องมือด้านบน แล้วคลิกที่ Dialogflow API ผลลัพธ์ในเมนูแบบเลื่อนลง

เปิดใช้ Dialogflow API

  1. คลิกปุ่มจัดการสีน้ำเงิน แล้วคลิกข้อมูลเข้าสู่ระบบในแถบเมนูซ้าย (เมื่อยังไม่ได้เปิดใช้ Dialogflow ให้กดเปิดใช้ก่อน)

ข้อมูลเข้าสู่ระบบ GCP ในคอนโซล

  1. คลิกสร้างข้อมูลเข้าสู่ระบบ (ที่ด้านบนของหน้าจอ) แล้วเลือกบัญชีบริการ

สร้างข้อมูลรับรอง

  1. ระบุชื่อบัญชีบริการ: flutter_dialogflow, รหัส และคำอธิบาย แล้วกดสร้าง

สร้างบัญชีบริการ

  1. ในขั้นตอนที่ 2 คุณจะต้องเลือกบทบาท: Dialogflow API Admin คลิกต่อไป และเสร็จสิ้น
  2. คลิกบัญชีบริการ flutter_dialogflow จากนั้นคลิกแท็บ Keys แล้วกด Add Key > สร้างคีย์ใหม่

สร้างคีย์

  1. สร้างคีย์ JSON เปลี่ยนชื่อเป็น credentials.json และจัดเก็บไว้ในตำแหน่งที่ปลอดภัยของฮาร์ดไดรฟ์ เราจะใช้ในภายหลัง

คีย์ JSON

เยี่ยมเลย เครื่องมือทั้งหมดที่ต้องใช้ได้รับการตั้งค่าอย่างถูกต้อง เริ่มจากการผสานรวม Dialogflow ในแอปได้แล้ว

3. Flutter: การสร้างแอปพลิเคชัน Chat

สร้างแอป Boilerplate

  1. เปิด Android Studio แล้วเลือกเริ่มโปรเจ็กต์ Flutter ใหม่
  2. เลือกแอปพลิเคชัน Flutter เป็นประเภทโปรเจ็กต์ จากนั้นคลิกถัดไป
  3. ยืนยันว่าเส้นทาง Flutter SDK ระบุตำแหน่งของ SDK (เลือก "ติดตั้ง SDK... หากช่องข้อความว่างเปล่า)
  4. ป้อนชื่อโปรเจ็กต์ (เช่น flutter_dialogflow_agent) จากนั้นคลิกถัดไป
  5. แก้ไขชื่อแพ็กเกจ แล้วคลิกเสร็จสิ้น

สร้างแอปพลิเคชัน Flutter ใหม่

ซึ่งจะสร้างแอปพลิเคชันตัวอย่างที่มี Material Components

รอให้ Android Studio ติดตั้ง SDK และสร้างโปรเจ็กต์

การตั้งค่าและ การอนุญาต

  1. ไลบรารีการบันทึกเสียง sound_stream ที่เราจะใช้ต้องมี minSdk อย่างน้อย 21 ดังนั้น มาทำการเปลี่ยนแปลงนี้ใน android/app/build.gradle ในบล็อก defaultConfig (โปรดทราบว่ามีไฟล์build.gradle 2 ไฟล์ในโฟลเดอร์ Android แต่ไฟล์ในโฟลเดอร์ของแอปไม่ใช่ไฟล์ที่ถูกต้อง)
defaultConfig {
   applicationId "com.myname.flutter_dialogflow_agent"
   minSdkVersion 21
   targetSdkVersion 30
   versionCode flutterVersionCode.toInteger()
   versionName flutterVersionName
}
  1. หากต้องการให้สิทธิ์เข้าถึงไมโครโฟนและอนุญาตให้แอปติดต่อตัวแทน Dialogflow ซึ่งทำงานในระบบคลาวด์ เราจะต้องเพิ่มสิทธิ์อินเทอร์เน็ตและ RECORD_AUDIO ลงในไฟล์ app/src/main/AndroidManifest.xml มีไฟล์ AndroidManifest.xml ในโปรเจ็กต์ Flutter อยู่ แต่ต้องมีไฟล์ดังกล่าวในโฟลเดอร์หลัก คุณเพิ่มบรรทัดภายในแท็กไฟล์ Manifest ได้
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />

การเพิ่มทรัพยากร Dependency

เราจะใช้แพ็กเกจ sound_stream, rxdart และ dialogflow_grpc

  1. เพิ่มทรัพยากร Dependency 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. เพิ่มทรัพยากร Dependency 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. เพิ่มทรัพยากร Dependency 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. ย้ายไฟล์ credentials.json ซึ่งดาวน์โหลดจากคอนโซล Google Cloud ไปยังโฟลเดอร์ 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 เป้าหมายเพื่อให้เราสามารถทดสอบแอปพลิเคชันของเราได้โดยไม่ต้องมีอุปกรณ์จริง โปรดดูรายละเอียดที่หัวข้อการจัดการ AVD เมื่อเลือกอุปกรณ์เสมือนใหม่แล้ว ให้ดับเบิลคลิกอุปกรณ์ดังกล่าวเพื่อเริ่มต้น

จัดการ AVD

อุปกรณ์เสมือน

  1. ในแถบเครื่องมือหลักของ Android Studio ให้เลือกอุปกรณ์ Android เป็นเป้าหมายจากเมนูแบบเลื่อนลง และตรวจสอบว่าได้เลือก main.dart แล้ว แล้วกดปุ่มเรียกใช้ (รูปสามเหลี่ยมสีเขียว)

คุณจะเห็นบันทึกในคอนโซลที่ด้านล่างของ IDE คุณจะเห็นว่าแอปกำลังติดตั้ง Android และแอป Flutter เริ่มต้น การดำเนินการนี้จะใช้เวลาสักครู่ เมื่ออุปกรณ์เสมือนพร้อมแล้ว การเปลี่ยนแปลงจะเป็นไปอย่างรวดเร็ว เมื่อเสร็จแล้ว แอป Flutter จะเปิดแอปเริ่มต้น

แอป Boilerplate

  1. มาเปิดใช้ไมโครโฟนสำหรับแอปแชทบ็อตกัน คลิกปุ่มตัวเลือกของอุปกรณ์เสมือนเพื่อเปิดตัวเลือก ในแท็บไมโครโฟน ให้เปิดใช้สวิตช์ทั้ง 3 ตัว

ตัวเลือก AVD

  1. มาลองใช้ Hot Reload เพื่อแสดงให้เห็นว่าสามารถเปลี่ยนแปลงได้เร็วแค่ไหน

ใน lib/main.dart ให้เปลี่ยนชื่อ MyHomePage ในคลาส MyApp เป็น Flutter Dialogflow Agent และเปลี่ยน primarySwatch เป็น Colors.orange

โค้ดแรก

บันทึกไฟล์หรือคลิกไอคอนกลอนประตูในแถบเครื่องมือ Android Studio คุณจะเห็นการเปลี่ยนแปลงที่ทำในอุปกรณ์เสมือนโดยตรง

4. Flutter: การสร้างอินเทอร์เฟซ Chat ด้วยการรองรับ STT

การสร้างอินเทอร์เฟซแชท

  1. สร้างไฟล์วิดเจ็ต Flutter ใหม่ในโฟลเดอร์ lib (คลิกขวาที่โฟลเดอร์ lib ใหม่ > วิดเจ็ต Flutter > วิดเจ็ตการเก็บสถานะ) เรียกใช้ไฟล์นี้: chat.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 สำหรับบิลด์ของวิดเจ็ต สิ่งนี้จะสร้างอินเทอร์เฟซแชทบ็อต ซึ่งมี:

  • 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 เมื่อแตะปุ่มไมโครโฟนแล้วหยุด คุณจะเห็นสตรีมเสียงที่บันทึกไว้ในแท็บเรียกใช้

บันทึกสตรีมเสียง

เยี่ยมมาก ตอนนี้เราพร้อมผสานรวมแอปพลิเคชันกับ 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);

การดำเนินการนี้จะสร้างอินสแตนซ์ Dialogflow ที่ได้รับอนุญาตไปยังโปรเจ็กต์ Google Cloud ของคุณด้วยบัญชีบริการ (ตรวจสอบว่าคุณมี credentials.json ในโฟลเดอร์ assets)

ขอแจ้งอีกครั้งว่า สำหรับการสาธิตวิธีทำงานกับ Dialogflow gRPC แต่แอปเวอร์ชันที่ใช้งานจริง คุณไม่ควรจัดเก็บไฟล์ข้อมูลเข้าสู่ระบบ.json ในโฟลเดอร์เนื้อหา เพราะวิธีนี้ไม่ถือว่าปลอดภัย

กำลังทำการเรียกใช้ DetectionIntent

  1. คราวนี้ให้ค้นหาเมธอด handleSubmitted() แล้วเวทมนตร์จะมาช่วยในเรื่องนี้ ใต้ความคิดเห็น TODO ให้เพิ่มโค้ดต่อไปนี้ โค้ดนี้จะเพิ่มข้อความพิมพ์ของผู้ใช้ลงใน ListView:
ChatMessage message = ChatMessage(
 text: text,
 name: "You",
 type: true,
);

setState(() {
 _messages.insert(0, message);
});
  1. ตอนนี้ที่ใต้โค้ดก่อนหน้านี้ เราจะทำการเรียกใช้ DetectionIntent ส่งข้อความจากอินพุต และรหัสภาษา - ผลลัพธ์ (ภายใน 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. เริ่มต้นอุปกรณ์เสมือน แล้วทดสอบการเรียกใช้แบบตรวจจับ Intent ประเภท: hi ระบบจะทักทายคุณด้วยข้อความต้อนรับเริ่มต้น เมื่อคุณพิมพ์คำอื่น ระบบจะแสดงตัวเลือกสำรองเริ่มต้นให้คุณ

กำลังทำการเรียกใช้ StreamingDetectIntent

  1. มาดูวิธีใช้เมธอด handleStream() ในการสตรีมเสียงกัน อันแรกที่ใต้ TODO อันแรก ให้สร้างเสียง 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. ต่อไป เราจะเรียกเมธอด streamingDetectIntent ในออบเจ็กต์ dialogflow ซึ่งมีเซสชัน Dialogflow ของเรา
final responseStream = dialogflow.streamingDetectIntent(config, _audioStream);
  1. ด้วยการตอบกลับสตรีม ในที่สุดเราสามารถฟังข้อความถอดเสียงที่เข้ามา ข้อความค้นหาของผู้ใช้ที่ตรวจพบ และการตอบกลับความตั้งใจที่ตรงกันที่ตรวจพบได้ เราจะพิมพ์เอกสารนี้ใน 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');
});

เท่านี้ก็เรียบร้อย ให้เริ่มแอปพลิเคชันแล้วทดสอบในอุปกรณ์เสมือน กดปุ่มไมโครโฟน แล้วพูดว่า "สวัสดี"

การดำเนินการนี้จะทริกเกอร์ Intent เริ่มต้นของ Dialogflow ผลลัพธ์จะพิมพ์ออกมาบนหน้าจอ ตอนนี้ Flutter ทำงานกับการผสานรวม Dialogflow ได้ดีแล้ว เราสามารถเริ่มสนทนากับแชทบ็อตได้

5. Dialogflow: การสร้างโมเดล Agent ของ Dialogflow

Dialogflow Essentials เป็นชุดโปรแกรมการพัฒนาสำหรับการสร้าง UI แบบสนทนา ไม่ว่าจะเป็นแชทบ็อต บ็อตเสียง เกตเวย์โทรศัพท์ คุณสามารถสร้างแอปด้วยเครื่องมือเดียวกัน หรือรองรับช่องหลายช่องในภาษาต่างๆ กว่า 20 ภาษา นักออกแบบ UX ของ Dialogflow (ผู้สร้างโมเดลตัวแทน นักภาษาศาสตร์) หรือนักพัฒนาสร้างความตั้งใจด้วยการระบุวลีที่ใช้ฝึกเพื่อฝึกโมเดลแมชชีนเลิร์นนิงที่สำคัญ

ความตั้งใจจะจัดหมวดหมู่ความตั้งใจของผู้ใช้ สำหรับ Agent แต่ละรายการของ Dialogflow ES คุณจะกำหนด Intent หลายรายการได้ ซึ่ง Intent ที่จะรวมไว้จะรองรับการสนทนาที่สมบูรณ์ได้ Intent แต่ละรายการจะมีพารามิเตอร์และการตอบกลับได้

การจับคู่ความตั้งใจอาจเรียกอีกอย่างหนึ่งว่าการแยกประเภทความตั้งใจหรือการจับคู่ความตั้งใจ นี่คือแนวคิดหลักใน Dialogflow ES เมื่อจับคู่ความตั้งใจได้แล้ว ฟังก์ชันจะแสดงการตอบสนอง รวบรวมพารามิเตอร์ (การแยกเอนทิตี) หรือทริกเกอร์โค้ดเว็บฮุค (Fulfillment) เช่น เพื่อดึงข้อมูลจากฐานข้อมูล

เมื่อผู้ใช้ปลายทางเขียนหรือพูดบางอย่างในแชทบ็อต ซึ่งเรียกว่าการแสดงออกหรือคำพูดของผู้ใช้ Dialogflow ES จะจับคู่นิพจน์กับความตั้งใจที่ดีที่สุดของตัวแทน Dialogflow โดยอิงจากวลีที่ฝึก โมเดลแมชชีนเลิร์นนิงภายใน Dialogflow ES ได้รับการฝึกด้วยวลีการฝึกเหล่านั้น

Dialogflow ES ทำงานกับแนวคิดที่เรียกว่าบริบท Dialogflow ES จำบริบทได้ด้วยการผลัดที่ 2 และ 3 เช่นเดียวกับมนุษย์ การติดตามเสียงพูดของผู้ใช้คนก่อนทำได้ด้วยวิธีนี้

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Dialogflow Intents

การแก้ไขจุดประสงค์ในการต้อนรับเริ่มต้น

เมื่อคุณสร้าง Agent ของ Dialogflow ใหม่ ระบบจะสร้าง Intent เริ่มต้น 2 รายการโดยอัตโนมัติ จุดประสงค์ในการต้อนรับเริ่มต้นเป็นขั้นตอนแรกที่คุณไปถึงเมื่อเริ่มต้นการสนทนากับตัวแทน Intent สำรองเริ่มต้น คือขั้นตอนที่คุณจะได้รับเมื่อตัวแทนไม่เข้าใจคุณ หรือจับคู่ Intent กับสิ่งที่คุณพูดไม่ได้

ข้อความต้อนรับสำหรับจุดประสงค์เริ่มต้นในการต้อนรับมีดังนี้

ผู้ใช้

ตัวแทน

สวัสดี

"สวัสดี นี่คือบ็อตคำถามที่พบบ่อยของ Dialogflow ฉันตอบคำถามใน Dialogflow ได้"

"อยากรู้เรื่องอะไร"

  1. คลิกความตั้งใจ > จุดประสงค์เริ่มต้นในการต้อนรับ
  2. เลื่อนลงไปที่การตอบกลับ
  3. ล้างการตอบกลับข้อความทั้งหมด
  4. ในแท็บเริ่มต้น ให้สร้างคำตอบ 2 รายการต่อไปนี้
  • สวัสดี นี่คือบ็อตคำถามที่พบบ่อยของ Dialogflow และตอบคำถามเกี่ยวกับ Dialogflow ได้ คุณต้องการทราบเรื่องใด
  • สวัสดี ฉันชื่อบ็อตคำถามที่พบบ่อยของ Dialogflow คุณมีข้อสงสัยเกี่ยวกับ Dialogflow ไหม ฉันจะช่วยอะไรได้บ้าง

การกําหนดค่าควรคล้ายกับภาพหน้าจอนี้

แก้ไขจุดประสงค์ในการต้อนรับเริ่มต้น

  1. คลิกบันทึก
  2. มาทดสอบความตั้งใจนี้กัน ขั้นแรก เราสามารถทดสอบได้ใน Dialogflow Simulator.Type: สวัสดี ซึ่งควรส่งคืนข้อความใดข้อความหนึ่งต่อไปนี้
  • สวัสดี นี่คือบ็อตคำถามที่พบบ่อยของ Dialogflow และตอบคำถามเกี่ยวกับ Dialogflow ได้ คุณต้องการทราบเรื่องใด
  • สวัสดี ฉันชื่อบ็อตคำถามที่พบบ่อยของ Dialogflow คุณมีข้อสงสัยเกี่ยวกับ Dialogflow ไหม ฉันจะช่วยอะไรได้บ้าง

การแก้ไข Intent สำรองเริ่มต้น

  1. คลิกความตั้งใจ > Intent สำรองเริ่มต้น
  2. เลื่อนลงไปที่การตอบกลับ
  3. ล้างการตอบกลับข้อความทั้งหมด
  4. ในแท็บเริ่มต้น ให้สร้างคำตอบต่อไปนี้
  • ขออภัย ฉันไม่ทราบคำตอบของคำถามนี้ คุณตรวจสอบเว็บไซต์ของเราหรือยัง http://www.dialogflow.com?
  1. คลิกบันทึก

การเชื่อมต่อกับฐานความรู้ออนไลน์

เครื่องมือเชื่อมต่อความรู้จะช่วยส่งเสริม Intent ที่กำหนด แล้วแยกวิเคราะห์เอกสารความรู้เพื่อค้นหาคำตอบอัตโนมัติ (ตัวอย่างเช่น คำถามที่พบบ่อยหรือบทความจากไฟล์ CSV, เว็บไซต์ออนไลน์ หรือแม้แต่ไฟล์ PDF!) หากต้องการกำหนดค่า ให้กำหนดฐานความรู้อย่างน้อย 1 รายการ ซึ่งเป็นคอลเล็กชันของเอกสารความรู้

อ่านเพิ่มเติมเกี่ยวกับเครื่องมือเชื่อมต่อความรู้

มาลองกันเลย

  1. เลือกความรู้ (เบต้า) ในเมนู

ฐานความรู้

  1. คลิกปุ่มสีน้ำเงินด้านขวาสร้างฐานความรู้
  2. พิมพ์เป็นชื่อฐานความรู้ คำถามที่พบบ่อยเกี่ยวกับ Dialogflow แล้วกดบันทึก
  3. คลิกลิงก์สร้างลิงก์แรก

ขั้นตอนแรกของฐานความรู้

  1. หน้าต่างจะเปิดขึ้น

ใช้การกำหนดค่าต่อไปนี้

ชื่อเอกสาร: DialogflowFAQ ประเภทความรู้: FAQ ประเภท Mime: text/html

  1. URL ที่เราโหลดข้อมูลคือ

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

  1. คลิกสร้าง

มีการสร้างฐานความรู้ขึ้น

สร้างฐานความรู้แล้ว

  1. เลื่อนลงไปที่ส่วนคำตอบแล้วคลิกเพิ่มคำตอบ

สร้างคำตอบต่อไปนี้แล้วกดบันทึก

$Knowledge.Answer[1]
  1. คลิกดูรายละเอียด

ดูรายละเอียด

  1. เลือกเปิดใช้การโหลดซ้ำอัตโนมัติ เพื่อดึงข้อมูลการเปลี่ยนแปลงโดยอัตโนมัติเมื่อมีการอัปเดตหน้าเว็บคำถามที่พบบ่อย แล้วกดบันทึก

ขั้นตอนนี้จะแสดงคำถามที่พบบ่อยทั้งหมดที่คุณนำไปใช้ใน Dialogflow ได้ง่ายๆ

ทั้งนี้คุณสามารถชี้ไปยังเว็บไซต์ HTML ออนไลน์ที่มีคำถามที่พบบ่อยเพื่อนำเข้าคำถามที่พบบ่อยไปยังตัวแทนของคุณ หรืออาจอัปโหลดเป็น PDF ที่มีบล็อกข้อความก็ได้ แล้ว Dialogflow จะถามตัวเองเอง

ตอนนี้คำถามที่พบบ่อยควรเปลี่ยนเป็น "ข้อมูลเพิ่มเติม" เพื่อเพิ่มไปยังตัวแทนของคุณข้างโฟลว์ Intent คำถามที่พบบ่อยเกี่ยวกับฐานความรู้ฝึกโมเดลไม่ได้ ดังนั้นการถามคำถามด้วยวิธีที่ต่างไปจากเดิมอย่างสิ้นเชิง อาจจะไม่ได้รับคำตอบที่ตรงกัน เนื่องจากไม่ได้ใช้การทำความเข้าใจภาษาธรรมชาติ (โมเดลแมชชีนเลิร์นนิง) ด้วยเหตุนี้ บางครั้งจึงคุ้มค่าที่จะเปลี่ยนคำถามที่พบบ่อยไปเป็นความตั้งใจ

  1. ทดสอบคำถามในเครื่องมือจำลองทางด้านขวา
  2. เมื่อทุกอย่างใช้งานได้แล้ว ให้กลับไปที่แอป Flutter แล้วทดสอบแชทและบ็อตเสียงด้วยเนื้อหาใหม่นี้ ถามคำถามที่โหลดไว้ใน Dialogflow

ผลลัพธ์

6. ขอแสดงความยินดี

ยินดีด้วย คุณสร้างแอป Flutter แรกพร้อมการผสานรวมแชทบ็อต Dialogflow เรียบร้อยแล้ว

สรุปประเด็นที่ได้พูดถึง

  • วิธีสร้างแชทบ็อตด้วย Dialogflow Essentials
  • วิธีผสานรวม Dialogflow กับแอป Flutter
  • วิธีตรวจหา Intent ข้อความด้วย Dialogflow
  • วิธีสตรีมเสียงผ่านไมโครโฟนไปยัง Dialogflow
  • วิธีใช้เครื่องมือเชื่อมต่อฐานความรู้

ขั้นตอนถัดไปคือ

ชอบ Code Lab นี้ไหม ลองดูห้องทดลองของ Dialogflow เจ๋งๆ เหล่านี้

ไม่ทราบว่าฉันจะสร้างแพ็กเกจ gRPC ของ Dialogflow สำหรับ Dart/Flutter ได้อย่างไร