Contexts

Some user commands may only make sense within a specific context. For example, when the user says Yes or No, the AI assistant 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:

Dialog 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:

Dialog script
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.

Dialog script
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:

  1. When the user places an order

  2. 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:

Dialog script
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.

Dialog script
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.

Dialog script
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:

Dialog script
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:

Dialog script
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?

Dialog script
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 assistant 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:

Dialog script
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.

Dialog script
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.

Dialog script
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:

Dialog script
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:

Dialog script
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:

Dialog script
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:

Dialog script
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:

Dialog script
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);
});