A rough idea for a conversation simulator

by Gregory Brown

These days, it seems I hardly have the time for doing fun random hacks. So here I've started one, and if anyone finds it interesting, please take it from here and let me know how it turns out.

Loosely based off of AIML, kind of, but not really:

class Conversation

def initialize(person)
@person = person
@response_id = 0

attr_reader :response_id

def say(msg)
print "#{@person}: "
@response_id = Response[@response_id].respond_to(msg)

class Response

def self.responses
@responses ||= {}

def self.[](id)

def initialize(id)
@id = id
self.class.responses[id] = self
@matchers = []
@messages = []

attr_reader :id,:matchers

def when(pattern,id)
@matchers << [pattern,id]

def inherits(arr)
arr.each do |id|
@matchers += Response[id].matchers

def might_say(msg,weight=1)
weight.times do
@messages << msg

def talk
puts @messages.sort_by { rand }.pop

def respond_to(msg)
@matchers.each do |pattern,id|
if msg =~ pattern
return id
return @id



def response(id)
r = Conversation::Response[id] || Conversation::Response.new(id)

UPDATE: Check out this annotated code submitted by a reader.


Stefan Edlich
2007-09-01 04:37:50
How about giving a running example so that we can see what it does? Thanks!
Gregory Brown
2007-09-01 05:45:02
If you can't see what it does or might do, you probably won't have much fun or luck extending it :)
2007-09-03 07:08:40
Oh, come on :-), it wouldn't hurt to put some intentional comments in there if you aren't providing example use!

This is an interesting approach, but it might be difficult to control since, from my reading of the code, the selected response depends on the order of insertion of the matching regexps. In AIML it depends on the type of wildcard, _ or *, and how specific the match is, as well as the topic.

I'm quite impressed with AIML, but it has an annoying lack of DRYness in that for each pattern, even within one topic, one can only have one THAT or response clause, so one must repeat the pattern again and again for different contexts instead of grouping them. Also, there is not much to hold and access state implicitly, so conversations degenerate when the Alicebot looses track of what the conversation was about. It would be interesting to attempt to tackle that, perhaps by allowing searching backwards through the conversation for patterns that help give context, without having to number the THAT clauses explicitly.
There are some natural language ruby libraries now, and some necessary AI topics have been covered in Ruby Quizzes to a certain depth, so this sort of thing might be more feasible than it was. I think the main difficulty is managing the amount of text and how it is structured, so a DSL of some kind seems useful.

Gregory Brown
2007-09-03 07:38:13
@hgs, fair enough:

c = Conversation.new("Ralph")

response(0) { |r|
r.when /Hello/i, 1

response(1) { |r|
r.might_say "Hello"
r.might_say "Hi"
r.might_say "Howdy"
r.when /Hello/i, 4
r.when /./, 3

response(3) { |r|
r.might_say "Finally, something interesting"
r.inherits [1]

response(4) { |r|
r.might_say "You already said that, didn't you?"
r.inherits [1]

loop do
print "Greg: "
r = gets

Greg: Hello
Ralph: Howdy
Greg: Hello
Ralph: You already said that, didn't you?
Greg: Yes, I guess I did.
Ralph: Finally, something interesting
Greg: Hello
Ralph: You already said that, didn't you?

You're right that it depends on the ordering, which I use as a feature of sorts to allow patterns to be stacked on top of others, thus overriding some of the inherited patterns.

Of course, we do need things like partial matching, and doing things with state could get quite interesting. Perhaps if we could store action blocks that get fed the matches, it would become *very* interesting.

The idea of wrapping some of the Ruby natural language libraries is interesting, but as I mentioned, there's no way I'd find time to dig into this deeply. Of course, I'm very interested in anyone's progress down this road, and if you or anyone else come up with anything neat, let me know and I'll be sure to blog it here.


2007-12-17 01:15:51

I am new to Ruby, but as I am interested in AI, I was curious to see how your code works. I had to comment the code in order to be able to understand it. I hope you don't mind.

Gregory Brown
2007-12-17 01:33:01
Hi Laki,

Of course I don't mind, it's neat to see you add commentary to this somewhat intentionally barren code.

This may not be the clearest example if you're new to Ruby, but some comments:

a ||= b is just shorthand for a = a || b

@foo is an instance variable, @@foo a class variable, and $foo A global variable. Try to stay away from those last two :)

def self.[](id) just lets me do Conversation::Response[id]

It seems like you add a few might_say calls to my response(0) branch, and yeah, those won't get called unless something points back to the 0 response block.

Finally, this stuff isn't quite AI. You should check out some of the RubyQuiz stuff if that's where your interests lie. Hopefully you had fun playing with hack though. :)


2007-12-17 02:11:37
Thanks for clarifications.

Regarding AI I know this small code is not AI, but since you mentioned AIML, I was curious to understand your approach.

However, with little expansion, and decent database, it may achieve success of 'Chomsky smiley bot' or 20Q game! You can never know. ;)

2008-07-21 13:19:01
huh... where did the link to the commentary go?
Gregory Brown
2008-07-21 13:41:02

Not sure. I didn't edit the comment stream, so it's a bit of a mystery. There are some major overhauls going on behind the scenes at the O'Reilly blogs.... maybe it got lost that way.

2008-07-21 13:53:09
yeah, databases can get overly complex, and synchronizing them can be a real mess...

so here it is again: comments by a novice (me)

or you can just place the link in my first comment above, so that it would make sense again!

2008-07-21 13:55:15
hmm, yeah, its oreilly system error. it erases link during posting.

another test: link to google

Gregory Brown
2008-07-21 14:22:04
@Laki, I added a link to the bottom of the post.
2008-07-21 15:45:47

i guess 'overhauls going on behind the scenes' affect the scenes ;-)

i will let webmasters know about the problem...