使用 Dialogflow 基本功能构建 Android 语音聊天机器人Flutter

1. 准备工作

在此 Codelab 中,您将学习如何将简单的 Dialogflow Essentials (ES) 文本和语音聊天机器人集成到 Flutter 应用中。Dialogflow ES 是一个用于构建对话界面的开发套件。因此,聊天机器人、语音聊天机器人和电话网关。所有这些都可以使用同一工具制作而成,甚至可以支持 20 多种语言的多个频道。Dialogflow 可以与 Google 助理、Slack 和 Facebook Messenger 等许多热门对话平台集成。如果您想为其中一个平台构建代理,则应从众多集成方案中选择一个。但是,如需为移动设备创建聊天机器人,您必须创建自定义集成。您将通过指定训练短语来创建意图,以训练底层机器学习模型。

本实验旨在反映常见的云开发者体验:

  1. 环境设置
  • Dialogflow:创建新的 Dialogflow ES 代理
  • Dialogflow:配置 Dialogflow
  • Google Cloud:创建服务账号
  1. Flutter:构建聊天应用
  • 创建一个 Flutter 项目
  • 配置设置和权限
  • 添加依赖项
  • 关联服务账号。
  • 在虚拟设备或实体设备上运行应用
  1. Flutter:构建支持语音转文字的聊天界面
  • 创建聊天界面
  • 关联聊天界面
  • 将 Dialogflow gRPC 软件包集成到应用中
  1. Dialogflow:对 Dialogflow 代理建模
  • 配置欢迎和后备 intent
  • 利用常见问题解答知识库

前提条件

  • Dart/Flutter 基本体验
  • 基本 Google Cloud Platform 体验
  • 具备使用 Dialogflow ES 的基本经验

构建内容

此 Codelab 将介绍如何构建一个移动常见问题解答聊天机器人,它可以解答有关 Dialogflow 工具的最常见问题。最终用户可以通过移动设备的内置麦克风与文字界面互动或流式传输语音,以获取答案。

学习内容

  • 如何使用 Dialogflow Essentials 创建聊天机器人
  • 如何使用 Dialogflow gRPC 软件包将 Dialogflow 集成到 Flutter 应用中
  • 如何使用 Dialogflow 检测文本意图
  • 如何通过麦克风将语音流式传输到 Dialogflow
  • 如何使用知识库连接器导入公开常见问题解答
  • 在虚拟或实体设备中通过文本和语音界面测试聊天机器人

所需条件

  • 您需要一个 Google 身份 / Gmail 地址才能创建 Dialogflow 代理。
  • 您需要拥有 Google Cloud Platform 的访问权限才能下载服务账号
  • Flutter 开发环境

设置您的 Flutter 开发环境

  1. 选择您要安装 Flutter 的操作系统。
  1. 您可以结合使用任何文本编辑器和我们的命令行工具,通过 Flutter 构建应用。不过,此研讨会将使用 Android Studio。适用于 Android Studio 的 Flutter 和 Dart 插件可为您提供代码补全、语法突出显示、微件编辑辅助、运行和调试支持等等。按照 https://flutter.dev/docs/get-started/editor 中的步骤操作

2. 环境设置

Dialogflow:创建新的 Dialogflow ES 代理

  1. 打开
  2. 在左栏中徽标的正下方,选择“Create New Agent”(创建新代理)。(请注意,不要点击显示“Global”(全球)的下拉菜单,因为我们需要一个为“Global”(全球)的 Dialogflow 实例来利用常见问题解答知识库。)
  3. 指定代理名称 yourname-dialogflow(使用您自己的名称)
  4. 选择 English - en 作为默认语言。
  5. 选择离您最近的时区作为默认时区。
  6. 请勿选择 Mega Agent。(借助此功能,您可以创建一个总体代理,该代理可以在“子级”代理之间进行编排。我们目前不需要该资源。)
  7. 点击创建

“Create new project”屏幕

配置 Dialogflow

  1. 点击左侧菜单中项目名称旁边的齿轮图标。

“Create new project”下拉菜单

  1. 输入以下代理说明:Dialogflow 常见问题解答聊天机器人
  2. 启用 Beta 版功能,切换开关。

Dialogflow 基本功能版 V2(Beta 版)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:构建 Chat 应用

创建样板应用

  1. 打开 Android Studio,然后选择启动新的 Flutter 项目
  2. 选择 Flutter Application 作为项目类型。然后点击“下一步”。
  3. 验证 Flutter SDK 路径是否指定了 SDK 的位置(如果文本字段为空,请选择“Install SDK...”)。
  4. 输入项目名称(例如 flutter_dialogflow_agent)。然后,点击下一步
  5. 修改软件包名称,然后点击 Finish

