Contexts¶
Some user commands may only make sense within a specific context. For example, when the user says Yes
or No
, the AI agent must comprehend the context to process this phrase effectively.
To let you organize dialogs with such commands, Alan AI provides contexts. Contexts are useful if you need to create a multi-stage conversation and some commands or dialog branches do not make sense on their own. In this case, you can place them in contexts.
Consider the following script:
intent('What do you have?', p => {
p.play('I can offer you a pizza or a burger');
});
intent('Get me a $(ITEM: pizza, burger)', p => {
p.play(`Your order is: ${p.ITEM.value}. Is it correct?`);
});
intent('Yes', p => {
p.play('Your order has been confirmed');
});
The second and third commands make more sense if given after the first. As a result, they should be presented as a part of a context.
Every dialog script has one or more contexts. Alan AI implicitly creates the global context that wraps commands added directly to the script, and you can define your own contexts as needed. Every context has a unique ID that is assigned to it automatically, and the context title.
Contexts typically comprise commands required at a given point of the dialog. To let the user give commands from a context, this context must be activated. Once the context is active, commands in it become active, and Alan AI considers them for matching. When the part of the dialog presented as a context is no longer required, the user exits the context, and the context is deactivated.
Warning
All dialog scripts added to the project can have a maximum of 50 contexts.
Defining contexts¶
To define a context in the dialog script, use the context()
function:
let chooseDish = context(() => {
intent('Get me a $(ITEM: pizza, burger...)', p => {
p.play(`Your order is: ${p.ITEM.value}.`);
})
});
Activating contexts¶
To activate a context, add the then() function to an intent in the dialog script and pass the context name to it. When such an intent is matched, the context becomes active, and all commands in the context become available for matching.
intent('What do you have?', p => {
p.play('I can offer you a pizza or a burger');
p.then(chooseDish);
});
let chooseDish = context(() => {
intent('Get me a $(ITEM: pizza, burger...)', p => {
p.play(`Your order is: ${p.ITEM.value}. Is it correct?`);
p.then(confirmOrder);
})
});
let confirmOrder = context(() => {
intent('Yes', p => {
p.play('Your order has been confirmed');
});
intent('No', p => {
p.play('Your order has been cancelled');
});
});
In the example above, the dialog script contains the chooseDish
and confirmOrder
contexts that comprise commands that are required at specific points of the dialog:
When the user places an order
When the user confirms the order
To activate these contexts, we use the then()
function within intents. When the user asks: What do you have?
, Alan AI activates the chooseDish
context. After the user chooses the dish, Alan AI activates the confirmOrder
context.
Exiting contexts¶
When you activate several contexts, Alan AI maintains a recursive context structure. Every newly activated context is associated with the parent intent from which it was activated and the parent context that contains this intent.
If the user gives a command outside an active context, this context is deactivated. In case several contexts are activated sequentially and the user gives a command outside all active contexts, these contexts are deactivated in a recursive manner.
Let’s take the above example and add the Cancel the order
intent to the global context:
intent('What do you have?', p => {
p.play('I can offer you a pizza or a burger');
p.then(chooseDish);
});
intent('Cancel the order', p => {
p.play('Your order has been cancelled');
});
let chooseDish = context(() => {
intent('Get me a $(ITEM: pizza, burger...)', p => {
p.play(`Your order is: ${p.ITEM.value}. Is it correct?`);
p.then(confirmOrder);
})
});
let confirmOrder = context(() => {
intent('Yes', p => {
p.play('Your order has been confirmed');
});
intent('No', p => {
p.play('Your order has been cancelled');
});
});
After the user asks: What do you have?
> Get me a pizza
, we have 3 active contexts:
global
chooseDish
confirmOrder
If the user says: Cancel the order
, the chooseDish
and confirmOrder
contexts will be deactivated.
You can also deactivate a context manually. To do this, use the resolve()
function. In the example below, after the user says Yes
, the confirmOrder
context is immediately deactivated.
intent('What do you have?', p => {
p.play('I can offer you a pizza or a burger');
p.then(chooseDish);
});
let confirmOrder = context(() => {
intent('Yes', p => {
p.play('Your order has been confirmed');
p.resolve();
});
intent('No', p => {
p.play('Your order has been cancelled');
});
});
let chooseDish = context(() => {
intent('Get me a $(ITEM: pizza, pasta, burger...)', p => {
p.play(`Your order is: ${p.ITEM.value}. Is it correct?`);
p.then(confirmOrder);
})
});
Using commands in contexts¶
You can place the following types of commands in contexts:
Intents in contexts¶
Intents in contexts perform their regular functions — they let you complete tasts and answer questions. Such intents are context-dependent: they remain inactive until the context to which they belong is activated.
intent('What is the weather in Boston?', p => {
p.play('45 °F, light cloud and a fresh breeze');
p.then(weatherDetails);
});
let weatherDetails = context(() => {
intent('What about tomorrow?', p => {
p.play('42 °F, light rain and a moderate breeze');
});
intent('And the humidity?', p => {
p.play('92%');
});
});
In the example above, the What about tomorrow?
and And the humidity?
intents become active only after the parent intent to which the context is bound is matched — the user asks: What is the weather in Boston?
noContext intents¶
In some cases, you may need to add commands that must not affect the user’s path in the conversation. For example, you can provide the user with the ability to ask general questions like: What information can I get?
or What phrases can I use here?
at any moment of the dialog, without deactivating the current context.
To define such commands, you can use noContext intents. When Alan AI matches such an intent, it fulfills the request defined by it, but does not switch to the context to which this intent belongs. As a result, the user remains in the same dialog branch as before, and the commands from this dialog branch (context) are still available to the user.
You can use noContext intents in the global context and user-defined contexts. To define a noContext intent, wrap the intent in the noContext()
function:
noContext(() => {
intent('What weather details can I get?',
reply('Any details like temperature, humidity for any location'));
});
Alternatively, you can pass the noctx
parameter to such an intent:
intent(noctx, 'What weather details can I get?',
reply('Any details like temperature, humidity for any location')
);
In the example below, when the user asks about the weather in Boston, the weatherDetails
context will become active. All context-dependent commands in this context will be available for matching.
However, the user can ask What weather details can I get?
at any time later. Alan AI will answer this question, but will not switch back to the global context since the What weather details can I get?
intent has the noctx
parameter. The weatherDetails
context will remain active, and the user will have an ability to go on with follow-up questions like: What about tomorrow?
and And the humidity?
intent(noctx, 'What weather details can I get?',
reply ('Any details like temperature, humidity for any location')
);
intent('What is the weather in Boston?', p => {
p.play('45 °F, light cloud and a fresh breeze');
p.then(weatherDetails);
});
let weatherDetails = context(() => {
intent('What about tomorrow?', p => {
p.play('42 °F, light rain and a moderate breeze');
});
intent('And the humidity?', p => {
p.play('92%');
});
});
Fallbacks in contexts¶
The fallback()
function lets you handle unexpected user’s phrases. This command is triggered if the AI agent does not understand what the user is saying and cannot match the user’s utterance to any command in the context. You can add a fallback to the context to prompt the user for a specific type of input or fail gracefully.
Fallbacks can be used in user-defined and global contexts. For details, see Error handling and re-prompts.
Using async contexts¶
Alan AI fully supports asynchronous JavaScript programming in dialog scripts. You can use the async
keyword to declare an ansynchronous function and the await
keyword to stop the script execution until the user responds:
intent('What do you have?', async p => {
p.play('I can offer you a pizza or a burger');
let dish = await p.then(chooseDish);
p.play('Anything to drink?');
let drink = await p.then(chooseDrink);
p.play(`Your order is: ${dish} and ${drink}`);
});
let chooseDish = context(() => {
intent('Get me a $(ITEM: pizza, burger...)', async p => {
p.play(`Got it`);
p.resolve(p.ITEM.value);
})
});
let chooseDrink = context(() => {
intent('Get me a $(DRINK: coke, water...)', async p => {
p.play(`Thank you`);
p.resolve(p.DRINK.value);
})
});
Labeling contexts¶
Every context gets a unique identifier:
The global context is identified with the
global
label.User-defined contexts are identified with numeric IDs.
You can find the context ID in Alan AI Studio logs.
If necessary, you can label contexts in the dialog script with user-friendly names. The context names are displayed in Alan AI Studio logs instead of numeric IDs, which allows for better debugging. To label a context, add the title()
function to it. You can label both user-defined and global contexts.
title('Main context');
intent('What do you have?', p => {
p.play('I can offer you a pizza or a burger');
p.then(chooseDish);
});
let confirmOrder = context(() => {
title('Confirmation context');
intent('Yes', p => {
p.play('Your order has been confirmed');
p.resolve();
});
intent('No', p => {
p.play('Your order has been cancelled');
});
});
let chooseDish = context(() => {
title('Choose dish context');
intent('Get me a $(ITEM: pizza, pasta, burger...)', p => {
p.play(`Your order is: ${p.ITEM.value}. Is it correct?`);
p.then(confirmOrder);
})
});
Sharing information between contexts¶
You can use the following objects and functions to save and share data between contexts:
userData object¶
Use the userData
object to store data that must be available during the dialog session with the user. The data in userData
is accessible from any context, between intent calls and throughout all project scripts.
intent('I will have a $(COFFEE: latte, black coffee, americano...)', p => {
p.userData.coffee = p.COFFEE.value;
p.play(`Sure, I'll get you ${p.COFFEE.value}`);
});
intent('What is in the cart?', p => {
p.play(`You have ordered ${p.userData.coffee}`);
});
For more details, see Predefined script objects.
state object¶
Every context contains a preconfigured state
object. You can think of state as a dictionary or knowledge base where you can keep any information you need.
The data stored in state
is available in the current context and can be accessed through the p.state
object:
intent('I will have a $(COFFEE: latte, black coffee, americano...)', p => {
p.state.coffee = p.COFFEE.value;
p.play(`Sure, I'll get you ${p.COFFEE.value}`);
});
intent('What is in the cart?', p => {
p.play(`You have ordered ${p.state.coffee}`);
});
When a new context is activated, you can pass data to it with the then()
function and state
object:
let coffeeContext = context(() => {
intent('What is in the cart?', p => {
p.play(`You have ordered ${p.state.coffee}`);
});
});
intent('I will have a $(COFFEE: latte,black coffee, americano...)', p => {
p.state.coffee = p.COFFEE.value;
p.play(`Sure, I'll get you ${p.COFFEE.value}`);
p.then(coffeeContext, {state: p.state});
});
You can also create a new state
object and pass it to the context:
let coffeeContext = context(() => {
intent('What is in the cart?', p => {
p.play(`You have ordered ${p.state.coffee}`);
});
});
intent('I will have a $(COFFEE: latte,black coffee, americano...)', p => {
let state = {coffee:p.COFFEE.value};
p.play(`Sure, I'll get you ${p.COFFEE.value}`);
p.then(coffeeContext, {state: state});
});
For more details, see Predefined script objects.
resolve() function¶
You can pass data between contexts with the resolve()
function. In the example below, the user’s rating is
captured with the userRating
context and passed to the global context:
let userRating = context(() => {
intent(`$(RATING: 1, 2, 3, 4, 5..)`, async p => p.resolve(p.RATING.value))
});
intent('How can I provide feedback?', async p => {
p.play('Please rate the product on the scale of 1 to 5');
let rating = await p.then(userRating);
p.play(`Thank you for sharing your opinion. Your rating is: ${rating}`);
});
onEnter() callback¶
To pass data to the context when it is activated, use the onEnter()
callback:
let countContext = context(() => {
onEnter(p => {
p.state.result = 0;
});
intent('Yes', p => {
p.state.result += 1;
p.play(p.state.result.toString());
});
});
intent("Count the number of times I've said yes", p =>{
p.play("Sure, let's go");
p.then(countContext);
});