Chatbot - 봇 작동 방식(기초 중의 기초)


봇 작동 방식

이제 기초 중의 기초, 봇 작동 방식에 대해 보겠다.

봇은 사용자가 텍스트, 그림, 음성을 사용해서 대화형 방식으로 상호 작용하는 앱이다.

사용자와 봇이 상호 작용할때마다 “Activity”가 생성된다.

Bot Framework Service는 Azure Bot Service의 구성 요소로, 사용자 봇에 연결된 앱(Facebook, Skype => 얘네를 Channel이라고 함)봐 봇 간의 정보를 제공한다. 즉 채널과 봇 간에 정보를 전송하는 역할을 한다.

bot-builder-activity

위 그림은 에코 봇(사용자가 메세지를 보낸 그대로 repeat만 하는 기본 봇)을 실행할 때 이루어지는 활동들을 그림으로 나타낸 것이다.

여기서 이루어진 활동은 1. 대화 업데이트랑 2. 메세지 이다.

대화 상대가 대화에 참여하면 Bot Framework Service에서 대화 업데이트를 보낼 수 있다. 예를 들어 Bot Framework Emulator와 대화를 시작하면 두 가지 대화 업데이트 활동(사용자 대화 참여 활동과 봇 참여 활동)이 표시된다. 바로 그게 그림에 상호작용 첫번째, 두번째에 있는 “Http post conversation Update” 인것 같다.

이런 대화 업데이트 활동을 구분하려면 members added 속성에 봇 아닌 멤버가 포함되어 있는지 확인하라고 되어있다.

메세지 활동은 대화 상대 간에 대화 정보를 전달한다. 그것이 그림에 밑에 있는 “http post message”로 메세지를 주고 받는 과정을 보여주는 것 같다.

물론 에코 봇처럼 단순히 사용자가 보낸 메세지를 다시 반복하는 것이 아니라 다른 방식으로 대화 업데이트 활동에 응답할 수 있다.

HTTP 세부 정보

위에서 Bot Framework Service는 채널과 봇 간에 정보를 전송하는 역할을 한다고 했다.

Activity는 HTTP POST 요청을 통해 Bot Framework Service에서 봇에 도착한다.

봇은 HTTP 상태 코드 200으로 인바운드 POST 요청에 응답한다. 즉 200 HTTP 상태 코드로 승인이 요청된다.

프로토콜은 POST 요청과 승인 요청이 수행되는 순서를 저장하지 않는다. 하지만 이런 요청들은 일반적인 HTTP 서비스 프레임워크에 맞추기 위해 중첩된다. 인바운드 HTTP 요청의 범위 내에서봇이 아웃바운드 HTTP 요청을 수행하낟. 인바운드는 사용자가 수행한 것이고 아웃바운드는 봇이 수행한 것인가 보다.

위의 그림에서 두 개의 개별적인 HTTP 연결이 연달아 수행되므로 보안 모델이 제공되어야 한다.

순서 정의

대화를 할 때 보통 사람들은 한 번에 한 명씩 순서대로 말을 한다.

Bot Framework SDK에서 order는 사용자가 봇에 보내는 작업과 봇이 응답하는 작업으로 구성된다.

order context는 발신자, 수신자, 채널, 활동을 처리하는데 필요한 기타 데이터와 같이 활동에 대한 정보를 제공한다. 또한 봇의 다양한 계층에서 순서가 처리되는 동안 정보를 추가할 수 있게 해준다.

order context는 sdk에서 가장 중요한 추상화 중 하나이다.

왜냐하면 모든 미들웨어 구성 요소에 대한 인바운드 활동과 애플리케이션 논리를 전달하고 미들웨어 구성 요소와 애플리케이션 논리가 아웃바운드 활동을 전송하는데 사용할 수 있는 매커니즘을 제공하기 때문이다.

활동 처리 스택

bot-builder-activity-processing-stack

먼저 HTTP POST 요청부터 처리되고, Activity 정보는 Json 페이로드로 전달돼서 웹 서버에 도착한다.

SDK의 통합 구성 요소인 adapter는 SDK 런타임의 핵심이다. HTTP POST 본문에서 JSON으로 작업이 전달되면, 이 JSON을 역직렬화해서 작업 개체를 만든다. 그 다음 processActivity method를 호출하여 어댑터에 전달한다.

