LUIS(Language Understanding)는 2025년 10월 1일에 사용 중지됩니다.
2023년 4월 1일부터 새 LUIS 리소스를 만들 수 없습니다.
이제 최신 버전의 언어 이해가 Azure AI Language의 일부로 제공됩니다.
Azure AI Language의 기능인 CLU(대화형 언어 이해)는 업데이트된 LUIS 버전입니다.
Bot Framework SDK의 언어 이해 지원에 대한 자세한 내용은 자연어 이해를 참조하세요.
사용자가 대화 및 상황에 맞는 의미를 이해하는 기능은 어려운 작업이 될 수 있지만 봇에게 보다 자연스러운 대화 느낌을 제공할 수 있습니다. LUIS(Language Understanding) 는 클라우드 기반 API 서비스로, 봇이 사용자 메시지의 의도를 인식하고, 사용자의 자연어를 더 잘 허용하고, 대화 흐름을 더 잘 지시할 수 있도록 할 수 있습니다.
이 항목에서는 사용자 입력에 포함된 다양한 의도 및 엔터티를 인식하기 위해 항공편 예약 애플리케이션에 LUIS를 추가하는 방법을 안내합니다.
참고 항목
Bot Framework JavaScript, C#및 Python SDK는 계속 지원되지만 Java SDK는 2023년 11월에 종료되는 최종 장기 지원으로 사용 중지됩니다.
이 핵심 봇 샘플은 공항 항공편 예약 애플리케이션의 예를 보여줍니다. LUIS 서비스를 사용하여 사용자 입력을 인식하고 인식된 상위 LUIS 의도를 반환합니다.
언어 모델에는 세 가지 의도 Book Flight, 즉 , Cancel및 None. LUIS는 이러한 의도를 사용하여 사용자가 봇에 메시지를 보낼 때의 의미를 이해합니다. 언어 모델은 또한 LUIS가 원본 또는 대상 공항과 같은 사용자의 입력에서 추출할 수 있는 엔터티를 정의합니다.
사용자 입력 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시작됩니다. 이 대화 상자는 다음과 같은 사용자로부터 필요에 따라 추가 정보를 가져옵니다.
샘플의 CognitiveModels 폴더에서 FlightBooking.json 파일을 선택합니다.
앱의 선택적 이름으로 입력 FlightBooking 하고 완료를 선택합니다.
사이트에 효과적인 LUIS 앱을만들고 복합 엔터티를 업그레이드하는 방법 대화 상자가 표시 될 수 있습니다. 이러한 대화 상자를 해제하고 계속할 수 있습니다.
앱을 학습한 다음, 프로덕션 환경에 앱을 게시합니다.
자세한 내용은 앱을 학습하고 게시하는 방법에 대한 LUIS 설명서를 참조하세요.
엔터티를 사용하는 이유
LUIS 엔터티를 사용하면 봇이 표준 의도 이외의 이벤트를 이해할 수 있습니다. 이를 통해 사용자에게 추가 정보를 수집할 수 있으므로 봇이 질문을 하고 보다 지능적으로 응답할 수 있습니다. 세 개의 LUIS 의도 'Book Flight', 'Cancel', 'None'에 대한 정의와 함께 FlightBooking.json 파일에는 'From.Airport' 및 'To.Airport'와 같은 엔터티 집합도 포함되어 있습니다. 이러한 엔터티를 통해 LUIS는 새 여행 예약을 요청할 때 사용자의 원래 입력에 포함된 추가 정보를 검색하고 반환할 수 있습니다.
LUIS 앱에 연결할 값 가져오기
LUIS 앱이 게시되면 봇에서 액세스할 수 있습니다. 봇 내에서 LUIS 앱에 액세스하려면 여러 값을 기록해야 합니다. LUIS 포털을 사용하여 해당 정보를 검색할 수 있습니다.
LUIS.ai 포털에서 애플리케이션 정보 검색
설정 파일(appsettings.json.env또는config.py)은 모든 서비스 참조를 한 곳에서 함께 가져오는 위치 역할을 합니다. 검색한 정보는 다음 섹션에서 이 파일에 추가됩니다.
애플리케이션 ID, 작성 키 및 지역을 포함하여 LUIS 앱에 appsettings.json 액세스하는 데 필요한 정보를 파일에 추가합니다. 이전 단계에서는 게시된 LUIS 앱에서 이러한 값을 검색했습니다. API 호스트 이름은 형식 <your region>.api.cognitive.microsoft.com이어야 합니다.
애플리케이션 ID, 작성 키 및 지역을 포함하여 LUIS 앱에 .env 액세스하는 데 필요한 정보를 파일에 추가합니다. 이전 단계에서는 게시된 LUIS 앱에서 이러한 값을 검색했습니다. API 호스트 이름은 형식 <your region>.api.cognitive.microsoft.com이어야 합니다.
애플리케이션 ID, 작성 키 및 지역을 포함하여 LUIS 앱에 application.properties 액세스하는 데 필요한 정보를 파일에 추가합니다. 이전 단계에서는 게시된 LUIS 앱에서 이러한 값을 검색했습니다. API 호스트 이름은 형식 <your region>.api.cognitive.microsoft.com이어야 합니다.
애플리케이션 ID, 작성 키 및 지역을 포함하여 LUIS 앱에 config.py 액세스하는 데 필요한 정보를 파일에 추가합니다. 이전 단계에서는 게시된 LUIS 앱에서 이러한 값을 검색했습니다. API 호스트 이름은 형식 <your region>.api.cognitive.microsoft.com이어야 합니다.
프로젝트에 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는 추출 위치, 대상 및 TravelDate 논리를 포함하고 있으며, MainDialog.cs에서 FlightBookingRecognizer.RecognizeAsync<FlightBooking>을 호출할 때 LUIS 결과를 저장하는 데 사용되는 partial 클래스 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 서비스에 연결하기 위해 봇은 파일에 추가 .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;
}
}
추출 위치, 대상 및 TravelDate 논리는 flightBookingRecognizer.js 내부에 도우미 메서드로 구현됩니다. 이러한 메서드는 다음에서 호출 flightBookingRecognizer.executeLuisQuery() 한 후 사용됩니다. mainDialog.js
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);
}
From, To 및 TravelDate를 추출하는 논리를 포함하고 Luis 쿼리 결과의 결과를 디코딩하기 위해 호출 MainDialog.java 됩니다.FlightBookingRecognizer.cs
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 추출하는 논리는 내부 luis_helper.py클래스에서 LuisHelper 도우미 메서드로 구현됩니다. 이러한 메서드는 다음에서 호출 LuisHelper.execute_luis_query() 한 후 사용됩니다. main_dialog.py
도우미/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