1. 시작하기 전에
이 Codelab에서는 간단한 Dialogflow Essentials (ES) 텍스트 및 음성 봇을 Flutter 앱에 통합하는 방법을 알아봅니다. Dialogflow ES는 대화형 UI를 빌드하기 위한 개발 도구 모음입니다. 따라서 챗봇, 음성 봇, 전화 게이트웨이 모두 동일한 도구로 빌드할 수 있으며 20개가 넘는 다양한 언어로 여러 채널을 지원할 수도 있습니다. Dialogflow는 Google 어시스턴트, Slack, Facebook Messenger와 같은 인기 있는 여러 대화 플랫폼과 통합됩니다. 이러한 플랫폼 중 하나에 대해 에이전트를 빌드하려면 여러 통합 옵션 중 하나를 사용해야 합니다. 하지만 모바일 기기용 챗봇을 만들려면 맞춤 통합을 만들어야 합니다. 기본 머신러닝 모델을 학습시키기 위해 학습 문구를 지정하여 인텐트를 만듭니다.
이 실습은 일반적인 클라우드 개발자 경험을 반영하여 다음과 같은 순서로 진행됩니다.
- 환경 설정
- Dialogflow: 새 Dialogflow ES 에이전트 만들기
- Dialogflow: Dialogflow 구성
- Google Cloud: 서비스 계정 만들기
- Flutter: 채팅 애플리케이션 빌드
- Flutter 프로젝트 생성
- 설정 및 권한 구성
- 종속 항목 추가
- 서비스 계정에 연결
- 가상 기기 또는 실제 기기에서 애플리케이션 실행
- Flutter: 음성 텍스트 변환 지원으로 채팅 인터페이스 빌드
- 채팅 인터페이스 만들기
- 채팅 인터페이스 연결
- Dialogflow gRPC 패키지를 앱에 통합
- 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 개발 환경 설정
- Flutter를 설치할 운영체제를 선택합니다.
- macOS 사용자: https://flutter.dev/docs/get-started/install/macos
- Windows: https://flutter.dev/docs/get-started/install/windows
- Linux: https://flutter.dev/docs/get-started/install/linux
- ChromeOS: https://flutter.dev/docs/get-started/install/chromeos
- 명령줄 도구와 결합된 텍스트 편집기를 사용하여 Flutter로 앱을 빌드할 수 있습니다. 하지만 이 워크숍에서는 Android 스튜디오를 사용합니다. Android 스튜디오용 Flutter 및 Dart 플러그인은 코드 완성, 구문 강조표시, 위젯 수정 지원, 실행 및 디버그 지원 등을 제공합니다. https://flutter.dev/docs/get-started/editor의 단계를 따르세요.
2. 환경 설정
Dialogflow: 새 Dialogflow ES 에이전트 만들기
- 을 엽니다.
- 왼쪽 막대에서 로고 바로 아래에 있는 드롭다운에서 '새 에이전트 만들기'를 선택합니다. (참고로 '전역'이라고 표시된 드롭다운을 클릭하지 마세요. FAQ 기술 자료를 사용하려면 전역 Dialogflow 인스턴스가 필요합니다.)
- 에이전트 이름
yourname-dialogflow지정 (자신의 이름 사용) - 기본 언어로 영어 - en을 선택합니다.
- 기본 시간대로 가장 가까운 시간대를 선택합니다.
- 메가 에이전트를 선택하지 마세요. 이 기능을 사용하면 '하위' 에이전트 간에 오케스트레이션할 수 있는 포괄적인 에이전트를 만들 수 있습니다. 지금은 필요하지 않습니다.)
- 만들기를 클릭합니다.

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

- Dialogflow FAQ Chatbot이라는 에이전트 설명을 입력합니다.
- 베타 기능을 사용 설정하고 스위치를 전환합니다.

