Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents
minLevel1
maxLevel2
outlinefalse
typelist
printablefalse

Dialogflow

Ingredient (type) and cuisine entities

We now want to move on to providing our recipe recommendation agent with some more serious capabilities for filtering the recipe database in search of a recipe that meets the features that the user has requested. We will begin with developing a capability for filtering recipes in the database using an ingredient or ingredient type. The idea is that a user then can indicate to the agent that they would like to cook a recipe which uses carrots, fish, or something else as ingredient. Before you can create an intent to match user expressions such as “do you have recipes that use fish as ingredient”, you have to create ingredient entities. These entities are needed to be able to specify which parts of these user expressions should be extracted as ingredient (type) entity entries. This is done as part of the intent specification, and the presence of an entity type is used as a feature by the Dialogflow classifier model to recognize intents.

...

Note

A similar issue as for the ingredients also applies to the @cuisine entity. Only some countries are included, while others are not. To address that issue, you might want to think about a solution that uses the @sys.geo-country entity of Dialogflow instead of the csv file.

Request for recipe features intent

The intent that allows a user to ask for specific recipe features is one of the most important intents for our recipe recommendation agent. The agent will use these features for filtering recipes to narrow down the choices that a user still has from the remaining recipes from the database. These features will include a variety of criteria, such as specifying an ingredient (type) that is used in the recipe. As we will use the features that a user will ask for as filters on the recipe database, we will name the intent addFilter. Its main purpose is to add filters of the form featureName=featureInstance, for example, ingredient=taleggio.

...

Warning

Test that your intent is correctly recognizing user requests by using the microphone button in the Dialogflow test console (you can also enter phrases in the test console by typing). Try various phrases and check whether what you say is classified as your recipe request intent, and whether the ingredient (types) in your requests are being recognized as ingredient (type) entity (use the Diagnostic Info to check this).

Prolog and Patterns

We need to refine our logic now for retrieving only those recipes that satisfy specific filters. We already introduced a recipeFiltered/3 predicate for Capability 2: Request a Recommendation but there it did not have to do any serious work yet. We only introduced the base case without any filters (an empty list of filters) but now need to deal with the case where we have some filters that a user provided. The basic idea to define a recursive clause for recipeFiltered/3 is simple: apply the first filter in the list to the recipes and recursively filter the remaining recipes using the remaining filters. We provide you below with the recursive clause that you should add below the base clause that we added earlier in the recipe_selection.pl file:

...

Inspect this rule carefully and make sure that you understand what it is doing.

Checking that a recipe uses an ingredient

If we want to check that a recipe uses an ingredient, we need to be able to check whether the ingredient is included in the ingredient list of that recipe. For ingredient types, things are slightly more complicated. To do both checks, we will add two rules for defining the hasIngredient(RecipeID, Ingr) predicate that succeed if the recipe with identifier RecipeID uses the ingredient (type) Ingr. Both rules need to be added to the ingredient_hiearchy.pl file (at a location indicated in that file).

...

Tip

Extend the number of available ingredient types in ingredient_hierarchies.pl file to include pasta and at least one other type of ingredient such as vegetable, fruit, fish, or something else.

There are several ways to do this. Key, of course, is to find specific types of pasta that you can use to add facts such as typeIngredient('bucatini', 'pasta') to the file. It should not be difficult to find this information using e.g. Wikipedia or other tools such as ChatGPT (which knows surprisingly many things about food; apparently something we talk a lot about, which is less surprising).

Applying filters: Ingredient, ingredient type, and cuisine

