Challenge Submission: Quick Baseball Stats
What Does My Chatbot Do?
The chatbot lets a user retrieve season stats for a baseball player currently in the major leagues. The bot detects that the user wants stats, then has to recognize the players name – not as simple as you would think, since “Aaron Judge”‘s last name is detected as the #job gold entity 😀.
The chatbot asks for a year – also, not so easy since 2015 can be interpreted as a @greeting – and then provides a basic set of season stats. The user can ask for a specific stat, a different year, or a different player.
Cool Things You’ll Learn
The tutorial has a few techniques that you may also need in your bot. I don’t promise that I implemented them perfectly, but I tried to make the most of the features as I understand them. Feel free to correct me or offer alternatives.
Resetting Requirement Values
A skill can have a requirement. Once that requirement is fulfilled – often by an entity in the utterance – that entity value is saved in memory. If a subsequent utterance contains the same entity but a different value, the memory is NOT updated. Developers, if they need, can clear the value from memory so that the value can be updated.
But … what if I do not know until after the utterance whether I want to update the value, that is, what if I want to keep the old value unless a new value is provided. So I added a Set Memory Field action, using the scripting option for setting a memory value.
Retrieve Entire Utterance
For the most part, I used the #Person gold entity for recognizing the name of the player – and it worked amazingly well. But there were times it didn’t. For example, one of the more famous baseball players is called Aaron Judge. But when I typed in “Aaron Judge”, “Aaron” was recognized as a #Person but “Judge” was recognized as a #Job.
So whenever I explicitly asked for a name, I did a technique I first saw in a blog for when I want to simply retrieve whatever is entered and not let the NLP and Bot Builder determine from the utterance what actions to take next.
- You set a variable in memory as a flag.
- You create a skill that is triggered by that variable.
- Where you want to request a name, you execute a GO TO (with wait for user input) to that skill.
Enrichments to Enable Alternative Expressions for Entity
I wanted the user to be able to enter a statistical category, like home runs or stolen bases. So I created the #Stat-categories entity. But I wanted to let users enter various expressions to indicate each category, for example, for home runs they could enter “homer”, “HR”, “HRs”, or “round-trippers”.
So I entered all those different options for all the different categories inside the entity. Then I created an enrichment with a group for each statistical category that contains all its expressions. And finally, I created a new field called “statcode” and put the API code for that statistical category. So now, users can specify that category in a variety of ways – but still I have just one entity for them all. And I can simply retrieve the API code for retrieving that stat.
Using Matching Strictness
Here’s a case for using matching strictness. I want the user to enter a year for which he wants stats. The chatbot interprets the year as a date, but it also detects the @greetings intent. I raised the @greetings matching strictness to 92, and this solved the problem.
@greetings was not a main intent, anyway, and it could still detect “hi” and “hello” at 99%, and even “hello bot” at 94%.
Using Secondary Requirements
I never really understood optional requirements, but now I see the need.
I wanted users to pick a player, stat category and year, and get the stat for that year. But I also wanted the user to be able to not provide a stat category and then the bot would provide overall stats. Or provide a stat category but not a year and the chatbot would use the previously selected year.
Create Chatbot Shell
Use the New Bot wizard to create a chatbot called mlb (Major league Baseball), and add the Greetings and Customer Satisfaction skills.
- @selectplayer: This intent indicates the user wants to enter a player for whom to get stats. Expressions like:
- get player
- stats for player
- player stats
- @stats-parameter: This intent indicates the user wants to specify what type of stats he wants, and for which year.
- rizzo 2015
- homers for 2019
- Raise the matching strictness for the default @greetings intent to 92 so as not to conflict with the other intents.
Create an entity called #stat-categories, which specifies a type of stat, like home runs or stolen bases. Make it restricted entity, but I’m not sure it can’t be a free entity.
Add expressions for all types of ways to call the various stats, like “homers”, “home runs”, “HR” and “runs batted in”, “RBIs”, “RBI”.
Finally, create enrichments by adding the field statcode, and then grouping the related expressions and setting statcode based on the API code for that stat.
Create Helper Skills
Create the following skills:
- getplayer-name: This skill is used just to receive the entire input from the user and store it as a name. So:
- Create the skill.
- Trigger if the flag _memory.getplayer-name is true.
- Add action to unset the flag getplayer-name and the stat-category.
- Add action to set player-search (search term) into memory with whatever the user entered, and then start again the skill getplayer-enter to handle the input.
- getplayer-restart: This skill is run when the user indicates they want to select a new player. It has no trigger or requirement.
- Add action to unset player-id, player-name, player-search, and stat-category.
- getplayer-stats: This skill retrieves the stats for the already selected player, and is called when the main skill getplayer-enter has helped the user select a player. It can also be run when the user selects a different set of category/year for the current player.
- Trigger this skill when a #stat-categories or #datetime is entered, but not when a #person is entered. When a person is selected, we must help user to select the new user.
- Make #datetime a requirement, and store as statyear.
- Make #stat-categories and #person secondary requirements, so users can – but don’t have to – specify a new year and category.
- Create a set of message groups, based on the data the user provided:
- If there is no player ID, send back to main skill.
- If player ID is present, run the API to get the stats.
- If category is not present, display the overall stats.
- If category is present, show only for that category.
Create Main Skill
Create skill called getplayer-enter:
This is the main one, triggered either when user indicates @selectplayer or #person, but not when @greetings is uttered. It’s designed so at any time the user can enter a players name and it will change the current user.
Requirements is #person, but also optional requirements for #stat-categories and #datetime (for the year).
For the actions, do the following:
- Call the MLB player search API, and store in memory the number of matches.
- Create a message group for each possibility:
- No one is found (ask for new input and send to getplayer-name).
- Exactly 1 player is found (verify with user correct player – and show picture 😎- and create buttons for stats or new player).
- 2 or 3 are found (let the user choose, by using scripting to display 2-3 buttons).
- More than 3 are found (ask for new input and send to getplayer-name).
When only 1 player is found, store the player ID and name in memory, and send to the getplayer-stats skill.
When we need to ask the user for a name, we set the memory flag getplayer-name to true, and send to the get-playername skill. This is because we do not want the NLP to interpret the input, just to send it to the search API.
At the end of the skill, delete the search term from memory.
The full bot is located at: https://cai.tools.sap/thecodester/mlb
See all SAP Conversational AI tutorials (and earn some badges)