본문 바로가기

2021스텍 개발 일기~

외부 연동 서버 (Backend proxy) 연결하기, 구축하기

Backend proxy 구축하기

 

 

 

Backend proxy는 응답을 생성하기 위해 필요한 정보를 외부로부터 가져와야 하는 경우 사용하는 서버이다.

Play에서는 사용자의 발화를 분석한 내용을 기반으로 적절한 응답을 내보내 주거나 동작을 수행한다. 이 응답에 필요한 정보가 외부 서버로부터 가져와야 하는 경우는 REST API를 통해 요청해야 하며, 음악을 재생하는 등의 디바이스를 동작시키는 명령을 정의하는 것도 Backend proxy에서 처리해야 한다.

 

NUGU 플랫폼의 Dialog Manager는 지정된 포맷으로 요청을 하기 때문에 외부 서비스의 REST API 포맷이 Backend proxy 규격과 다르다면 포맷을 변환해주기 위한 Backend proxy 서버를 개발해야 한다.

서버는 Play Builder를 통해 Play를 만드는 과정에서 정의한 파라미터와 Backend proxy API Reference에서 제공하는 규격을 사용하여 직접 개발해야 한다. 

 

첫번째 단계와 두번째 단계는 Play Builder에서 설정하는 작업이다.

Play Builder에서 Action별로 설정된 정보를 기반으로 Action 당 하나의 REST API 포맷을 파악하고 Backend proxy 호출을 필요로 하는 Action 수만큼 REST API를 지원하는 서버를 개발한다. 

 

Backend proxy API 규격은 다음과 같은 정보를 Backend proxy 서버로 전달한다.

정보 설명
Action 이름 Backend proxy에서 처리해야 하는 요청을 구분하는 데 사용된다. 어떤 Action이 Backend proxy를 호출했는지 확인할 수 있다.
Parameters Play에서 정의된 Parameter들이 전달된다. Utterance Parameter에는 Play 사용자의 실제 발화에 담긴 Entity 혹은 그 Entity가 정규화된 값이 "value"로 전달된다. Backend Parameter는 "value"를 담아서 Play로 전달하게 될 Parameter이며 value는 "null"로 전달된다. utterance/Backend Parameter를 구분할 수 없으므로, play에서 어떻게 정의를 했는지 파악한 후 구현해야 한다.
Context 정보 사용자 식별 token, 디바이스 상태 정보 등이 전달된다.
Event 정보  디바이스에서 발생한 Event 정보
Backend proxy 서버는 위의 정보를 바탕으로 특정 사용자가 전달한 요청에 대해 적절한 정보를 전달해야 한다.

 

 

 


 

Backend proxy를 사용하는 경우

  • Action의 응답을 정의할 때, 외부 서버로부터 정보를 가져와야하는 경우
    • 날씨 상태, 지하철 도착 예정 시간, 특정 POI의 전화번호 등
  • 특정 Entity에 대하여 서버의 판단이 필요할 때
    • 사용자가 말한 날짜가 무슨 요일인지 판단
      • 사용자로부터 2개의 Entity를 받아서 처리해야 할 때, 2개의 Entity의 정합성을 체크하는 경우
        • 2월 31일의 날씨 알려줘, 일본의 뉴욕 시간 알려줘
  • 서버에서 연산하여 결과를 제공할 수 있는 경우
    • 현재 시간 : 2 + 3의 결과
  • Directive를 사용하여 디바이스의 기능을 동작시켜야 하는 경우
    • 오디오 재생, 멈춤
  • 예외 상황에 대한 판단이 필요할 때
    • 콘텐츠 서버의 장애
      • 사용자가 지원하지 않는 범위의 정보를 요청했을 때, Intent는 유지하면서 Entity만 다시 받아 처리하고 싶은 경우

 


