这个实验室的目标是让你了解并使用Google的Dialogflow服务。Dialogflow是一个可以让你创建聊天机器人的服务,这个过程不需要或者只需要很少的编程技能。
为了完成这个实验室,你需要在以下网站上创建账号:
Dialogflow: www.dialogflow.com (如果你有Google账号,可以直接使用它登录);
ngrok: www.ngrok.com (同样,如果你有Google账号,可以使用它登录)。
完成ngrok的账号创建后,下载与你的操作系统相对应的安装程序,并在你的电脑上安装它。接着,访问网址https://dashboard.ngrok.com/get-started/setup/macos,找到“tab Authentication>Your Authtoken”标签页,并复制命令行。
苹果系统
使用以下命令通过 Homebrew 安装 ngrok:
brew install ngrok/ngrok/ngrok
运行以下命令将您的 authtoken 添加到默认的ngrok.yml配置文件中。
ngrok config add-authtoken 2biVXhWVYnruFK4jVDsdEc6Eutp_3JcWjV6eQMvL32TtTf5VN
/usr/local/bin
如果你打算在本地服务器上使用Python编程,那么你还需要安装Flask包:
pip3 install Flask --user
要在Dialogflow下创建一个聊天机器人,你需要完成以下四个步骤:
1. 在纸上设计对话流程;
2. 在Dialogflow中创建一个代理(agent),添加意图(intentions)、动作(actions)、参数(parameters)、实体(entities)、上下文(contexts);
3. 设置与你机器上的服务器的通讯;
4. 在服务器层面编写代码,以便与聊天机器人对话并为其提供信息。
我已经为一个旅行预订聊天机器人准备了一些数据,你可以使用这些数据,或者创建另一个难度相当的聊天机器人。
设计对话的第一步是在纸上绘制出所有可能的交互图谱,展示在所需对话中两个参与者之间的所有可能互动。首先记录标准情况(即信息顺利提供,交流顺畅的情况),然后预测可能由人类给出错误答案的情况,以及人类对其决策进行反悔的情况等。
一旦考虑了所有的分支情况,就需要用不同颜色标记两个参与者的意图以及交换的信息(即对应不同类型值的“实体”,如日期、数量、产品等)。
只要人类的回答有所不同,我们就可以编写一个简单的对话,在这个对话中分析回答并检测意图。但是,一旦在对话的两个不同位置发现相同的潜在回答(例如“是”),我们就需要定义上下文。因此,意图的检测将在给定的上下文中进行,并导致转移到另一个上下文。
当你的对话在纸上用回答、意图、实体和上下文标记完整后,就可以开始实施了。在实施过程中,保持这张纸张在身边,并在图谱上进行测试,看看Dialogflow如何跟随所绘制的图谱。尝试让聊天机器人陷入困境。
在语言学课上我们会看到,Grice的会话原则支配着人类的对话,理想情况下聊天机器人也应遵循这些原则。
我们的项目是建立一个旅行社预订对话:
我们的目标是实现这种行为,以及更好的功能:如果关于方式、起点、目的地和日期的信息同时给出,那么这些信息也会被收集。
在Dialogflow的网页上,我们访问 https://dialogflow.cloud.google.com/#/getStarted 来开始创建一个代理。我们为其选择一个名称、一种语言以及一个时区。
创建完成后,我们需要提供:
实体在Dialogflow中用于识别用户对话中的关键信息。在这个例子中,涉及两个主要实体:出行方式和二元答案(是或否)。
步骤如下:
1. 创建名为@travelmode的实体:我们首先点击“实体(Entities)”按钮,然后点击右侧的“+”号,输入实体名称“travelmode”。在接下来的空白行中,我们将输入所有可能的出行方式类型,并在右侧填写它们的同义词。例如,飞行(flight)的同义词包括飞机(plane)、飞机(aeroplane)和航班(airflight);火车(train)的同义词包括铁路(railways);公交车(bus)的同义词包括道路交通(road transportation);船(ship)的同义词包括海(sea)和小船(boat)。
2. 保存实体:完成实体及其同义词的输入后,不要忘记点击“保存(Save)”以保存这些信息。
3. 创建二元答案实体:我们还需要创建一个名为“choice”的实体,用于识别用户的是或否回答。点击“创建实体(CREATE ENTITY)”,并为其添加值“yes”和“no”,以及它们的同义词,比如“绝对是(absolutely)”、“绝对不(definitely not)”、“好的(ok)”、“当然(sure)”等。
通过这样的设置,Dialogflow代理能够理解和处理用户关于出行方式的查询,以及接收用户的确认或否认回答。这对于创建流畅的对话体验至关重要。
这段文字描述的是在Dialogflow中创建和配置意图(Intents)的过程,特别是默认欢迎意图(Default Welcome Intent)和默认后备意图(Default Fallback Intent)。意图包含一个或多个训练短语、一个动作名称、参数和响应:
这是当用户开始与代理交互时触发的意图。它通常包含一系列的预定义问候语。在Dialogflow中设置这个意图时,选择the Default Welcome intent ,勾选这个复选框并双击相应的行,然后可以看到并编辑一系列预定义的问候语,比如:“What can I do for you?”设置完成后,记得保存。
当代理不能匹配到用户说话中的任何其他意图时,将触发这个后备意图。它通常用于告诉用户代理没有理解他们的意图,并可以提示用户重新表述他们的请求。
在设置了默认欢迎意图之后,你需要创建具体的意图来处理用户的特定请求。例如,你可能会创建一个名为“Travel”的意图来处理与旅行相关的请求。要创建这样一个意图,你需要到“Intents”菜单,点击“创建意图(Create Intent)”并给它命名。
接下来,你需要提供该意图的特征性句子,并确保其中的实体能被正确识别。这可以通过点击“添加用户表达(Add user expression)”来完成,并添加如“I have to go to Berlin”或“I need to travel from Nantes to Strasbourg”这样的句子。
这个过程是为了让Dialogflow代理能够理解用户的意图并做出适当的响应。训练短语是教导代理如何识别特定用户请求的示例。动作名称、参数和响应则用于定义代理在识别到意图后应当执行的动作和给出的答复。通过这种方式,你可以为代理配置一系列不同的意图,以处理各种用户可能提出的请求。
当你在Dialogflow中输入示例句子时,如果系统识别出了城市名称,它会自动将这些名称标记为内置系统实体@sys.geo-city的值。对于这些自动识别的值,你需要创建参数:
对于第一个城市名称(起点),你需要创建一个参数并命名为“origin”,通常会用粉红色来标记。
对于第二个城市名称(目的地),创建另一个参数并命名为“destination”,通常会用紫色来标记。
你还需要为旅行方式和日期创建参数,这些参数的类型分别是`@travelmode`和`@sys.date`,并将它们分别命名为`travelmode`和`date`。
在你将这些参数标记为“必需(REQUIRED)”之后,如果用户在对话中没有提供这些信息,系统就会询问用户这些问题来获取这些值。你设置的参数顺序将决定系统询问这些问题的顺序。
此外,你还需要为用户提供一个响应。这个响应会在所有必要信息被收集完毕后给出。例如:“So you wish to travel from $origin to $destination on $date, via $travelmode, is that correct?”这里的带有美元符号的变量代表着收集到的参数值。
接下来,你需要构建一个名为“确认(Confirmation)”的第二个意图。当用户回复确认所提供的信息是正确的时,用户的回复将是二元的(即是或否),系统必须能够识别出这个“是”或“否”是对预订信息的确认。为了实现这一点,你需要使用上下文(Context)。上下文可以帮助系统理解这个简单的“是”或“否”回复是在确认预订的特定环境中给出的。
在Dialogflow中,上下文(Contexts)是用来保存用户与代理之间对话的状态信息的。上下文分为输入上下文(Input Contexts)和输出上下文(Output Contexts)两种:
可以附加到一个意图(Intent)上。
当一个意图被触发后,它可以设置一个或多个输出上下文,这些上下文会影响接下来哪个意图可能会被激活。
输出上下文相当于告诉Dialogflow:“在这个对话的下一个回合中,如果用户说了某些话,你应该考虑这个上下文信息来理解他们的意图”。
在创建或编辑一个意图时,可以指定一个或多个输入上下文,这些输入上下文必须已经在之前的对话中被设置为输出上下文。
输入上下文相当于一个先决条件,只有当这个上下文存在时,意图才能被触发。
通过使用上下文,Dialogflow可以处理更复杂的对话流程,使得即使是非常通用的用户表达,如“是”或“否”,也能在对话的特定点得到正确的理解。例如,如果用户在预订流程的确认步骤中说“是”,Dialogflow可以理解这是对预订确认的肯定回答;而如果用户在被问及是否需要帮助时说“是”,Dialogflow则会理解这是在请求帮助。
Dialogflow中的上下文有一个持续时间的概念,最长可以持续20分钟。如果在这段时间内没有被更新,那么上下文会自动清除,Dialogflow就会“忘记”这些上下文信息。这意味着代理不会永久记住对话的每个环节,除非开发者通过编程方式去维护和更新上下文的状态。
在Dialogflow中,Fulfillment是一个功能,它允许每个意图在被触发时与一个外部的服务器进行交云。这通常用于处理复杂的逻辑、与外部API交互、访问数据库或执行一些不适合直接在Dialogflow中处理的任务。
对于一个特定的意图,你可以选择是否启用Fulfillment。如果启用,当这个意图被触发时,Dialogflow会向你指定的URL发送一个webhook请求。
在Dialogflow的左侧面板中,找到并点击“Fulfillment”部分。
在这里,你需要启用“Webhook”选项,并且输入你的服务器端点URL。
当一个意图触发Fulfillment时,Dialogflow会使用POST方法向你的服务器发送JSON格式的数据。
这个数据包含了触发意图的用户的原始输入、解析出的参数、意图的信心分数等信息。
你的服务器在接收到Dialogflow的请求后,需要对其进行处理并返回一个JSON格式的响应。
这个响应可以包含返回给用户的文本消息,也可以包含其他复杂的指令,如设置上下文、触发事件等。
例如,用户说“是的”,触发了名为“Confirmation”的意图,Dialogflow会发送一个包含所有必要预订参数的JSON数据到你的服务器。这个数据中包含了用户的确认信息、预订的起始地、目的地、旅行方式、日期等信息。
然后你的webhook服务器简洁地回复了一条消息:
{
"fulfillmentText": "Thank you very much for your booking."
}
这条消息随后会被Dialogflow处理,并显示给用户作为回答。这样,Fulfillment就连接了Dialogflow代理和你的服务逻辑,实现了代理的扩展和集成。
如下是一个事例:
{
"responseId": "ac201302-9fe4-4ba2-b83d-50de537ab5f8-59c3eb0f",
"queryResult": {
"queryText": "Yes",
"parameters": {
"choice": "yes"
},
"allRequiredParamsPresent": true,
"fulfillmentText": "Thank you very much for your booking.",
"fulfillmentMessages": [
{
"text": {
"text": [
"Thank you very much for your booking."
]
}
}
],
"outputContexts": [
{
"name": "projects/travel-lcky/agent/sessions/846564dc-8fc3-eae6-9349-8347e4a95b30/contexts/confirmation",
"lifespanCount": 5,
"parameters": {
"origin": "Caracas",
"origin.original": "Caracas",
"destination": "York",
"destination.original": "York",
"travelmode": "train",
"travelmode.original": "Train",
"date": "2021-02-03T12:00:00+01:00",
"date.original": "Tomorrow",
"choice": "yes",
"choice.original": "Yes"
}
},
{
"name": "projects/travel-lcky/agent/sessions/846564dc-8fc3-eae6-9349-8347e4a95b30/contexts/__system_counters__",
"parameters": {
"no-input": 0,
"no-match": 0,
5
"choice": "yes",
"choice.original": "Yes"
}
}
],
"intent": {
"name": "projects/travel-lcky/agent/intents/c718acfb-e050-4dda-8c9b-e7e0c940baf9",
"displayName": "Confirmation2",
"endInteraction": true
},
"intentDetectionConfidence": 1,
"languageCode": "en"
},
"originalDetectIntentRequest": {
"source": "DIALOGFLOW_CONSOLE",
"payload": {}
},
"session": "projects/travel-lcky/agent/sessions/846564dc-8fc3-eae6-9349-8347e4a95b30"
}
To this, our webhook (see Section 3 for the code) has answered very laconically by
{
"fulfillmentText": "Thank you very much for your booking."
}
本节讲述了如何与本地服务器进行通信,我们使用Python设置本地服务器。当然,你也可以选择其他编程语言,主要任务是读取JSON数据,分析它,并以JSON格式响应。例如,除了本地Python服务器,还可以使用学校的PHP平台,并通过ngrok进行重定向。
我们开始搭建一个能够处理Dialogflow webhook请求的本地服务器环境。通过这样的服务器,你可以对Dialogflow从用户那里收集到的数据进行更复杂的处理,并根据分析结果返回响应。这对于创建更加动态和交互性强的对话体验非常有帮助。
使用Python是因为它的简洁性和广泛的应用,但核心概念和流程对于使用其他语言(如PHP)的服务器设置同样适用。关键在于正确处理进来的JSON格式的请求数据,并返回一个符合Dialogflow webhook格式要求的JSON响应。
具体步骤:
首先,你需要去到一个用于存放文件的目录。需要注意的是,出于安全考虑,不应选择存放个人数据的目录。
在选定的目录中创建一个名为`app.py`的Python文件。如下
# import flask dependencies
from flask import Flask, request, make_response, jsonify
# initialize the flask app
app = Flask(__name__)
# default route
@app.route('/')
def hello_world():
return 'Hello World!'
# function for responses
def results():
# build a request object
req = request.get_json(force=True)
# fetch action from json
action = req.get('queryResult').get('parameters').get("pizza")
# return a fulfillment response
return {'fulfillmentText': u'La pizza qui vous intéresse est : '+str(action)}
# create a route for webhook
@app.route('/webhook', methods=['POST', 'GET'])
def webhook():
# return response
return make_response(jsonify(results()))
# run the app
if __name__ == '__main__':
app.run(debug=True)
在控制台中,导航到你放置`app.py`文件的目录。
通过输入以下命令来设置Flask环境变量:
export FLASK_APP=app.py
指定Flask应用的入口文件。
export FLASK_ENV=development
设置Flask运行在开发环境,启用调试模式。
输入flask run来启动Flask服务器。如果提示找不到`flask`命令,可能是因为Flask安装在系统路径之外的目录中。你可能需要找到Flask的安装位置并相应地调整系统路径。
例如:
/usr/local/Cellar/[email protected]/3.9.1/Frameworks/Python.framework/Versions/3.9/bin/flask
控制台将显示服务器运行的信息,包括环境设置、调试模式状态和服务运行的本地地址(默认是http://127.0.0.1:5000)。
* Serving Flask app "app.py" (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with watchdog (fsevents)
* Debugger is active!
* Debugger PIN: 496-665-426
保持这个控制台窗口开启,不要关闭。
在另一个终端窗口中,导航到你解压ngrok的位置。
./ngrok http http://127.0.0.1:5000
输入上方命令来启动ngrok,它会创建一个公网可访问的URL,并将请求转发到你的本地服务器。
ngrok会提供一个HTTPS格式的公网URL(例如:https://27f95cd2.ngrok.io),这是用于外部访问你的本地服务器的地址。例如:
ngrok (Ctrl+C to quit)
Build better APIs with ngrok. Early access: ngrok.com/early-access
Session Status online
Account [email protected] (Plan: Free)
Version 3.5.0
Region Europe (eu)
Latency 41ms
Web Interface http://127.0.0.1:4040
Forwarding https://269d-89-234-160-214.ngrok-free.app -> http://127.0.0.1:5000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
将ngrok提供的HTTPS URL加上`/webhook`后缀(非常重要,不要遗忘`/webhook`),复制到Dialogflow的Fulfillment页面中的URL字段。
点击“保存”完成配置。
现在,你的本地服务器就可以接收来自Dialogflow的请求,并返回响应了。我们拥有了完全的自由来编写Python代码,分析传入的请求并做出适当的响应.这为你提供了极大的自由度来编写Python代码,分析传入的请求,并做出适当的响应你可以在https://cloud.google.com/dialogflow/docs/fulfillment-how 上找到更多信息,包括本地服务器的响应也可以改变上下文(使用output_contexts[]
键)、发送一个将触发另一个意图的“事件”等。响应必须在64kb以下,并且在收到请求后的10秒内到达(Dialogflow对响应时间有要求)。
这段代码是一个简单的Flask应用的示例,用于创建一个本地服务器。以下是该Python代码的详细解释:
使用from flask import Flask, request, make_response, jsonify来导入Flask框架所需的依赖项。
app = Flask(__name__)`初始化一个Flask应用实例。
使用`@app.route('/')装饰器定义一个路由。当访问http://127.0.0.1:5000/时,这个路由会被触发。
def hello_world(): return 'Hello World!'定义了一个函数,当访问根路径('/')时,返回"Hello World!"字符串。
@app.route 是一个Python装饰器,用于修改紧随其后的函数。它的参数是查询中请求的路径。在这里,它指的是Flask执行的“目录”的根路径。当我们看到以/webhook结尾的URL时,我们可能会误以为我们将进入一个名为webhook的目录,实际上并非如此:Flask拦截我们向服务器请求的URL,并使用它来执行紧随装饰器之后的函数,这里是`hello_world`(函数名可以是任意的)。
定义了一个`results`函数,该函数用于检索附加到请求的JSON数据并将其存储在`req`中。然后它遵循JSON结构来获取键值`choice`并将其存储在`resu`中。注意,当变量`resu`接收其值时,其类型为None,使用它之前必须将其转换为字符串。最后,我们创建一个字典条目,其键是`fulfillmentText`,其值是我们想要发送的字符串;最后我们将这个字典作为函数的输出返回。
使用`@app.route('/webhook', methods=['POST', 'GET'])`装饰器定义一个webhook路由,并激活POST和GET方法。我们给它的函数是`make_response`,并向其提供`results`函数(如上定义)的json化输出。
if __name__ == '__main__: app.run(debug=True)`这段代码表明,如果应用作为主程序执行(而不是作为另一个应用的模块),则名称将是`__main__`,因此我们将启动应用。这三行代码对我们来说并不重要。
总的来说,这段代码展示了如何使用Flask创建一个简单的Web服务器,以及如何定义路由来处理不同的HTTP请求,并根据请求返回相应的响应。
# import flask dependencies
from flask import Flask, request, jsonify
# initialize the flask app
app = Flask(__name__)
# default route
@app.route('/')
def hello_world():
return 'Hello World!'
# function for responses
def results():
# build a request object
req = request.get_json(force=True)
# fetch parameters from json
parameters = req.get('queryResult').get('parameters')
# Check if the user's choice or queryText is 'yes'
user_says = req.get('queryResult').get('queryText').lower() # Convert to lower case to ensure consistency
# Initialize an empty response_data
response_data = {}
# Check if the user said 'yes'
if user_says == 'yes':
# Construct the response for 'yes'
response_data = {
'fulfillmentText': 'Thank you very much for your booking.'
}
else:
# Handle other inputs: you might want to fetch other parameters or construct a default response
origin = parameters.get('origin')
destination = parameters.get('destination')
travelmode = parameters.get('travelmode')
date = parameters.get('date')
response_data = {
'origin': origin,
'destination': destination,
'travelmode': travelmode,
'date': date,
}
print("Sending response data to Dialogflow:")
print(response_data)
# Directly return the constructed JSON data
return response_data
# create a route for webhook
@app.route('/webhook', methods=['POST', 'GET'])
def webhook():
# directly return jsonify of results()
return jsonify(results())
# run the app
if __name__ == '__main__':
app.run(debug=True)
记得开启
"小谈话"(Small Talk)在Dialogflow的上下文中指的是一种社交性的对话模式,旨在进行非信息性的社交互动,而不是交换实际信息。这种模式更多地是关于日常的寒暄或打招呼,比如聊天气、个人感受等。这与Smalltalk编程语言不同,后者是一种在1972年于施乐公司(Xerox)创建,并曾经在一些学校教授的面向对象编程语言。
在Dialogflow中,可以通过侧边栏中的“Small Talk”选项来激活这个功能。启用后,代理(Agent)将能够自动处理用户的常见非信息性问题,如“我睡不着”或“你多大了”。Dialogflow的小谈话功能内置了一系列预定义的回答,用于响应用户的这类问题,从而不需要开发者额外编写这部分对话逻辑。
Eliza是一个更早的程序,用于模拟罗杰斯心理治疗师(Rogerian psychotherapist),它通过反问用户来引导对话。它是人工智能早期的一个里程碑,展示了机器能够如何模仿人类的非信息性交流。
两者的区别在于:
Dialogflow的小谈话功能提供了更加人性化和多样化的预设回复,让聊天机器人能够更自然地融入日常对话中。
Eliza通过特定的对话模式来模仿心理治疗师,通过反问用户的问题,旨在让用户自己探索自己的想法和感受。
如果你有兴趣了解这些早期的人工智能对话系统,Eliza是一个很好的起点,它提供了一个简单的模型来理解机器是如何尝试模拟人类的对话的。而Dialogflow的小谈话功能则体现了在这方面取得的进步,它提供了更多预设的社交寒暄回复,可以直接集成在现代的聊天机器人中。