About Introduction Design Goals Tutorials Downloads

Chats with Self or Other Local Bot Databases

(by Gary Dubuque)


It seems that all experimenters eventually go through the phase of getting a bot to chat with another.

Here we have our first chance to take advantage of the script language. So some preliminaries are in order. The sample scripts are found in the data directory where the AIMLpad is installed. They are plain ol' text files. The two we'll be interested in for this tutorial are chat.txt and chat2.txt.

To load a script file you open the immediate command window. The shortcut to this window is Ctrl-E or you can use the menu option Tools -> Context -> Commands... or you can click the toolbar icon that looks like a scroll of plans, that is, the second from the far right. In the immediate commands window you type pose chat. Or you can type pose chat2 if you want the second sample script file loaded.

The script command bot $ask submits the value in the predicate named ask to the interpreter. The response from the interpreter is returned in a predicate named REPLY. So a very simple script routine to see what the bot would respond to hello might look like the following line typed in at the immediate command window:

bot "hello","Alice says:" $REPLY

Or we could embellish this a little more with the script requesting what to ask the bot:

"You say: ", $ask, bot $ask, "Alice replies: " $REPLY, nl

Here we have a simple prompt being typed; then a simple input to the predicate named ask; the submit to the interpreter; the result displayed after a label of "Alice replies:"; and finally a new line. If this was entered into the immediate command window, you could run it over and over to get a chat with the bot.

It seems logical to just take the reply and ask it again. So instead of prompting us for the ask predicate, lets just assign the value in $REPLY to it like in the following:

bot $ask, $ask = $REPLY, "" $REPLY, nl

Notice in this example I didn't put any characters in the label when displaying $ask. You can run this over and over again from the immediate command window to make the bot respond to itself. It just keeps asking what it last replied.

There is one problem with this solution. If the bot responds with an reply that is more than one sentence, the next thing it is asked contains multiple sentences. Since the standard interpreter process is to make a response for each sentence that was asked, the output can grow to be very long after a few of these exchanges.

Let's just take the first sentence the bot responds to ask back in self conversation. The script would look like the following:

bot $ask, $results = $REPLY, sentences $results, $ask = $results(1), "" $REPLY, nl

Notice that the internal predicate $REPLY was copied to a predicate named results before we attached to it the multiple possible sentences. We assigned to the predicate named ask, the first attached value that the sentences command created in the predicate named results.

Let's take a look at the contents of the script file chat.txt:

{chat}
"How many exchanges?",$cnt,
"Start with input?",$ask,nl,
:sayit
{sayit}
bot $ask,
"" $REPLY,nl,
$ask = $REPLY,
sentences $ask,
$mv = @MVCNT("ask"),
[$mv > 1] %x = @RND($mv - 1), %x = @INT(%x) + 1, $ask = $ask(%x)?
$cnt = $cnt - 1,
[$cnt > 0] restart?
nl

This script file has two frames {chat} and {sayit}. To run this script we would branch to the {chat} frame. In the immediate command window that would be :chat.

In the chat frame we are prompted for the number of exchanges to make the bot answer. That goes into the predicate named cnt. We are also prompted for a first input which goes into the predicate named ask. The chat frame then branches to the {sayit} frame.

The sayit frame will restart as it keeps decrementing the predicate named cnt until cnt becomes 0. In the last few lines in the sayit frame we see that loop: $cnt = $cnt 1,[$cnt > 0] restart?. The beginning of sayit submits the question to the interpreter and displays the reply. Next it finds the sentences as it assigns the reply to the predicate named ask. The predicate mv is the count of sentences in the reply from which it picks a random one if there is more than one sentence found. Note that %x is a temporary or local variable that exists only during the execution of the current frame.

Let's try something more ambitious. Instead of asking the same bot to respond, lets try two bots. To do this we will need the AIML sets for two bots loaded locally. Lets make Anna talk to Alice (standard).

First load the Anna AIML set into the interpreter and sort the patterns. So now the files in the db directory contain the Anna database. These files are named index.txt, patterns.txt and templates.txt. We want to save the Anna database to another folder. Since we may eventually have many bot brains, lets make a folder called dbbots in our application installed location and put another folder in dbbots called Anna. We'll copy the three files from the db folder to the dbbots/Anna folder.