Backend proxy 연결 순서

 

  1. Backend proxy 서버를 구축한다.
  2. Backend proxy를 Play와 연결한다.
    • Play Bulider에서 해당 Play의 General > 외부 서버 연결 정보 페이지에서 Backend proxy의 Web URL을 입력한다.
    • Web URL 입력시 http:// 또는 https://를 포함하여 작성한다.
    • 이 Backend proxy과의 통신이 실패했을 때 사용자에게 전달할 메시지를 '연결 실패 시 prompt'에 입력한다.
  3. Play 개발자는 Backend proxy 개발자와 다음 사항을 공유해야 한다.
    • 어떤 Utterance Parameter를 정의하였고, 해당 Utterance Parameter는 어떠한 값들이 전달이 될 것인지 전달해야 한다. Entity를 정규화 한다면 대표값이 Utterance Parameter에 담겨서 전달이 될 것이므로 해당 대표값을 안내해야 한다.
    • 정의한 Backend Parameter에 어떤 값이 담기기를 기대하는지 안내해야 한다.
    • Play를 만들면서, 예외 상황을 처리해야 한다면 예외 상항 관리에 등록을 하고, 이를 Exception Code로 요청해야 한다.
    • Capability Interface를 사용하는 경우 어떤 Intent에서 어떻게 Directive를 내보낼 것인지 논의한다.
  4. Backend proxy과 연결화 Action에서 Backend proxy 사용 여부를 On으로 설정한다.
    • Backend proxy는 어떤 이름의 Action이 자신을 호출할지 알고 있어야 한다. Backend proxy에서 알지 못하는 Action이 호출을 하면, Play는 그 자리에서 멈춘다. 즉, Play 개발자는 Backend proxy를 호출할 Action을 Backend proxy 개발자에게 전달한다.
    • Capability Interface를 사용하는 경우, 트리 최하단에서 응답을 내보내는 Action도 Backend proxy와 연결하여 Directive를 받아와야 한다. 

 


 

 

 

REST API URL

REST API 호출은 Backend proxy를 사용하도록 지정한 Action에서만 이루어지며, 각 Action 별로 고유한 REST API URL이 결정된다.

REST API URL 생성 규칙

  • play Builder > General > 외부 서버 연결 정보 > Web URL + Play Builder > Actions > Action name

예를 들어 외부 서버 연결 정보의 Web URL에 http://backend_proxy.nugu.com  를 설정하고, Action 이름을 "playMusic"으로 설정했다면 해당 Action을 처리하는 REST API URL은 http://backend_proxy.nugu.com/playMusic 이 된다.

 


Request Body

Request의 Body로 전달되는 JSON 포맷의 데이터는 Backend proxy API Reference를 참조한다. 이 JSON 데이터는 임의의 필드가 추가될 수 있으므로, 이에 대한 영향이 없도록 구현해야 한다.

Request Body에 정의된 필드 중 Play Builder에 설정한 값들에 의해 결정되는 필드는 action.parameters 이다.

 

 

action.parameters 필드를 채우는 규칙

필드명 생성규칙
KEY Play Builder의 "응답에 필요한 정보 가져오기" 화면에서 다음의 두 위치에 설정된 파라미터가 포함되어야 한다.  Utterance Parameter의 paramter Name, Backend Parameter의 Parameter Name
type Utterance Parameter에만 적용되며, Play Builder의 Entity Mapping에 설정된 값 중 " : " 앞의 값만 전송한다. Backend Parameter의 경우에는 Entity Mapping을 설정하지 않기 때문에 이 필드는 사용되지 않는다.
value 실제 해당 파라미터에 할당된 값을 전송한다. Utterance Parameter의 경우 필수가 체크되지 않는 파라미터는 값이 있을 수도 있고 없을 수도 있다. Backend Paraemeter의 경우에도 Backend proxy가 몇 번 호출되느냐에 따라 값이 있을 수도 있고 없을 수도 있다.

 

예시

 

Backend Parameter의 경우 Backend proxy에서 처리한 결과를 가져오는 용도로 사용되기 때문에 Request Body에선 null을 갖게 된다.

Backend Parameter에 어떤 값을 채워줄지는 Play Builder에서 설정한 Action의 용도에 따라 다르며, Play Builder 작성자와 Backend proxy 개발자 간에 어떻게 처리해야 할지 정확하게 내용을 공유해야 한다.

 

"action": {
  "actionName": "action이름",
  "parameters": {
    "datetime": {
      "type": "BID_DT_DAY",
      "value": "오늘"
    },
    "isValidTime": {
      "type": null,
      "value": null
    }  
  }
}

 

 

 


 

 

Response Body