어댑터가 작업을 수신하면 order context를 만들고 미들웨어를 호출한다.

order context는 봇이 인바운드 작업에 대한 응답으로 아웃바운드 작업을 보내는 데 사용하는 매커니즘이다.

이를 위해 order context는 작업 보내기, 업데이트 및 삭에 와 같은 응답 method를 제공한다. 이 응답 메서드는 비동기 프로세스에서 실행된다.

Bot turn을 처리하는 thread는 사용을 마친 Context 개체를 폐기한다. 따라서 turn context의 처리와, 이게 삭제가 되기 전에 기본 스레드에서 생성된 활동을 기다리도록 활동 호출을 await(대기)해야 한다.

작업 처리기

봇은 작업을 수신하면 Activity Processor로 전달한다. 내부적으로는 “order Processor”라는 기본 처리기가 하나 있다.

모든 작업은 이곳을 거쳐 routing 된다. 이때 order Processor는 작업의 유형과 상관없이 수신되기만 하면 개별 작업 처리기를 호출한다.

그래서 Echobot에서 어떤 메세지를 사용자가 입력하기만 하면 OnMessageActivity어쩌구 메서드가 항상 실행이 되나보다. 이런 메서드들을 작업 처리기라고 하나보다.

예를 들어 봇이 메세지 작업을 수신하면 order processor가 수신 작업을 확인하고 OnMessageActivityAsync 작업 처리기로 보낸다.

봇을 빌드할 때 메세지 처리와 응답용 봇 logic은 이 OnMessageActivityAsync 처리기로 들어간다.

대화에 추가되는 멤버를 처리하는 logic같은 경우는 OnMembersAddedAsync 처리기로 들어가게 되고, 대화에 멤버가 추가될 때마다 이 처리기가 호출된다.

이런 처리기의 logic을 구현하려면, 그니까 메서드들을 기존과 다르게 구현하고 싶다면 메서드들을 override하면된다.

처리기들에 기본적으로 되어있는 구현같은 것은 없으므로 override 할 때 원하는 logic을 추가하기만 하면 된다.

순서 종료 시 상태 저장과 같은 logic을 추가하여 기본 순서 처리기를 override 할 경우에는 먼저 await base.OnTurnAsync(turnContext, cancellationToken); 을 호출해서 OnTurnAsync의 기본 구현이 추가 코드 전에 실행되도록 하세요.

미들웨어

미들웨어는 순서대로 각각 실행되는 선형 구성 요소 집합을 구성하고 각 작업에서 작동할 수 있는 기회를 제공한다.

미들웨어 파이프라인의 최종 단계는 어플래케이션이 어댑터의 Activity Process 메서드에 등록한 봇 클래스의 순서 처리기 함수를 호출하는 콜백이다.

C#에서는 일반적으로 OnTurnAsync이다.

Order Processor는 order context를 인수로 설정하고, order Processor 내에서 실행 중인 애플리케이션 logic은 인바운드 작업의 contents를 처리하고, 응답에서 하나 이상의 작업을 생성하여 order context에서 send activity 함수를 사용해서 보낸다.

그니까 정리하면 순서 처리기는 간단하게 사용자가 보낸 메세지의 내용을 처리하고 send activity 함수를 통해 응답을 한다는 것 같다.

미들웨어 구성 요소는 봇의 order processor 함수 전 후에서 실행된다.

공식 문서에서는 러시아 인형처럼 중첩된다고 비유했다.

봇 구조체

ASP.NET 기본 사항을 보면 Program.csStartup.cs같은 파일이 있는데, 이런 파일들은 웹앱에 필요한 거지 봇 전용은 아니다.

appsettings.json 파일

appsettings.json파일은 앱 ID, 암호와 같은 봇의 구성 정보를 지정한다. QnA Maker와 같이 특정 기술을 사용하거나 production 환경에서 봇을 사용하려는 경우 여기에 특정 key나 url을 추가해야 한다.

봇 logic

봇 logic은 하나 이상의 channel에서 들어오는 작업을 처리하고, 나가는 작업을 생성해서 응답한다.

주로 봇 logic은 코드(Bots/사용자가설정한봇이름.cs)에 정의된다(여기서는 EchoBot이라고 하자).

