在每次處理使用者輸入之後,DialogBot
儲存和 ConversationState
的UserState
目前狀態。 收集所有必要的資訊之後,程式代碼撰寫範例會建立示範航班預約保留。 在本文中,我們將涵蓋此範例的 LUIS 層面。 不過,範例的一般流程如下:
OnMembersAddedAsync
當新的使用者連線並顯示歡迎卡片時,就會呼叫 。
OnMessageActivityAsync
會針對收到的每個使用者輸入呼叫 。
模組 OnMessageActivityAsync
會透過 Run
對話框擴充方法執行適當的對話框。 然後,主要對話框會呼叫 LUIS 協助程式,以尋找評分最高的用戶意圖。 如果使用者輸入的最上層意圖傳回 「BookFlight」,協助程式會從 LUIS 傳回的使用者填寫資訊。 之後,主要對話框會啟動 BookingDialog
,這會視需要從使用者取得其他資訊,例如:
Origin
原始城市
TravelDate
預訂航班的日期
Destination
目的地城市
在每次處理使用者輸入之後,dialogBot
儲存和 conversationState
的userState
目前狀態。 收集所有必要的資訊之後,程式代碼撰寫範例會建立示範航班預約保留。 在本文中,我們將涵蓋此範例的 LUIS 層面。 不過,範例的一般流程如下:
onMembersAdded
當新的使用者連線並顯示歡迎卡片時,就會呼叫 。
OnMessage
會針對收到的每個使用者輸入呼叫 。
這個模組會 onMessage
執行 mainDialog
會收集使用者輸入的 。
然後,主要對話框會呼叫 LUIS 協助程式 FlightBookingRecognizer
,以尋找評分最高的用戶意圖。 如果使用者輸入的最上層意圖傳回 「BookFlight」,協助程式會從 LUIS 傳回的使用者填寫資訊。
在回應回復時, mainDialog
保留 LUIS 傳回之使用者的資訊,並啟動 bookingDialog
。 bookingDialog
視需要從使用者取得其他資訊,例如
destination
目的地城市。
origin
原始城市。
travelDate
預訂航班的日期。
- 在每次處理使用者輸入之後,
DialogBot
儲存和 ConversationState
的UserState
目前狀態。
- 收集所有必要的資訊之後,程式代碼撰寫範例會建立示範航班預約保留。
- 在本文中,我們將涵蓋此範例的 LUIS 層面。 不過,範例的一般流程如下:
onMembersAdded
當新的使用者連線並顯示歡迎卡片時,就會呼叫 。
onMessageActivity
會針對收到的每個使用者輸入呼叫 。
模組 onMessageActivity
會透過 run
對話框擴充方法執行適當的對話框。 然後,主要對話框會呼叫 LUIS 協助程式,以尋找評分最高的用戶意圖。 如果使用者輸入的最上層意圖傳回 「BookFlight」,協助程式會從 LUIS 傳回的使用者填寫資訊。 之後,主要對話框會啟動 BookingDialog
,這會視需要從使用者取得其他資訊,例如:
Origin
原始城市
TravelDate
預訂航班的日期
Destination
目的地城市
在每次處理使用者輸入之後,DialogBot
儲存和 conversation_state
的user_state
目前狀態。 收集所有必要的資訊之後,程式代碼撰寫範例會建立示範航班預約保留。 在本文中,我們將涵蓋此範例的 LUIS 層面。 不過,範例的一般流程如下:
on_members_added_activity
當新的使用者連線並顯示歡迎卡片時,就會呼叫 。
on_message_activity
會針對收到的每個使用者輸入呼叫 。
模組 on_message_activity
會透過 run_dialog
對話框擴充方法執行適當的對話框。 然後,主要對話框會呼叫 LuisHelper
以尋找評分最高的用戶意圖。 如果使用者輸入的最上層意圖傳回 「BookFlight」,協助程式函式會從 LUIS 傳回的使用者填寫資訊。 之後,主要對話框會啟動 BookingDialog
,這會視需要從使用者取得其他資訊,例如:
destination
目的地城市。
origin
原始城市。
travel_date
預訂航班的日期。
LUIS 實體可讓 Bot 瞭解超出標準意圖的事件。 這可讓您從使用者收集其他資訊,讓 Bot 可以提出問題並更聰明地回應。 除了三個 LUIS 意圖 'Book Flight'、'Cancel' 和 'None' 的定義,FlightBooking.json檔案也包含一組實體,例如 'From.Airport' 和 'To.Airport'。 這些實體可讓 LUIS 在要求新的旅行預約時,偵測並傳回使用者原始輸入內含的其他資訊。
發佈 LUIS 應用程式之後,您可以從 Bot 存取它。 您必須記錄數個值,才能從 Bot 記憶體取 LUIS 應用程式。 您可以使用 LUIS 入口網站來擷取該資訊。
將存取 LUIS 應用程式所需的資訊,包括應用程式識別碼、撰寫密鑰和區域新增至 appsettings.json
檔案。 在上一個步驟中,您已從已發佈的 LUIS 應用程式擷取這些值。 API 主機名稱的格式 <your region>.api.cognitive.microsoft.com
應為 。
appsetting.json
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"LuisAppId": "",
"LuisAPIKey": "",
"LuisAPIHostName": ""
}
將存取 LUIS 應用程式所需的資訊,包括應用程式識別碼、撰寫密鑰和區域新增至 .env
檔案。 在上一個步驟中,您已從已發佈的 LUIS 應用程式擷取這些值。 API 主機名稱的格式 <your region>.api.cognitive.microsoft.com
應為 。
.env
MicrosoftAppType=
MicrosoftAppId=
MicrosoftAppPassword=
MicrosoftAppTenantId=
LuisAppId=
LuisAPIKey=
LuisAPIHostName=
將存取 LUIS 應用程式所需的資訊,包括應用程式識別碼、撰寫密鑰和區域新增至 application.properties
檔案。 在上一個步驟中,您已從已發佈的 LUIS 應用程式擷取這些值。 API 主機名稱的格式 <your region>.api.cognitive.microsoft.com
應為 。
application.properties
MicrosoftAppId=
MicrosoftAppPassword=
LuisAppId=
LuisAPIKey=
LuisAPIHostName=
server.port=3978
將存取 LUIS 應用程式所需的資訊,包括應用程式識別碼、撰寫密鑰和區域新增至 config.py
檔案。 在上一個步驟中,您已從已發佈的 LUIS 應用程式擷取這些值。 API 主機名稱的格式 <your region>.api.cognitive.microsoft.com
應為 。
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 服務,Bot 會提取您新增至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 的邏輯;它會擴充從 呼叫 FlightBookingRecognizer.RecognizeAsync<FlightBooking>
MainDialog.cs
時用來儲存 LUIS 結果的部分類別FlightBooking.cs
。
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 服務,Bot 會使用您新增至 .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
實作為協助程式方法。 從呼叫 flightBookingRecognizer.executeLuisQuery()
之後,會使用這些方法 mainDialog.js
請確定 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 服務,Bot 會提取您新增至 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 服務,Bot 會使用您新增至 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
協助程式方法。 從呼叫 LuisHelper.execute_luis_query()
之後,會使用這些方法 main_dialog.py
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
如果 LUIS 傳回的最上層意圖解析為「預訂正式發行前小眾測試版」,您的 Bot 會詢問更多問題,直到其儲存足夠的資訊來建立旅行預約為止。 屆時,會將此預訂資訊傳回給您的使用者。