Bixby Developer Center

Guides
References

Action 모델링하기

Action은 Bixby가 사용자를 대신해 수행할 수 있는 오퍼레이션(operation)을 정의합니다. concept이 명사라면 action은 동사입니다. action의 예는 다음과 같습니다.

  • FindRestaurants: 식당을 검색합니다.
  • ConvertTemperature: 온도 변환을 계산합니다.
  • BookHotel: 호텔 객실을 예약하는 트랜잭션 오퍼레이션(operation)을 수행합니다.

프로그래밍 용어와 비교해보면 action 모델은 action inputaction output을 기술하는 인터페이스 사양(interface specification)이라고 볼 수 있습니다. API 요청이나 계산 같이 action을 실제로 수행하기 위한 JavaScript 함수도 구현해야 합니다. 이 JavaScript 함수에 액세스하려면 endpoint 선언(endpoints.bxb)을 추가해야 합니다. JavaScript는 로컬 endpoint를 사용하는 경우 Bixby 서버에서 실행되고, 원격 endpoint를 사용하는 경우에는 사용자의 서버에서 실행됩니다.

Action 정의하기

Action은 type, output, 일부 input 자식 키(key)로 구성됩니다. 각각의 항목은 모두 참조 가이드에 정의되어 있습니다. 경우에 따라 action은 Bixby를 위한 dialog를 정의할 수도 있습니다.

Concept property와 마찬가지로, action input에는 개수 제한규칙(cardinality)의 제약 조건을 지정할 수 있습니다. min 제약 조건에는 input이 optional인지 아니면 required인지를 정의할 수 있고, max 제약 조건에는 input이 single-cardinal인지 아니면 multi-cardinal인지를 정의할 수 있습니다.

action (RollDice) {
collect{
input (numDice) {
type (NumDiceConcept)
min (Required)
max (One)
}

input (numSides) {
type (NumSidesConcept)
min (Required)
max (One)
}
}
output (RollResultConcept)
type (Calculation)
}

View on GitHub

사용자 요청이 input의 개수 제한규칙(cardinality)를 준수하지 않으면 실행이 중지된다는 사실을 유의하세요. Bixby는 프롬프트를 통해 추가 정보가 필요하다고 사용자에게 알려줍니다.

필수 입력 input에 value가 지정되지 않은 경우에는 사용자에게 value를 묻는 정보 요청 프롬프트(elicitation prompt)가 표시됩니다. 이것은 min 제약 조건의 위반에 해당합니다. 반면, 싱글 카디널(single-cardinal)인 input에 둘 이상의 value가 들어오도록 설정된 경우에는 선택 프롬프트(selection prompt)가 표시됩니다. 이 경우는 max 제약 조건의 위반에 해당합니다.

일부 action은 또다른 동작을 발생시킬 수 있습니다. FindRestaurants 또는 ConvertTemperature는 또다른 동작 없이 실행되지만, BookHotel 같은 경우에는 다른 시스템의 상태를 변경시키는 action을 모델링합니다. action을 정의할 때는 적절한 action type을 선택하여 Bixby가 해당 action에 또다른 동작이 있는지 여부를 알 수 있도록 해주세요.

다음과 같은 비 트랜잭션 action type은 해당 action이 외부 시스템과 관련된 또다른 동작이 없음을 의미합니다. Action type을 선택할 때는 최대한 좁은 범위의 value를 선택하세요.

  • Calculation: input을 독립적으로 계산하여 결과를 반환합니다.
  • Constructor: 외부 서비스 없이 제공된 input만을 사용하여 독립적으로 output을 생성합니다.
  • Fetch: 단일 input에서 추가된 데이터를 단순히 조회합니다.
  • Search: input을 제약 조건으로 사용하는 검색 action입니다.

Action이 외부 시스템과 관련된 또다른 동작이 있는 경우에는 트랜잭션 action type을 사용합니다. 트랜잭션 워크플로우에 대한 자세한 내용은 이 섹션의 심화 항목으로 설명되어 있습니다.

아래의 action 정의 FindShoe는 여러 input을 검색 제약 조건으로 받고 Search type을 사용하는 것을 볼 수 있습니다.

action (FindShoe) {
type (Search)
collect {
input (name) {
type (Name)
min (Optional)
}
input (type) {
type (Type)
min (Optional)
}
input (minPrice) {
type (money.MinPrice)
min (Optional)
max (One)
}
input (maxPrice) {
type (money.MaxPrice)
min (Optional)
max (One)
}
}
output (Shoe)
}

View on GitHub