EchoBot은 ActivityHandler에서 파생되며, 이 ActivityHandler는 IBot 인터페이스에서 파생된다.

ActivityHandler는 다양한 작업을 위해 다양한 처리기(위에서 본 것처럼 OnMessageActivityAsync나 OnMembersAddedAsync)를 정의한다.

이벤트Handler설명
작업 유형이 수신됨OnTurnAsync수신된 작업의 유형에 따라 처리기 중 하나를 호출한다.
메세지 작업이 수신됨OnMessageActivityAsyncmessage 작업을 처리하도록 재정의 한다.
대화 업데이트 작업이 수신됨OnConversationUpdateActivityAsyncconversationUPdate 작업에서 봇 이외 멤버가 대화에 참가하거나 대화를 나간 경우 처리기를 호출한다.
봇 이외 멤버가 대화에 참여함OnMembersAddedAsync대화에 참가한 멤버를 처리하도록 재정의한다.
봇 이외 멤버가 대화를 나감OnMemebersRemovedAsync대화를 나간 멤버를 처리하도록 재정의한다.
이벤트 작업이 수신됨OnEventActivityAsyncevent 작업에서 이벤트 유형과 관련된 처리기를 호출한다.
토큰-응답 이벤트 작업이 수신됨OnTokenResponseEventAsync토큰 응답 이벤트를 처리하도록 재정의한다.
토큰-응답 이외 이벤트 작업이 수신 됨OnEventAsync다른 유형의 이벤트를 처리하도록 재정의한다.
메세지 반응 작업이 수신됨OnMessageReactionActivityAsyncmessageReaction 작업에서 하나 이상의 반응이 메세지에서 추가되거나 제거된 경우 처리기를 호출한다.
메세지에 추가된 메세지 반응OnReactionsAddedAsync메세지에 추가된 반응을 처리하도록 재정의한다.
메세지에서 제거된 메세지 반응OnReactionsRemovedAsync메세지에서 제거된 반응을 처리하도록 재정의한다.
다른 작업 유형이 수신됨OnUnrecognizedActivityTypeAsync달리 처리되지 않은 작업 유형을 처리하도록 재정의한다.

이런 처리기들에는 수신 작업에 대한 정보를 제공하는 turnContext가 있다.(인바운드 HTTP 요청)

대부분의 경우 OnMessageActivityAsync를 많이 쓰고, 이 메서드는 항상 처리되는 경우가 많다.

**OnTurnAsync 메서드를 override 하는 경우에는 base.OnTurnAsync를 호출해서 기본 구현을 가지고 온 다음 다른 OnAsync 처리기를 모두 호출하거나 이런 처리기를 직접 호출해야 한다. 안그러면 처리기가 처리되지 않으며,며, 코드가 실행되지 않는다**

아래의 예제는 SendActivityAsync 호출을 사용하여 새 사용자를 환영하거나 사용자가 보낸 메시지를 출력한다. 아웃 바운드 작업은 아웃바운드 HTTP POST 요청에 해당된다.

public class MyBot : ActivityHandler //ActivityHandler를 상속하고 있다. 이제 무슨 말인지 알겠음
{
    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
    }

    protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
    {
        foreach (var member in membersAdded)
        {
            await turnContext.SendActivityAsync(MessageFactory.Text($"welcome {member.Name}"), cancellationToken);
        }
    }
}

앱에서 봇에 액세스

서비스 설정

Startup.cs 파일의 ConfigureServices method는 appsettings.json이나 Azure Key Valut에서 연결된 서비스와 키를 load하고, 상태를 연결하는 등의 역할을 한다.

여기에서는 MVC(model view controller)를 추가하고, 서비스의 호환성 버전을 설정하고 봇 controller에 종속성을 넣어 사용할 수 있게 어댑터와 봇을 설정한다.

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    // Create the credential provider to be used with the Bot Framework Adapter.
    services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

    // Create the Bot Framework Adapter.
    services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();

    // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, EchoBot>();
}

ConfigureServices랑 Configure는 앱이 시작될 때 런타임에서 호출된다.

봇 컨트롤러

표준 MVC 구조를 따르는 컨트롤러를 사용해서 메세지나 HTTP POST 요청의 routing을 결정할 수 있다.

+