Now load the Alice AIML set into the interpreter and sort the patterns. So now the files in the db directory contain the Alice database. For this lesson, we'll save Alice's brain in the dbbots folder too. Copy the three files again from the db folder to the dbbots/Alice folder.

We now have two bots that can converse (they are missing their unique bot variable, but we can fix that later.)

Here is the script file Chat2.txt that makes these two bots talk:

{chat}
"How many exchanges?",$cnt,
"Start with input?",$ask,nl,
:sayfirst
{sayfirst}
bot $ask,
getbot $Thats Alice $Inputs Alice $Topic Alice,
"" $REPLY,nl,
$ask = $REPLY,
sentences $ask,
%mv = @MVCNT("ask"),
[%mv > 1] %x = @RND(%mv - 1), %x = @INT(%x) + 1, $ask = $ask(%x)?
use dbbots/Anna,
bot $ask,
getbot $Thats Anna $Inputs Anna $Topic Anna,
"" $REPLY,nl,
$ask = $REPLY,
sentences $ask,
%mv = @MVCNT("ask"),
[%mv > 1] %x = @RND(%mv - 1), %x = @INT(%x) + 1, $ask = $ask(%x)?
use dbbots/Alice,
setbot $Thats Alice $Inputs Alice $Topic Alice,
:sayit
{sayit}
bot $ask,
getbot $Thats Alice $Inputs Alice $Topic Alice,
nl,"" $REPLY,nl,
$ask = $REPLY,
sentences $ask,
%mv = @MVCNT("ask"),
[%mv > 1] %x = @RND(%mv - 1), %x = @INT(%x) + 1, $ask = $ask(%x)?
use dbbots/Anna,
setbot $Thats Anna $Inputs Anna $Topic Anna,
bot $ask,
getbot $Thats Anna $Inputs Anna $Topic Anna,
"" $REPLY,nl,
$ask = $REPLY,
sentences $ask,
%mv = @MVCNT("ask"),
[%mv > 1] %x = @RND(%mv - 1), %x = @INT(%x) + 1, $ask = $ask(%x)?
use dbbots/Alice,
setbot $Thats Alice $Inputs Alice $Topic Alice,
$cnt = $cnt - 1,
[$cnt > 1] restart?
remove $Thats Alice $Inputs Alice $Topic Alice,
remove $Thats Anna $Inputs Anna $Topic Anna,
remove $cnt $ask,
nl

The two bot chat starts the same way as the one bot chat. But now we have to start the conversation to establish the context and then we can do the cycle of exchanges. Three new script commands need explaining. They are getbot, setbot, and use. The getbot command takes all the interpreter's history stacks of input's, that's, and the topic and puts them in the predicates $Inputs, $Thats, and $Topic. The setbot command puts the predicates back into the interpreter's history of input's, that's and the topic. The use command tells the interpreter to change the bot database.

So {sayfirst} asks the Alice bot a question and then saves the Alice history stacks. It gets the answer and switches bot brains. It asks the Anna bot the question and then saves the Anna history stacks.

The rest is {sayit} which loops like in the self talk example. The slight difference here is that when the looping is done, {sayit} removes the predicates that stored the history stacks.

Starting with the Alice history stack, it asks Alice and saves Alice's history. It gets the Alice answer to ask Anna. It switches bot brains to Anna. It gets Anna history, asks Anna the question and saves Anna history. It gets the Anna answer to ask Alice. It switches bot brains back to Alice and gets Alice's history before it loops to do it all again.

Changing the interpreter database is also a way to have specialized areas of expertise. Given the proper parameters, the script language can choose the appropriate AIML set to interpret. Just using a different database doesn't require the swapping of the history stacks. The getbot and setbot are only required when several conversations are running through the same interpreter. This is something the web server versions of interpreters have built-in, but AIMLpad has to emulate through scripting.

I'll leave the next iteration of having a party of bots chat to the creative script writer. The tools are there in AIMLpad. But how would the social structure be set up?

In a later tutorial we explore how to chat with other bots on the Internet using script. This would be a good extension for the party makers too.

Another application of bots chatting with other bots could be for testing or training. It is conceivable to make one bot that analyzes other sample bots to built its own ultimate personality.


(Edited for the SourceForge site by Stefan Zakarias)

About Introduction Design Goals Tutorials Downloads