创建新的 Flutter 应用

这将创建一个使用 Material 组件的示例应用。

等待 Android Studio 安装相应 SDK 并创建项目。

设置和权限

  1. 我们将使用的音频录制器库 sound_stream 要求 minSdk 至少为 21。所以,让我们在 android/app/build.gradle 中的 defaultConfig 代码块中进行更改。(请注意,android 文件夹中有 2 个 build.gradle 文件,但应用文件夹中的是正确的文件。)
defaultConfig {
   applicationId "com.myname.flutter_dialogflow_agent"
   minSdkVersion 21
   targetSdkVersion 30
   versionCode flutterVersionCode.toInteger()
   versionName flutterVersionName
}
  1. 要授予麦克风使用权限,并让应用与在云端运行的 Dialogflow 代理通信,我们必须在 app/src/main/AndroidManifest.xml 文件中添加 INTERNET 和 RECORD_AUDIO 权限。您的 Flutter 项目中有多个 AndroidManifest.xml 文件,但您需要将该文件放在主文件夹中。您可以直接在清单标记内添加以下代码行。
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />

添加依赖项

我们将使用 sound_streamrxdartdialogflow_grpc 软件包。

  1. 添加 sound_stream 依赖项
$ flutter pub add sound_stream
Resolving dependencies...
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
  matcher 0.12.10 (0.12.11 available)
+ sound_stream 0.3.0
  test_api 0.4.2 (0.4.5 available)
  vector_math 2.1.0 (2.1.1 available)
Downloading sound_stream 0.3.0...
Changed 1 dependency!
  1. 添加 dialogflow_grpc 依赖项
flutter pub add dialogflow_grpc
Resolving dependencies...
+ archive 3.1.5
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
+ crypto 3.0.1
+ dialogflow_grpc 0.2.9
+ fixnum 1.0.0
+ googleapis_auth 1.1.0
+ grpc 3.0.2
+ http 0.13.4
+ http2 2.0.0
+ http_parser 4.0.0
  matcher 0.12.10 (0.12.11 available)
+ protobuf 2.0.0
  test_api 0.4.2 (0.4.5 available)
+ uuid 3.0.4
  vector_math 2.1.0 (2.1.1 available)
Downloading dialogflow_grpc 0.2.9...
Downloading grpc 3.0.2...
Downloading http 0.13.4...
Downloading archive 3.1.5...
Changed 11 dependencies!
  1. 添加 rxdart 依赖项
$ flutter pub add rxdart
Resolving dependencies...
  async 2.8.1 (2.8.2 available)
  characters 1.1.0 (1.2.0 available)
  matcher 0.12.10 (0.12.11 available)
+ rxdart 0.27.2
  test_api 0.4.2 (0.4.5 available)
  vector_math 2.1.0 (2.1.1 available)
Downloading rxdart 0.27.2...
Changed 1 dependency!

加载服务账号和 Google Cloud 项目信息

  1. 在项目中创建一个目录,并将其命名为 assets
  2. 将您从 Google Cloud 控制台下载的 credentials.json 文件移动到 assets 文件夹中。
  3. 打开 pubspec.yaml,然后将服务账号添加到 flutter 块。
flutter:
  uses-material-design: true
  assets:
    - assets/credentials.json

在实体设备上运行应用

如果您有 Android 设备,可以通过 USB 线将手机插入手机,然后在设备上进行调试。按照这些步骤在 Android 设备上通过开发者选项屏幕进行设置。

在虚拟设备上运行应用

如果您想在虚拟设备上运行应用,请按以下步骤操作:

  1. 点击工具 >AVD 管理器。(或从顶部的工具栏中选择 AVD Manager,下图中以粉色突出显示)

Android Studio 顶部工具栏

  1. 我们将创建一个目标 Android 虚拟设备,以便在没有实体设备的情况下测试我们的应用。如需了解详情,请参阅管理 AVD。选择新的虚拟设备后,双击即可启动该设备。

管理 AVD

虚拟设备

  1. 在 Android Studio 主工具栏中,通过下拉菜单选择一个 Android 设备作为目标,并确保已选中 main.dart。然后按 Run 按钮(绿色三角形)。

在 IDE 的底部,您会在控制台中看到这些日志。您会发现它正在安装 Android 和您的 Flutter 起始应用。这需要一点时间,在虚拟设备准备就绪后,您将可非常快速地进行更改。全部完成后,它将打开起始 Flutter 应用。