선택 규칙(Selection Rule)

선택 규칙(selection rule)을 사용하면 여러 개의 모호한 input value가 발생할 경우에 이를 사용자에게 프롬프트로 물어보지 않고도 그 중에서 더 나은 value를 선택하도록 할 수 있습니다. 이를 통해 사용자를 대신해 이루어지는 선택에 대한 컨트롤을 강화할 수 있습니다. 이러한 선택 규칙(selection rule)은 최대 개수 제한규칙(cardinality) 확인이 위반될 경우에 호출됩니다. 따라서 maxMany로 설정된 경우에는 선택 규칙(selection rule)을 사용할 수 없습니다.

default-select 키(key) 내에서 with-learning 키(key)를 사용하여 selection learning을 활성화합니다. 그런 다음 새 with-rule 키(key)를 사용하여 규칙을 추가합니다.

또한 NoPreferences 같은 다른 선택 학습(selection learning) 동작도 옵션으로 추가할 수 있습니다.

선택 규칙(selection rule) 내에서 select-firstselect-min을 사용하면 가장 순위가 높은 후보 value와 가장 낮은 value를 선택할 수 있습니다. 또한 select-first 내에서 sort-key를 사용하여 input 목록을 재정렬할 수도 있습니다. sort-key를 사용하지 않으면 정렬되지 않은 상태에서 첫 번째로 나온 input이 선택됩니다. 여러 개의 sort-key를 사용해서 복합적인 정렬을 수행하여 select-first 규칙을 더욱 구체화할 수도 있습니다.

아래는 셔츠 Item을 생성하기 위해 size를 선택하는 예제입니다. 여기에서는 기본 사이즈를 결정하기 위해 select-min표현식(expression)을 사용했습니다.

action (CreateItem) {
type (Constructor)
collect {
input (shirt) {
type (Shirt)
min (Required)

default-init {
intent {
goal: FindShirt
}
}
}
input-group (itemProperties) {
requires (OneOrMoreOf)
collect {
input (size) {
type (Size)
min (Optional)

//Lists all available options - this is used by default-select
default-init {
intent {
goal: Size
value-set: Size { Size(ExtraSmall) Size(Small) Size(Medium) Size(Large) Size(ExtraLarge) }
}
}
default-select {
//picks Medium, otherwise pick first item from the list of candidates
with-rule {
select-min {
expression (exists(size) && size == 'Medium' ? 0 : 1)
}
}
with-learning {
}
}
}

input (quantity) {
type (Quantity)
min (Optional)

default-init {
intent {
goal: Quantity(1)
}
}
}
}
}
}

output (Item)
}

View on GitHub

선택 규칙(selection rule)과 선택 학습(selection learning)이 어떤 차이가 있는지에 대한 자세한 내용은 선택 학습(Selection Learning) 항목을 참조하세요.

선택 규칙(selection rule)과 선택 학습(selection learning)이 각각 동작하는 방식에 대한 자세한 내용은 선택 동작(Selection Behavior) 항목을 참조하세요.

Input 그룹

Action을 정의할 때 개별 input에 대한 개수 제한규칙(cardinality)를 지정할 수 있습니다.

더 나아가 input-group 키(key)를 사용하면 이 요구 사항을 여러 개의 input에 적용할 수 있습니다.

우주 리조트(Space Resorts) 샘플 캡슐을 예로 들어 보면, 사용자가 리조트를 찾고 있는 경우에 이 사용자가 구체적으로 원하는 리조트 타입을 지정하도록 유도할 수 있습니다. input 그룹을 사용하면 사용자가 화성에 있는 리조트, 스파가 포함된 리조트, 화성에 있는 스파가 포함된 리조트를 찾아달라고 요청할 수 있습니다. 그러나 이러한 그룹은 하나 이상의 제약 조건을 반드시 지정해야 합니다.

action (SelectResort) {
description (Select a HabitatProd from a list by name)
type (Calculation)

collect {

input (resorts) {
type (SpaceResort)
min (Required)
max (Many)
default-init {
//get the candidates from the input-view (prompt context)
intent {
goal-set: SpaceResort {$expr("getPreviousPage('SpaceResort')")}
}
}
}

input-group(search) {
requires (OneOrMoreOf)
collect {
input (name) {
description (space resort name)
type (Name)
min (Optional)
}
input (planet) {
description (space resort name)
type (Planet)
min (Optional)
}
input(searchCriteria) {
description (search criteria)
type (SearchCriteria)
min (Optional)
max (Many)
}
}
}
}
output (SpaceResort) {
on-empty {
// TODO
}
throws {
error (MultipleMatches) {
property (matches) {
type (SpaceResort)
min (Required)
max (Many)
}
on-catch {
replan {
intent {
goal { SpaceResort @prompt-behavior(AlwaysSelection) }
value { $expr(matches) }
}
}
}
}
}
}
}