- 음성 탭을 클릭하고 자동 음성 적응 체크박스가 활성화되어 있는지 확인합니다.
- 원하는 경우 첫 번째 스위치를 전환할 수도 있습니다. 이렇게 하면 음성 모델이 개선되지만 Dialogflow 무료 체험을 업그레이드할 때만 사용할 수 있습니다.
- 저장을 클릭합니다.
Google Cloud: 서비스 계정 가져오기
Dialogflow에서 에이전트를 만든 후 Google Cloud 콘솔에서 Google Cloud 프로젝트를 만들어야 합니다.
- Google Cloud 콘솔을 엽니다().
- Dialogflow와 동일한 Google 계정으로 로그인하고 상단의 파란색 표시줄에서 프로젝트
yourname-dialogflow를 선택합니다. - 그런 다음 상단 툴바에서
Dialogflow API를 검색하고 드롭다운에서 Dialogflow API 결과를 클릭합니다.

- 파란색 관리 버튼을 클릭하고 왼쪽 메뉴 바에서 사용자 인증 정보를 클릭합니다. (Dialogflow가 아직 사용 설정되지 않은 경우 먼저 사용 설정을 누르세요.)

- 화면 상단의 사용자 인증 정보 만들기를 클릭하고 서비스 계정을 선택합니다.

- 서비스 계정 이름(
flutter_dialogflow), ID, 설명을 지정하고 만들기를 누릅니다.

- 2단계에서
Dialogflow API Admin역할을 선택하고 계속 및 완료를 클릭해야 합니다. flutter_dialogflow서비스 계정을 클릭하고 키 탭을 클릭한 후 키 추가 > 새 키 만들기를 클릭합니다.

- JSON 키를 만듭니다. 이름을
credentials.json으로 바꾸고 하드 드라이브의 안전한 위치에 저장합니다. 이 값은 나중에 사용합니다.

좋습니다. 필요한 모든 도구가 올바르게 설정되었습니다. 이제 앱에 Dialogflow를 통합하는 작업을 시작할 수 있습니다.
3. Flutter: 채팅 애플리케이션 빌드
보일러플레이트 앱 만들기
- Android 스튜디오를 열고 Start a new Flutter project를 선택합니다.
- 프로젝트 유형으로 Flutter Application을 선택합니다. 그런 다음 '다음'을 클릭합니다
- Flutter SDK 경로가 SDK의 위치를 지정하는지 확인합니다 (텍스트 필드가 비어 있다면 'Install SDK...'를 선택합니다).
- 프로젝트 이름 (예:
flutter_dialogflow_agent)을 입력합니다. 그런 다음 다음을 클릭합니다. - 패키지 이름을 수정하고 Finish를 클릭합니다.