REST API 요청에 대한 응답으로 생성되는 Body는 Request Body의 action.parameters에 전달했떤 모든 KEY를 동일하게 Response에 전달해줘야 한다.

  1. Request의 action.parameters에 사용된 KEY는 모두 Response Body의 action.parameters에 전달했던 모든 KEY를 동일하게 Response에 전달해줘야 한다.
  2. "ouput" 내의 KEY는 Request와 다르게 "type", "value" 필드를 갖지 않고, string 타입의 값(VALUE)만을 포함한다.
  3. Utterance Parameter와 Backend Parameter 모두 Backend proxy에 요청을 한 뒤에 값이 바뀔 수 있다. (Request에서 값을 갖거나 null일 수 있으며, Response에서는 요청 값을 그대로 갖거나 변경되거나 null이 될 수 있다.)

Response에서 모든 값을 그대로 똑같이 포함하는 이유는 입출력이 명확히 구분되지 않기 때문이다.

사용자 발화에 의해 설정된 Utterance Parameter의 경우 Backend proxy에서 값을 바꾼 뒤에 응답 텍스트에 사용될 수도 있다. 규격만을 정확히 따른다면 어떻게 사용하든 문제 되지 않는다.

 

 

 

 

 


 

예외 상황 관리 

정상적인 응답을 못하는 경우를 별도로 관리하는 공간으로, Exception Code와 그 상황에 나가게 될 메시지를 Prompt로 작성할 수 있다. 어떠한 상황이 예외 상황에 해당한다는 판단은 Backend proxy에서 하며, Backend proxy에서 Exception Code를 전달받으면 Action은 동작을 멈추고 Exception Prompt를 사용자에게 전달하게 된다.

 

예외 상황 관리는 Play의 모든 Action에 자동으로 적용되게 하거나 각 Action별로 설정할 수 있다. 각 Action 별로 정의한 Exception Code는 해당 Action에서만 유효하다. 예외 상황 관리는 Backend proxy의 URL이 등록되어 있어야 하고, Action의 Backend proxy 사용여부가 On으로 설정 되어 있어야 한다.

 

등록 방법

  1. 외부 연동 서버의 URL을 입력한다.
    • http:// 또는 https://를 함께 입력해야 한다.
  2. Backend proxy 사용 여부를 ON으로 설정한다.
  3. 예외 처리 영역에서 Exception Code를 작성하고 [Enter]키를 누른다.
  4. Exception Prompt 필드에 응답할 Prompt를 입력한 후 [Enter] 키를 누른다.
    • Exception Code 하나당 Exception Prompt는 총 2개까지 입력 가능하다.
    • 입력창에 처음 입력하는 Prompt의 유형은 종료 Prompt로, 두번째로 입력하는 Prompt는 대기 Prompt로 자동설정된다.
    • 예외 상황의 Prompt는 스택으로 구성되어 LIFO(Last In First Out)로 동작한다.

예외 상황은 일반적으로 두가지 종류가 있다.

  1. 서버 장애, 네트워크 문제 등으로 인해 서비스 제공이 불가능한 경우
    1. 사용자에게 서비스 제공이 불가능한 이유를 간단히 설명하고 세션을 종료해야 하므로 종료 Prompt만 등록해야 한다.
  2. 사용자의 발화에서 분석된 Entity에 문제가 있거나 Entity간 정합성 문제 등으로 기능 동작이 불가능한 경우
    1. 사용자에게 Entity에 대해 문제가 있음을 안내하고 종료 Prompt를 통해 세션을 종료할 수도 있으나 사용자로부터 정보를 입력받도록 대기 Prompt를 추가할 수도 있다.

 

Exception Prompt를 통해 이동한 대기 상태의 특징

대기 Prompt에 의해 활성화 되는 대기 상태는 사용자가 발화한 Intent에 포커스를 둔 상태에서 추가로 발화를 듣는 상태이다. 이 대기 상태에서는 사용자의 추가 발화로부터 Entity를 신규로 입력 받거나, 업데이트할 수 있다. 이 Entity는 해당 Action의 Utterance Parameter에 연결된 Entity Type일 때만 유효하다. 

예를 들어 "2월 31일 날씨 알려줘"라고 하여, 예외 상황으로 체크하고 대기 상태로 이동하였다고 가정한다. 사용자가 "2월 28일 금요일 날씨 알려줘"라고 다시 발화했다면, 특정 일을 의미하는 '31일'은 '28일'로 업데이트되고, '금요일'은 신규로 입력 받아 Parameter에 담아 Backend proxy로 다시 전달한다.

 