View on GitHub

input-group에서는 개수 제한규칙(cardinality)이 약간 다른 의미를 갖습니다. input-group 키(key)에서 min value는 input이 required인지 아닌지를 지정하며, max value는 쿼리에서 허용되는 input-group 내 최대 input 선언 수를 지정합니다.

이 예에서 사용자는 다음과 같이 여러 input을 지정할 수 있습니다.

"Find me a space resort on Mars that's good for kids and has a spa."

반복가능한 입력(Iterable Input)

Input을 멀티 카디널(multi-cardinal)이 아닌 iterable로 표시하면 해당 type의 각 input에 대해 action이 다시 호출됩니다. 예를 들어, FindRestaurants에는 searchRegion이 iterable로 설정되어 있습니다.

input (searchRegion) {
iterable
type (SearchRegion)
min (Required)
}

"산호세와 샌프란시스코에 있는 음식점 찾아줘"와 같은 발화는 각 도시마다 한 번씩, FindRestaurants action을 두 번 호출합니다.

Input이 많으면 action도 그만큼 여러 차례 호출되기 때문에 iterable은 신중하게 사용해야 합니다. 많은 수의 조합이 가능해지므로 한 번에 둘 이상의 iterable input을 사용할 수는 없습니다. 이 쿼리 결과는 하나의 목록으로 병합됩니다.

Input Validation

Action 선언은 간단한 유효성 검사(validation) 로직도 지원합니다. 유효성 검사(validation)는 validatecondition으로 구성되며, 이 condition은 halt, prompt, replan 중 하나를 트리거합니다.

셔츠 주문을 취소하는 action이 있다고 가정해봅시다. 주문 취소 과정에서 셔츠 주문이 존재하는지를 검사해볼 수 있을 것입니다. 이때 action input validation을 사용하면 이 부분을 구현할 수 있습니다.

action (CancelCommittedOrder) {

type (CancelActivity)

confirm {
by (core.Confirmation)
}

collect {
input(receipt){
type(Receipt)
min (Optional)

default-init {
intent {
goal: FindLastReceipt
}
}

validate {
if (!exists(receipt)) {
halt {
dialog {
template("Not sure what to cancel. I didn't find any recent shirt orders.")
}
}
}
if (exists(receipt) && receipt.orderState != 'Ordered') {
halt {
dialog{
template("This order is already #{value (receipt.orderState)}!")
}
}
}
}
}
}

output(Receipt)
}

View on GitHub

일반적으로는 action 모델에 input validation을 선언하는 것이 가장 좋습니다. 유효성 검사(validation) 규칙이 모델의 일부로 포함되기 때문입니다. action 구현 사전 조건을 사용해서도 input 유효성을 검사할 수 있습니다. 참고로 이것은 모델의 일부가 아니며 특수한 $id 식별자에 액세스할 수 있습니다. 하지만 이러한 유효성 검사(validation) 규칙은 action의 input에만 사용할 수 있고 API 통신의 유효성은 검사할 수 없습니다. 에러 처리(error handling)를 통해서, 유효성 검사(validation) 로직을 JavaScript에 구현하고 예외를 발생시켜 에러 확인(checked error)에 사용할 수 있습니다.

사전 조건을 사용하는 방법과 예외를 발생시키는 방법 두 가지 모두 유효성을 검사할 수 있지만, input이 정의된 위치와 가장 가까운 곳에서 input validation을 수행하는 것이 가장 유연하지는 않더라도 제일 깔끔한 방법입니다. 유연성을 높이고자 한다면 사전 조건과 예외를 사용할 수 있습니다.

Input validation은 default-init 블록을 평가하고 개수 제한규칙(cardinality)과 requires 제약 조건을 확인한 후에 이루어집니다. 이때 input 블록 내 validate 키(key) 위치는 상관 없습니다.

에러 처리(Error Handling)

에러 확인(checked error)을 사용하면 JavaScript를 통해 명명된 에러를 발생시킬 수 있습니다. 그런 다음 action에서 해당 에러를 캐치하고 처리할 수 있습니다. 에러 처리(error handling)는 크게 두 부분으로 나뉘는데, 여기서 설명하고 있는 action 모델을 통해 에러를 처리하는 것이 한 부분이고, 해당 에러를 발생시켜 action 구현에서 JavaScript를 통해 에러를 처리하는 것이 나머지 한 부분입니다.