样板应用

  1. 让我们为聊天机器人应用启用麦克风。点击虚拟设备的 options 按钮以打开选项。在“麦克风”标签页中,启用全部 3 个开关。

AVD 选项

  1. 我们用热重载来演示一下更改速度有多快。

lib/main.dart 中,将 MyApp 类中的 MyHomePage title 更改为 Flutter Dialogflow Agent。此外,将 primarySwatch 更改为 Colors.orange。

第一个代码

保存文件,或点击 Android Studio 工具栏中的螺栓图标。您应该会在虚拟设备中看到直接进行的更改。

4. Flutter:构建支持 STT 的聊天界面

创建聊天界面

  1. lib 文件夹中创建一个新的 Flutter 微件文件。(右键点击 lib 文件夹,依次选择 New > Flutter Widget > Stateful widget),将此文件命名为 chat.dart

将以下代码粘贴到此文件中。此 dart 文件会创建聊天界面。Dialogflow 还无法运行,它只是所有组件的布局,以及集成了麦克风组件以允许数据流。文件中的注释会指出,我们稍后会在其中集成 Dialogflow。

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sound_stream/sound_stream.dart';

// TODO import Dialogflow


class Chat extends StatefulWidget {
  Chat({Key key}) : super(key: key);

  @override
  _ChatState createState() => _ChatState();
}

class _ChatState extends State<Chat> {
  final List<ChatMessage> _messages = <ChatMessage>[];
  final TextEditingController _textController = TextEditingController();

  bool _isRecording = false;

  RecorderStream _recorder = RecorderStream();
  StreamSubscription _recorderStatus;
  StreamSubscription<List<int>> _audioStreamSubscription;
  BehaviorSubject<List<int>> _audioStream;

  // TODO DialogflowGrpc class instance

  @override
  void initState() {
    super.initState();
    initPlugin();
  }

  @override
  void dispose() {
    _recorderStatus?.cancel();
    _audioStreamSubscription?.cancel();
    super.dispose();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlugin() async {
    _recorderStatus = _recorder.status.listen((status) {
      if (mounted)
        setState(() {
          _isRecording = status == SoundStreamStatus.Playing;
        });
    });

    await Future.wait([
      _recorder.initialize()
    ]);



    // TODO Get a Service account

  }

  void stopStream() async {
    await _recorder.stop();
    await _audioStreamSubscription?.cancel();
    await _audioStream?.close();
  }

  void handleSubmitted(text) async {
    print(text);
    _textController.clear();

    //TODO Dialogflow Code

  }

  void handleStream() async {
    _recorder.start();

    _audioStream = BehaviorSubject<List<int>>();
    _audioStreamSubscription = _recorder.audioStream.listen((data) {
      print(data);
      _audioStream.add(data);
    });


    // TODO Create SpeechContexts
    // Create an audio InputConfig

    // TODO Make the streamingDetectIntent call, with the InputConfig and the audioStream
    // TODO Get the transcript and detectedIntent and show on screen

  }

  // The chat interface
  //
  //------------------------------------------------------------------------------------
  @override
  Widget build(BuildContext context) {
    return Column(children: <Widget>[
      Flexible(
          child: ListView.builder(
            padding: EdgeInsets.all(8.0),
            reverse: true,
            itemBuilder: (_, int index) => _messages[index],
            itemCount: _messages.length,
          )),
      Divider(height: 1.0),
      Container(
          decoration: BoxDecoration(color: Theme.of(context).cardColor),
          child: IconTheme(
            data: IconThemeData(color: Theme.of(context).accentColor),
            child: Container(
              margin: const EdgeInsets.symmetric(horizontal: 8.0),
              child: Row(
                children: <Widget>[
                  Flexible(
                    child: TextField(
                      controller: _textController,
                      onSubmitted: handleSubmitted,
                      decoration: InputDecoration.collapsed(hintText: "Send a message"),
                    ),
                  ),
                  Container(
                    margin: EdgeInsets.symmetric(horizontal: 4.0),
                    child: IconButton(
                      icon: Icon(Icons.send),
                      onPressed: () => handleSubmitted(_textController.text),
                    ),
                  ),
                  IconButton(
                    iconSize: 30.0,
                    icon: Icon(_isRecording ? Icons.mic_off : Icons.mic),
                    onPressed: _isRecording ? stopStream : handleStream,
                  ),
                ],
              ),
            ),
          )
      ),
    ]);
  }
}


//------------------------------------------------------------------------------------
// The chat message balloon
//
//------------------------------------------------------------------------------------
class ChatMessage extends StatelessWidget {
  ChatMessage({this.text, this.name, this.type});