즉, 정의되어 있으나 비어있는 Utterance parameter가 있다면 해당 Parameter를 Entity로 채울 수 있고, 이미 Parameter를 채운 Entity와 같은 Type의 값이 있다면 기존 Parameter의 값을 새로운 Entity로 대체하게 된다. 이 Exception Prompt를 통한 대기 상태에서는 이와 가이 Entity만 추가로 받을 수 있다. 사용자의 발화로부터 추가 발화를 입력 받아도 Entity를 채울 수 없는 경우에는 두 번째 Exception Prompt를 내보내고 세션이 종료된다.

 

 

 

 

 

 

 

 

 


 

Request sample

POST /action_name HTTP/1.1
Accept: application/json, */*
Content-Length: 400
Content-Type: application/json
Host: builder.open.co.kr
Authorization: token TOKEN_STRING


{
    "version": "2.0",
    "action": {
        "actionName": "{{string}}",
        "parameters": {
            KEY: {
                "type": "{{string}}",
                "value": VALUE
            }
        }
    },
    "event": {
        "type": "{{string}}"
    },  
    "context": {
        "session": {
            "accessToken": "{{string}}"
        },
        "device": {
            "type": "{{string}}",
            "state": {
                KEY: VALUE
            }
        },
        "supportedInterfaces": {
            "AudioPlayer": {
                "playerActivity": "PLAYING",
                "token": "string value",
                "offsetInMilliseconds": 100000
            }  
        },
        "privatePlay" : { } // reserved
    }
}

 

Request header

Parameter Location Mandatory Description
Authorization header N Backend proxy에서 유효한 요청인지 검증(validation)하기 위해 사용한다.

 

Request body

Paramerter Type Mandatory Description
version string Y Backend proxy API 버전을 표시한다.
action json    
action.actionName string Y 현재 요청하는 Action의 이름이다.
action.parameters string Y Action에서 설정된 파라미터로 Play Builder에서 설정한 내용을 포함한다. (단, 값이 null인 경우 요청에서 제외된다. 요청에서 생략되었더라도 Backend parameter를 응답 값으로 포함해야 한다.)
KEY - Play Builder에서 Action 내에 정의한 parameter 이름
type - 사용자 발화에서 분석된 Entity인 경우 Play Builder에서 설정한 Entity 타입
value - 파라미터의 값으로 string타입
event json Y  
event.type string  Y 디바이스에서 발생한 event의 종류를 나타내며, 이 값에 따라 event의 데이터가 달라진다.
context json Y  
context.session json Y  
context.session.id string Y 대화가 유지되는 동안의 유효한 키 값
context.session.isNew boolean Y 대화의 처음을 알려주는 값
context.session.accessToken string N OAuth2에 사용되는 인증 token
context.session.isPlayBuilderRequest bool N Play Builder에서 테스트용으로 전달한 요청(기본값 : false)
context.device json Y  
context.devie.type string Y 현재 사용 중인 디바이스 종류
context.device.state json N 디바이스의 상태를 나타내는 값으로 현재는 정의된 값이 없음
context.suppotedInterfaces json Y 개발한 Play가 특정 Capability Interface를 지원하는 경우 각 Interface별로 상태 정보를 표시
profile json N private Play에서만 사용된다.
profile.privatePlay json N Private Play인 경우 정보를 추가한다.
profile.privatePlay.deviceUniqueld string N Private Play인 경우에만 입력
profile.privatePlay.userKey string Y 해시된 사용자ID(hashed user id)를 나타낸다.
profile.privatePlay.deviceKey string Y 해시된 디바이스ID(hashed device id)를 나타낸다.
profile.privatePlay.enrolledUser json N 초대 사용자(enrolled user)인 경우 정보를 추가한다.
profile.privatePlay.enrolledUser.name string Y 초대 사용자 추가 이름을 나타낸다
profile.privatePlay.enrolledUser.phoneNO string Y 초대 사용자 추가 전화번호를 나타낸다
profile.privatePlay.enrolledUser.email string Y 초대 사용자 추가 이메일을 나타낸다.
profile.privatePlay.enrolledUser.tag string N 초대 사용자 추가 정보를 나타낸다.



 


 

 

Response Sample

 

{
  "version": "2.0",
  "resultCode": "OK",
  "output": {
    "datetime": "오늘",
    KEY1: VALUE1,
    KEY2: VALUE2,
    ...
  },
  "directives": [
    {
      "type": "AudioPlayer.Play",
      "audioItem": {     
          "stream": {
            "url": "{{STRING}}",
            "offsetInMilliseconds": {{LONG}},
            "progressReport": {
              "progressReportDelayInMilliseconds": {{LONG}},
              "progressReportIntervalInMilliseconds": {{LONG}}
            },
            "token": "{{STRING}}",
            "expectedPreviousToken": "{{STRING}}"
          },
          "metadata": { } // reserved
      }
    }
  ]
}

 

Response Body

parameter type mandatory description
version string Y Backend proxy API 버전을 표시한다.
resultCode string Y OK : 성공인 경우 사용하는 값으로 다른 값을 전송하면 성공이 아닌 것으로 처리하기 때문에 주의해야 한다.
성공이 아닌 경우는 PlayBuilder의 General > 기본정보 페이지의 예외처리 또는
Action > Custom Actions > 선택한 Action의 예외처리에서 설정된 Result Code(Exception Code)값 전송한다.
output json Y Request에서 전송한 action.parameters의 KEY : VALUE를 처리한 결과를 전송한다,
Request의 모든 KEY : VALUE가 동일하게 나와야 한다.
VALUE는 Request의 값과 같거나 다를 수 있다.
변경되지 않은 VALUE들은 Request의 값을 그대로 써줘야 한다.
-KEY : Request의 action.parameters에 정의된 KEY
-VALUE : backend proxy에서 처리한 결과
directives json N 특정 Capability Interface를 지원하는 Play에서 Directive를 전송하는 경우 이 필드를 통해 전송한다.
각 Capability Interface의 Directive 포맷은 해당 Capability Interface 규력을 참조한다.

 

resultCode

"output" 필드 외에 "resultCode"가 정의되어 있다.

 

Play Builder는 Backend proxy 호출 시 발생할 수 있는 다양한 예외 상황마다 적절히 대응할 수 있도록 설정할 수 있다.

Play Builder에서는 "Exception Code"와 각 상황에서의 Exception Prompt 또는 사용자에게 오류 상황을 알려주고 필요한 Parameter를 다시 물어볼 수 있는데, 이때의 각 상황은 Backend proxy의 "resultCode"로 전달되는 값과 Play Builder에서 설정한 "Exception Code"를 매칭하여 동작을 결정한다.

 

따라서 Response Body로 전달되는 JSON 포맷에서 "resultCode"에 올 수 있는 값의 종류와 수행되는 로직은 다음과 같다.

  • "OK" - 성공일 경우 Backend proxy는 고정된 값을 보내줘야 한다. (이 값 외의 모든 경우는 예외처리를 한다.)
  • Exception Code - Play Builder에서 정의한 Exception Code 값으로 Play Builder에서 설정한 동작을 수행한다.
  • 이외의 모든 값 ("", null 포함) - Play Builder > 외부 서버 연결 정보 > 연동 실패 시 Prompt 영역에서 지정한 기본(Default) Prompt를 보내준다

directives

directive에는 Play가 지원하는 Capability Interface에서 정의한 Directive가 올 수 있다. Play는 임의 개수의 Capability Interface를 지원하도록 설정할 수 있고, Play가 지원하는 Capability Interface의 모든 directive가 Response Body 내에 포함될 수 있다.

 

어떤 Play가 2개의 Capability Interface를 지원한다면 "directive"필드에는 0개, 1개, 2개의 Directive가 포함될 수 있다.

 


Health check

서비스 정상 여부를 확인하기 위해 다음의 /health url를 다음과 같이 구현해야 한다. NUGU developers에서는 이 URL을 주기적으로 요청해서 서버의 정상 여부를 판단한다. 정상적으로 서비스가 가능하면 HTTP Status code를  "200 OK"로 리턴한다. (결과 텍스트는 OK 등 아무 문자나 리턴 가능)

 

만약 서비스에 문제가 있을 경우 "500 Internal Server Error" 등 200 이외의 HTTP Status Code를 리턴한다.

 

GET /health HTTP/1.1
Accept: */*