Action은 action outputthrows 키(key)를 통해 특정한 에러 확인(checked error)의 처리를 선언할 수 있습니다. 다음은 개발자가 정의한 UnsupportedSearchCriteria 에러를 선언하는 예를 보여줍니다.

action (FindSpaceResorts) {
description (Find space resorts)
type (Search)
collect {
input (name) {
type (Name)
}
input (planet) {
type (Planet)
}
input (searchCriteria) {
type (SearchCriteria)
max (Many)
}
}
output (SpaceResort) {
throws {
error (UnsupportedSearchCriteria) {
on-catch {
// TODO: drop the unsupported criterion with a message and continue
halt {
dialog {
template-macro (UNSUPPORTED_SEARCH_OPTION)
}
}
}
}
unknown-error {
on-catch {
halt {
dialog {
template-macro (UNKNOWN_ERROR)
}
}
}
}
}
}
}

View on GitHub

각 에러 확인(checked error) 선언에는 에러 코드가 지정되어 있습니다. 이때 에러를 원활하게 해결하는 에러 처리(error handling) 방식을 구현하는 것이 좋습니다. 예를 들어 에러가 권한 허용과 관련되어 있는 경우, 권한 필요 에러를 표시하는 것으로 끝낼 것이 아니라 사용자가 권한 허용 과정을 진행할 수 있도록 안내하는 것이 좋습니다.

Dialog뿐 아니라 replace, halt, drop, replan 등 다양한 효과도 사용할 수 있습니다.

이 밖에, 에러 확인(checked error)이 action에 대해 사용자의 추가 input이나 추가 설명이 필요함을 의미할 수도 있습니다. 이러한 상황은 prompt를 통해 처리할 수 있습니다.

  output (SelectedItem) {
throws {

error(MultipleMatches) {
property (matches) {
type (Item)
max (Many)
}
on-catch {
//prompt for matched items, clear searchTerm
prompt (items) {
min (1) max (1)
candidates (matches)
}
}
}
}
on-empty {
//prompt for items, clear searchTerm
ordered-effects {
drop (searchTerm)
prompt (items) {
min (1) max (1)
candidates (items)
}
}
}
}

View on GitHub

경우에 따라 replan을 사용하여 대체 인텐트(intent)를 사용자에게 표시할 수도 있습니다.

    throws {
error (MultipleMatches) {
property (matches) {
type (HabitatPod)
min (Required)
max (Many)
}
on-catch {
replan {
intent {
goal { HabitatPod @prompt-behavior(AlwaysSelection) }
value { $expr(matches) }
}
}
}
}
}
}

View on GitHub

property를 에러 메시지에 포함시키고 에러 dialog에 property를 사용해야 함을 알리는 에러 확인(checked error)도 있을 수 있습니다. 이 경우 먼저 필요한 concept type을 선언한 다음, 에러를 발생시킬 JavaScript action 구현에서 해당 concept에 대한 데이터를 전달할 수 있습니다.

예를 들어 이 RequiredOptionNotSpecified 에러에서는 optionCategory property가 필요합니다.

output (Order) {
throws {

error (RequiredOptionNotSpecified) {
property (optionCategory) {
type (ProductOptionCategory)
description (The optionCategory to prompt for!)
}
on-catch {
prompt (options) {
min (optionCategory.minCardinality)
max (optionCategory.maxCardinality)
mode (Add)
candidates (optionCategory.options)
}
}
}
}
}

또한 명명되지 않은 에러나 알 수 없는 에러도 캐치할 수 있습니다. 그러려면 에러가 생성될 수 있는 action에서 직접 수행해야 합니다.

다음은 action 내에서 unknown-error를 사용하는 방법의 예시입니다.

  output (SpaceResort) {
throws {
error (UnsupportedSearchCriteria) {
on-catch {
// TODO: drop the unsupported criterion with a message and continue
halt {
dialog {
template-macro (UNSUPPORTED_SEARCH_OPTION)
}
}
}
}
unknown-error {
on-catch {
halt {
dialog {
template-macro (UNKNOWN_ERROR)
}
}
}
}
}
}

View on GitHub

에러 처리(error handling)의 JavaScript 구현에 대한 자세한 내용은 예외 발생 또는 참조 문서의 throws를 참조하세요.

런타임 시 에러 처리(error handling)의 효과를 변경해야 할 경우 런타임 플래그를 사용할 수 있습니다.