그러면 Material Components가 포함된 샘플 애플리케이션이 생성됩니다.
Android 스튜디오가 SDK를 설치하고 프로젝트를 만들 때까지 기다립니다.
설정 및 권한
- 사용할 오디오 레코더 라이브러리 sound_stream에는 최소 SDK가 21 이상이어야 합니다. 따라서 defaultConfig 블록의 android/app/build.gradle에서 이를 변경하겠습니다. (Android 폴더에 build.gradle 파일이 2개 있지만 app 폴더에 있는 파일이 올바른 파일입니다.)
defaultConfig {
applicationId "com.myname.flutter_dialogflow_agent"
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
- 마이크에 권한을 부여하고 앱이 클라우드에서 실행되는 Dialogflow 에이전트에 연결할 수 있도록 하려면 app/src/main/AndroidManifest.xml 파일에 INTERNET 및 RECORD_AUDIO 권한을 추가해야 합니다. Flutter 프로젝트에 AndroidManifest.xml 파일이 여러 개 있지만 main 폴더에 있는 파일이 필요합니다. 매니페스트 태그 바로 안에 줄을 추가하면 됩니다.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
종속 항목 추가
sound_stream, rxdart, dialogflow_grpc 패키지를 사용합니다.
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!
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!
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 프로젝트 정보 로드
- 프로젝트에서 디렉터리를 만들고 이름을
assets로 지정합니다. - Google Cloud 콘솔에서 다운로드한 credentials.json 파일을 assets 폴더로 이동합니다.
- pubspec.yaml을 열고 서비스 계정을 Flutter 블록에 추가합니다.
flutter:
uses-material-design: true
assets:
- assets/credentials.json
실제 기기에서 애플리케이션 실행
Android 기기가 있는 경우 USB 케이블을 통해 휴대전화를 연결하고 기기에서 디버깅할 수 있습니다. Android 기기의 개발자 옵션 화면을 통해 이를 설정하려면 이 단계를 따르세요.
가상 기기에서 애플리케이션 실행
가상 기기에서 애플리케이션을 실행하려면 다음 단계를 따르세요.
- Tools> AVD Manager를 클릭합니다. (또는 아래 그림에서 분홍색으로 강조 표시된 상단 툴바에서 AVD Manager를 선택합니다.)

- 실제 기기 없이 애플리케이션을 테스트할 수 있도록 타겟 Android 가상 기기를 만듭니다. 자세한 내용은 AVD 관리를 참고하세요. 새 가상 기기를 선택한 후 더블클릭하여 시작할 수 있습니다.


- 기본 Android 스튜디오 툴바에서 드롭다운을 통해 Android 기기를 타겟으로 선택하고 main.dart가 선택되어 있는지 확인합니다. 그런 다음 실행 버튼 (녹색 삼각형)을 누릅니다.
IDE 하단에 콘솔의 로그가 표시됩니다. Android와 시작 Flutter 앱이 설치되는 것을 확인할 수 있습니다. 가상 기기가 준비되면 변경사항을 매우 빠르게 적용할 수 있습니다. 모든 작업이 완료되면 시작 Flutter 앱이 열립니다.

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

- 핫 리로드를 사용해 변경사항이 얼마나 빠르게 적용되는지 확인해 보겠습니다.
lib/main.dart에서 MyApp 클래스의 MyHomePage title을 Flutter Dialogflow Agent로 변경합니다. primarySwatch를 Colors.orange로 변경합니다.

파일을 저장하거나 Android 스튜디오 툴바에서 번개 아이콘을 클릭합니다. 가상 기기에서 직접 변경한 사항이 표시됩니다.
4. Flutter: STT 지원으로 채팅 인터페이스 빌드
채팅 인터페이스 만들기
- lib 폴더에 새 Flutter 위젯 파일을 만듭니다. (lib 폴더를 마우스 오른쪽 버튼으로 클릭하고 새로 만들기 > Flutter 위젯 > 상태 저장 위젯) 이 파일을
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를 검색합니다. 이는 다음을 포함하는 챗봇 인터페이스를 빌드합니다.
- 사용자와 챗봇의 모든 채팅 풍선을 포함하는 ListView 아바타와 텍스트가 포함된 채팅 메시지를 만드는 ChatMessage 클래스를 사용합니다.
- 텍스트 쿼리를 입력하는 TextField
- Dialogflow에 텍스트 쿼리를 전송하기 위한 전송 아이콘이 있는 IconButton
- Dialogflow에 오디오 스트림을 전송하는 마이크가 있는 IconButton입니다. 이 버튼을 누르면 상태가 변경됩니다.
채팅 인터페이스 연결
- 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())
);
}
}
- 애플리케이션을 실행합니다. (앱이 이전에 시작된 경우. 가상 기기를 중지하고 main.dart를 다시 실행합니다. 채팅 인터페이스로 앱을 처음 실행할 때 마이크를 허용할지 묻는 권한 팝업이 표시됩니다. 앱 사용 중에만 허용을 클릭합니다.

- 텍스트 영역과 버튼을 사용해 보세요. 텍스트 쿼리를 입력하고 Enter 키를 누르거나 보내기 버튼을 탭하면 Android 스튜디오의 Run 탭에 텍스트 쿼리가 로깅됩니다. 마이크 버튼을 탭했다가 중지하면 실행 탭에 오디오 스트림이 기록됩니다.

이제 애플리케이션을 Dialogflow와 통합할 준비가 되었습니다.
Flutter 앱을 Dialogflow_gRPC와 통합
- chat.dart를 열고 다음 가져오기를 추가합니다.
import 'package:dialogflow_grpc/dialogflow_grpc.dart';
import 'package:dialogflow_grpc/generated/google/cloud/dialogflow/v2beta1/session.pb.dart';
- 파일 상단의
// TODO DialogflowGrpcV2Beta1 class instance바로 아래에 Dialogflow 클래스 인스턴스를 보유하는 다음 줄을 추가합니다.
DialogflowGrpcV2Beta1 dialogflow;
- 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 파일을 assets 폴더에 저장하면 안 됩니다.
detectIntent 호출하기
- 이제
handleSubmitted()메서드를 찾습니다. 여기에서 마법이 시작됩니다. TODO 주석 바로 아래에 다음 코드를 추가합니다. 이 코드는 사용자가 입력한 메시지를 ListView에 추가합니다.
ChatMessage message = ChatMessage(
text: text,
name: "You",
type: true,
);
setState(() {
_messages.insert(0, message);
});
- 이제 이전 코드 바로 아래에서 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);
});
}
- 가상 기기를 시작하고 감지 인텐트 호출을 테스트합니다. 유형:
hi기본 환영 메시지로 인사해야 합니다. 다른 내용을 입력하면 기본 대체가 반환됩니다.
streamingDetectIntent 호출하기
- 이제
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]
);
- 다음으로 Dialogflow 세션을 보유하는
dialogflow객체에서streamingDetectIntent메서드를 호출합니다.
final responseStream = dialogflow.streamingDetectIntent(config, _audioStream);
- 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는 두 번째와 세 번째 턴에서 컨텍스트를 기억할 수 있습니다. 이러한 방식으로 이전 사용자 발화를 추적할 수 있습니다.
Dialogflow 인텐트에 대한 자세한 정보는 여기를 참고하세요.
기본 시작 인텐트 수정
새 Dialogflow 에이전트를 만들면 두 개의 기본 인텐트가 자동으로 생성됩니다. 기본 시작 인텐트는 에이전트와 대화를 시작할 때 가장 먼저 표시되는 흐름입니다. 기본 대체 인텐트는 에이전트가 사용자를 이해할 수 없거나 사용자가 방금 말한 내용과 일치하는 인텐트를 찾을 수 없을 때 표시되는 흐름입니다.
다음은 기본 시작 인텐트의 환영 메시지입니다.
사용자 | Agent |
안녕하세요. | "안녕하세요. Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변해 드릴 수 있습니다." |
- 인텐트 > 기본 시작 인텐트를 클릭합니다.
- 응답까지 아래로 스크롤합니다.
- 모든 텍스트 응답을 지웁니다.
- 기본 탭에서 다음 2개의 응답을 만듭니다.
- 안녕하세요. Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변해 드릴 수 있습니다. 무엇을 알고 싶으신가요?
- 안녕하세요. Dialogflow FAQ 봇입니다. Dialogflow에 대해 궁금한 점이 있으신가요? 무엇을 도와드릴까요?
구성은 다음 스크린샷과 유사해야 합니다.

