Reusable Dialogs in the Alexa Conversations Description Language
In the Alexa Conversations Description Language (ACDL), you can write reusable dialogs with predefined turns to carry out common tasks. Reusable dialogs enable you to invoke other dialogs by using a syntax similar to function calls. With reusable dialogs, you can write succinct, hierarchical dialogs instead of multiple flattened dialogs.
- Reusable dialog components
- Invoking reusable dialogs
- Deployable dialogs
- Using conditional expressions in reusable dialogs
- Reusable dialog example
- Related topics
Reusable dialog components
The basic structure of a reusable dialog is as follows.
dialog Type Name(Parameters) {
Samples
}
Reusable dialogs contain the following components:
dialog
keyword- Type
- Name
- Parameters
- Samples
The following is an example of a reusable dialog.
dialog TIME GetTime(APLA aplaResponse, Argument arg) {
sample {
response(aplaResponse, Request {argument = [arg]})
timeRequest = expect(Inform, informTimeEvent)
timeRequest.time
}
}
The type of this reusable dialog is TIME
, the name is GetTime
, the parameters are aplaResponse
and arg
, and there is one sample in the dialog.
Type
The type of a reusable dialog adheres to the following rules:
-
The type of a reusable dialog is the return type of the sample, which is the return type of the final line in the sample. In the previous example, the final line,
timeRequest.time
, is of typeTIME
, so the return type of the entire sample isTIME.
-
If the reusable dialog has more than one sample, the return type of all samples must match, and must also match the type of the dialog. Therefore, in the previous example, additional samples must all have the return type
TIME
. In other words, each additional sample must have a final line of typeTIME
. -
If a sample ends with an Alexa response, the
response()
action must be the final line of the sample. Because theresponse()
action returns typeNothing
, you don't have to provide the type of the dialog in the dialog declaration. The following two dialogs are equivalent.// The type of the dialog is explicitly declared to be Nothing. dialog Nothing OrderTaxi { sample { orderTaxiRequest = expect(Invoke, orderTaxiEvent) ensure( {arguments = [orderTaxi.arguments.date], response = datePrompt}, {arguments = [orderTaxi.arguments.time], response = timePrompt}, {arguments = [orderTaxi.arguments.address], response = addressPrompt} ) confirmAction(confirm_TaxiOrder_apla, orderTaxi) orderTaxi(orderTaxiRequest.date, orderTaxiRequest.time, orderTaxiRequest.address, orderTaxiRequest.passengers) // This response line will return type Nothing, and this must match the type of the dialog. response(notify_api_response_OrderTaxi_apla, Notify {actionName = orderTaxi}) } }
// No type is declared for the dialog. dialog OrderTaxi { sample { orderTaxiRequest = expect(Invoke, orderTaxiEvent) ensure( {arguments = [orderTaxi.arguments.date], response = datePrompt}, {arguments = [orderTaxi.arguments.time], response = timePrompt}, {arguments = [orderTaxi.arguments.address], response = addressPrompt} ) confirmAction(confirm_TaxiOrder_apla, orderTaxi) orderTaxi(orderTaxiRequest.date, orderTaxiRequest.time, orderTaxiRequest.address, orderTaxiRequest.passengers) // This response line will return type Nothing, and this must match the type of the dialog. response(notify_api_response_OrderTaxi_apla, Notify {actionName = orderTaxi}) } }
Name
The name of the reusable dialog enables you to reference the reusable dialog from other dialogs. For more information about names, see Names in the ACDL.
Parameters
The parameters of a reusable dialog are similar to the arguments of an action. The parameters are passed to the reusable dialog when the reusable dialog is called. The reusable dialog can then use the parameters.
In the following example, the reusable dialog passes its parameters, aplaResponse
and arg
, to the response
action.
dialog TIME GetTime(APLA aplaResponse, Argument arg) {
sample {
response(aplaResponse, Request {argument = [arg]})
timeRequest = expect(Inform, informTimeEvent)
timeRequest.time
}
}
Nothing
, the ACDL compiler marks it as a deployable dialog, and it therefore must meet the requirements of a deployable dialog. For details, see Deployable dialogs.Samples
As with all dialogs, reusable dialogs contain one or more sample
sections. A sample
brings together a set of events and actions that represent the conversational experience between the user and Alexa. When you build the model, the sample
is input to the Alexa Conversations simulator. The sample
conveys the relationship between input events and outgoing actions. The trained model predicts actions based on the observed events at runtime. For the syntax of events and actions, see Use Events in the ACDL and Use Actions in the ACDL.
Invoking reusable dialogs
All dialogs are reusable and therefore can be invoked by other dialogs. You invoke dialogs the same way you invoke actions. In the following example, the GetDateTime
dialog calls the GetTime
reusable dialog.
dialog GetDateTime {
sample {
...
time = GetTime(timePrompt, timeArgument)
...
}
}
Deployable dialogs
There are two types of reusable dialogs: deployable and non-deployable.
Deployable dialogs can be standalone dialogs and are at the highest level of a dialog hierarchy. Because they can be standalone dialogs, they have the following requirements:
- Deployable dialogs must start with a user turn and end with an Alexa response.
- Deployable dialogs must not have any parameters.
The examples in Dialogs in the ACDL are deployable dialogs.
Some reusable dialogs don't meet these requirements, thereby rendering them non-deployable.
Non-deployable dialogs are dialogs that have parameters and/or have a return type that isn't Nothing
. For example, the dialogs in the DateTime.acdl
file of the reusable dialog example are all non-deployable dialogs.
Using conditional expressions in reusable dialogs
You can use conditional expressions with reusable dialogs in the following ways:
- You can use a conditional expression within a reusable dialog.
- You can call a reusable dialog from within a conditional expression.
- You can branch based on the return value of a call to a reusable dialog.
The following example shows how to use a conditional expression within a reusable dialog called Pay
. The argument to Pay
is the payment amount. Pay
then passes the payment amount to an external API.
dialog Boolean Pay(Amount amount) {
sample {
result = payApi(amount)
if (result.status != "no_payment_method") {
// Please add a payment method.
response(...)
// Please add a credit card.
expect(...)
...
} else if {
}
...
}
}
The following example shows how to call a reusable dialog, Pay
, from within a conditional expression.
dialog Nothing FlightBot {
sample {
..
searchResult = searchFlights(...)
if (searchResult.flights == 1) {
// Reusable dialog call within a conditional expression.
Pay(searchResult.flights[0].amount)
}
}
}
The following example shows how to branch based on the return value of a call to a reusable dialog. In this case, the return value of the reusable dialog call refers to the return value of an external action invocation.
dialog Nothing FlightBot {
...
// Status refers to the return value of an external action
// invocation in the Pay dialog.
status = Pay(searchResult.flights[0].amount)
if (status == false) {
}
}
Reusable dialog example
This DateTime
reusable dialog example involves the following two files:
DateTime.acdl
defines the reusable dialogs.TaxiOrder.acdl
calls the reusable dialogs.
DateTime.acdl
The following file is DateTime.acdl
.
// DateTime.acdl
import com.amazon.ask.types.builtins.AMAZON.*
type InformTimeType {
TIME time
}
informTimeEvent = utterances<InformTimeType>([
"{time}",
"at {time}"
])
dialog TIME GetTime(APLA response, Argument argument) {
sample {
response(response, Request {argument = [argument]})
timeRequest = expect(Inform, informTimeEvent)
timeRequest.time
}
}
type InformDateType {
DATE date
}
informDateEvent = utterances<InformDateType>([
"{date}",
"on {date}",
"for {date}"
])
dialog DATE GetDate(APLA response, Argument argument) {
sample {
response(response, Request(arguments = [argument]})
dateRequest = expect(Inform, informDateEvent)
dateRequest.date
}
}
type DateTimeArgs {
Argument date
Argument time
}
type DateTime {
optional DATE date
optional TIME time
}
informDateTimeEvent = utterances<DateTime>([
"at {time} on {date}",
"for {date} at {time}",
"at {time}",
"{time}",
"on {date}",
"{date"
])
dialog DateTime GetDateTime(APLA timePrompt, APLA datePrompt, DateTimeArgs args) {
// Request "date" and "time" separately with the reusable dialogs.
sample {
dateArg = args.date
date = GetDate(datePrompt, dateArg)
timeArg = args.time
time = GetTime(timePrompt, timeArg)
DateTime {date = date, time = time}
}
}
TaxiOrder.acdl
The following file, TaxiOrder.acdl
, calls the reusable dialogs. Because neither this file nor DateTime.acdl
has a namespace, they are compiled under the same namespace, and there is no need to import the names that the DateTime.acdl
file initializes.
// TaxiOrder.acdl
import com.amazon.ask.types.builtins.AMAZON.*
import prompts.*
type TaxiOrder {
optional DATE date
optional TIME time
optional NUMBER passengers
StreetAddress address
}
orderTaxiEvent = utterances<TaxiOrder>([
"order a taxi",
"order a taxi for pickup from {address} for {passengers} passengers",
"order a taxi for pickup at {time}",
"order a taxi for {passengers} at {time}",
"order a taxi for pickup at {time} from {address}",
"order a taxi for pickup frono worm {address}",
"order a taxi for pickup at {time} for {passengers} passengers from {address}"
])
type OrderTaxiResult {
TIME time
FirstName driverName
Literal carMake
Literal carModel
}
action OrderTaxiResult orderTaxi(DATE date, TIME time, StreetAddress address, NUMBER passengers)
dialog OrderTaxi {
// Time and date not provided in the first invocation, so there is a call to the reusable GetDateTime dialog.
sample {
orderTaxiRequest = expect(Invoke, orderTaxiEvent)
// Create an object that contains the arguments to be passed into the reusable dialogs.
args = DateTimeArgs { time = orderTaxi.arguments.time, date = orderTaxi.arguments.date }
// Call the reusable dialog.
dateTime = GetDateTime(timePrompt, datePrompt, args)
// The "date" and "time" arguments have request actions in the reusable dialogs, so the only remaining required arguments for the orderTaxi() action that need a request action are the "address" argument and the "passengers" argument.
ensure(
{arguments = [orderTaxi.arguments.address], response = addressPrompt},
{arguments = [orderTaxi.arguments.passengers], response = passengersPrompt}
)
confirmAction(confirm_TaxiOrder_prompt, orderTaxi)
orderTaxiResult = orderTaxi(dateTime.date, dateTime.time, orderTaxiRequest.address, orderTaxiRequest.passengers)
response(orderTaxi_prompt, Notify {actionName = orderTaxi}, payload = {orderTaxiResult = orderTaxiResult})
}
// Time and date are provided in the first invocation, so there is no need to call the GetDateTime dialog.
sample {
orderTaxiRequest = expect(Invoke, orderTaxiEvent)
ensure (
{arguments = [orderTaxi.arguments.date], response = datePrompt},
{arguments = [orderTaxi.arguments.time], response = timePrompt},
{arguments = [orderTaxi.arguments.address], response = addressPrompt},
{arguments = [orderTaxi.arguments.passengers], response = passengersPrompt}
)
confirmAction(confirm_taxiOrder_prompt, orderTaxi)
orderTaxiResult = orderTaxi(orderTaxiRequest.date, orderTaxiRequest.time, orderTaxiRequest.address, orderTaxiRequest.passengers)
response(orderTaxi_prompt, Notify {actionName = orderTaxi}, payload = {orderTaxiResult = orderTaxiResult})
}
}
Related topics
Last updated: Nov 27, 2023