  final String text;
  final String name;
  final bool type;

  List<Widget> otherMessage(context) {
    return <Widget>[
      new Container(
        margin: const EdgeInsets.only(right: 16.0),
        child: CircleAvatar(child: new Text('B')),
      ),
      new Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(this.name,
                style: TextStyle(fontWeight: FontWeight.bold)),
            Container(
              margin: const EdgeInsets.only(top: 5.0),
              child: Text(text),
            ),
          ],
        ),
      ),
    ];
  }

  List<Widget> myMessage(context) {
    return <Widget>[
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            Text(this.name, style: Theme.of(context).textTheme.subtitle1),
            Container(
              margin: const EdgeInsets.only(top: 5.0),
              child: Text(text),
            ),
          ],
        ),
      ),
      Container(
        margin: const EdgeInsets.only(left: 16.0),
        child: CircleAvatar(
            child: Text(
              this.name[0],
              style: TextStyle(fontWeight: FontWeight.bold),
            )),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: this.type ? myMessage(context) : otherMessage(context),
      ),
    );
  }
}

在 chat.dart 文件中搜索微件 build。这会构建聊天机器人界面,其中包含:

  • 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 键或点按“send”按钮时,您将看到该文本查询记录在 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 文件存储在资源文件夹中,因为这并不安全。

进行 detectIntent 调用

  1. 现在,找到 handleSubmitted() 方法,这正是其中的神奇之处。在 TODO 注释的正下方,添加以下代码。此代码会将用户输入的消息添加到 ListView:
ChatMessage message = ChatMessage(
 text: text,
 name: "You",
 type: true,
);

setState(() {
 _messages.insert(0, message);
});
  1. 现在,在上一个代码的正下方,我们将进行 detectIntent 调用,传入输入中的文本和 languageCode。- 结果(在 data.queryResult.fulfillment 内)将输出到 ListView 中:
DetectIntentResponse data = await dialogflow.detectIntent(text, 'en-US');
String fulfillmentText = data.queryResult.fulfillmentText;
if(fulfillmentText.isNotEmpty) {
  ChatMessage botMessage = ChatMessage(
    text: fulfillmentText,
    name: "Bot",
    type: false,
  );

  setState(() {
    _messages.insert(0, botMessage);
  });
}
  1. 启动虚拟设备,并测试检测意图调用。类型:hi应用应该会以默认的欢迎辞问候语。当您输入其他内容时,系统会为您返回默认的后备选项。

进行 StreamingDetectIntent 调用

  1. 现在找到 handleStream() 方法,这就是音频流式传输的神奇之处。首先,在第一个 TODO 正下方,创建一个音频 InputConfigV2beta1,并在其中使用 offsetList 来使语音模型实现偏差。由于我们使用的是手机(虚拟设备),因此 sampleHertz 为 16000,编码为 Linear16。这取决于所使用的计算机硬件 / 麦克风。对于我的内置 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 对象调用 streamingDetectIntent 方法,该对象用于存放 Dialogflow 会话:
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 默认欢迎 intent。结果将输出在屏幕上。现在 Flutter 可与 Dialogflow 集成了。我们可以开始处理聊天机器人对话了。

5. Dialogflow:为 Dialogflow 代理建模

Dialogflow Essentials 是一款用于构建对话界面的开发套件。因此,聊天机器人、语音聊天机器人和电话网关。所有这些都可以使用同一工具制作而成,甚至可以支持 20 多种语言的多个频道。Dialogflow 用户体验设计师(代理建模者、语言学家)或开发者通过指定训练短语来训练底层机器学习模型来创建意图。

intent 会对用户的意图进行分类。您可以为每个 Dialogflow ES 代理定义多个意图,组合意图可以处理一个完整的对话。每个意图都可以包含参数和响应。

匹配意图也称为意图分类或意图匹配。这是 Dialogflow ES 中的主要概念。一旦匹配了某个意图,它就可以返回响应,收集参数(实体提取)或触发 webhook 代码 (fulfillment),例如,从数据库中提取数据。

当最终用户在聊天机器人中撰写或说出某些内容(称为用户表达或话语)时,Dialogflow ES 会根据训练短语将该表述与 Dialogflow 代理的最佳意图进行匹配。在后台,Dialogflow ES 机器学习模型是基于这些训练短语训练的。

Dialogflow ES 使用名为“上下文”的概念。就像人一样,Dialogflow ES 可以在第 2 和第 3 轮回合中记住上下文。这就是它跟踪之前的用户话语的方式。

