Dialogflow Essentials로 Android용 음성 봇 빌드하기 플러터

1. 시작하기 전에

이 Codelab에서는 간단한 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 에이전트 모델링
  • 환영 및 대체 인텐트
  • FAQ 기술 자료 활용

기본 요건

  • 기본 Dart/Flutter 환경
  • 기본 Google Cloud Platform 환경
  • Dialogflow ES 관련 기본 경험

빌드할 항목

이 Codelab에서는 Dialogflow 도구에 관한 가장 일반적인 질문에 답변할 수 있는 모바일 FAQ 봇을 빌드하는 방법을 보여줍니다. 최종 사용자는 텍스트 인터페이스와 상호작용하거나 휴대기기의 내장 마이크를 통해 음성을 스트리밍하여 답변을 얻을 수 있습니다.

학습할 내용

  • Dialogflow Essentials에서 챗봇을 만드는 방법
  • Dialogflow gRPC 패키지를 사용하여 Dialogflow를 Flutter 앱에 통합하는 방법
  • Dialogflow로 텍스트 인텐트를 감지하는 방법
  • 마이크를 통해 Dialogflow에 음성을 스트리밍하는 방법
  • 기술 자료 커넥터를 사용하여 공개 FAQ를 가져오는 방법
  • 가상 기기나 실제 기기에서 텍스트 및 음성 인터페이스를 통해 챗봇을 테스트합니다.

필요한 항목

  • Dialogflow 에이전트를 만들려면 Google ID / Gmail 주소가 필요합니다.
  • 서비스 계정을 다운로드하려면 Google Cloud Platform에 액세스해야 합니다.
  • Flutter 개발 환경

Flutter 개발 환경 설정

  1. Flutter를 설치할 운영체제를 선택합니다.
  1. 명령줄 도구와 함께 사용한 텍스트 편집기를 사용하여 Flutter로 앱을 빌드할 수 있습니다. 하지만 이 워크숍에서는 Android 스튜디오를 활용합니다. Android 스튜디오용 Flutter 및 Dart 플러그인은 코드 완성, 구문 강조 표시, 위젯 편집 지원, 실행 및 디버그 지원 등이 있습니다. https://flutter.dev/docs/get-started/editor의 단계를 따르세요.

2. 환경 설정

Dialogflow: 새 Dialogflow ES 에이전트 만들기

  1. 을 엽니다.
  2. 왼쪽 표시줄의 로고 바로 아래에서 'Create New Agent(새 에이전트 만들기)를 선택합니다. 선택합니다. '전역' 드롭다운을 클릭하지 마세요. FAQ 기술 자료를 사용하려면 전역인 Dialogflow 인스턴스가 필요합니다.
  3. 에이전트 이름 지정yourname-dialogflow (사용자 이름 사용)
  4. 기본 언어로 English - en을 선택합니다.
  5. 기본 시간대로 가장 가까운 시간대를 선택합니다.
  6. 메가 에이전트를 선택하지 마세요. (이 기능을 사용하면 'sub' 에이전트 간에 조정할 수 있는 중요한 에이전트를 만들 수 있습니다. 지금은 필요하지 않습니다.)
  7. 만들기를 클릭합니다.

새 프로젝트 만들기 화면

Dialogflow 구성

  1. 왼쪽 메뉴에서 프로젝트 이름 옆에 있는 톱니바퀴 아이콘을 클릭합니다.

새 프로젝트 드롭다운 만들기

  1. 에이전트 설명 Dialogflow FAQ 챗봇을 입력합니다.
  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 스튜디오를 열고 Start a new Flutter project를 선택합니다.
  2. 프로젝트 유형으로 Flutter Application을 선택합니다. 그리고 다음을 클릭합니다.
  3. Flutter SDK 경로가 SDK 위치를 지정하는지 확인합니다 (텍스트 필드가 비어 있으면 Install SDK... 선택).
  4. 프로젝트 이름을 입력합니다 (예: flutter_dialogflow_agent). 그런 다음 다음을 클릭합니다.
  5. 패키지 이름을 수정하고 Finish를 클릭합니다.

새 Flutter 애플리케이션 만들기

그러면 머티리얼 구성요소가 포함된 샘플 애플리케이션이 생성됩니다.

Android 스튜디오가 SDK를 설치하고 프로젝트를 만들 때까지 기다립니다.

설정 및 권한

  1. 사용할 오디오 녹음기 라이브러리 sound_stream에 최소 21의 minSdk가 필요합니다. defaultConfig 블록의 android/app/build.gradle에서 이것을 변경해 보겠습니다. (android 폴더에는 2개의 build.gradle 파일이 있지만 app 폴더에 있는 파일이 올바른 파일입니다.)
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_stream, rxdart, dialogflow_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 스튜디오 상단 툴바

  1. 실제 기기 없이 애플리케이션을 테스트할 수 있도록 대상 Android Virtual Device를 만들어 보겠습니다. 자세한 내용은 AVD 관리를 참고하세요. 새 가상 기기를 선택했으면 더블클릭하여 시작할 수 있습니다.