HTTP/1.1 200 OK
Content-Length: 2
OK

 

 



 

 

 

 

Prompt 사용하기

Prompt는 사용자에게 응답으로 전달할 메시지를 의미한다. Prompt를 작성할 때 다음과 같은 구성 요소들을 사용할 수 있다.

 

구성요소 하위요소 특징 자동완성 단축키
일반 텍스트 - '가나다'와 같은 일반적인 텍스트를 그대로 읽어 발화한다.  
Parameter - {{}}와 같은 형태로 사용하며, Parameter에 담긴 값을 텍스트로 변형하여 발화한다.
Parameter 사용하기
{{
Response Filter NLG Translator Parameter와 항상 결합하여 사용되며, 단독으로 쓰일 수 없다. >
- NLG Normalizer Parameter에 담겨있는 값이 Filter의 Source와 100% 일치할 경우에만 동작한다. =
- NLG Function Response Filter :
조사 - '을/를'과 같이 앞 음절에 따라 조사가 바뀌는 경우 자동으로 변환하여 발화한다. 조사 자동 변환은 parameter와 함께 사용될 때만 동작한다.
조사 처리하기
/
SKML 태그 - TTS 엔진이 텍스트 혹은 Parameter의 값을 SKML 태그에 따라 발화한다.
발화 옵션 사용하기
<

 

 

 

Prompt의 유형

유형 설명
대기 Prompt Prompt가 발화된 이후 Play의 세션이 유지된다.
연속 Prompt 뒤이어 올 Prompt가 있는 경우에만 발화되며, 뒤이어 오는 prompt와 더해져서 하나의 문장처럼 발화된다.
종료 Prompt Prompt가 발화된 이후 Play의 세션이 종료된다.

 

 

Prompt는 사용 위치에 따라 구분된다.

위치 Action 위치 특징
일반 Response의 Prompt Welcome Action을 제외한 모든 Action 대기 Prompt와 종료 Prompt가 사용된다. 여러 Prompt가 입력된 경우, 무작위로 선택되어 발화된다.
- Built-in Action > welcome 대기 Prompt와 연속 Prompt가 사용된다. 여러 Prompt가 입력된 경우, 무작위로 선택되어 발화된다.
- Built-in Action > exit 종료 Prompt가 사용된다.
여러 Prompt가 입력된 경우, 무작위로 선택되어 발화된다.
- Built-in > fallback  대기 Prompt와 종료 Prompt가 사용된다. 여러 Prompt가 입력된 경우, 위에서부터 순차적으로 발화된다.
Slot-filling Prompt - 대기 Prompt와 종료 Prompt가 사용된다. 여러 Prompt가 입력된 경우, 위에서부터 순차적으로 발화된다.
예외 상황 처리의 Exception Prompt - 대기 Prompt와 종료 Prompt가 사용된다. 여러 Prompt가 입력된 경우, 위에서부터 순차적으로 발화된다.

 

사용자에게 빈 응답을 전달하고 싶을 경우, Silent Prompt를 사용할 수 있다. Silent Prompt는 Prompt 입력 창 우측의 버튼을 눌러 설정 가능하다. Silent Prompt를 응답으로 설정하면, 아무 발화 없이 대기 모드로 넘어가거나 세션이 종료되므로 꼭 필요한 경우에만 사용하는 것이 좋다. Silent Prompt는 모든 유형의 Prompt에서 사용할 수 있다. 

 

 

 



 

 

Parameter 

Parameter는 정보를 담아두고 사용하는 일종의 그릇이다. Play에서 응답을 만들 때 문장 내에 parameter를 입력하면, 해당 Parameter에 담긴 정보(Entity)에 따라 Play가 응답한다.

 

Utterance Parameter : 사용자의 발화에서 가져온 정보를 담는 Paramter

Backend Parameter : 외부 서버 등 사용자의 발화 외의 곳에서 정보를 가져와 담는 Parameter

 

 

 

 

 

 

참조 : NUGU Delveloper 제작 가이드

'2021스텍 개발 일기~' 카테고리의 다른 글

대충 가늠잡아 본 서버 구조  (2) 2021.08.06