每次处理用户输入以后,DialogBot
都会保存 UserState
和 ConversationState
的当前状态。 收集所有必需的信息以后,编码示例会创建一个演示版的航班预订。 在本文中,我们将介绍此示例 LUIS 方面的内容。 但是,示例的常规流程为:
- 连接新用户时,会调用
OnMembersAddedAsync
并显示欢迎卡片。
- 每次收到用户输入都会调用
OnMessageActivityAsync
。
OnMessageActivityAsync
模块通过 Run
对话扩展方法运行相应的对话。 然后,主对话会调用 LUIS 帮助程序来查找排名靠前的评分用户意向。 如果用户输入的排名靠前的意向返回“BookFlight”,则帮助程序会填充 LUIS 返回的用户的信息。 然后,主对话启动 BookingDialog
,后者会根据需要从用户获取其他信息,例如:
Origin
始发城市
TravelDate
预订航班的日期
Destination
目标城市
每次处理用户输入以后,dialogBot
都会保存 userState
和 conversationState
的当前状态。 收集所有必需的信息以后,编码示例会创建一个演示版的航班预订。 在本文中,我们将介绍此示例 LUIS 方面的内容。 但是,示例的常规流程为:
- 连接新用户时,会调用
onMembersAdded
并显示欢迎卡片。
- 每次收到用户输入都会调用
OnMessage
。
onMessage
模块运行 mainDialog
,后者收集用户输入。
然后,主对话会调用 LUIS 帮助程序 FlightBookingRecognizer
来查找排名靠前的评分用户意向。 如果用户输入的排名靠前的意向返回“BookFlight”,则帮助程序会填充 LUIS 返回的用户的信息。
返回响应以后,mainDialog
会保存 LUIS 返回的用户信息并启动 bookingDialog
。 bookingDialog
根据需要获取用户提供的其他信息,例如:
destination
目标城市。
origin
始发城市。
travelDate
预订航班的日期。
- 每次处理用户输入以后,
DialogBot
都会保存 UserState
和 ConversationState
的当前状态。
- 收集所有必需的信息以后,编码示例会创建一个演示版的航班预订。
- 在本文中,我们将介绍此示例 LUIS 方面的内容。 但是,示例的常规流程为:
- 连接新用户时,会调用
onMembersAdded
并显示欢迎卡片。
- 每次收到用户输入都会调用
onMessageActivity
。
onMessageActivity
模块通过 run
对话扩展方法运行相应的对话。 然后,主对话会调用 LUIS 帮助程序来查找排名靠前的评分用户意向。 如果用户输入的排名靠前的意向返回“BookFlight”,则帮助程序会填充 LUIS 返回的用户的信息。 然后,主对话启动 BookingDialog
,后者会根据需要从用户获取其他信息,例如:
Origin
始发城市
TravelDate
预订航班的日期
Destination
目标城市
每次处理用户输入以后,DialogBot
都会保存 user_state
和 conversation_state
的当前状态。 收集所有必需的信息以后,编码示例会创建一个演示版的航班预订。 在本文中,我们将介绍此示例 LUIS 方面的内容。 但是,示例的常规流程为:
- 连接新用户时,会调用
on_members_added_activity
并显示欢迎卡片。
- 每次收到用户输入都会调用
on_message_activity
。
on_message_activity
模块通过 run_dialog
对话扩展方法运行相应的对话。 然后,主对话会调用 LuisHelper
帮助程序来查找排名靠前的评分用户意向。 如果用户输入的排名靠前的意向返回“BookFlight”,则帮助程序函数会填充 LUIS 返回的用户信息。 然后,主对话启动 BookingDialog
,后者会根据需要从用户获取其他信息,例如:
destination
目标城市。
origin
始发城市。
travel_date
预订航班的日期。
LUIS 实体使机器人能够了解超出标准意图的事件。 这样,便可以从用户收集其他信息,使机器人可以提出问题并更智能地做出回答。 除了“预订航班”、“取消”和“None”这三个 LUIS 意向的定义,FlightBooking.json 文件还包含一组实体,例如“From.Airport”和“To.Airport”。 LUIS 可以通过这些实体检测用户在发出新的旅行预订请求时其原始输入中包含的其他信息并将其返回。
在 appsettings.json
文件中添加访问 LUIS 应用所需的信息,包括应用程序 ID、创作密钥和区域。 在上一步中,你已从已发布的 LUIS 应用中检索了这些值。 API 主机名称应采用 <your region>.api.cognitive.azure.cn
格式。
appsetting.json
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"LuisAppId": "",
"LuisAPIKey": "",
"LuisAPIHostName": ""
}
在 .env
文件中添加访问 LUIS 应用所需的信息,包括应用程序 ID、创作密钥和区域。 在上一步中,你已从已发布的 LUIS 应用中检索了这些值。 API 主机名称应采用 <your region>.api.cognitive.azure.cn
格式。
.env
MicrosoftAppType=
MicrosoftAppId=
MicrosoftAppPassword=
MicrosoftAppTenantId=
LuisAppId=
LuisAPIKey=
LuisAPIHostName=
在 application.properties
文件中添加访问 LUIS 应用所需的信息,包括应用程序 ID、创作密钥和区域。 在上一步中,你已从已发布的 LUIS 应用中检索了这些值。 API 主机名称应采用 <your region>.api.cognitive.azure.cn
格式。
application.properties
MicrosoftAppId=
MicrosoftAppPassword=
LuisAppId=
LuisAPIKey=
LuisAPIHostName=
server.port=3978
在 config.py
文件中添加访问 LUIS 应用所需的信息,包括应用程序 ID、创作密钥和区域。 在上一步中,你已从已发布的 LUIS 应用中检索了这些值。 API 主机名称应采用 <your region>.api.cognitive.azure.cn
格式。
config.py
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
APP_TYPE = os.environ.get("MicrosoftAppType", "MultiTenant")
APP_TENANTID = os.environ.get("MicrosoftAppTenantId", "")
LUIS_APP_ID = os.environ.get("LuisAppId", "")
LUIS_API_KEY = os.environ.get("LuisAPIKey", "")
请确保为项目安装 Microsoft.Bot.Builder.AI.Luis NuGet 包。
为了连接到 LUIS 服务,机器人会拉取你添加到 appsetting.json 文件的信息。 FlightBookingRecognizer
类包含的代码有 appsetting.json 文件中的设置,它调用 RecognizeAsync
方法来查询 LUIS 服务。
FlightBookingRecognizer.cs
public class FlightBookingRecognizer : IRecognizer
{
private readonly LuisRecognizer _recognizer;
public FlightBookingRecognizer(IConfiguration configuration)
{
var luisIsConfigured = !string.IsNullOrEmpty(configuration["LuisAppId"]) && !string.IsNullOrEmpty(configuration["LuisAPIKey"]) && !string.IsNullOrEmpty(configuration["LuisAPIHostName"]);
if (luisIsConfigured)
{
var luisApplication = new LuisApplication(
configuration["LuisAppId"],
configuration["LuisAPIKey"],
"https://" + configuration["LuisAPIHostName"]);
// Set the recognizer options depending on which endpoint version you want to use.
// More details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3
var recognizerOptions = new LuisRecognizerOptionsV3(luisApplication)
{
PredictionOptions = new Bot.Builder.AI.LuisV3.LuisPredictionOptions
{
IncludeInstanceData = true,
}
};
_recognizer = new LuisRecognizer(recognizerOptions);
}
}
// Returns true if luis is configured in the appsettings.json and initialized.
public virtual bool IsConfigured => _recognizer != null;
public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
=> await _recognizer.RecognizeAsync(turnContext, cancellationToken);
public virtual async Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken)
where T : IRecognizerConvert, new()
=> await _recognizer.RecognizeAsync<T>(turnContext, cancellationToken);
}
FlightBookingEx.cs
包含的逻辑可以使用 From、To 和 TravelDate 来提取内容;它扩展的分部类 FlightBooking.cs
在从 MainDialog.cs
调用 FlightBookingRecognizer.RecognizeAsync<FlightBooking>
时可以用来存储 LUIS 结果。
CognitiveModels\FlightBookingEx.cs
// Extends the partial FlightBooking class with methods and properties that simplify accessing entities in the luis results
public partial class FlightBooking
{
public (string From, string Airport) FromEntities
{
get
{
var fromValue = Entities?._instance?.From?.FirstOrDefault()?.Text;
var fromAirportValue = Entities?.From?.FirstOrDefault()?.Airport?.FirstOrDefault()?.FirstOrDefault();
return (fromValue, fromAirportValue);
}
}
public (string To, string Airport) ToEntities
{
get
{
var toValue = Entities?._instance?.To?.FirstOrDefault()?.Text;
var toAirportValue = Entities?.To?.FirstOrDefault()?.Airport?.FirstOrDefault()?.FirstOrDefault();
return (toValue, toAirportValue);
}
}
// This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
// TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
public string TravelDate
=> Entities.datetime?.FirstOrDefault()?.Expressions.FirstOrDefault()?.Split('T')[0];
}
若要使用 LUIS,项目需安装 botbuilder-ai npm 包。
为了连接到 LUIS 服务,机器人会使用你添加到 .env
文件的信息。 flightBookingRecognizer.js
类包含的代码可以导入 .env
文件中的设置,并调用 recognize()
方法来查询 LUIS 服务。
dialogs/flightBookingRecognizer.js
class FlightBookingRecognizer {
constructor(config) {
const luisIsConfigured = config && config.applicationId && config.endpointKey && config.endpoint;
if (luisIsConfigured) {
// Set the recognizer options depending on which endpoint version you want to use e.g v2 or v3.
// More details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3
const recognizerOptions = {
apiVersion: 'v3'
};
this.recognizer = new LuisRecognizer(config, recognizerOptions);
}
}
get isConfigured() {
return (this.recognizer !== undefined);
}
/**
* Returns an object with preformatted LUIS results for the bot's dialogs to consume.
* @param {TurnContext} context
*/
async executeLuisQuery(context) {
return await this.recognizer.recognize(context);
}
getFromEntities(result) {
let fromValue, fromAirportValue;
if (result.entities.$instance.From) {
fromValue = result.entities.$instance.From[0].text;
}
if (fromValue && result.entities.From[0].Airport) {
fromAirportValue = result.entities.From[0].Airport[0][0];
}
return { from: fromValue, airport: fromAirportValue };
}
getToEntities(result) {
let toValue, toAirportValue;
if (result.entities.$instance.To) {
toValue = result.entities.$instance.To[0].text;
}
if (toValue && result.entities.To[0].Airport) {
toAirportValue = result.entities.To[0].Airport[0][0];
}
return { to: toValue, airport: toAirportValue };
}
/**
* This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
* TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
*/
getTravelDate(result) {
const datetimeEntity = result.entities.datetime;
if (!datetimeEntity || !datetimeEntity[0]) return undefined;
const timex = datetimeEntity[0].timex;
if (!timex || !timex[0]) return undefined;
const datetime = timex[0].split('T')[0];
return datetime;
}
}
用于通过 From、To 和 TravelDate 提取内容的逻辑在 flightBookingRecognizer.js
中作为帮助程序方法实现。 从 mainDialog.js
调用 flightBookingRecognizer.executeLuisQuery()
以后,会使用这些方法。
确保 com.microsoft.bot.bot-ai-luis-v3 包已添加到 pom.xml 文件中。
<dependency>
<groupId>com.microsoft.bot</groupId>
<artifactId>bot-ai-luis-v3</artifactId>
<version>4.14.1</version>
</dependency>
为了连接到 LUIS 服务,机器人会拉取你添加到 application.properties 文件的信息。 FlightBookingRecognizer
类包含的代码有 application.properties 文件中的设置,它调用 recognize
方法来查询 LUIS 服务。
FlightBookingRecognizer.java
/**
* The constructor of the FlightBookingRecognizer class.
*
* @param configuration The Configuration object to use.
*/
public FlightBookingRecognizer(Configuration configuration) {
Boolean luisIsConfigured = StringUtils.isNotBlank(configuration.getProperty("LuisAppId"))
&& StringUtils.isNotBlank(configuration.getProperty("LuisAPIKey"))
&& StringUtils.isNotBlank(configuration.getProperty("LuisAPIHostName"));
if (luisIsConfigured) {
LuisApplication luisApplication = new LuisApplication(
configuration.getProperty("LuisAppId"),
configuration.getProperty("LuisAPIKey"),
String.format("https://%s", configuration.getProperty("LuisAPIHostName"))
);
// Set the recognizer options depending on which endpoint version you want to use.
// More details can be found in
// https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3
LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(luisApplication);
recognizerOptions.setIncludeInstanceData(true);
this.recognizer = new LuisRecognizer(recognizerOptions);
}
}
/**
* Runs an utterance through a recognizer and returns a generic recognizer result.
*
* @param turnContext Turn context.
* @return Analysis of utterance.
*/
@Override
public CompletableFuture<RecognizerResult> recognize(TurnContext turnContext) {
return this.recognizer.recognize(turnContext);
}
FlightBookingRecognizer.cs
包含用于提取 From、To 和 TravelDate 的逻辑;并且是从 MainDialog.java
中调用,以解码 Luis 查询结果。
FlightBookingRecognizer.java
/**
* Gets the From data from the entities which is part of the result.
*
* @param result The recognizer result.
* @return The object node representing the From data.
*/
public ObjectNode getFromEntities(RecognizerResult result) {
String fromValue = "", fromAirportValue = "";
if (result.getEntities().get("$instance").get("From") != null) {
fromValue = result.getEntities().get("$instance").get("From").get(0).get("text")
.asText();
}
if (!fromValue.isEmpty()
&& result.getEntities().get("From").get(0).get("Airport") != null) {
fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0)
.asText();
}
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
ObjectNode entitiesNode = mapper.createObjectNode();
entitiesNode.put("from", fromValue);
entitiesNode.put("airport", fromAirportValue);
return entitiesNode;
}
/**
* Gets the To data from the entities which is part of the result.
*
* @param result The recognizer result.
* @return The object node representing the To data.
*/
public ObjectNode getToEntities(RecognizerResult result) {
String toValue = "", toAirportValue = "";
if (result.getEntities().get("$instance").get("To") != null) {
toValue = result.getEntities().get("$instance").get("To").get(0).get("text").asText();
}
if (!toValue.isEmpty() && result.getEntities().get("To").get(0).get("Airport") != null) {
toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0)
.asText();
}
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
ObjectNode entitiesNode = mapper.createObjectNode();
entitiesNode.put("to", toValue);
entitiesNode.put("airport", toAirportValue);
return entitiesNode;
}
/**
* This value will be a TIMEX. And we are only interested in a Date so grab the first result and
* drop the Time part. TIMEX is a format that represents DateTime expressions that include some
* ambiguity. e.g. missing a Year.
*
* @param result A {link RecognizerResult}
* @return The Timex value without the Time model
*/
public String getTravelDate(RecognizerResult result) {
JsonNode datetimeEntity = result.getEntities().get("datetime");
if (datetimeEntity == null || datetimeEntity.get(0) == null) {
return null;
}
JsonNode timex = datetimeEntity.get(0).get("timex");
if (timex == null || timex.get(0) == null) {
return null;
}
String datetime = timex.get(0).asText().split("T")[0];
return datetime;
}
确保为项目安装 botbuilder-ai PyPI 包。
为了连接到 LUIS 服务,机器人会使用你添加到 config.py
文件的信息。 FlightBookingRecognizer
类包含的代码可以导入 config.py
文件中的设置,并调用 recognize()
方法来查询 LUIS 服务。
flight_booking_recognizer.py
class FlightBookingRecognizer(Recognizer):
def __init__(self, configuration: DefaultConfig):
self._recognizer = None
luis_is_configured = (
configuration.LUIS_APP_ID
and configuration.LUIS_API_KEY
and configuration.LUIS_API_HOST_NAME
)
if luis_is_configured:
# Set the recognizer options depending on which endpoint version you want to use e.g v2 or v3.
# More details can be found in https://docs.microsoft.com/azure/cognitive-services/luis/luis-migration-api-v3
luis_application = LuisApplication(
configuration.LUIS_APP_ID,
configuration.LUIS_API_KEY,
"https://" + configuration.LUIS_API_HOST_NAME,
)
self._recognizer = LuisRecognizer(luis_application)
@property
def is_configured(self) -> bool:
# Returns true if luis is configured in the config.py and initialized.
return self._recognizer is not None
async def recognize(self, turn_context: TurnContext) -> RecognizerResult:
return await self._recognizer.recognize(turn_context)
用于通过 From、To 和 travel_date 从 LuisHelper
类中提取内容的逻辑在 luis_helper.py
中作为帮助程序方法实现。 从 main_dialog.py
调用 LuisHelper.execute_luis_query()
以后,会使用这些方法。
helpers/luis_helper.py
class LuisHelper:
@staticmethod
async def execute_luis_query(
luis_recognizer: LuisRecognizer, turn_context: TurnContext
) -> (Intent, object):
"""
Returns an object with preformatted LUIS results for the bot's dialogs to consume.
"""
result = None
intent = None
try:
recognizer_result = await luis_recognizer.recognize(turn_context)
intent = (
sorted(
recognizer_result.intents,
key=recognizer_result.intents.get,
reverse=True,
)[:1][0]
if recognizer_result.intents
else None
)
if intent == Intent.BOOK_FLIGHT.value:
result = BookingDetails()
# We need to get the result from the LUIS JSON which at every level returns an array.
to_entities = recognizer_result.entities.get("$instance", {}).get(
"To", []
)
if len(to_entities) > 0:
if recognizer_result.entities.get("To", [{"$instance": {}}])[0][
"$instance"
]:
result.destination = to_entities[0]["text"].capitalize()
else:
result.unsupported_airports.append(
to_entities[0]["text"].capitalize()
)
from_entities = recognizer_result.entities.get("$instance", {}).get(
"From", []
)
if len(from_entities) > 0:
if recognizer_result.entities.get("From", [{"$instance": {}}])[0][
"$instance"
]:
result.origin = from_entities[0]["text"].capitalize()
else:
result.unsupported_airports.append(
from_entities[0]["text"].capitalize()
)
# This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop
# the Time part. TIMEX is a format that represents DateTime expressions that include some ambiguity.
# e.g. missing a Year.
date_entities = recognizer_result.entities.get("datetime", [])
if date_entities:
timex = date_entities[0]["timex"]
if timex:
datetime = timex[0].split("T")[0]
result.travel_date = datetime
else:
result.travel_date = None
except Exception as exception:
print(exception)
return intent, result