Table of Contents | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
Including ingredients, meal types, cuisines
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 https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%202%3A%20Request%20a%20Recommendation [TBU]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).
The first Prolog rule for hasIngredient
is designed to determine if a specific ingredient is used in a given recipe. It simply checks if Ingr
is included in the ingredient list of recipe RecipeID
by using the ingredient/2
predicate (see the recipe_database.pl
file). The second rule is designed to determine if a recipe uses an ingredient type. It assumes that Ingr
is a type of ingredient (such as meat). To check whether a recipe uses that ingredient type, however, we need to find a specific ingredient such as steak that is an example instance of the type (only those are directly associated with a recipe in the recipe database). Another example would be apple as an instance of the type fruit. We do not have to specify all of these relations as a lot of the work has already done for you and you can use the typeIngredient/2
predicate in the ingredient_hiearchy.pl
file (check out the typeIngredient/2
facts in that file). Now use that predicate in combination with the ingredient/2
predicate to define the second rule. Add your rules to the ingredient_hierarchies.pl
file.
Tip |
---|
Extend the number of available ingredient types in 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 |
Applying filters: Ingredient, ingredient type, and cuisine
...
Applying filters: Ingredient,meal 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, the 'mealType'
feature, and the 'cuisine'
feature. This information is found in recipe_database.pl
.
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. The first Prolog rule for hasIngredient is designed to determine if a specific ingredient is used in a given recipe. It simply checks if Ingr is included in the ingredient list of recipe RecipeID by using the ingredient/2 predicate (see the recipe_database.pl file).
The bodies of the 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 satisfy 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).
...
Let’s inspect this interaction in more detail and analyse how it is organized. In Moore and Arar’s taxonomy, the parts of the dialog in this example where the user adds a feature request in the second and fourth move can be classified as Pattern A2.1: Open Request. We will use the a21featureRequest
label for this pattern and define different variants of it below. The agent initiates with an inquiry about what the user is looking for. This move is part of the a50recipeSelect
pattern, a top level pattern that we added to the agent’s agenda. We previously defined variants for that pattern for https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%202%3A%20Request%20a%20Recommendation and https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%203%3A%20Select%20Recipes%20by%20Name. The user responds by informing the agent that they are interested in a particular cuisine (Japanese). This is a feature request that we want the agent to manage by means of a new a21featureRequest
pattern. The idea is that this pattern is inserted while the user and agent are still performing the top level a50recipeSelect
pattern. The a21featureRequest
pattern is inserted as a subdialog, which we indicated in the example above by adding indentation. The response of the agent is an acknowledgement move combined with a renewal of the question whether the user wants to add any other feature. We’ll take it that this move is also part of the a21featureRequest
pattern and ends this pattern. The fourth user move (a second feature request for the an ingredient type pasta) and the fifth agent move repeats the same a21featureRequest
pattern. The interaction concludes with a move of the user mentioning a specific recipe title (Teriyaki salmon). That last move and the agent move following it are both part of the a50recipeSelect
pattern we specified for https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%203%3A%20Select%20Recipes%20by%20Name. That pattern added the a50recipeConfirm
pattern into the current session of the agent. This pattern asks the user to check the recipe (while showing all relevant details). You still need to add this pattern to implement the capability we are working on right now below.
...
Concluding, either way, when a user makes a feature request, we make a design choice to move the conversation to go back to (option 1) or stay (option 2) in the recipe selection stage (i.e., the a50recipeSelect
top level context). Implicitly, we are also saying here that making a feature request dialog move is an out of context intent (see https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%204%3A%20Handling%20Unexpected%20Intents) when the conversation is neither in the recipe selection nor confirmation stage.
...
First, we want the pattern to be available only when either the
a50recipeSelect
or thea50recipeConfirm
is the top level pattern. We should add this as a condition to the rule defining the pattern similar to how we added the agent name condition to the greeting pattern (see https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%201%3A%20Greet%2C%20and%20Self-Identify).Second, we want to remove any conflicting feature requests. You can use the special action
removeConflicts(Params)
and insert it in the pattern as a move of the agent. This action is defined in thedialog_update.mod2g
file; check it out to better understand what happens. Of course, we need to collect the parametersParams
somehow to tell the agent which feature requests (called parameters here) should be checked for conflicts. Use thegetParamsPatternInitiatingIntent(user, addFilter, Params)
query for this and add it to the condition of each of the rules you implement for thea21featureRequest
pattern variants. The predicate is defined in thedialog.pl
file; check it out to better understand how it works.Third, we need agent intent labels for the two cases that we can end up: we suggest using
ackFilter
for acknowledging there are still recipes that meet all requests, andnoRecipesLeft
when there are no recipes left. The logic that we need to implement to make these moves work differently will be implemented in theresponses.pl
file below.
...
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 https://socialrobotics.atlassian.net/wiki/pages/createpage.action?spaceKey=PM2&title=2025%20Capability%202%3A%20Request%20a%20Recommendation: 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.
...