Dialogflow
최근 의도 분류기(Intent Classification) 를 실험해보고 이용해 볼 일이 생겨 의도 분류기로 사용할 수 있는 구글의 Dialogflow 를 어떻게 이용할 수 있는지 적어보려고 합니다.
Dialogflow 는 2016년에 기존 챗봇 빌더 시장에서 가장 유명한 플랫폼인 api.ai 를 구글이 인수하여 기존의 우수한 대화식 인터페이스는 살리고 구글의 강력한 머신러닝을 추가하여 만든 챗봇 빌더 플랫폼 입니다.
Dialogflow 는 영어뿐아니라 한국어, 중국어, 우크라이나어 등 다양한 언어를 지원 가능하며 더 많은 언어도 추가적으로 업데이트할 예정이라고 합니다.(하지만 영어를 가장 잘 지원하긴 합니다.)
어떤 경우에 사용할 수 있을까?
단순한 작업의 경우 코드를 직접 구성하지 않아도 되기 때문에 인공지능을 사용해보지 않은 사용자가 편하게 사용할 수 있습니다. 그렇지만 인공지능을 다뤄본 개발자의 경우 앞의 내용을 보고서 BERT 나 ROBERTA 같은 Encoder 모델을 사용하면 되는데 왜 굳이 Dialogflow 를 사용할까? 와 같은 의문점이 생길 수 있습니다. 하지만 위와 같은 딥러닝 모델 즉, Encoder 모델은 충분한 양(최소 만 단위의 데이터가 있어야 어느정도의 성능을 기대해 볼 수 있습니다)의 데이터가 있어야 합니다. 특정 의도 분류의 경우 충분히 많은 데이터를 모으기 어렵기 때문에 위와 같은 경우 구글의 Dialogflow 와 같은 서비스를 이용해 볼 수 있습니다.
Dialogflow 사전 지식
Dialogflow 사용에 앞서 도움이 될만한 기본 개념들을 몇개 설명드리고자 합니다. Dialogflow 는 챗봇 빌더 이기 때문에
- 화자의 의도인 Intent
- 화자의 발화에서 정보를 얻을 수 있는 속성인 Entity
- 대화의 문맥인 Context
위 3가지를 이해하는 것이 중요하고 이를 잘 지원하도록 설계되어 있습니다.
해당 그림을 보면 날씨가 어떻게 되죠?, 지금 날씨는 어떤가요?, 씨애틀의 기온기온은 내일 어떠나요? 와 같은 질문을 통해 Dialogflow Agent 는 해당 발화들을 듣고 의도(Intent)를 Forecast Intent 로 분류 하였고 화자의 질문에 대하여 추가적인 정보인 시간과 위치(Entity) 정보도 뽑아낸것을 알 수 있습니다. 마지막으로 현재의 대화가 날씨와 관련된 문맥이므로 이후 대화에 날씨와 관련된 문맥을 참고하여 말을 해야하는것을 알 수 있습니다.(즉, 사용자가 날씨를 물어볼 지역인 Seattle 을 챗봇에 대한 대답으로 했는데 챗봇이 이에대한 응답으로Oh I love Seattle. Seattle is the city..... 와 같은 문맥에 맞지 않는 답을 하지 않도록 도와주는 것이 Context 입니다.)
파란색으로 되어있는 부분은 의도를 분류 할때 중요한 영향을 미친 요소이고 노란색을 시간($time) , 빨간색은 위치($location) 즉, Entity 를 의미합니다.
Dialogflow 시작하기
처음으로 시작하게 되면 구글 아이디로 로그인하라는 표시가 나옵니다.
그렇게 로그인을 하고 나면 아래와 같이 약관을 체크후 바로 서비스를 사용 할 수 있습니다.
첫번째로 저희가 해야 될 일은 Agent 를 만드는 일입니다. google 의 Dialogflow 를 사용하기 위한 모든 출발점은 Agent 로 시작합니다.
- Agent name : 사용할 Agent 의 이름
- DEFAULT LANGUAGE : Chatbot builder 에 사용할 기본 언어를 추가할 수 있습니다. 다른 언어도 추가로 넣어줄 수 있습니다.
- DEFAULT TIME ZONE : Chatbot builder 에 사용할 시간대를 입력합니다
- GOOGLE PROJECT : 특정 GCP 프로젝트에 Dialogflow 를 넣고 싶으면 넣을 수 있습니다. 따로 값을 지정해주시 않으면 자동적으로 신규 GCP 가 생성됩니다.
- AGENT TYPE : Mega Agent 로 설정할 수 있습니다. Mega Agent 는 여러 하위 에이전트를 결합하여 메가 에이전트라는 단일 에이전트로 만들 수 있습니다. 설정하지 않으면 일반적인 하위 에이전트로 생성됩니다.
Agent 를 생성하고 난뒤 위의 그림처럼 Intent, Entity, Context 등을 사용할 수 있습니다. 차례대로 어떻게 사용할 수 있는지 알아보겠습니다.
Intent
Intent 는 앞서 설명했던 의도를 분류할 때 사용합니다. 위의 그림에서 CREATE INTENT를 클릭하게 되면 분류하고 하는 의도를 생성해낼 수있습니다.
- Intent name: 분류하고자 하는 Intent 의 이름을 입력합니다
- Training phrases : 해당 Intent 로 분류될 사용자의 발화 예시를 입력합니다. 입력된 예시는 의도를 분류할 때 학습 용도로 사용됩니다.
- Action: 사용자의 발화가 해당 Intent 에 분류되었을 경우 API 응답을 할 때나 fulfillment webhook 을 걸어줄 때 Action 필드를 사용하여 유용하게 사용할 수 있습니다.
- Parameters : Traning phrase 에 넣어준 학습 문구에 추가적인 정보를 사용할 때 사용합니다.
- Responseㄴs : 해당Intent` 에 분류되었을 때 대답할 문장을 설정합니다.
이해를 위해서 추가적인 설명을 해보겠습니다.
저는 사용자가 날씨를 물어보는 의도를 잡아내고 싶어 Forecast 라는 이름으로 Intent 를 생성하였습니다. 해당 의도를 잡아내기 위해 학습에 사용할 문구로 아래 문장들을 Tranining phrases에 추가해주었습니다.
- Can you tell me weather in Seoul Tomorrow and 2 days after?
- Was the weather hot in Tokyo yesterday afternoon?
- How's the weather in Seattle tomorrow?
- What's the weather in Seoul?
사용자가 날씨를 물어볼 때 필수적으로 알아야 하는 정보는 위치 와 시간 입니다. 따라서 Action and parameters 에 위치 즉, 특정 도시를 나타내는 @sys.geo-city 와 시간을 @sys.date-time 을 넣어주었습니다. 표에 있는 필드들의 설명은 아래와 같습니다.
- REQUIRED : 해당 parameter 정보가 없을 경우 올바른 Intent 의 응답을 하기 어려운 경우 체크해줍니다.
- PARAMETER NAME : 해당 Parameter 의 이름을 적습니다.
- ENTITY : 해당 Parameter 와 일치하는 ENTITY를 입력합니다. ENTITY Dialogflow 가 미리 정의해 놓은 Entity 를 사용할 수도 있고 사용자가 새롭게 정의할 수 있습니다.
- @sys.geo-city 와 @sys.date-time 은 Dialogflow 가 정의해 놓은 값을 사용한 것입니다.
- VALUE : 해당 Parameter 의 변수명 입니다.
- IS LIST : 한 개 사용자의 발화에 대해서 해당 Parameter 가 2개 이상 나올 수 있으면 IS LIST 로 체크합니다.
- 예를들어 How's the weather in Seoul tomorrow and the day after tomorrow? 이라는 문장이 있다면 여기서 날짜에 해당 되는 Parameter 는 tomorrow 와 the day after tomorrow 입니다. 이 경우 date-time 은 IS LIST 로 체크됩니다.
- PROMPTS : 해당 Parameter 가 REQUIRED 일때 유저의 발화로 해당 Parameter 를 얻어내지 못했을 때 유저에게 물어볼 질문입니다.
- 예를들어 What's the weather in seoul? 을 사용자가 물어보았을 때 필수 Parameter 인 date-time 이 없으므로 Should I tell you about yesterday's weather? 를 PROMPTS 로 설정할 수 있습니다.
- Default value : 만약 값이 주어지지 않았을 경우 해당 Parameter 의 초기 값을 설정해줄 수 있습니다.
- Default value 는 아래의 사진 처럼 메뉴버튼을 눌러야 확인할 수 있습니다.
모두 완료가 되었으면 save 버튼을 누른후 저장해줍니다. 한번 Dialogflow 가 의도를 잘 분류하는지 확인해볼까요? 의도 분류는 간단하게 웹을 통해서 할 수도 있고 CURL 을 통해서도 확인할 수 있습니다.
{
"responseId": "c7f4d998-f587-44c9-a745-6358c18c1bd6-53cb9be6",
"queryResult": {
"queryText": "How's the weather in Los Angeles at tomorrow?",
"action": "ASK_FORCAST",
"parameters": {
"geo-city": "Los Angeles",
"date-time": [
"2022-03-08T12:00:00+09:00"
]
},
"allRequiredParamsPresent": true,
"fulfillmentText": "Well, I don't know........",
"fulfillmentMessages": [
{
"text": {
"text": [
"Well, I don't know........"
]
}
}
],
"intent": {
"name": "projects/test-fyyj/agent/intents/026147e7-3897-49ce-8a03-3fef1e9e4be5",
"displayName": "Forecast"
},
"intentDetectionConfidence": 1,
"languageCode": "en",
"sentimentAnalysisResult": {
"queryTextSentiment": {}
}
}
}
위의 예시는 간단하게 웹을 통해서 했고 그 아래의 예시는 CURL의 결과문 입니다. 예문으로는 How's the weather in Los Angeles at tomorrow? 를 넣었습니다. 그 결과 Intent 로 Forecast 를 잡은걸 볼 수 있습니다. 또한 action 필드도 ASK_FORCAST 로 나온것을 확인할 수 있습니다. 이를 나중에 코드레벨로 사용할 땐 아래와 같이 사용할 수 있습니다.
DialogFlowIntentResult result = restTemplate.getForObject("url...");
if (result.queryResult.action == "ASK_FORCAST") {
// 일기예보 비즈니스 로직 수행
}
Entity
이전 글에서 Entity 를 일부 사용했지만(ex. @sys.date-time, @sys.geo-city) 이 예시들은 제가 별도로 추가해준 Entity 가 아닌 Dialogflow 에서 기본적으로 설정되어 있는 Entity 입니다. Entity 는 언어마다 다르게 설정 되어있기 때문에 해당 링크 를 참조 하시면 언어별 기본으로 사용할 수 있는 Entity를 알려줍니다. 이렇게 기본으로 사용할 수 있는 Entity를 System entity 라고 합니다 Dialogflow 에선 이미 많은 Entity 를 제공해주고 있지만 앱을 개발하다보면 추가적으로 커스터마이징을 해야할 Entity 를 추가해줘야하는 경우도 있습니다. 이를 Custom Entity 라고 합니다.
System Entity : 날짜, 시간, 이메일 등
Custom Entity : 야채, 고기, 사람 나이 등
Entity 와 관련된 작업을 하고 싶다면 Entities 에 들어가면 됩니다.
Entities 에 처음 들어가게 되면 아직 아무런 Entity를 만들지 않았기 때문에 Create the first one. 이라는 문구가 보입니다. 해당 문구를 클릭하거나 CREATE ENTITY를 눌러 새로윤 Entity를 만들어보겠습니다. 우리는 여기서 weather 이라는 Entity 를 정의해보도록 하겠습니다.
Entity 를 정의해줄 때 Dialogflow 에서 제공해주는 4가지 기능을 사용할 수 있습니다.
- Define Synonyms
- 동의어를 넣어주어 Entity 를 잡아낼 수 있습니다. 기본적으로 넣어준 단어뿐 아니라 동의어를 추가적으로 넣어 이를 잡아낼 수 있습니다. (Ex. Raining, raining, rainy)
- Regexp entity
- 정규표현식을 이용하여 정의하는 방법으로 여기서 사용할 수 있는 정규표현식은 이곳 을 참조하면 됩니다.
- Allow automated expansion
- 한 Entity에 대하여 자동적으로 확장해 줄 수 있습니다. 예를 들어 Shopping list 라는 Entity 에 대하여 bread, butter, milk, apple, ice cream 과 같은 단어들을 Entity 의 예시로 넣어주고 사용자가 I need to buy some carrots 이라고 하면 carrots 은 Entity 에 대한 예시로 넣어져 있지 않지만 자동적으로 추가됩니다.
- 하지만 2개 이상의 Entity 에 대해서 이 기능을 모두 활성화 시키면 두개의 Entity 에 모두 해당되어 충돌이 일어나기도 합니다.
- 이 기능을 올바르게 동작시키려면 충분한 예시를 넣어주는게 중요합니다.
- Fuzzy matching
- 사용자가 스펠링을 틀리거나 단어의 일부분만 입력으로 넣었을 때 이를 내부적으로 교정하여 Entity 에 매칭시켜줍니다.
저는 오타 교정과 동의어 기능은 사용하고 싶어 각각 Fuzzy matching 기능과 Define Synonyms 기능을 체크하였습니다. 동의어로는 Raining 에 Rainy, raining 을 넣어주었습니다. 이렇게 Entity 를 정의하고 나면 Intent 의 학습 문구에 이를 사용할 수 있습니다.
학습에 사용할 학습문구로 Is it raining now? 라는 문구를 넣어주었고 이에 대응하는 Entity 를 raining 은 Weather 에, now 는 date-time 에 넣어주었습니다. 이렇게 하면 추가적으로 Weather이란 Entity 를 정의해서 추가적인 정보를 얻을 수 있습니다.
Context
사람이 대화를 할땐 주로 Multi-turn 으로 대화가 이루어집니다. 아래의 예시는 날씨에 관한 Multi-turn 대화 형식입니다.
사람 : 날씨 어때?
챗봇 : 어디?
사람 : 부산!
챗봇 : 언제?
사람 : 오늘!
챗봇 : 오늘 부산 날씨는 ......
위에 설명 했던 Intent, Entity 도 챗봇을 만들 수 있는 강력한 도구들이지만 이것들만으로는 Multi-turn 챗봇을 만들기 힘듭니다. 이렇게 Multi-turn 대화를 만들기 위해선 이전의 대화의 내용들을 잘 기억하고 있어야합니다. 여기서 도움을 줄 수 있는 것이 Context 입니다.
설명 전 ASKING WEATHER 은 날씨를 물어보는 질문(ex. How's the weather?)을 학습시켜 비슷한 질문을 하면 해당 Intent 로 잡히고 Where? 을 대답하게끔 설정했습니다.
Intent 로 돌아가 임의의 Intent 에 마우스를 가져가 대보면 Add follow-up intent 가 나오는걸 확인할 수 있습니다. 버튼을 클릭한뒤 custom 을 눌러줍니다. 그럼 바로 아래에 ASKING WEATHER - custom 이라고 나와 있는것을 확인할 수 있습니다.(저는 Intent 의 이름을 ASKING WEATHER - region 으로 수정해주겠습니다.)
이 Intent 를 클릭해서 들어가보면 Contexts 의 Input context ASKINGWEATHER-followup 이라는 Context 가 있는것을 확인할 수 있습니다.
바로 이 부분이 우리가 사용할 Context 입니다. Context 의 주된 사용처는 이전 대화의 흐름과 함께 이전 대화에 나왔던Entity 를 사용할 수 있다는 것 입니다. 여기선 지역에 대한 정보를 물어볼 것이기 때문에 사용자가 특정 지역(ex. I want to know about Pusan) 과 같은 발화를 하면 When? 을 응답하도록 설정해주었습니다.
여기서 똑같은 방법으로 ASKING WEATHER - region에 Add follow-up intent 를 눌러 ASKING WEATHER - region - time 을 만들어줍니다.
ASKING WEATHER - region - time 에선 시간(ex. Yesterday, Tomorrow)에 대한 대답을 잡아내도록 학습을 하였습니다. 그 후 Responses 로 #ContextName.ParameterName 형식으로 이전 Context 에서 나온 Parameter 정보를 가져와 대답하도록 하였습니다.
참고로 Context 는 Input Context 와 Output Context 로 나눠져 있습니다.
Input Context : 해당 Intent 에서 입력으로 받는 Context 1개 이상 가질 수 있습니다.
Output Context : 해당 Intent 에 뒤에올 Intent 에게 줄 수 있는 Context 입니다 1개 이상 가질 수 있습니다.
또한 Output Context 는 LifeCycle 을 가지고 있습니다. 한번 대화가 진행될 때마다 Life Cycle 이 1씩 감소하고 만약 Life Cycle 이 0이되면 더이상 해당 Output Context 를 참조할 수 없습니다.
그럼 이제 Context 를 활용한 응답이 잘 되는지 확인해 보겠습니다. 보다 원활한 대화를 위해 Integrations 의 Web Demo 를 활용하겠습니다. Web Demo 를 누른뒤 해당 Url 로 이동하면 됩니다.
대화를 진행해보면 아래와 같이 올바르게 Context 를 이해하여 대화를 진행하고 있는 모습을 볼 수 있습니다.
파이선 코드
from google.cloud import dialogflow
import os
def detect_intent_texts(project_id, session_id, texts, language_code):
"""
Returns the result of detect intent with texts as inputs.
Using the same `session_id` between requests allows continuation
of the conversation.
"""
session_client = dialogflow.SessionsClient()
session = session_client.session_path(project_id, session_id)
print("Session path: {}\n".format(session))
for text in texts:
text_input = dialogflow.TextInput(text=text, language_code=language_code)
query_input = dialogflow.QueryInput(text=text_input)
response = session_client.detect_intent(
request={"session": session, "query_input": query_input}
)
print("=" * 20)
print("Query text: {}".format(response.query_result.query_text))
print(
"Detected intent: {} (confidence: {})\n".format(
response.query_result.intent.display_name,
response.query_result.intent_detection_confidence,
)
)
print("Fulfillment text: {}\n".format(response.query_result.fulfillment_text))
if response.query_result.intent.display_name == "Photo or URL send intention":
print("나는 지금 텍스트로된 말을 빼고 보낼 수 없어 언제든 편하게 말해줘!")
# Press the green button in the gutter to run the script.
if __name__ == "__main__":
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = # key 경로
detect_intent_texts(
project_id="test-ufws",
session_id="123456789",
texts=[
"Can you share your picture?",
"Please don't send me your photo",
"I would like to get some picture of you",
"How the weather today?",
],
language_code="en-US",
)