AVD 관리

가상 기기

  1. 기본 Android 스튜디오 툴바에서 드롭다운을 통해 Android 기기를 타겟으로 선택하고 main.dart가 선택되어 있는지 확인합니다. 그런 다음 실행 버튼 (녹색 삼각형)을 누릅니다.

IDE 하단의 콘솔에 로그가 표시됩니다. Android와 시작 Flutter 앱이 설치되고 있는 것을 확인할 수 있습니다. 1분 정도 소요됩니다. 가상 기기가 준비되면 매우 빠르게 변경할 수 있습니다. 모두 완료되면 시작 Flutter 앱이 열립니다.

상용구 앱

  1. 챗봇 앱의 마이크를 사용 설정해 보겠습니다. 가상 기기의 options 버튼을 클릭하여 옵션을 엽니다. 마이크 탭에서 스위치 3개를 모두 사용 설정합니다.

AVD 옵션

  1. 핫 리로드를 사용하여 얼마나 빠르게 변경할 수 있는지 보여 보겠습니다.

lib/main.dart에서 MyApp 클래스의 MyHomePage titleFlutter Dialogflow Agent로 변경합니다. primarySwatch를 Colors.orange로 변경합니다.

첫 번째 코드

파일을 저장하거나 Android 스튜디오 툴바에서 볼트 아이콘을 클릭합니다. 가상 기기에서 직접 변경된 것을 확인할 수 있어야 합니다.

4. Flutter: STT 지원을 통한 채팅 인터페이스 빌드

채팅 인터페이스 만들기

  1. lib 폴더에서 새로운 Flutter 위젯 파일을 생성합니다. (lib 폴더를 마우스 오른쪽 버튼으로 클릭하고 New > Flutter Widget > 스테이트풀(Stateful) 위젯) 파일을 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 파일에서 위젯 빌드를 검색합니다. 이렇게 하면 다음을 포함하는 챗봇 인터페이스가 빌드됩니다.

  • 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 스튜디오의 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 바로 아래에서 음성 모델을 편중하는 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 통합과 원활하게 작동하므로 챗봇 대화 작업을 시작할 수 있습니다.

5. Dialogflow: Dialogflow 에이전트 모델링

Dialogflow Essentials는 대화형 UI를 빌드하기 위한 개발 제품군입니다. 챗봇, 음성 봇, 전화 게이트웨이 등입니다. 모두 같은 도구로 빌드할 수 있으며 20개 이상의 다양한 언어로 여러 채널을 지원할 수도 있습니다. Dialogflow UX 디자이너 (에이전트 모델 작성자, 언어 전문가) 또는 개발자는 학습 문구를 지정하여 기본 머신러닝 모델을 학습시키는 방식으로 인텐트를 만듭니다.

인텐트는 사용자의 의도를 분류합니다. Dialogflow ES 에이전트 하나에 여러 개의 인텐트를 정의할 수 있으며, 조합된 인텐트는 전체 대화를 처리할 수 있습니다. 각 인텐트에는 매개변수와 응답이 포함될 수 있습니다.

인텐트 일치를 인텐트 분류 또는 인텐트 일치라고도 합니다. Dialogflow ES의 기본 개념입니다. 인텐트가 일치되면 응답을 반환하거나 매개변수를 수집(항목 추출)하거나 웹훅 코드를 트리거(fulfillment)할 수 있습니다(예: 데이터베이스에서 데이터 가져오기).

최종 사용자가 챗봇에 사용자 표현 또는 발화라고 하는 무언가를 쓰거나 말하면 Dialogflow ES는 학습 문구를 기반으로 Dialogflow 에이전트의 최상의 인텐트와 표현을 일치시킵니다. 내부적으로 Dialogflow ES 머신러닝 모델은 이러한 학습 문구를 기반으로 학습되었습니다.

Dialogflow ES는 컨텍스트라는 개념으로 작동합니다. Dialogflow ES는 사람과 마찬가지로 2차와 3차 차례의 컨텍스트를 기억할 수 있습니다. 이를 통해 이전 사용자 발화를 추적할 수 있습니다.

Dialogflow 인텐트에 대해 자세히 알아보세요.

기본 시작 인텐트 수정

새 Dialogflow 에이전트를 만들면 기본 인텐트 두 개가 자동으로 생성됩니다. 기본 시작 인텐트는 에이전트와 대화를 시작할 때 가장 먼저 접하게 되는 흐름입니다. 기본 대체 인텐트는 에이전트가 사용자를 이해할 수 없거나 인텐트를 방금 말한 내용과 일치시킬 수 없을 때 받게 되는 흐름입니다.

다음은 기본 시작 인텐트의 환영 메시지입니다.

사용자

에이전트

안녕하세요

"안녕하세요, 저는 Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변해 드릴 수 있습니다."

