[Aiml-programr-developers] Tests and Architecture
mauro at cicio.org
mauro at cicio.org
Sun Sep 16 15:19:46 EDT 2007
Hi,
=================
This is a very long mail, a lot of topics...
Here you find an afterward written "resume".
I think it was very useful what Nicholas wrote, it points out that
what he has in mind is not only what the original pR wanted to be (a
simple request/answer library) but a BOT framework AIML enabled.
I like the idea and I think it is a good and natural evolution of pR.
Still, they are two separate things:
- a pure AIML answerer
- a framework (in large part AIML independent).
I sign in to support both.
By far, my skills and interests are more in the framework area than in
the AIML one.
I think it essential to keep the two parts as distinct as possible.
Now it is your turn to shoot on all what I said.
I would suggest to split the answers to this mail: let's open 1 thread
per topic!
=================
here I try to answer to the not yet covered points of
Nicholas' mail.
> We're writing a library and as a result I'll define two sets of tests:
>
> * UAT - that define the expected behaviour of the classes that we expect
> other developers to have to use to write their applications
> * Unit Tests - that additionally encompass the expected behaviour of the
> wider classes / methods / areas of the library that we hope other
> developers will not have to use.
Right. BTW, it looks to me like that the public interface of pR is
gonna be very simple (there are not too many things a BOT can do...).
Probably we will have something _like_
- configure
- start
- stop
- answer(challenge)
Probably a TestFacade would do...
> As a result I'll do two things:
>
> 1. Specify our "public" classes that third party developers should use
> to implement our library by means of example "use" cases written in code
"implement our library"
I assume this is a typo and you meant "use our library". If not, I
need more explanation.
> 2. Define the architecture behind these public classes so we can start
> to write unit tests.
I have difficulties to match the strategy you suggest with the
previously discussed one. We just wanted to
- see what pR is not yet doing
- show it with tests
- fix/add/refactorize until the program reaches the point we want.
If our goal is still this, the natural steps are:
- have a full set of challenge/responses testing the program Facade
(i.e. not caring about the program internals, i.e. black box testing)
- fix/add/refactorize
- _along_ the process write UnitTests
New UnitTests will be developed as
- new Units (classes or small set of classes) are necessary
- existing Units without existing test are to be modified
Defining the UnitTests in advance means having a strong commitment in
the early phase on the architectural/design decisions. It is possible,
but has little to do with TestFirst strategy.
As said: it might well be that your intention matches with what I just
wrote and I just missed your point.
> It is important to remember that I intend to unit test as locally to the
> implementation of a method / class as possible.
We agree here, of course.
> By this I mean that
> there will not be any tests against the high-level "bot" class for the
> "lower level" functionality of interpreting AIML tags (I'll test this
> against the appropriate class(es)).
Again I agree.
> So onto some example use cases (I'll formalise these into proper Ruby
> unit tests - the purpose of this email is to make public my testing
> strategy so you guys can shout if you disagree and we can then modify as
> needs be)...
I am shooting, I hope you appreciate it :-)
> I imagine a developer using our library should only have to deal with
> five classes:
>
> 1. Bot - that encapsulates an instance of a particular bot.
> 2. User - that encapsulates all the information about a particular user
> who is interacting with a "Bot"
> 3. Conversation - encapsulates and exchange between a particular "User"
> and a "Bot"
> 4. Request - encapsulates an single item of raw input from a "User" to a
> "Bot" as part of a "Conversation"
> 5 Response - encapsulates an item of output from a "Bot" to a "User" as
> part of a "Conversation".
The _smallest possible library_ that could be useful to a third party
is one offering:
@bot = Bot.new
@bot.configure('aiml_set')
response = @bot.respond_to(request)
where request.class == response.class == String
When you talk about Request, Response, Conversation, User of course
you are mixing in much more in the game.
I believe that we need to have all of those, but that the library must
be modular enough to let the user to the most elementary thing I wrote
above.
Do we agree on this?
> [...already discussed analogy...]
> I'll UMLify these relationships when I get some time later this week.
Not necessary for me. If also Ben is clear with the relations between
Bot/User/Conversation/Req/Res you can save the UML time.
It might be more direct to use small prototypes (like the one I sent before).
We can UML the key aspects later, once we all agree on the parts
naming and on the prog aspects.
Of course if you feel more comfortable UMLing int, it wouldn't harm
sharing the result :-)
> Now onto a quick example of how I hope a developer might use these
> classes, an improvisation in Ruby :-)
Nice!
> <code>
>
> myBot = Bot.new() # for default settings or:
> myBot = Bot.new(path) # where path is to a config.yaml file setting up
> the bot to non default settings
> myBot.ToYAML # bot's predicate list as YAML
----------------------------------------------------
I cut the long story short and introduce my_both (instead of myBot).
If any of you want the long story, just ask.
----------------------------------------------------
It is probably a good idea to have
my_both.configure(aiml_set)
or even
my_both.configure(configuration)
where
configuration = Configurator.new(:aiml_sets => [...], :master =>
'...', :name =>'...')
In this way it is easy to have the hot configuration.
This technique is used - among others - with chess engines: they do
not load all the info set as the game begins, but they load the
modules as they are necessary. If there are only Kings Rooks and Pawns
left, the RooksEndgames is loaded.
We can think to use the same: e.g. if the user talks of food, we load
the 'recipies.aiml'.
> # Attributes might include
> myBot.Name # name of the bot
> myBot.Botmaster # who the bot-master is
> myBot.Size # number of categories in the graphmaster
all right. The notation is my_bot.name.
> # loading AIML
> myBot.Learn(SomeRawAIML) # well formed obviously
> myBot.Load("pathToAIMLFile(s)") # if an individual AIML file is
> specified the file is loaded, if a directory all AIML files within it.
Fine, this answers (for the aiml part at least) to my point about hot
configuration.
BTW: the current pR should already act like that.
> # User class:
>
> myUser = User.new("Unique id") # where Unique id might be something like
> a PK in a database so information can be retrieved.
> myUser = User.new("Unique id", userconfig.yaml) # as above but with some
> yaml passed that includes user predicate table and / or conversation history
> myUser["Name"] # how to reference the user's predicates
> myUser.Conversations[0] # most recent conversation
> myUser.ToYAML # predicate list etc as YAML
Probably we want to have
PR::User.new
PR::User.new(:config => 'userconfig.yaml')
PR::User.save
PR::User.find(id)
In this way we offer a Active::Record compliant PR::User.
In order not to introduce unwanted dependencies, we could use a
transient DB (marshal or YAML based).
I keep it short because this mail is already pretty long. Probably we
can depthen the topic.
> # Conversation:
>
> c = Conversation.new(myUser, myBot) # create a new conversation session
> with the specified user and bot
Maybe not strictly necessary. The bot and the bot configuration might
depend on the User. It might be enough to have c =
Conversation.new(my_user)
> c.Request[0] # the first thing said to the bot in this conversation
> c.Response[0] # the first thing said to the user
> c.StartedOn # Timestamp
> c.TimeOut # a session timeout setting (any activity after this mount of
> time results in the session closing)
> c.Close # close the session
> c.Open # re-open a session
> c.Open? # is the session open?
It might be better to call it Session. It is so spontaneous...
> [... other code ...]
> </code>
> Nota Bene, the above code examples are off the top of my head and I'm
> not sure how Rubyesque they are...
Not a problem! Important is to throw the basis.
> Comments and suggestions would be most welcome. :-)
Done! :-)
Your turn to shoot!
Mauro
More information about the Aiml-programr-developers
mailing list