After you add the recursive clause for recipesFiltered/3 above, you will see that Eclipse will start to complain that the applyFilter/4 predicate has not been defined. We have reduced the problem of filtering recipes to the problem of applying a single filter to a set of (remaining) recipes. The idea is that the applyFilter(+ParamName, +Value, +RecipeIDs, -FilteredRecipes) filters the RecipeIDs provided as input by means of the feature ParamName using a specific Value and returns the recipes that satisfy this feature in the output argument FilteredRecipes. The parameter name ParamName refers to a feature that a recipe should have, for example, it should be of a certain cuisine. The specific value Value of the recipe feature specifies what exact feature request is made, for example, the cuisine should be Chinese. For each feature we need to write a separate applyFilter/4 rule. For now, we need to provide a rule for the ‘ingredient' feature (use the same name for the feature as the name of the corresponding parameter name used in the addFilter intent in Dialogflow), the 'ingredienttype' feature, and the 'cuisine' feature. The bodies of the first two rules can be designed as exact copies of each other when we use the hasIngredient/2 predicate (which basically hides the difference between an ingredient and an ingredient type). You must find all (Hint for what Prolog built-in predicate you should use) recipes from the list that you start with that satsify the filter and return these recipes in the output argument list. Add your rules to the recipe_selection.pl file at the location indicated in the file; there you will also find the heads of the rules that you need to define. The rule for filtering on cuisine is very similar too but instead of the hasIngredient/2 predicate you should use the cuisine/2 predicate (see the recipe_database.pl for examples).

Requesting a recipe feature

Looking ahead, we would like to be able to conduct conversational interactions like the following example:

...

Apart from this design choice, there are a few other aspects of the filtering process that we should take into account. One aspect that we need to consider is whether the user requests are consistent or not. If a new user request conflicts with some of the features that have been requested before, we want the agent to remove those that are conflicting with the new request(s). Here we assume that newer requests should override the older ones. A second important aspect is the distinction between the case where after adding a new filter there are still recipes that satisfy all feature requests versus the case where there are no recipes left that meet all requests. For the latter case, we want the agent to respond differently and instead of an acknowledgement move make a dialog move that informs the user that there is no recipe that meets all feature requests.

Implementing the a21featureRequest pattern variants

To implement the a21featureRequest pattern in the patterns.pl file, we need a few key ingredients to implement the design choice made and the other design aspects that we discussed:

...

In total, you should be implemented four variants of a21featureRequest pattern for the different conditions: 2 main variants x 2 cases.

Implementing agent responses

We need to create responses in responses.pl for all the agent intents in the patterns that we added to the patterns.pl file: ackFilter, noRecipesLeft, and featureRemovalRequest.

...

For the featureRemovalRequest intent, you should add a simple text/2 fact. Use as response text, for example, "Can you have a look again and remove one of your recipe requirements?".

Visuals

We want to differentiate what we show to a user depending on the number of recipes that still meet the user’s requests. The basic idea is that we should not show a large number of recipes to a user but we can show recipe details when the number of remaining recipes becomes sufficiently small (we chose <16, see also above). As a consequence, we want to create two different versions of the a50recipeSelect page that we created for Capability 2: Request a Recommendation: one that just shows the feature requests made when there are more than 15 recipes that meet these requests, and another for when there are less that 16 which shows the recipe details (titles and pictures) for all of the remaining recipes.

The first recipe recommendation page

Start by extending the code for the recipe recommendation page that we already created. The first thing we want to do is add a condition for when to show this page. This page should be shown when there is still a long list of recipes that match the requests made thus far by a user. We would like to show this page when the number of filtered recipes is still above 15. Use the recipesFiltered/1, check the length of the list of recipes this predicate returns, and add a condition that makes sure it consists of more than 15 recipes.

You should also add more information to this first a50recipeSelect page. The main requirement is that the page shows all the user’s feature requests or filters. You can collect these from the agent’s memory and use the predicate filters_to_strings/1 for this that has already been defined in the dialogflow.pl file. For styling the layout of the page, use rules defined in the html.pl file or add new ones (see also the Visual Support Guide).

Add a second recipe recommendation page

A simple way to start writing some code for this page is to copy-paste the code you already have for the recipe recommendation page and modify it. Most importantly, you need to make sure this page is only shown if the number of filtered recipes is below or equal to 15 and if the user is still trying to select a recipe.

...

Your final page should look something like this (just an example, you should be able to easily improve!).

...

Test it Out

Test it out by Run your Conversational Agent. Try different scenarios where you filter on ingredients, ingredient types, and/or cuisine. For example, try adding requests for the following ingredients one by one in order: ginger, salt, sugar, rice. You should end up with exactly 2 recipes left.

...