"무엇을 알고 싶으신가요?"

  1. 인텐트 > 기본 시작 인텐트
  2. 응답까지 아래로 스크롤합니다.
  3. 모든 텍스트 응답을 삭제합니다.
  4. 기본 탭에서 다음 두 가지 응답을 만듭니다.
  • 안녕하세요, 저는 Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변을 드릴 수 있습니다. 어떤 점을 알고 싶으신가요?
  • 안녕하세요, 저는 Dialogflow FAQ 봇입니다. Dialogflow에 대해 궁금한 점이 있으신가요? 어떻게 도와드릴까요?

구성은 이 스크린샷과 비슷해야 합니다.

기본 시작 인텐트 수정

  1. 저장을 클릭합니다.
  2. 이 인텐트를 테스트해 보겠습니다. 먼저 Dialogflow 시뮬레이터에서 테스트할 수 있습니다.Hello를 입력합니다. 다음 메시지 중 하나가 반환되어야 합니다.
  • 안녕하세요, 저는 Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변을 드릴 수 있습니다. 어떤 점을 알고 싶으신가요?
  • 안녕하세요, 저는 Dialogflow FAQ 봇입니다. Dialogflow에 대해 궁금한 점이 있으신가요? 어떻게 도와드릴까요?

기본 대체 인텐트 수정

  1. 인텐트 > 기본 대체 인텐트
  2. 응답까지 아래로 스크롤합니다.
  3. 모든 텍스트 응답을 삭제합니다.
  4. 기본 탭에서 다음 응답을 만듭니다.
  • 죄송하지만 이 질문에 대한 해답을 알지 못합니다. 저희 웹사이트를 확인해 보셨나요? http://www.dialogflow.com?
  1. 저장을 클릭합니다.

온라인 기술 자료에 연결

지식 커넥터는 정의된 인텐트를 보완하며, 자동화된 응답을 찾기 위해 기술 문서를 파싱합니다. (예: FAQ 또는 CSV 파일, 온라인 웹사이트 또는 PDF 파일의 도움말) 지식 문서를 구성하려면 기술 문서 모음인 하나 이상의 기술 자료를 정의해야 합니다.

지식 커넥터 자세히 알아보기

한 번 시도해 보겠습니다.

  1. 메뉴에서 지식 (베타)을 선택합니다.

기술 자료

  1. 오른쪽 파란색 버튼(기술 자료 만들기)을 클릭합니다.
  2. 기술 자료 이름으로 입력합니다. Dialogflow FAQ를 클릭하고 저장을 누릅니다.
  3. 첫 번째 항목 만들기 링크를 클릭합니다.

기술 자료 첫 번째 항목

  1. 그러면 창이 열립니다.

다음 구성을 사용합니다.

문서 이름: DialogflowFAQ 지식 유형: FAQ MIME 유형: text/html

  1. 데이터를 로드하는 URL은 다음과 같습니다.

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

  1. 만들기를 클릭합니다.

기술 자료가 생성되었습니다.

기술 자료 생성됨

  1. 응답 섹션까지 아래로 스크롤한 다음 응답 추가를 클릭합니다.

다음 답변을 작성하고 저장을 누릅니다.

$Knowledge.Answer[1]
  1. 세부정보 보기를 클릭합니다.

세부정보 보기

  1. FAQ 웹페이지가 업데이트될 때 변경사항을 자동으로 가져오려면 자동 새로고침 사용을 선택하고 저장을 누릅니다.

그러면 Dialogflow에서 구현한 모든 FAQ가 표시됩니다. 간단합니다.

FAQ가 있는 온라인 HTML 웹사이트로 이동하여 에이전트로 FAQ를 가져올 수도 있습니다. 텍스트 블록이 포함된 PDF를 업로드할 수도 있으며, Dialogflow가 질문을 스스로 생성합니다.

이제 FAQ는 '추가'로 표시되어야 합니다. 에이전트에 추가할 수 있습니다. 기술 자료 FAQ로는 모델을 학습시킬 수 없습니다. 완전히 다른 방식으로 질문을 하면 자연어 이해 (머신러닝 모델)를 사용하지 않으므로 매칭되지 않을 수 있습니다. 이것이 FAQ를 인텐트로 변환하는 것이 중요한 이유입니다.

  1. 오른쪽의 시뮬레이터에서 문제를 테스트합니다.
  2. 모두 작동한다면 Flutter 앱으로 돌아가서 이 새로운 콘텐츠로 채팅과 음성 봇을 테스트해 보세요! Dialogflow에 로드한 질문을 합니다.

결과

6. 축하합니다

축하합니다. Dialogflow 챗봇 통합으로 첫 번째 Flutter 앱을 만들었습니다. 수고하셨습니다.

학습한 내용

  • Dialogflow Essentials에서 챗봇을 만드는 방법
  • Dialogflow를 Flutter 앱에 통합하는 방법
  • Dialogflow로 텍스트 인텐트를 감지하는 방법
  • 마이크를 통해 Dialogflow에 음성을 스트리밍하는 방법
  • 기술 자료 커넥터 사용 방법

다음 단계

이 Codelab에 만족하셨나요? 멋진 Dialogflow 실습을 살펴보세요.

Dart/Flutter용 Dialogflow gRPC 패키지를 어떻게 빌드했는지 관심이 있으신가요?