Reconocimiento del lenguaje conversacional (CLU), una característica del lenguaje de Azure AI, es la versión actualizada de LUIS.
Para obtener más información sobre la compatibilidad con reconocimiento del lenguaje en el SDK de Bot Framework, consulte Reconocimiento del lenguaje natural.
La tarea de entender qué quiere decir el usuario conversacionalmente y contextualmente puede ser difícil, pero hace que la conversación del bot parezca más natural. Language Understanding LUIS es un servicio de API en la nube que permite hacer precisamente esto, de modo que el bot pueda reconocer la intención de los mensajes de usuario, permitir al usuario emplear un lenguaje más natural y dirigir mejor el flujo de conversación.
En este tema se explica cómo agregar LUIS a una aplicación de reserva de vuelos para reconocer las diferentes intenciones y entidades que contiene la entrada del usuario.
Nota:
Los SDK de JavaScript, C# y Python de Bot Framework seguirán siendo compatibles, pero el SDK de Java se va a retirar con la compatibilidad final a largo plazo que finaliza en noviembre de 2023.
Los bots existentes creados con el SDK de Java seguirán funcionando.
En este ejemplo de bot básico se muestra un ejemplo de una aplicación de reservas de vuelos del aeropuerto. Utiliza un servicio de LUIS para reconocer lo que introduce el usuario y devolver la primera intención de LUIS que reconoce.
El modelo de lenguaje contiene tres intenciones: Book Flight, Cancel y None. LUIS usará estas intenciones para entender qué quiso decir el usuario cuando envió un mensaje al bot. El modelo de lenguaje también define las entidades que LUIS puede extraer de la entrada del usuario, como el aeropuerto de origen o de destino.
Después de cada procesamiento de la entrada del usuario, DialogBot guarda el estado actual tanto de UserState como de ConversationState. Una vez recopilada toda la información necesaria, el ejemplo de código crea una aplicación de reservas de vuelos de demostración. En este artículo se van a tratarán los aspectos de LUIS de este ejemplo. Sin embargo, el flujo general del ejemplo es:
Se llama a OnMembersAddedAsync cuando se conecta un usuario nuevo y muestra una tarjeta de bienvenida.
Se llama a OnMessageActivityAsync para cada entrada del usuario recibida.
El módulo OnMessageActivityAsync ejecuta el diálogo adecuado a través del método de extensión del diálogo Run. Después, el diálogo principal llama a la aplicación auxiliar de LUIS para buscar la intención del usuario con mayor puntuación. Si la intención con mayor puntuación de la entrada del usuario devuelve "BookFlight", la aplicación auxiliar rellena la información del usuario que LUIS devolvió. Después, el diálogo principal inicia BookingDialog, que adquiere la información adicional del usuario que sea necesaria, como:
Origin: la ciudad de origen.
TravelDate: la fecha para la que se reserva el vuelo.
Destination: la ciudad de destino.
Después de cada procesamiento de la entrada del usuario, dialogBot guarda el estado actual tanto de userState como de conversationState. Una vez recopilada toda la información necesaria, el ejemplo de código crea una aplicación de reservas de vuelos de demostración. En este artículo se van a tratarán los aspectos de LUIS de este ejemplo. Sin embargo, el flujo general del ejemplo es:
Se llama a onMembersAdded cuando se conecta un usuario nuevo y muestra una tarjeta de bienvenida.
Se llama a OnMessage para cada entrada del usuario recibida.
El módulo onMessage ejecuta mainDialog, que recopila los datos de entrada del usuario.
Después, el diálogo principal llama a la aplicación auxiliar de LUIS FlightBookingRecognizer para buscar la intención del usuario con mayor puntuación. Si la intención con mayor puntuación de la entrada del usuario devuelve "BookFlight", la aplicación auxiliar rellena la información del usuario que LUIS devolvió.
Al recibir una respuesta, mainDialog conserva la información del usuario que ha devuelto LUIS e inicia bookingDialog. bookingDialog adquiere la información adicional que sea necesaria, como
destination la ciudad de destino.
origin la ciudad de origen.
travelDate la fecha para la que se reserva el vuelo.
Después de cada procesamiento de la entrada del usuario, DialogBot guarda el estado actual tanto de UserState como de ConversationState.
Una vez recopilada toda la información necesaria, el ejemplo de código crea una aplicación de reservas de vuelos de demostración.
En este artículo se van a tratarán los aspectos de LUIS de este ejemplo. Sin embargo, el flujo general del ejemplo es:
Se llama a onMembersAdded cuando se conecta un usuario nuevo y muestra una tarjeta de bienvenida.
Se llama a onMessageActivity para cada entrada del usuario recibida.
El módulo onMessageActivity ejecuta el diálogo adecuado a través del método de extensión del diálogo run. Después, el diálogo principal llama a la aplicación auxiliar de LUIS para buscar la intención del usuario con mayor puntuación. Si la intención con mayor puntuación de la entrada del usuario devuelve "BookFlight", la aplicación auxiliar rellena la información del usuario que LUIS devolvió. Después, el diálogo principal inicia BookingDialog, que adquiere la información adicional del usuario que sea necesaria, como:
Origin: la ciudad de origen.
TravelDate: la fecha para la que se reserva el vuelo.
Destination: la ciudad de destino.
Después de cada procesamiento de la entrada del usuario, DialogBot guarda el estado actual tanto de user_state como de conversation_state. Una vez recopilada toda la información necesaria, el ejemplo de código crea una aplicación de reservas de vuelos de demostración. En este artículo se van a tratarán los aspectos de LUIS de este ejemplo. Sin embargo, el flujo general del ejemplo es:
Se llama a on_members_added_activity cuando se conecta un usuario nuevo y muestra una tarjeta de bienvenida.
Se llama a on_message_activity para cada entrada del usuario recibida.
El módulo on_message_activity ejecuta el diálogo adecuado a través del método de extensión del diálogo run_dialog. Después, el diálogo principal llama a LuisHelper para buscar la intención del usuario con mayor puntuación. Si la intención con mayor puntuación de la entrada del usuario devuelve "BookFlight", la función auxiliar rellena la información del usuario que LUIS devolvió. Después, el diálogo principal inicia BookingDialog, que adquiere la información adicional del usuario que sea necesaria, como:
destination la ciudad de destino.
origin la ciudad de origen.
travel_date la fecha para la que se reserva el vuelo.
En la página de aplicaciones de conversación en LUIS, seleccione Importar y, a continuación, Importar como JSON.
En el diálogo Import new app (Importar nueva aplicación):
Elija el archivo FlightBooking.json en la carpeta CognitiveModels del ejemplo.
Escriba FlightBooking como nombre opcional de la aplicación y seleccione Listo.
El sitio puede mostrar Cómo crear una aplicación de LUIS eficaz y Actualizar los cuadros de diálogo de entidades compuestas. Puede descartar estos diálogos y continuar.
Entrene la aplicación y, a continuación, publique la aplicación en el entorno de producción.
Para más información, consulte la documentación de LUIS sobre cómo formar y publicar una aplicación.
Por qué usar entidades
Las entidades de LUIS permiten al bot comprender los eventos más allá de las intenciones estándar. Esto le permite recopilar información adicional de los usuarios para que el bot pueda formular preguntas y responder de forma más inteligente. Además de las definiciones de las tres intenciones de LUIS ("Book Flight", "Cancel" y "None") el archivo FlightBooking.json contiene un conjunto de entidades, como "From.Airport" y "To.Airport". Dichas entidades permiten a LUIS detectar y devolver información adicional que se encuentra dentro de la entrada original del usuario original cuando se solicita una nueva reserva de viaje.
Obtención de valores para conectarse a la aplicación de LUIS
Una vez que la aplicación de LUIS se ha publicado, puede acceder a ella desde su bot. Para acceder a su aplicación de LUIS desde el bot, deberá registrar varios valores. Para recuperar esa información, puede usar el portal de LUIS.
Recuperar la información de la aplicación del portal LUIS.ai
El archivo de configuración (appsettings.json, .env o config.py) actúa como el lugar en que se reúnen todas las referencias de servicio. La información que recupere se agregará a este archivo en la siguiente sección.
Seleccione la aplicación de LUIS publicada en luis.ai.
Con la aplicación de LUIS publicada abierta, seleccione la pestaña MANAGE (ADMINISTRAR).
Seleccione la pestaña Configuración en el lado izquierdo y registre el valor mostrado para ID de aplicación como <YOUR_APP_ID>.
Seleccione Recursos de Azure y, a continuación, Recurso de predicción. Registre el valor mostrado para Ubicación como <YOUR_REGION> y la Clave Principal como <YOUR_AUTHORING_KEY>.
Como alternativa, puede usar la región y la clave principal para el recurso de creación.
Agregue la información necesaria para acceder a la aplicación de LUIS, lo que incluye el identificador de la aplicación, la clave de creación y la región al archivo appsettings.json. En el paso anterior, recuperó estos valores de la aplicación de LUIS publicada. El nombre de host de la API debe tener el formato <your region>.api.cognitive.microsoft.com.
Agregue la información necesaria para acceder a la aplicación de LUIS, lo que incluye el identificador de la aplicación, la clave de creación y la región al archivo .env. En el paso anterior, recuperó estos valores de la aplicación de LUIS publicada. El nombre de host de la API debe tener el formato <your region>.api.cognitive.microsoft.com.
Agregue la información necesaria para acceder a la aplicación de LUIS, lo que incluye el identificador de la aplicación, la clave de creación y la región al archivo application.properties. En el paso anterior, recuperó estos valores de la aplicación de LUIS publicada. El nombre de host de la API debe tener el formato <your region>.api.cognitive.microsoft.com.
Agregue la información necesaria para acceder a la aplicación de LUIS, lo que incluye el identificador de la aplicación, la clave de creación y la región al archivo config.py. En el paso anterior, recuperó estos valores de la aplicación de LUIS publicada. El nombre de host de la API debe tener el formato <your region>.api.cognitive.microsoft.com.
Asegúrese de que el paquete NuGet Microsoft.Bot.Builder.AI.Luis está instalado para el proyecto.
Para conectar con el servicio LUIS, el bot extrae la información que agregó al archivo appsetting.json. La clase FlightBookingRecognizer contiene código con la configuración del archivo appsetting.json y consulta el servicio de LUIS mediante una llamada al método RecognizeAsync.
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 contiene la lógica para a extraer From, To and TravelDate; extiende la clase parcial FlightBooking.cs que se usa para almacenar los resultados de LUIS cuando se llama a FlightBookingRecognizer.RecognizeAsync<FlightBooking> desde MainDialog.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];
}
Para usar LUIS, el proyecto debe instalar el paquete de npm botbuilder-ai.
Para conectar con el servicio LUIS, el bot usa la información que agregó al archivo .env. La clase flightBookingRecognizer.js contiene el código que importa la configuración del archivo .env y consulta el servicio de LUIS mediante una llamada al método recognize().
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;
}
}
La lógica para extraer From, To y TravelDate se implementa como métodos auxiliares dentro de flightBookingRecognizer.js. Estos métodos se usan después de llamar a flightBookingRecognizer.executeLuisQuery() desde mainDialog.js.
Asegúrese de que el paquete com.microsoft.bot.bot-ai-luis-v3 se agrega al archivo pom.xml.
Para conectarse al servicio LUIS, el bot extrae la información que ha añadido al archivo application.properties. La clase FlightBookingRecognizer contiene código con la configuración del archivo application.properties y consulta el servicio de LUIS mediante una llamada al método recognize.
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 contiene la lógica que se va a extraer From, To y TravelDate; y se llama desde MainDialog.java para descodificar los resultados de la consulta de 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;
}
Asegúrese de que el paquete de PyPI botbuilder-ai está instalado para el proyecto.
Para conectar con el servicio LUIS, el bot usa la información que agregó al archivo config.py. La clase FlightBookingRecognizer contiene el código que importa la configuración del archivo config.py y consulta el servicio de LUIS mediante una llamada al método recognize().
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)
La lógica para extraer los valores de From, To y travel_date se implementa como métodos auxiliares de la clase LuisHelper dentro de luis_helper.py. Estos métodos se usan después de llamar a LuisHelper.execute_luis_query() desde 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
En el emulador, escriba un mensaje como "travel to Paris" (viajar a París) o "going from Paris to Berlin" (ir de París a Berlín). Utilice cualquier expresión que se encuentra en el archivo FlightBooking.json para entrenar la intención "Book flight".
Si la intención superior que se devuelve desde LUIS se resuelve como "Book flight", el bot realizará preguntas adicionales hasta que tenga suficiente información almacenada para crear la reserva de un viaje. En ese momento devolverá la información de dicha reserva a su usuario.
En ese momento se restablecerá la lógica del bot del código y podrá crear más reservas.
Información adicional
Para más información acerca de LUIS, consulte la documentación de LUIS:
Distintos elementos del SDK definen a clases o elementos de entidad independientes.
Para las entidades de mensajes, consulte Tipos de entidades y actividades.