详细了解 Dialogflow intent

修改默认欢迎意图

创建新的 Dialogflow 代理时,系统会自动创建两个默认意图。默认欢迎意图是您与代理开始对话时到达的第一个流程。默认后备意图 (Default Fallback Intent) 是当代理无法理解您的指令或无法将某个意图与您刚刚说过的内容相匹配时您获得的流程。

以下是“默认欢迎意图”的欢迎消息:

用户

客服人员

您好!

“您好,我是 Dialogflow 常见问题解答的聊天机器人,我可以回答有关 Dialogflow 的问题。”

“您想了解什么?”

  1. 点击意图 >默认欢迎意图
  2. 向下滚动到响应
  3. 清除所有文本响应。
  4. 在默认标签页中,创建以下 2 个响应:
  • 您好,我是 Dialogflow 常见问题解答聊天机器人,我可以回答关于 Dialogflow 的问题。您想了解什么?
  • 您好,我是 Dialogflow 常见问题解答聊天机器人,您有什么关于 Dialogflow 的问题吗?您需要什么帮助?

配置应类似于此屏幕截图。

修改默认欢迎意图

  1. 点击保存
  2. 我们来测试此 intent。首先,我们可以在 Dialogflow Simulator.Type: Hello 中进行测试。它应该返回以下消息之一:
  • 您好,我是 Dialogflow 常见问题解答聊天机器人,我可以回答关于 Dialogflow 的问题。您想了解什么?
  • 您好,我是 Dialogflow 常见问题解答聊天机器人,您有什么关于 Dialogflow 的问题吗?您需要什么帮助?

修改默认回退 Intent

  1. 点击意图 >默认后备意图
  2. 向下滚动到响应
  3. 清除所有文本响应。
  4. 在默认标签页中,创建以下响应:
  • 很遗憾,我不知道这个问题的答案。您查看过我们的网站吗?http://www.dialogflow.com?
  1. 点击保存

连接到在线知识库

知识连接器是对所定义的意图的补充。它们通过解析知识文档来找出自动响应。(例如,CSV 文件、在线网站,甚至是 PDF 文件内的常见问题解答或文章!)如需配置知识库,您需要定义一个或多个知识库,即知识文档的集合。

详细了解知识连接器。

我们来试试看。

  1. 在菜单中选择知识(Beta 版)。

知识库

  1. 点击右侧蓝色按钮:Create Knowledge Base
  2. 输入知识库名称;Dialogflow 常见问题解答并点击保存
  3. 点击 Create the first one(创建首个)链接。

知识库第一

  1. 系统会打开一个窗口。

使用以下配置:

文档名称:DialogflowFAQ 知识类型:FAQ MIME 类型:text/html

  1. 我们从中加载数据的网址如下:

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

  1. 点击创建

知识库已创建:

已创建知识库

  1. 向下滚动到“响应”部分,然后点击添加响应

回答以下问题并点击保存

$Knowledge.Answer[1]
  1. 点击查看详情

查看详细信息

  1. 选择启用自动重新加载以在常见问题解答网页更新时自动提取更改,然后点击保存

这会显示您在 Dialogflow 中实现的所有常见问题解答。这很简单!

您也可以将光标指向包含常见问题解答的在线 HTML 网站,将常见问题解答导入您的代理。您甚至可以上传包含一块文本的 PDF 文件,Dialogflow 会自行提出问题。

现在,常见问题解答应被视为“额外内容”可添加到代理中。知识库常见问题解答无法训练模型。因此,以完全不同的方式提问,可能不会得到匹配结果,因为它没有利用自然语言理解(机器学习模型)。因此,有时有必要将常见问题解答转换为意图。

  1. 在右侧的模拟器中测试问题。
  2. 一切正常后,请返回您的 Flutter 应用,并使用这些新内容测试您的聊天和语音聊天机器人!提出您加载到 Dialogflow 中的问题。

结果

6. 恭喜

恭喜,您已成功集成了 Dialogflow 聊天机器人,创建了您的第一个 Flutter 应用,非常棒!

所学内容

  • 如何使用 Dialogflow Essentials 创建聊天机器人
  • 如何将 Dialogflow 集成到 Flutter 应用中
  • 如何使用 Dialogflow 检测文本意图
  • 如何通过麦克风将语音流式传输到 Dialogflow
  • 如何使用知识库连接器

后续操作

喜欢此 Codelab?看看这些优秀的 Dialogflow 实验!

有兴趣了解我是如何为 Dart/Flutter 构建 Dialogflow gRPC 软件包的吗?