- 저장을 클릭합니다.
- 이 인텐트를 테스트해 보겠습니다. 먼저 Dialogflow 시뮬레이터에서 테스트할 수 있습니다.Hello라고 입력합니다. 다음 메시지 중 하나가 반환되어야 합니다.
- 안녕하세요. Dialogflow FAQ 봇입니다. Dialogflow에 관한 질문에 답변해 드릴 수 있습니다. 무엇을 알고 싶으신가요?
- 안녕하세요. Dialogflow FAQ 봇입니다. Dialogflow에 대해 궁금한 점이 있으신가요? 무엇을 도와드릴까요?
기본 대체 인텐트 수정
- 인텐트 > 기본 대체 인텐트를 클릭합니다.
- 응답까지 아래로 스크롤합니다.
- 모든 텍스트 응답을 지웁니다.
- 기본 탭에서 다음 응답을 만듭니다.
- 죄송하지만 이 질문에 대한 답변을 모르겠습니다. 저희 웹사이트(http://www.dialogflow.com)를 확인하셨나요?
- 저장을 클릭합니다.
온라인 기술 자료에 연결
지식 커넥터는 정의된 인텐트를 보완하며, 자동 응답을 찾도록 기술 문서를 파싱합니다. (예: CSV 파일, 온라인 웹사이트 또는 PDF 파일의 FAQ 또는 도움말) 지식 커넥터를 구성하려면 기술 문서 집합 형태의 기술 자료를 하나 이상 정의해야 합니다.
한번 시도해 보겠습니다.
- 메뉴에서 지식 (베타)을 선택합니다.

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

- 그러면 창이 열립니다.
다음 구성을 사용합니다.
문서 이름: DialogflowFAQ 지식 유형: FAQ MIME 유형: text/html
- 데이터를 로드하는 URL은 다음과 같습니다.
https://www.leeboonstra.dev/faqs/
- 만들기를 클릭합니다.
기술 자료가 생성되었습니다.

- '응답' 섹션까지 아래로 스크롤하고 응답 추가를 클릭합니다.
다음 답변을 만들고 저장을 누릅니다.
$Knowledge.Answer[1]
- 세부정보 보기를 클릭합니다.

- FAQ 웹페이지가 업데이트될 때 변경사항을 자동으로 가져오려면 자동 새로고침 사용 설정을 선택하고 저장을 클릭합니다.
Dialogflow에 구현한 모든 FAQ가 표시됩니다.간단하죠?
FAQ가 포함된 온라인 HTML 웹사이트를 지정하여 FAQ를 에이전트로 가져올 수도 있습니다. 텍스트 블록이 포함된 PDF를 업로드하면 Dialogflow에서 질문을 직접 생성할 수도 있습니다.
이제 FAQ는 인텐트 흐름 옆에 있는 상담사에 추가할 수 있는 '추가 기능'으로 간주됩니다. 기술 자료 FAQ로는 모델을 학습시킬 수 없습니다. 따라서 완전히 다른 방식으로 질문하면 자연어 이해 (머신러닝 모델)를 사용하지 않기 때문에 일치하는 항목이 없을 수 있습니다. 따라서 FAQ를 인텐트로 변환하는 것이 유용한 경우가 있습니다.
- 오른쪽의 시뮬레이터에서 질문을 테스트합니다.
- 모든 것이 작동하면 Flutter 앱으로 돌아가 이 새로운 콘텐츠로 채팅 및 음성 봇을 테스트하세요. Dialogflow에 로드한 질문을 합니다.

6. 축하합니다
축하합니다. Dialogflow 챗봇 통합을 사용하여 첫 번째 Flutter 앱을 만들었습니다.
학습한 내용
- Dialogflow Essentials로 챗봇을 만드는 방법
- Dialogflow를 Flutter 앱에 통합하는 방법
- Dialogflow로 텍스트 인텐트를 감지하는 방법
- 마이크를 통해 음성을 Dialogflow로 스트리밍하는 방법
- 기술 자료 커넥터를 사용하는 방법
다음 단계
이 Codelab이 재미있었나요? 이 멋진 Dialogflow 실습을 살펴보세요.
- Dialogflow와 Google 어시스턴트 통합
- Dialogflow와 Google Chat 통합
- Dialogflow로 Google 어시스턴트용 작업 빌드하기 (레벨 1)
- Dialogflow를 Google Calendar와 통합하여 풀필먼트 이해하기 +. 첫 번째 Flutter 앱 빌드하기
Dart/Flutter용 Dialogflow gRPC 패키지를 빌드한 방법이 궁금하신가요?
- Google Cloud gRPC API 사용을 위한 숨겨진 매뉴얼 블로그 게시물을 확인하세요.